(Non-working) prototype for doing joins on unequal labels.

Allows you to do things like 'foo * on(bar==baz) blat'...

Signed-off-by: Tom Wilkie <tom@grafana.com>
join
Tom Wilkie 5 years ago
parent 820d7775eb
commit ac4594aaf4
No known key found for this signature in database
GPG Key ID: E32FC479235E15D8
  1. 18
      promql/ast.go
  2. 35
      promql/engine.go
  3. 34
      promql/generated_parser.y
  4. 700
      promql/generated_parser.y.go
  5. 6
      promql/lex.go
  6. 2
      promql/parse.go
  7. 4
      promql/printer.go

@ -232,7 +232,7 @@ type VectorMatching struct {
Card VectorMatchCardinality
// MatchingLabels contains the labels which define equality of a pair of
// elements from the Vectors.
MatchingLabels []string
MatchingLabels []*labels.Matcher
// On includes the given label names from matching,
// rather than excluding them.
On bool
@ -241,6 +241,22 @@ type VectorMatching struct {
Include []string
}
func justNames(in []*labels.Matcher) []string {
var result = make([]string, 0, len(in))
for _, i := range in {
result = append(result, i.Name)
}
return result
}
func justValues(in []*labels.Matcher) []string {
var result = make([]string, 0, len(in))
for _, i := range in {
result = append(result, i.Value)
}
return result
}
// Visitor allows visiting a Node and its child nodes. The Visit method is
// invoked for each node with the path leading to the node provided additionally.
// If the result visitor w is not nil and no error, Walk visits each of the children

@ -1519,18 +1519,19 @@ func (ev *evaluator) VectorAnd(lhs, rhs Vector, matching *VectorMatching, enh *E
if matching.Card != CardManyToMany {
panic("set operations must only use many-to-many matching")
}
sigf := enh.signatureFunc(matching.On, matching.MatchingLabels...)
sigfl := enh.signatureFunc(matching.On, justNames(matching.MatchingLabels)...)
sigfr := enh.signatureFunc(matching.On, justValues(matching.MatchingLabels)...)
// The set of signatures for the right-hand side Vector.
rightSigs := map[uint64]struct{}{}
// Add all rhs samples to a map so we can easily find matches later.
for _, rs := range rhs {
rightSigs[sigf(rs.Metric)] = struct{}{}
rightSigs[sigfr(rs.Metric)] = struct{}{}
}
for _, ls := range lhs {
// If there's a matching entry in the right-hand side Vector, add the sample.
if _, ok := rightSigs[sigf(ls.Metric)]; ok {
if _, ok := rightSigs[sigfl(ls.Metric)]; ok {
enh.out = append(enh.out, ls)
}
}
@ -1541,17 +1542,18 @@ func (ev *evaluator) VectorOr(lhs, rhs Vector, matching *VectorMatching, enh *Ev
if matching.Card != CardManyToMany {
panic("set operations must only use many-to-many matching")
}
sigf := enh.signatureFunc(matching.On, matching.MatchingLabels...)
sigfl := enh.signatureFunc(matching.On, justNames(matching.MatchingLabels)...)
sigfr := enh.signatureFunc(matching.On, justValues(matching.MatchingLabels)...)
leftSigs := map[uint64]struct{}{}
// Add everything from the left-hand-side Vector.
for _, ls := range lhs {
leftSigs[sigf(ls.Metric)] = struct{}{}
leftSigs[sigfl(ls.Metric)] = struct{}{}
enh.out = append(enh.out, ls)
}
// Add all right-hand side elements which have not been added from the left-hand side.
for _, rs := range rhs {
if _, ok := leftSigs[sigf(rs.Metric)]; !ok {
if _, ok := leftSigs[sigfr(rs.Metric)]; !ok {
enh.out = append(enh.out, rs)
}
}
@ -1562,15 +1564,16 @@ func (ev *evaluator) VectorUnless(lhs, rhs Vector, matching *VectorMatching, enh
if matching.Card != CardManyToMany {
panic("set operations must only use many-to-many matching")
}
sigf := enh.signatureFunc(matching.On, matching.MatchingLabels...)
sigfl := enh.signatureFunc(matching.On, justNames(matching.MatchingLabels)...)
sigfr := enh.signatureFunc(matching.On, justValues(matching.MatchingLabels)...)
rightSigs := map[uint64]struct{}{}
for _, rs := range rhs {
rightSigs[sigf(rs.Metric)] = struct{}{}
rightSigs[sigfr(rs.Metric)] = struct{}{}
}
for _, ls := range lhs {
if _, ok := rightSigs[sigf(ls.Metric)]; !ok {
if _, ok := rightSigs[sigfl(ls.Metric)]; !ok {
enh.out = append(enh.out, ls)
}
}
@ -1582,12 +1585,14 @@ func (ev *evaluator) VectorBinop(op ItemType, lhs, rhs Vector, matching *VectorM
if matching.Card == CardManyToMany {
panic("many-to-many only allowed for set operators")
}
sigf := enh.signatureFunc(matching.On, matching.MatchingLabels...)
sigfl := enh.signatureFunc(matching.On, justNames(matching.MatchingLabels)...)
sigfr := enh.signatureFunc(matching.On, justValues(matching.MatchingLabels)...)
// The control flow below handles one-to-one or many-to-one matching.
// For one-to-many, swap sidedness and account for the swap when calculating
// values.
if matching.Card == CardOneToMany {
sigfl, sigfr = sigfr, sigfl
lhs, rhs = rhs, lhs
}
@ -1603,7 +1608,7 @@ func (ev *evaluator) VectorBinop(op ItemType, lhs, rhs Vector, matching *VectorM
// Add all rhs samples to a map so we can easily find matches later.
for _, rs := range rhs {
sig := sigf(rs.Metric)
sig := sigfr(rs.Metric)
// The rhs is guaranteed to be the 'one' side. Having multiple samples
// with the same signature means that the matching is many-to-many.
if duplSample, found := rightSigs[sig]; found {
@ -1612,7 +1617,7 @@ func (ev *evaluator) VectorBinop(op ItemType, lhs, rhs Vector, matching *VectorM
if matching.Card == CardOneToMany {
oneSide = "left"
}
matchedLabels := rs.Metric.MatchLabels(matching.On, matching.MatchingLabels...)
matchedLabels := rs.Metric.MatchLabels(matching.On, justNames(matching.MatchingLabels)...)
// Many-to-many matching not allowed.
ev.errorf("found duplicate series for the match group %s on the %s hand-side of the operation: [%s, %s]"+
";many-to-many matching not allowed: matching labels must be unique on one side", matchedLabels.String(), oneSide, rs.Metric.String(), duplSample.Metric.String())
@ -1634,7 +1639,7 @@ func (ev *evaluator) VectorBinop(op ItemType, lhs, rhs Vector, matching *VectorM
// For all lhs samples find a respective rhs sample and perform
// the binary operation.
for _, ls := range lhs {
sig := sigf(ls.Metric)
sig := sigfl(ls.Metric)
rs, found := rightSigs[sig] // Look for a match in the rhs Vector.
if !found {
@ -1730,14 +1735,14 @@ func resultMetric(lhs, rhs labels.Labels, op ItemType, matching *VectorMatching,
Outer:
for _, l := range lhs {
for _, n := range matching.MatchingLabels {
if l.Name == n {
if l.Name == n.Name {
continue Outer
}
}
lb.Del(l.Name)
}
} else {
lb.Del(matching.MatchingLabels...)
lb.Del(justNames(matching.MatchingLabels)...)
}
}
for _, ln := range matching.Include {

@ -129,11 +129,13 @@ START_METRIC_SELECTOR
%type <matchers> label_match_list
%type <matcher> label_matcher
%type <item> aggregate_op grouping_label match_op maybe_label metric_identifier unary_op
%type <item> aggregate_op match_op maybe_label metric_identifier unary_op
%type <matcher> grouping_label
%type <labels> label_set label_set_list metric
%type <label> label_set_item
%type <strings> grouping_label_list grouping_labels maybe_grouping_labels
%type <matchers> grouping_label_list grouping_labels maybe_grouping_labels
%type <series> series_item series_values
%type <uint> uint
%type <float> number series_value signed_number
@ -206,13 +208,13 @@ aggregate_modifier:
BY grouping_labels
{
$$ = &AggregateExpr{
Grouping: $2,
Grouping: justNames($2),
}
}
| WITHOUT grouping_labels
{
$$ = &AggregateExpr{
Grouping: $2,
Grouping: justNames($2),
Without: true,
}
}
@ -276,13 +278,13 @@ group_modifiers: bool_modifier /* empty */
{
$$ = $1
$$.(*BinaryExpr).VectorMatching.Card = CardManyToOne
$$.(*BinaryExpr).VectorMatching.Include = $3
$$.(*BinaryExpr).VectorMatching.Include = justNames($3)
}
| on_or_ignoring GROUP_RIGHT maybe_grouping_labels
{
$$ = $1
$$.(*BinaryExpr).VectorMatching.Card = CardOneToMany
$$.(*BinaryExpr).VectorMatching.Include = $3
$$.(*BinaryExpr).VectorMatching.Include = justNames($3)
}
;
@ -292,7 +294,7 @@ grouping_labels : LEFT_PAREN grouping_label_list RIGHT_PAREN
| LEFT_PAREN grouping_label_list COMMA RIGHT_PAREN
{ $$ = $2 }
| LEFT_PAREN RIGHT_PAREN
{ $$ = []string{} }
{ $$ = []*labels.Matcher{} }
| error
{ yylex.(*parser).unexpected("grouping opts", "\"(\""); $$ = nil }
;
@ -300,9 +302,9 @@ grouping_labels : LEFT_PAREN grouping_label_list RIGHT_PAREN
grouping_label_list:
grouping_label_list COMMA grouping_label
{ $$ = append($1, $3.Val) }
{ $$ = append($1, $3) }
| grouping_label
{ $$ = []string{$1.Val} }
{ $$ = []*labels.Matcher{$1} }
| grouping_label_list error
{ yylex.(*parser).unexpected("grouping opts", "\",\" or \")\""); $$ = $1 }
;
@ -312,10 +314,20 @@ grouping_label : maybe_label
if !isLabel($1.Val) {
yylex.(*parser).unexpected("grouping opts", "label")
}
$$ = $1
$$ = yylex.(*parser).newLabelMatcher($1, Item{Typ:EQL}, $1.Quote());
}
| maybe_label EQL maybe_label
{
if !isLabel($1.Val) {
yylex.(*parser).unexpected("grouping opts", "label")
}
if !isLabel($3.Val) {
yylex.(*parser).unexpected("grouping opts", "label")
}
$$ = yylex.(*parser).newLabelMatcher($1, Item{Typ:EQL}, $3.Quote());
}
| error
{ yylex.(*parser).unexpected("grouping opts", "label"); $$ = Item{} }
{ yylex.(*parser).unexpected("grouping opts", "label"); $$ = nil }
;
/*

File diff suppressed because it is too large Load Diff

@ -15,6 +15,7 @@ package promql
import (
"fmt"
"strconv"
"strings"
"unicode"
"unicode/utf8"
@ -27,6 +28,11 @@ type Item struct {
Val string // The value of this Item.
}
func (i Item) Quote() Item {
i.Val = strconv.Quote(i.Val)
return i
}
// String returns a descriptive string for the Item.
func (i Item) String() string {
switch {

@ -490,7 +490,7 @@ func (p *parser) checkAST(node Node) (typ ValueType) {
for _, l1 := range n.VectorMatching.MatchingLabels {
for _, l2 := range n.VectorMatching.Include {
if l1 == l2 && n.VectorMatching.On {
if l1.Name == l2 && n.VectorMatching.On {
p.addParseErrf(opRange(), "label %q must not occur in ON and GROUP clause at once", l1)
}
}

@ -90,9 +90,9 @@ func (node *BinaryExpr) String() string {
vm := node.VectorMatching
if vm != nil && (len(vm.MatchingLabels) > 0 || vm.On) {
if vm.On {
matching = fmt.Sprintf(" on(%s)", strings.Join(vm.MatchingLabels, ", "))
matching = fmt.Sprintf(" on(%s)", strings.Join(justNames(vm.MatchingLabels), ", "))
} else {
matching = fmt.Sprintf(" ignoring(%s)", strings.Join(vm.MatchingLabels, ", "))
matching = fmt.Sprintf(" ignoring(%s)", strings.Join(justNames(vm.MatchingLabels), ", "))
}
if vm.Card == CardManyToOne || vm.Card == CardOneToMany {
matching += " group_"

Loading…
Cancel
Save