diff --git a/go.mod b/go.mod index 5e0591b4577..fa80cecf211 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/armon/go-metrics v0.4.1 github.com/aws/aws-sdk-go v1.55.6 github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 - github.com/cortexproject/promqlsmith v0.0.0-20250203072244-cbb5738d00ca + github.com/cortexproject/promqlsmith v0.0.0-20250407233056-90db95b1a4e4 github.com/dustin/go-humanize v1.0.1 github.com/efficientgo/core v1.0.0-rc.3 github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb diff --git a/go.sum b/go.sum index bf87d7b2663..01f463fd1b6 100644 --- a/go.sum +++ b/go.sum @@ -938,8 +938,8 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cortexproject/promqlsmith v0.0.0-20250203072244-cbb5738d00ca h1:TvKuPFRUQ39O07xv3b+TO6GBRhchYKyBCCXrlmmDE3Y= -github.com/cortexproject/promqlsmith v0.0.0-20250203072244-cbb5738d00ca/go.mod h1:xbYQa0KX6Eh6YWbTBfZ9kK3N4hRxX+ZPIfVIY2U/y00= +github.com/cortexproject/promqlsmith v0.0.0-20250407233056-90db95b1a4e4 h1:dpo7kQ24uFSV6Zgm9/kB34TIUWjGmadlbKrM6fNfQko= +github.com/cortexproject/promqlsmith v0.0.0-20250407233056-90db95b1a4e4/go.mod h1:jh6POgN18lXU133HBMfwr/1TjvBp8e5kL4ZtRsAPvGY= github.com/cortexproject/weaveworks-common v0.0.0-20241129212437-96019edf21f1 h1:UoSixdl0sBUhfEOMpIGxFnJjp3/y/+nkw6Du7su05FE= github.com/cortexproject/weaveworks-common v0.0.0-20241129212437-96019edf21f1/go.mod h1:7cl8fS/nivXe2DmBUUmr/3UGTJG2jVU2NRaIayR2Zjs= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= diff --git a/integration/query_fuzz_test.go b/integration/query_fuzz_test.go index 4d562727d0e..470859b72e3 100644 --- a/integration/query_fuzz_test.go +++ b/integration/query_fuzz_test.go @@ -397,7 +397,7 @@ func TestDisableChunkTrimmingFuzz(t *testing.T) { expr = ps.WalkRangeQuery() query = expr.Pretty(0) // timestamp is a known function that break with disable chunk trimming. - if isValidQuery(expr, 5, false) && !strings.Contains(query, "timestamp") { + if isValidQuery(expr, false) && !strings.Contains(query, "timestamp") { break } } @@ -566,7 +566,7 @@ func TestExpandedPostingsCacheFuzz(t *testing.T) { matchers := make([]string, 0, testRun) for i := 0; i < testRun; i++ { expr := ps.WalkRangeQuery() - if isValidQuery(expr, 5, true) { + if isValidQuery(expr, true) { break } queries = append(queries, expr.Pretty(0)) @@ -1731,7 +1731,7 @@ func runQueryFuzzTestCases(t *testing.T, ps *promqlsmith.PromQLSmith, c1, c2 *e2 for i := 0; i < run; i++ { for { expr = ps.WalkInstantQuery() - if isValidQuery(expr, 5, skipStdAggregations) { + if isValidQuery(expr, skipStdAggregations) { query = expr.Pretty(0) break } @@ -1752,7 +1752,7 @@ func runQueryFuzzTestCases(t *testing.T, ps *promqlsmith.PromQLSmith, c1, c2 *e2 for i := 0; i < run; i++ { for { expr = ps.WalkRangeQuery() - if isValidQuery(expr, 5, skipStdAggregations) { + if isValidQuery(expr, skipStdAggregations) { query = expr.Pretty(0) break } @@ -1803,9 +1803,8 @@ func shouldUseSampleNumComparer(query string) bool { return false } -func isValidQuery(generatedQuery parser.Expr, maxDepth int, skipStdAggregations bool) bool { +func isValidQuery(generatedQuery parser.Expr, skipStdAggregations bool) bool { isValid := true - currentDepth := 0 // TODO(SungJin1212): Test limitk, limit_ratio if strings.Contains(generatedQuery.String(), "limitk") { // current skip the limitk @@ -1820,13 +1819,5 @@ func isValidQuery(generatedQuery parser.Expr, maxDepth int, skipStdAggregations // If skipStdAggregations enabled, we skip to evaluate for stddev and stdvar aggregations. return false } - parser.Inspect(generatedQuery, func(node parser.Node, path []parser.Node) error { - if currentDepth > maxDepth { - isValid = false - return fmt.Errorf("generated query has exceeded maxDepth of %d", maxDepth) - } - currentDepth = len(path) + 1 - return nil - }) return isValid } diff --git a/vendor/github.com/cortexproject/promqlsmith/opts.go b/vendor/github.com/cortexproject/promqlsmith/opts.go index 1ab2efa9e7d..1c5a31c6561 100644 --- a/vendor/github.com/cortexproject/promqlsmith/opts.go +++ b/vendor/github.com/cortexproject/promqlsmith/opts.go @@ -15,6 +15,7 @@ var ( AggregateExpr, SubQueryExpr, CallExpr, + NumberLiteral, UnaryExpr, } @@ -85,6 +86,8 @@ type options struct { atModifierMaxTimestamp int64 enforceLabelMatchers []*labels.Matcher + + maxDepth int // Maximum depth of the query expression tree } func (o *options) applyDefaults() { @@ -112,6 +115,10 @@ func (o *options) applyDefaults() { if o.atModifierMaxTimestamp == 0 { o.atModifierMaxTimestamp = time.Now().UnixMilli() } + + if o.maxDepth == 0 { + o.maxDepth = 5 // Default max depth + } } // Option specifies options when generating queries. @@ -184,3 +191,10 @@ func WithEnforceLabelMatchers(matchers []*labels.Matcher) Option { o.enforceLabelMatchers = matchers }) } + +// WithMaxDepth sets the maximum depth for generated query expressions +func WithMaxDepth(depth int) Option { + return optionFunc(func(o *options) { + o.maxDepth = depth + }) +} diff --git a/vendor/github.com/cortexproject/promqlsmith/promqlsmith.go b/vendor/github.com/cortexproject/promqlsmith/promqlsmith.go index 184d939ae9d..a66c67d8af1 100644 --- a/vendor/github.com/cortexproject/promqlsmith/promqlsmith.go +++ b/vendor/github.com/cortexproject/promqlsmith/promqlsmith.go @@ -20,6 +20,18 @@ const ( UnaryExpr ) +// Add minimum depth requirements for each ExprType +var exprMinDepth = map[ExprType]int{ + VectorSelector: 1, + MatrixSelector: 1, // Needs one child expression but we consider 1 depth + AggregateExpr: 2, // Needs at least one child expression + BinaryExpr: 2, // Needs two child expressions but whichever higher + SubQueryExpr: 2, // Needs one child expression + CallExpr: 2, // Needs at least one argument + NumberLiteral: 1, + UnaryExpr: 2, // Needs one child expression +} + var ( valueTypeToExprsMap = map[parser.ValueType][]ExprType{ parser.ValueTypeVector: {VectorSelector, BinaryExpr, AggregateExpr, CallExpr, UnaryExpr}, @@ -45,6 +57,7 @@ type PromQLSmith struct { enableVectorMatching bool enableExperimentalPromQL bool atModifierMaxTimestamp int64 + maxDepth int seriesSet []labels.Labels labelNames []string @@ -78,6 +91,7 @@ func New(rnd *rand.Rand, seriesSet []labels.Labels, opts ...Option) *PromQLSmith enableVectorMatching: options.enableVectorMatching, enableExperimentalPromQL: options.enableExperimentalPromQLFunctions, enforceMatchers: options.enforceLabelMatchers, + maxDepth: options.maxDepth, } ps.labelNames, ps.labelValues = labelNameAndValuesFromLabelSet(seriesSet) return ps @@ -99,14 +113,62 @@ func (s *PromQLSmith) WalkSelectors() []*labels.Matcher { return s.walkSelectors() } +// intersectExprTypes returns the intersection of two ExprType slices +func intersectExprTypes(a, b []ExprType) []ExprType { + result := make([]ExprType, 0) + for _, expr := range a { + for _, other := range b { + if expr == other { + result = append(result, expr) + break + } + } + } + return result +} + // Walk will walk the ast tree using one of the randomly generated expr type. func (s *PromQLSmith) Walk(valueTypes ...parser.ValueType) parser.Expr { + return s.walk(s.maxDepth, valueTypes...) +} + +// filterNumberLiteral removes NumberLiteral from validExprs unless it's the only option +func filterNumberLiteral(validExprs []ExprType) []ExprType { + if len(validExprs) <= 1 { + return validExprs + } + + filtered := make([]ExprType, 0, len(validExprs)) + for _, expr := range validExprs { + if expr != NumberLiteral { + filtered = append(filtered, expr) + } + } + return filtered +} + +func (s *PromQLSmith) walk(depth int, valueTypes ...parser.ValueType) parser.Expr { supportedExprs := s.supportedExprs if len(valueTypes) > 0 { - supportedExprs = exprsFromValueTypes(valueTypes) + supportedExprs = intersectExprTypes(supportedExprs, exprsFromValueTypes(valueTypes)) + } + + // Filter expressions based on remaining depth + validExprs := make([]ExprType, 0, len(supportedExprs)) + for _, expr := range supportedExprs { + if minDepth := exprMinDepth[expr]; depth >= minDepth { + validExprs = append(validExprs, expr) + } } - e := supportedExprs[s.rnd.Intn(len(supportedExprs))] - expr, _ := s.walkExpr(e, valueTypes...) + + // Return nil if no valid expressions are available + if len(validExprs) == 0 { + return nil + } + + validExprs = filterNumberLiteral(validExprs) + e := validExprs[s.rnd.Intn(len(validExprs))] + expr, _ := s.walkExpr(e, depth, valueTypes...) return expr } diff --git a/vendor/github.com/cortexproject/promqlsmith/walk.go b/vendor/github.com/cortexproject/promqlsmith/walk.go index ae83d19db80..6732b364b19 100644 --- a/vendor/github.com/cortexproject/promqlsmith/walk.go +++ b/vendor/github.com/cortexproject/promqlsmith/walk.go @@ -24,13 +24,13 @@ const ( // walkExpr generates the given expression type with one of the required value type. // valueTypes is only used for expressions that could have multiple possible return value types. -func (s *PromQLSmith) walkExpr(e ExprType, valueTypes ...parser.ValueType) (parser.Expr, error) { +func (s *PromQLSmith) walkExpr(e ExprType, depth int, valueTypes ...parser.ValueType) (parser.Expr, error) { switch e { case AggregateExpr: - return s.walkAggregateExpr(), nil + return s.walkAggregateExpr(depth), nil case BinaryExpr: // Wrap binary expression with paren for readability. - return wrapParenExpr(s.walkBinaryExpr(valueTypes...)), nil + return wrapParenExpr(s.walkBinaryExpr(depth, valueTypes...)), nil case SubQueryExpr: return s.walkSubQueryExpr(), nil case MatrixSelector: @@ -38,25 +38,25 @@ func (s *PromQLSmith) walkExpr(e ExprType, valueTypes ...parser.ValueType) (pars case VectorSelector: return s.walkVectorSelector(s.enableAtModifier), nil case CallExpr: - return s.walkCall(valueTypes...), nil + return s.walkCall(depth, valueTypes...), nil case NumberLiteral: return s.walkNumberLiteral(), nil case UnaryExpr: - return s.walkUnaryExpr(valueTypes...), nil + return s.walkUnaryExpr(depth, valueTypes...), nil default: return nil, fmt.Errorf("unsupported ExprType %d", e) } } -func (s *PromQLSmith) walkAggregateExpr() parser.Expr { +func (s *PromQLSmith) walkAggregateExpr(depth int) parser.Expr { expr := &parser.AggregateExpr{ Op: s.supportedAggrs[s.rnd.Intn(len(s.supportedAggrs))], Without: s.rnd.Int()%2 == 0, - Expr: s.Walk(parser.ValueTypeVector), + Expr: s.walk(depth-1, parser.ValueTypeVector), Grouping: s.walkGrouping(), } if expr.Op.IsAggregatorWithParam() { - expr.Param = s.walkAggregateParam(expr.Op) + expr.Param = s.walkAggregateParam(expr.Op, depth-1) } return expr } @@ -76,16 +76,18 @@ func (s *PromQLSmith) walkGrouping() []string { return grouping } -func (s *PromQLSmith) walkAggregateParam(op parser.ItemType) parser.Expr { +func (s *PromQLSmith) walkAggregateParam(op parser.ItemType, depth int) parser.Expr { switch op { case parser.TOPK, parser.BOTTOMK: - return s.Walk(parser.ValueTypeScalar) + // s.walk prefers generating non-NumberLiteral for scalar. + // To simplify generated queries we hardcode number literal here. + return &parser.NumberLiteral{Val: float64(s.rnd.Intn(5) + 1)} case parser.QUANTILE: - return s.Walk(parser.ValueTypeScalar) + return s.walk(depth, parser.ValueTypeScalar) case parser.COUNT_VALUES: return &parser.StringLiteral{Val: "value"} case parser.LIMITK, parser.LIMIT_RATIO: - return s.Walk(parser.ValueTypeScalar) + return s.walk(depth, parser.ValueTypeScalar) } return nil } @@ -93,7 +95,7 @@ func (s *PromQLSmith) walkAggregateParam(op parser.ItemType) parser.Expr { // Can only do binary expression between vector and scalar. So any expression // that returns matrix doesn't work like matrix selector, subquery // or function that returns matrix. -func (s *PromQLSmith) walkBinaryExpr(valueTypes ...parser.ValueType) parser.Expr { +func (s *PromQLSmith) walkBinaryExpr(depth int, valueTypes ...parser.ValueType) parser.Expr { valueTypes = keepValueTypes(valueTypes, vectorAndScalarValueTypes) expr := &parser.BinaryExpr{ Op: s.walkBinaryOp(!slices.Contains(valueTypes, parser.ValueTypeVector)), @@ -106,20 +108,14 @@ func (s *PromQLSmith) walkBinaryExpr(valueTypes ...parser.ValueType) parser.Expr valueTypes = []parser.ValueType{parser.ValueTypeVector} expr.VectorMatching.Card = parser.CardManyToMany } - expr.LHS = wrapParenExpr(s.Walk(valueTypes...)) - expr.RHS = wrapParenExpr(s.Walk(valueTypes...)) - lvt := expr.LHS.Type() - rvt := expr.RHS.Type() - // ReturnBool can only be set for comparison operator. It is - // required to set to true if both expressions are scalar type. - if expr.Op.IsComparisonOperator() { - if lvt == parser.ValueTypeScalar && rvt == parser.ValueTypeScalar || s.rnd.Intn(2) == 0 { - expr.ReturnBool = true - } - } - if !expr.Op.IsSetOperator() && s.enableVectorMatching && lvt == parser.ValueTypeVector && - rvt == parser.ValueTypeVector && s.rnd.Intn(2) == 0 { + // Generate vector matching only if we know it asks for vector value type. + if !expr.Op.IsSetOperator() && len(valueTypes) == 1 && valueTypes[0] == parser.ValueTypeVector && s.enableVectorMatching && s.rnd.Float64() > 0.8 { + lhs, _ := s.walkExpr(VectorSelector, depth-1, valueTypes...) + expr.LHS = wrapParenExpr(lhs) + rhs, _ := s.walkExpr(VectorSelector, depth-1, valueTypes...) + expr.RHS = wrapParenExpr(rhs) + leftSeriesSet, stop := getOutputSeries(expr.LHS) if stop { return expr @@ -128,12 +124,25 @@ func (s *PromQLSmith) walkBinaryExpr(valueTypes ...parser.ValueType) parser.Expr if stop { return expr } - s.walkVectorMatching(expr, leftSeriesSet, rightSeriesSet, s.rnd.Intn(4) == 0) + s.walkVectorMatching(expr, leftSeriesSet, rightSeriesSet, s.rnd.Intn(2) == 0, s.rnd.Intn(4) == 0) + } else { + expr.LHS = wrapParenExpr(s.walk(depth-1, valueTypes...)) + expr.RHS = wrapParenExpr(s.walk(depth-1, valueTypes...)) + } + + lvt := expr.LHS.Type() + rvt := expr.RHS.Type() + // ReturnBool can only be set for comparison operator. It is + // required to set to true if both expressions are scalar type. + if expr.Op.IsComparisonOperator() { + if lvt == parser.ValueTypeScalar && rvt == parser.ValueTypeScalar || s.rnd.Intn(2) == 0 { + expr.ReturnBool = true + } } return expr } -func (s *PromQLSmith) walkVectorMatching(expr *parser.BinaryExpr, seriesSetA []labels.Labels, seriesSetB []labels.Labels, includeLabels bool) { +func (s *PromQLSmith) walkVectorMatching(expr *parser.BinaryExpr, seriesSetA []labels.Labels, seriesSetB []labels.Labels, on, includeLabels bool) { sa := make(map[string]struct{}) for _, series := range seriesSetA { series.Range(func(lbl labels.Label) { @@ -153,48 +162,129 @@ func (s *PromQLSmith) walkVectorMatching(expr *parser.BinaryExpr, seriesSetA []l sb[lbl.Name] = struct{}{} }) } - expr.VectorMatching.On = true - matchedLabels := make([]string, 0) + + // Find all matching labels + allMatchedLabels := make([]string, 0) for key := range sb { if _, ok := sa[key]; ok { - matchedLabels = append(matchedLabels, key) + allMatchedLabels = append(allMatchedLabels, key) } } + // If there is no matching labels, we don't need to do vector matching. + if len(allMatchedLabels) == 0 { + return + } + + // Randomly select a subset of matched labels + sort.Strings(allMatchedLabels) // Sort for deterministic selection + numLabels := s.rnd.Intn(len(allMatchedLabels)) + 1 // Select at least 1 label + selectedIndices := s.rnd.Perm(len(allMatchedLabels))[:numLabels] + sort.Ints(selectedIndices) // Sort indices for consistent order + + matchedLabels := make([]string, numLabels) + for i, idx := range selectedIndices { + matchedLabels[i] = allMatchedLabels[idx] + } + + expr.VectorMatching.On = on + // We are doing a very naive approach of guessing side cardinalities // by checking number of series each side. oneSideLabelsSet := sa - if len(seriesSetA) > len(seriesSetB) { + if expr.VectorMatching.On { expr.VectorMatching.MatchingLabels = matchedLabels + } else { + // For 'ignoring', we need to use all labels except the matched ones + expr.VectorMatching.MatchingLabels = getDifference(getAllLabels(sa), matchedLabels) + } + + if len(seriesSetA) > len(seriesSetB) { expr.VectorMatching.Card = parser.CardManyToOne oneSideLabelsSet = sb } else if len(seriesSetA) < len(seriesSetB) { - expr.VectorMatching.MatchingLabels = matchedLabels expr.VectorMatching.Card = parser.CardOneToMany } + // Otherwise we do 1:1 match. - // For simplicity, we always include all labels on the one side. if expr.VectorMatching.Card != parser.CardOneToOne && includeLabels { - includeLabels := getIncludeLabels(oneSideLabelsSet, matchedLabels) + includeLabels := getRandomIncludeLabels(s.rnd, oneSideLabelsSet, expr.VectorMatching.MatchingLabels) expr.VectorMatching.Include = includeLabels } } +// Helper function to get all labels from a map +func getAllLabels(labelSet map[string]struct{}) []string { + labels := make([]string, 0, len(labelSet)) + for label := range labelSet { + labels = append(labels, label) + } + sort.Strings(labels) + return labels +} + +// Helper function to get the difference between two sorted string slices +func getDifference(all, exclude []string) []string { + result := make([]string, 0) + excludeMap := make(map[string]struct{}) + for _, e := range exclude { + excludeMap[e] = struct{}{} + } + + for _, label := range all { + if _, exists := excludeMap[label]; !exists { + result = append(result, label) + } + } + return result +} + +// Helper function to get all eligible labels that aren't in the matched set func getIncludeLabels(labelNameSet map[string]struct{}, matchedLabels []string) []string { + // Create a map of matched labels for quick lookup + matchedSet := make(map[string]struct{}) + for _, label := range matchedLabels { + matchedSet[label] = struct{}{} + } + + // Collect all eligible labels that aren't in the matched set output := make([]string, 0) -OUTER: for lbl := range labelNameSet { - for _, matchedLabel := range matchedLabels { - if lbl == matchedLabel { - continue OUTER - } + if _, matched := matchedSet[lbl]; !matched { + output = append(output, lbl) } - output = append(output, lbl) } + + // Sort for deterministic output sort.Strings(output) return output } +// Helper function to randomly select a subset of include labels +func getRandomIncludeLabels(rnd *rand.Rand, labelNameSet map[string]struct{}, matchedLabels []string) []string { + eligible := getIncludeLabels(labelNameSet, matchedLabels) + if len(eligible) == 0 { + return nil + } + + // Pick a random number of labels to include (at least 1 if available) + numLabels := rnd.Intn(len(eligible)) + 1 + if numLabels > len(eligible) { + numLabels = len(eligible) + } + + // Randomly select the labels + indices := rnd.Perm(len(eligible))[:numLabels] + sort.Ints(indices) + + // Create the final selection + result := make([]string, numLabels) + for i, idx := range indices { + result[i] = eligible[idx] + } + return result +} + // Walk binary op based on whether vector value type is allowed or not. // Since Set operator only works with vector so if vector is disallowed // we will choose comparison operator that works both for scalar and vector. @@ -232,7 +322,7 @@ func (s *PromQLSmith) walkSubQueryExpr() parser.Expr { return expr } -func (s *PromQLSmith) walkCall(valueTypes ...parser.ValueType) parser.Expr { +func (s *PromQLSmith) walkCall(depth int, valueTypes ...parser.ValueType) parser.Expr { expr := &parser.Call{} funcs := s.supportedFuncs @@ -250,49 +340,49 @@ func (s *PromQLSmith) walkCall(valueTypes ...parser.ValueType) parser.Expr { } sort.Slice(funcs, func(i, j int) bool { return strings.Compare(funcs[i].Name, funcs[j].Name) < 0 }) expr.Func = funcs[s.rnd.Intn(len(funcs))] - s.walkFunctions(expr) + s.walkFunctions(expr, depth) return expr } -func (s *PromQLSmith) walkFunctions(expr *parser.Call) { +func (s *PromQLSmith) walkFunctions(expr *parser.Call, depth int) { switch expr.Func.Name { case "label_join": - s.walkLabelJoin(expr) + s.walkLabelJoin(expr, depth) return case "sort_by_label", "sort_by_label_desc": - s.walkSortByLabel(expr) + s.walkSortByLabel(expr, depth) return default: } expr.Args = make([]parser.Expr, len(expr.Func.ArgTypes)) if expr.Func.Name == "holt_winters" { - s.walkHoltWinters(expr) + s.walkHoltWinters(expr, depth) return } else if expr.Func.Name == "label_replace" { - s.walkLabelReplace(expr) + s.walkLabelReplace(expr, depth) return } else if expr.Func.Name == "info" { - s.walkInfo(expr) + s.walkInfo(expr, depth) return } if expr.Func.Variadic != 0 { - s.walkVariadicFunctions(expr) + s.walkVariadicFunctions(expr, depth) return } for i, arg := range expr.Func.ArgTypes { - expr.Args[i] = s.Walk(arg) + expr.Args[i] = s.walk(depth-1, arg) } } -func (s *PromQLSmith) walkHoltWinters(expr *parser.Call) { - expr.Args[0] = s.Walk(expr.Func.ArgTypes[0]) +func (s *PromQLSmith) walkHoltWinters(expr *parser.Call, depth int) { + expr.Args[0] = s.walk(depth-1, expr.Func.ArgTypes[0]) expr.Args[1] = &parser.NumberLiteral{Val: getNonZeroFloat64(s.rnd)} expr.Args[2] = &parser.NumberLiteral{Val: getNonZeroFloat64(s.rnd)} } -func (s *PromQLSmith) walkInfo(expr *parser.Call) { - expr.Args[0] = s.Walk(expr.Func.ArgTypes[0]) +func (s *PromQLSmith) walkInfo(expr *parser.Call, depth int) { + expr.Args[0] = s.walk(depth-1, expr.Func.ArgTypes[0]) if s.rnd.Int()%2 == 0 { // skip second parameter expr.Args = expr.Args[:1] @@ -301,8 +391,8 @@ func (s *PromQLSmith) walkInfo(expr *parser.Call) { } } -func (s *PromQLSmith) walkLabelReplace(expr *parser.Call) { - expr.Args[0] = s.Walk(expr.Func.ArgTypes[0]) +func (s *PromQLSmith) walkLabelReplace(expr *parser.Call, depth int) { + expr.Args[0] = s.walk(depth-1, expr.Func.ArgTypes[0]) expr.Args[1] = &parser.StringLiteral{Val: destinationLabel} expr.Args[2] = &parser.StringLiteral{Val: "$1"} seriesSet, _ := getOutputSeries(expr.Args[0]) @@ -332,9 +422,9 @@ func (s *PromQLSmith) walkLabelReplace(expr *parser.Call) { expr.Args[4] = &parser.StringLiteral{Val: "(.*)"} } -func (s *PromQLSmith) walkSortByLabel(expr *parser.Call) { +func (s *PromQLSmith) walkSortByLabel(expr *parser.Call, depth int) { expr.Args = make([]parser.Expr, 0, len(expr.Func.ArgTypes)) - expr.Args = append(expr.Args, s.Walk(expr.Func.ArgTypes[0])) + expr.Args = append(expr.Args, s.walk(depth-1, expr.Func.ArgTypes[0])) seriesSet, _ := getOutputSeries(expr.Args[0]) // Let's try to not sort more than 1 label for simplicity. @@ -364,9 +454,9 @@ func (s *PromQLSmith) walkSortByLabel(expr *parser.Call) { } } -func (s *PromQLSmith) walkLabelJoin(expr *parser.Call) { +func (s *PromQLSmith) walkLabelJoin(expr *parser.Call, depth int) { expr.Args = make([]parser.Expr, 0, len(expr.Func.ArgTypes)) - expr.Args = append(expr.Args, s.Walk(expr.Func.ArgTypes[0])) + expr.Args = append(expr.Args, s.walk(depth-1, expr.Func.ArgTypes[0])) seriesSet, _ := getOutputSeries(expr.Args[0]) expr.Args = append(expr.Args, &parser.StringLiteral{Val: destinationLabel}) expr.Args = append(expr.Args, &parser.StringLiteral{Val: ","}) @@ -402,16 +492,16 @@ func (s *PromQLSmith) walkLabelJoin(expr *parser.Call) { // hour, minute, month, round. // Unsupported variadic functions include: // label_join, sort_by_label_desc, sort_by_label -func (s *PromQLSmith) walkVariadicFunctions(expr *parser.Call) { +func (s *PromQLSmith) walkVariadicFunctions(expr *parser.Call, depth int) { switch expr.Func.Name { case "round": - expr.Args[0] = s.Walk(expr.Func.ArgTypes[0]) + expr.Args[0] = s.walk(depth-1, expr.Func.ArgTypes[0]) expr.Args[1] = &parser.NumberLiteral{Val: float64(s.rnd.Intn(10))} default: // Rest of supported functions have either 0 or 1 function argument. // If not specified it uses current timestamp instead of the vector timestamp. // To reduce test flakiness we always use vector timestamp. - expr.Args[0] = s.Walk(expr.Func.ArgTypes[0]) + expr.Args[0] = s.walk(depth-1, expr.Func.ArgTypes[0]) } } @@ -673,12 +763,12 @@ func (s *PromQLSmith) walkMatrixSelector() parser.Expr { } // Only vector and scalar result is allowed. -func (s *PromQLSmith) walkUnaryExpr(valueTypes ...parser.ValueType) parser.Expr { +func (s *PromQLSmith) walkUnaryExpr(depth int, valueTypes ...parser.ValueType) parser.Expr { expr := &parser.UnaryExpr{ Op: parser.SUB, } valueTypes = keepValueTypes(valueTypes, vectorAndScalarValueTypes) - expr.Expr = s.Walk(valueTypes...) + expr.Expr = s.walk(depth-1, valueTypes...) return expr } @@ -851,6 +941,6 @@ func getOutputSeries(expr parser.Expr) ([]labels.Labels, bool) { return lbls, stop } -func randRange(min, max int) int { - return rand.Intn(max-min) + min +func randRange(low, high int) int { + return rand.Intn(high-low) + low } diff --git a/vendor/modules.txt b/vendor/modules.txt index f48ec73838b..57bdb1d2897 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -304,8 +304,8 @@ github.com/coreos/go-semver/semver ## explicit; go 1.12 github.com/coreos/go-systemd/v22/activation github.com/coreos/go-systemd/v22/journal -# github.com/cortexproject/promqlsmith v0.0.0-20250203072244-cbb5738d00ca -## explicit; go 1.22.0 +# github.com/cortexproject/promqlsmith v0.0.0-20250407233056-90db95b1a4e4 +## explicit; go 1.24.0 github.com/cortexproject/promqlsmith # github.com/cristalhq/hedgedhttp v0.9.1 ## explicit; go 1.16