From ac4594aaf49474ea9466eb449111df089d27f123 Mon Sep 17 00:00:00 2001 From: Tom Wilkie Date: Sun, 2 Feb 2020 13:17:06 +0100 Subject: [PATCH] (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 --- promql/ast.go | 18 +- promql/engine.go | 35 +- promql/generated_parser.y | 34 +- promql/generated_parser.y.go | 710 ++++++++++++++++++----------------- promql/lex.go | 6 + promql/parse.go | 2 +- promql/printer.go | 4 +- 7 files changed, 432 insertions(+), 377 deletions(-) diff --git a/promql/ast.go b/promql/ast.go index 803c9892eb..17226c695b 100644 --- a/promql/ast.go +++ b/promql/ast.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 diff --git a/promql/engine.go b/promql/engine.go index 0add9e1473..a24f819b82 100644 --- a/promql/engine.go +++ b/promql/engine.go @@ -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 { diff --git a/promql/generated_parser.y b/promql/generated_parser.y index 0cb98df755..4b0de8135a 100644 --- a/promql/generated_parser.y +++ b/promql/generated_parser.y @@ -129,11 +129,13 @@ START_METRIC_SELECTOR %type label_match_list %type label_matcher -%type aggregate_op grouping_label match_op maybe_label metric_identifier unary_op +%type aggregate_op match_op maybe_label metric_identifier unary_op + +%type grouping_label %type label_set label_set_list metric %type