diff --git a/runtime/pkg/metricssql/parser.go b/runtime/pkg/metricssql/parser.go index 46a37d2251d..9438cc76448 100644 --- a/runtime/pkg/metricssql/parser.go +++ b/runtime/pkg/metricssql/parser.go @@ -15,7 +15,6 @@ import ( runtimev1 "github.com/rilldata/rill/proto/gen/rill/runtime/v1" "github.com/rilldata/rill/runtime" "github.com/rilldata/rill/runtime/drivers" - "golang.org/x/exp/maps" // need to import parser driver as well _ "github.com/pingcap/tidb/pkg/parser/test_driver" @@ -68,11 +67,9 @@ func (c *Compiler) Compile(ctx context.Context, sql string) (string, string, []* } t := &transformer{ - controller: c.controller, - instanceID: c.instanceID, - userAttributes: c.userAttributes, - dimExprToCol: make(map[string]string), - measureExprToCol: make(map[string]string), + controller: c.controller, + instanceID: c.instanceID, + userAttributes: c.userAttributes, } compiledSQL, err := t.transformSelectStmt(ctx, stmt) if err != nil { @@ -92,11 +89,6 @@ type transformer struct { connector string dimsToExpr map[string]string measureToExpr map[string]string - - // resolved dimension expression to dimension name - dimExprToCol map[string]string - // resolved measure expression to measure name - measureExprToCol map[string]string } func (t *transformer) transformSelectStmt(ctx context.Context, node *ast.SelectStmt) (string, error) { @@ -111,7 +103,7 @@ func (t *transformer) transformSelectStmt(ctx context.Context, node *ast.SelectS } // parse from clause - if node.From.TableRefs == nil { + if node.From == nil || node.From.TableRefs == nil { return "", fmt.Errorf("metrics sql: need from clause") } @@ -122,7 +114,7 @@ func (t *transformer) transformSelectStmt(ctx context.Context, node *ast.SelectS return "", err } - selectList, err := t.transformSelectStmtColumns(ctx, node) + selectList, groupByClause, err := t.transformSelectStmtColumns(ctx, node) if err != nil { return "", err } @@ -132,32 +124,23 @@ func (t *transformer) transformSelectStmt(ctx context.Context, node *ast.SelectS sb.WriteString(fromClause) if node.Where != nil { - where, err := t.transformExprNode(ctx, node.Where, false) + where, err := t.transformExprNode(ctx, node.Where) if err != nil { return "", err } sb.WriteString(" WHERE ") - sb.WriteString(where) + sb.WriteString(where.expr) } - if len(t.measureExprToCol) > 0 && len(t.dimExprToCol) > 0 { - if node.GroupBy != nil { - return "", fmt.Errorf("metrics sql: group by clause is implicitly added when any measure is selected. The implicit group by includes all selected dimensions") - } - + if node.GroupBy != nil { + return "", fmt.Errorf("metrics sql: Explicit group by clause is not supported. Group by clause is implicitly added when both measure and dimensions are selected. The implicit group by includes all selected dimensions") + } + if groupByClause != "" { sb.WriteString(" GROUP BY ") - dimExprList := maps.Keys(t.dimExprToCol) - // sort so that consistent group by sql is produced - slices.Sort(dimExprList) - for i, expr := range dimExprList { - if i != 0 { - sb.WriteString(", ") - } - sb.WriteString(expr) - } + sb.WriteString(groupByClause) } if node.Having != nil { - having, err := t.transformHavingClause(ctx, node.Having, false) + having, err := t.transformHavingClause(ctx, node.Having) if err != nil { return "", err } @@ -166,7 +149,7 @@ func (t *transformer) transformSelectStmt(ctx context.Context, node *ast.SelectS } if node.OrderBy != nil { - orderBy, err := t.transformOrderByClause(ctx, node.OrderBy, false) + orderBy, err := t.transformOrderByClause(ctx, node.OrderBy) if err != nil { return "", err } @@ -175,7 +158,7 @@ func (t *transformer) transformSelectStmt(ctx context.Context, node *ast.SelectS } if node.Limit != nil { - limit, err := t.transformLimitClause(ctx, node.Limit, false) + limit, err := t.transformLimitClause(ctx, node.Limit) if err != nil { return "", err } @@ -185,12 +168,14 @@ func (t *transformer) transformSelectStmt(ctx context.Context, node *ast.SelectS return sb.String(), nil } -func (t *transformer) transformSelectStmtColumns(ctx context.Context, node *ast.SelectStmt) (string, error) { +func (t *transformer) transformSelectStmtColumns(ctx context.Context, node *ast.SelectStmt) (string, string, error) { if len(node.Fields.Fields) == 0 { - return "", fmt.Errorf("metrics sql: need to select atleast one dimension or measure") + return "", "", fmt.Errorf("metrics sql: need to select atleast one dimension or measure") } var sb strings.Builder + var groupByList []string + var hasMeasure bool for i, field := range node.Fields.Fields { if i != 0 { sb.WriteString(", ") @@ -198,35 +183,43 @@ func (t *transformer) transformSelectStmtColumns(ctx context.Context, node *ast. sb.WriteString("DISTINCT ") } if field.WildCard != nil { - return "", fmt.Errorf("metrics sql: wildcard is not supported") + return "", "", fmt.Errorf("metrics sql: wildcard is not supported") } - expr, err := t.transformExprNode(ctx, field.Expr, true) + res, err := t.transformExprNode(ctx, field.Expr) if err != nil { - return "", err + return "", "", err } - sb.WriteString(expr) + sb.WriteString(res.expr) // write alias if any if field.AsName.String() != "" { // if explicitly specified in the metrics_sql sb.WriteString(" AS \"") sb.WriteString(field.AsName.String()) sb.WriteString("\"") - } else { - var name string - // selecting a plain dimension or measure adds dimension/measure name as alias - if col, ok := t.dimExprToCol[expr]; ok { - name = col - } else if col, ok := t.measureExprToCol[expr]; ok { - name = col + } else if _, ok := field.Expr.(*ast.ColumnNameExpr); ok { // plain dimension or measure + sb.WriteString(" AS ") + sb.WriteString(res.columns[0]) + } + if len(res.types) > 0 { + for i := 1; i < len(res.types); i++ { + if res.types[i] != res.types[0] { + return "", "", fmt.Errorf("metrics sql: operations combining measure and dimension is not supported in select field: %v", restore(field)) + } + } + + if res.types[0] == "DIMENSION" { + groupByList = append(groupByList, res.expr) } else { - continue + hasMeasure = true } - sb.WriteString(" AS ") - sb.WriteString(name) } } - return sb.String(), nil + if hasMeasure && len(groupByList) > 0 { + slices.Sort(groupByList) + return sb.String(), strings.Join(groupByList, ", "), nil + } + return sb.String(), "", nil } func (t *transformer) transformFromClause(ctx context.Context, node *ast.TableRefsClause) (string, error) { @@ -261,90 +254,91 @@ func (t *transformer) transformFromClause(ctx context.Context, node *ast.TableRe return t.fromQueryForMetricsView(ctx, mv) } -func (t *transformer) transformHavingClause(ctx context.Context, node *ast.HavingClause, updateSelect bool) (string, error) { - return t.transformExprNode(ctx, node.Expr, updateSelect) +func (t *transformer) transformHavingClause(ctx context.Context, node *ast.HavingClause) (string, error) { + res, err := t.transformExprNode(ctx, node.Expr) + if err != nil { + return "", err + } + return res.expr, nil } -func (t *transformer) transformOrderByClause(ctx context.Context, node *ast.OrderByClause, updateSelect bool) (string, error) { +func (t *transformer) transformOrderByClause(ctx context.Context, node *ast.OrderByClause) (string, error) { var sb strings.Builder for i, item := range node.Items { if i != 0 { sb.WriteString(", ") } - expr, err := t.transformExprNode(ctx, item.Expr, updateSelect) + expr, err := t.transformExprNode(ctx, item.Expr) if err != nil { return "", err } if item.Desc { - sb.WriteString(expr + " DESC") + sb.WriteString(expr.expr + " DESC") } else { - sb.WriteString(expr + " ASC") + sb.WriteString(expr.expr + " ASC") } } return sb.String(), nil } -func (t *transformer) transformLimitClause(ctx context.Context, node *ast.Limit, updateSelect bool) (string, error) { - count, err := t.transformExprNode(ctx, node.Count, updateSelect) +func (t *transformer) transformLimitClause(ctx context.Context, node *ast.Limit) (string, error) { + count, err := t.transformExprNode(ctx, node.Count) if err != nil { return "", err } var sb strings.Builder - sb.WriteString(count) + sb.WriteString(count.expr) if node.Offset != nil { - offset, err := t.transformExprNode(ctx, node.Offset, updateSelect) + offset, err := t.transformExprNode(ctx, node.Offset) if err != nil { return "", err } sb.WriteString(" OFFSET ") - sb.WriteString(offset) + sb.WriteString(offset.expr) } return sb.String(), nil } -func (t *transformer) transformExprNode(ctx context.Context, node ast.ExprNode, updateSelect bool) (string, error) { +func (t *transformer) transformExprNode(ctx context.Context, node ast.ExprNode) (exprResult, error) { switch node := node.(type) { case *ast.ColumnNameExpr: - return t.transformColumnNameExpr(ctx, node, updateSelect) + return t.transformColumnNameExpr(ctx, node) case *ast.BinaryOperationExpr: - return t.transformBinaryOperationExpr(ctx, node, updateSelect) + return t.transformBinaryOperationExpr(ctx, node) case *ast.IsNullExpr: - return t.transformIsNullOperationExpr(ctx, node, updateSelect) + return t.transformIsNullOperationExpr(ctx, node) case *ast.IsTruthExpr: - return t.transformIsTruthOperationExpr(ctx, node, updateSelect) + return t.transformIsTruthOperationExpr(ctx, node) case *ast.ParenthesesExpr: - return t.transformParenthesesExpr(ctx, node, updateSelect) + return t.transformParenthesesExpr(ctx, node) case *ast.PatternInExpr: - return t.transformPatternInExpr(ctx, node, updateSelect) + return t.transformPatternInExpr(ctx, node) case *ast.PatternLikeOrIlikeExpr: return t.transformPatternLikeOrIlikeExpr(ctx, node) case *ast.UnaryOperationExpr: - return t.transformUnaryOperationExpr(ctx, node, updateSelect) + return t.transformUnaryOperationExpr(ctx, node) case ast.ValueExpr: return t.transformValueExpr(ctx, node) case *ast.FuncCallExpr: - return t.transformFuncCallExpr(ctx, node, updateSelect) + return t.transformFuncCallExpr(ctx, node) default: - return "", fmt.Errorf("metrics sql: unsupported expression %q", restore(node)) + return exprResult{}, fmt.Errorf("metrics sql: unsupported expression %q", restore(node)) } } -func (t *transformer) transformColumnNameExpr(_ context.Context, node *ast.ColumnNameExpr, updateSelect bool) (string, error) { +func (t *transformer) transformColumnNameExpr(_ context.Context, node *ast.ColumnNameExpr) (exprResult, error) { if node.Name == nil { - return "", fmt.Errorf("metrics sql: can only have dimension/measure name(s) in select list") + return exprResult{}, fmt.Errorf("metrics sql: can only have dimension/measure name(s) in select list") } if node.Name.Schema.String() != "" || node.Name.Table.String() != "" { - return "", fmt.Errorf("metrics sql: no alias or table reference is supported in column name. Found in `%s`", node.Name.String()) + return exprResult{}, fmt.Errorf("metrics sql: no alias or table reference is supported in column name. Found in `%s`", node.Name.String()) } col := node.Name.Name.O if colExpr, ok := t.measureToExpr[col]; ok { - if updateSelect { - t.measureExprToCol[colExpr] = restore(node.Name) // makes sure double quotes are added - } - return colExpr, nil + return exprResult{expr: colExpr, columns: []string{restore(node.Name)}, types: []string{"MEASURE"}}, nil } var expr string if colExpr, ok := t.dimsToExpr[col]; ok { @@ -352,77 +346,83 @@ func (t *transformer) transformColumnNameExpr(_ context.Context, node *ast.Colum } else if t.metricsView.Spec.TimeDimension == col { expr = col } else { - return "", fmt.Errorf("metrics sql: selected column `%s` not found in dimensions/measures in metrics view", col) - } - if updateSelect { - t.dimExprToCol[expr] = restore(node.Name) // makes sure double quotes are added + return exprResult{}, fmt.Errorf("metrics sql: selected column `%s` not found in dimensions/measures in metrics view", col) } - return expr, nil + return exprResult{expr: expr, columns: []string{restore(node.Name)}, types: []string{"DIMENSION"}}, nil } -func (t *transformer) transformBinaryOperationExpr(ctx context.Context, node *ast.BinaryOperationExpr, updateSelect bool) (string, error) { - left, err := t.transformExprNode(ctx, node.L, updateSelect) +func (t *transformer) transformBinaryOperationExpr(ctx context.Context, node *ast.BinaryOperationExpr) (exprResult, error) { + left, err := t.transformExprNode(ctx, node.L) if err != nil { - return "", err + return exprResult{}, err } - right, err := t.transformExprNode(ctx, node.R, updateSelect) + right, err := t.transformExprNode(ctx, node.R) if err != nil { - return "", err + return exprResult{}, err } - return fmt.Sprintf("%s %s %s", left, opToString(node.Op), right), nil + var cols []string + cols = append(cols, left.columns...) + cols = append(cols, right.columns...) + + var types []string + types = append(types, left.types...) + types = append(types, right.types...) + return exprResult{expr: fmt.Sprintf("%s %s %s", left.expr, opToString(node.Op), right.expr), columns: cols, types: types}, nil } -func (t *transformer) transformFuncCallExpr(ctx context.Context, node *ast.FuncCallExpr, updateSelect bool) (string, error) { +func (t *transformer) transformFuncCallExpr(ctx context.Context, node *ast.FuncCallExpr) (exprResult, error) { fncName := node.FnName if _, ok := supportedFuncs[fncName.L]; !ok { - return "", fmt.Errorf("metrics sql: unsupported function %v", fncName.O) + return exprResult{}, fmt.Errorf("metrics sql: unsupported function %v", fncName.O) } var sb strings.Builder sb.WriteString(fncName.O) sb.WriteString("(") // keeping it generic and not doing any arg validation for now, can be added later in future if required + var cols, types []string for i, arg := range node.Args { if i != 0 { sb.WriteString(", ") } - expr, err := t.transformExprNode(ctx, arg, updateSelect) + expr, err := t.transformExprNode(ctx, arg) if err != nil { - return "", err + return exprResult{}, err } - - sb.WriteString(expr) + cols = append(cols, expr.columns...) + types = append(types, expr.types...) + sb.WriteString(expr.expr) } sb.WriteString(")") - return sb.String(), nil + return exprResult{expr: sb.String(), columns: cols, types: types}, nil } -func (t *transformer) transformIsNullOperationExpr(ctx context.Context, node *ast.IsNullExpr, updateSelect bool) (string, error) { - expr, err := t.transformExprNode(ctx, node.Expr, updateSelect) +func (t *transformer) transformIsNullOperationExpr(ctx context.Context, node *ast.IsNullExpr) (exprResult, error) { + expr, err := t.transformExprNode(ctx, node.Expr) if err != nil { - return "", err + return exprResult{}, err } var sb strings.Builder - sb.WriteString(expr) + sb.WriteString(expr.expr) if node.Not { sb.WriteString(" IS NOT NULL") } else { sb.WriteString(" IS NULL") } - return sb.String(), nil + return exprResult{expr: sb.String(), columns: expr.columns, types: expr.types}, nil } -func (t *transformer) transformIsTruthOperationExpr(ctx context.Context, n *ast.IsTruthExpr, updateSelect bool) (string, error) { - expr, err := t.transformExprNode(ctx, n, updateSelect) +func (t *transformer) transformIsTruthOperationExpr(ctx context.Context, n *ast.IsTruthExpr) (exprResult, error) { + expr, err := t.transformExprNode(ctx, n) if err != nil { - return "", err + return exprResult{}, err } var sb strings.Builder - sb.WriteString(expr) + sb.WriteString(expr.expr) if n.Not { sb.WriteString(" IS NOT") } else { @@ -433,29 +433,32 @@ func (t *transformer) transformIsTruthOperationExpr(ctx context.Context, n *ast. } else { sb.WriteString(" FALSE") } - return sb.String(), nil + return exprResult{expr: sb.String(), columns: expr.columns, types: expr.types}, nil } -func (t *transformer) transformParenthesesExpr(ctx context.Context, node *ast.ParenthesesExpr, updateSelect bool) (string, error) { - expr, err := t.transformExprNode(ctx, node.Expr, updateSelect) +func (t *transformer) transformParenthesesExpr(ctx context.Context, node *ast.ParenthesesExpr) (exprResult, error) { + expr, err := t.transformExprNode(ctx, node.Expr) if err != nil { - return "", err + return exprResult{}, err } - return fmt.Sprintf("(%s)", expr), nil + return exprResult{expr: fmt.Sprintf("(%s)", expr), columns: expr.columns, types: expr.types}, nil } -func (t *transformer) transformPatternInExpr(ctx context.Context, node *ast.PatternInExpr, updateSelect bool) (string, error) { +func (t *transformer) transformPatternInExpr(ctx context.Context, node *ast.PatternInExpr) (exprResult, error) { if node.Sel != nil { - return "", fmt.Errorf("metrics sql: sub_query is not supported") + return exprResult{}, fmt.Errorf("metrics sql: sub_query is not supported") } - expr, err := t.transformExprNode(ctx, node.Expr, updateSelect) + expr, err := t.transformExprNode(ctx, node.Expr) if err != nil { - return "", err + return exprResult{}, err } var sb strings.Builder - sb.WriteString(expr) + sb.WriteString(expr.expr) + var cols, types []string + cols = append(cols, expr.columns...) + types = append(types, expr.types...) if node.Not { sb.WriteString(" NOT IN( ") } else { @@ -465,29 +468,31 @@ func (t *transformer) transformPatternInExpr(ctx context.Context, node *ast.Patt if i > 0 { sb.WriteString(", ") } - expr, err = t.transformExprNode(ctx, n, updateSelect) + expr, err = t.transformExprNode(ctx, n) if err != nil { - return "", err + return exprResult{}, err } - sb.WriteString(expr) + sb.WriteString(expr.expr) + cols = append(cols, expr.columns...) + types = append(types, expr.types...) } sb.WriteRune(')') - return sb.String(), nil + return exprResult{expr: sb.String(), columns: cols, types: types}, nil } -func (t *transformer) transformPatternLikeOrIlikeExpr(ctx context.Context, n *ast.PatternLikeOrIlikeExpr) (string, error) { +func (t *transformer) transformPatternLikeOrIlikeExpr(ctx context.Context, n *ast.PatternLikeOrIlikeExpr) (exprResult, error) { if string(n.Escape) != "\\" { // druid supports it, duckdb and clickhouse do not - return "", fmt.Errorf("metrics sql: `ESCAPE` is not supported") + return exprResult{}, fmt.Errorf("metrics sql: `ESCAPE` is not supported") } - expr, err := t.transformExprNode(ctx, n.Expr, false) + expr, err := t.transformExprNode(ctx, n.Expr) if err != nil { - return "", err + return exprResult{}, err } var sb strings.Builder - sb.WriteString(expr) + sb.WriteString(expr.expr) if n.IsLike { if n.Not { sb.WriteString(" NOT LIKE ") @@ -502,31 +507,36 @@ func (t *transformer) transformPatternLikeOrIlikeExpr(ctx context.Context, n *as } } - patternExpr, err := t.transformExprNode(ctx, n.Pattern, false) + patternExpr, err := t.transformExprNode(ctx, n.Pattern) if err != nil { - return "", err - } - sb.WriteString(patternExpr) - return sb.String(), nil + return exprResult{}, err + } + sb.WriteString(patternExpr.expr) + var cols, types []string + cols = append(cols, expr.columns...) + cols = append(cols, patternExpr.columns...) + types = append(types, expr.types...) + types = append(types, patternExpr.types...) + return exprResult{expr: sb.String(), columns: cols, types: types}, nil } -func (t *transformer) transformUnaryOperationExpr(ctx context.Context, node *ast.UnaryOperationExpr, updateSelect bool) (string, error) { - expr, err := t.transformExprNode(ctx, node.V, updateSelect) +func (t *transformer) transformUnaryOperationExpr(ctx context.Context, node *ast.UnaryOperationExpr) (exprResult, error) { + expr, err := t.transformExprNode(ctx, node.V) if err != nil { - return "", err + return exprResult{}, err } - return fmt.Sprintf("%s%s", opToString(node.Op), expr), nil + return exprResult{expr: fmt.Sprintf("%s%s", opToString(node.Op), expr.expr), columns: expr.columns, types: expr.types}, nil } -func (t *transformer) transformValueExpr(_ context.Context, node ast.ValueExpr) (string, error) { +func (t *transformer) transformValueExpr(_ context.Context, node ast.ValueExpr) (exprResult, error) { var sb strings.Builder rctx := format.NewRestoreCtx(format.DefaultRestoreFlags|format.RestoreStringWithoutCharset, &sb) if err := node.Restore(rctx); err != nil { - return "", err + return exprResult{}, err } - return sb.String(), nil + return exprResult{expr: sb.String()}, nil } func (t *transformer) fromQueryForMetricsView(ctx context.Context, mv *runtimev1.Resource) (string, error) { @@ -610,3 +620,13 @@ func restore(node ast.Node) string { _ = node.Restore(rctx) return sb.String() } + +type exprResult struct { + expr string + // underlying dimension/measure name + // can be empty for constants + columns []string + // typ DIMENSION/MEASURE + // can be empty for constants + types []string +} diff --git a/runtime/pkg/metricssql/parser_test.go b/runtime/pkg/metricssql/parser_test.go index 34721bb9e5d..caff291d906 100644 --- a/runtime/pkg/metricssql/parser_test.go +++ b/runtime/pkg/metricssql/parser_test.go @@ -19,33 +19,34 @@ func TestCompiler_Compile(t *testing.T) { compiler := New(ctrl, instanceID, make(map[string]any)) passTests := map[string]string{ - "select pub, dom from ad_bids_metrics": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\" FROM \"ad_bids\"", - "select pub, dom from ad_bids_metrics LIMIT 5": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\" FROM \"ad_bids\" LIMIT 5", - "select pub, dom from ad_bids_metrics order by pub desc": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\" FROM \"ad_bids\" ORDER BY \"publisher\" DESC", - "select pub, dom from ad_bids_metrics order by pub desc, dom asc": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\" FROM \"ad_bids\" ORDER BY \"publisher\" DESC, \"domain\" ASC", - "select pub, dom from ad_bids_metrics order by pub desc, dom asc limit 10": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\" FROM \"ad_bids\" ORDER BY \"publisher\" DESC, \"domain\" ASC LIMIT 10", - "select pub, dom from ad_bids_metrics where tld = 'Yahoo'": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\" FROM \"ad_bids\" WHERE regexp_extract(domain, '(.*\\.)?(.*\\.com)', 2) = 'Yahoo'", - "select pub, dom from ad_bids_metrics where dom like '%google%'": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\" FROM \"ad_bids\" WHERE \"domain\" LIKE '%google%'", - "select pub, dom from ad_bids_metrics where tld = 'Yahoo' LIMIT 5": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\" FROM \"ad_bids\" WHERE regexp_extract(domain, '(.*\\.)?(.*\\.com)', 2) = 'Yahoo' LIMIT 5", - "select pub, dom from ad_bids_metrics where tld = 'Yahoo' order by pub desc": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\" FROM \"ad_bids\" WHERE regexp_extract(domain, '(.*\\.)?(.*\\.com)', 2) = 'Yahoo' ORDER BY \"publisher\" DESC", - "select pub, dom from ad_bids_metrics where tld = 'Yahoo' order by pub desc, dom asc": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\" FROM \"ad_bids\" WHERE regexp_extract(domain, '(.*\\.)?(.*\\.com)', 2) = 'Yahoo' ORDER BY \"publisher\" DESC, \"domain\" ASC", - "select pub, dom from ad_bids_metrics where tld = 'Yahoo'order by pub desc, dom asc limit 10": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\" FROM \"ad_bids\" WHERE regexp_extract(domain, '(.*\\.)?(.*\\.com)', 2) = 'Yahoo' ORDER BY \"publisher\" DESC, \"domain\" ASC LIMIT 10", - "select pub, dom,measure_0,measure_1 from ad_bids_metrics": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\", count(*) AS \"measure_0\", avg(bid_price) AS \"measure_1\" FROM \"ad_bids\" GROUP BY \"domain\", \"publisher\"", - "select pub, dom,measure_0 from ad_bids_metrics LIMIT 5": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\", count(*) AS \"measure_0\" FROM \"ad_bids\" GROUP BY \"domain\", \"publisher\" LIMIT 5", - "select pub, dom,measure_0 from ad_bids_metrics order by pub desc": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\", count(*) AS \"measure_0\" FROM \"ad_bids\" GROUP BY \"domain\", \"publisher\" ORDER BY \"publisher\" DESC", - "select pub, dom,measure_0 from ad_bids_metrics order by pub desc, dom asc": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\", count(*) AS \"measure_0\" FROM \"ad_bids\" GROUP BY \"domain\", \"publisher\" ORDER BY \"publisher\" DESC, \"domain\" ASC", - "select pub, dom,measure_0 from ad_bids_metrics order by pub desc, dom asc limit 10": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\", count(*) AS \"measure_0\" FROM \"ad_bids\" GROUP BY \"domain\", \"publisher\" ORDER BY \"publisher\" DESC, \"domain\" ASC LIMIT 10", - "select pub, dom,measure_0 from ad_bids_metrics where tld = 'Yahoo'": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\", count(*) AS \"measure_0\" FROM \"ad_bids\" WHERE regexp_extract(domain, '(.*\\.)?(.*\\.com)', 2) = 'Yahoo' GROUP BY \"domain\", \"publisher\"", - "select pub, dom,measure_0 from ad_bids_metrics where tld = 'Yahoo' LIMIT 5": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\", count(*) AS \"measure_0\" FROM \"ad_bids\" WHERE regexp_extract(domain, '(.*\\.)?(.*\\.com)', 2) = 'Yahoo' GROUP BY \"domain\", \"publisher\" LIMIT 5", - "select pub, dom,measure_0 from ad_bids_metrics where tld = 'Yahoo' order by pub desc": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\", count(*) AS \"measure_0\" FROM \"ad_bids\" WHERE regexp_extract(domain, '(.*\\.)?(.*\\.com)', 2) = 'Yahoo' GROUP BY \"domain\", \"publisher\" ORDER BY \"publisher\" DESC", - "select pub, dom,measure_0 from ad_bids_metrics where tld = 'Yahoo' order by pub desc, dom asc": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\", count(*) AS \"measure_0\" FROM \"ad_bids\" WHERE regexp_extract(domain, '(.*\\.)?(.*\\.com)', 2) = 'Yahoo' GROUP BY \"domain\", \"publisher\" ORDER BY \"publisher\" DESC, \"domain\" ASC", - "select pub, dom,measure_0 from ad_bids_metrics where tld = 'Yahoo' order by pub desc, dom asc limit 10": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\", count(*) AS \"measure_0\" FROM \"ad_bids\" WHERE regexp_extract(domain, '(.*\\.)?(.*\\.com)', 2) = 'Yahoo' GROUP BY \"domain\", \"publisher\" ORDER BY \"publisher\" DESC, \"domain\" ASC LIMIT 10", - "select pub, dom,measure_0 from ad_bids_metrics where tld = 'Yahoo' having measure_0 > 10 order by pub desc, dom asc limit 10": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\", count(*) AS \"measure_0\" FROM \"ad_bids\" WHERE regexp_extract(domain, '(.*\\.)?(.*\\.com)', 2) = 'Yahoo' GROUP BY \"domain\", \"publisher\" HAVING count(*) > 10 ORDER BY \"publisher\" DESC, \"domain\" ASC LIMIT 10", - "select pub, dom from ad_bids_metrics where tld = '{{.user.domain}}'": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\" FROM \"ad_bids\" WHERE regexp_extract(domain, '(.*\\.)?(.*\\.com)', 2) = '{{.user.domain}}'", - "select pub, dom as site from ad_bids_metrics": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"site\" FROM \"ad_bids\"", - "select pub, dom, date_trunc('second', timestamp) from ad_bids_metrics": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\", date_trunc('second', timestamp) FROM \"ad_bids\"", - "select publisher, domain, measure_2, measure_3 as \"click rate\" from ad_bids_mini_metrics where publisher is not null": "SELECT \"publisher\" AS \"publisher\", \"domain\" AS \"domain\", sum(impressions) AS \"measure_2\", sum(clicks) AS \"click rate\" FROM \"ad_bids_mini\" WHERE \"publisher\" IS NOT NULL GROUP BY \"domain\", \"publisher\"", - "select distinct pub from ad_bids_metrics": "SELECT DISTINCT \"publisher\" AS \"pub\" FROM \"ad_bids\"", + "select pub, dom from ad_bids_metrics": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\" FROM \"ad_bids\"", + "select pub, dom from ad_bids_metrics LIMIT 5": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\" FROM \"ad_bids\" LIMIT 5", + "select pub, dom from ad_bids_metrics order by pub desc": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\" FROM \"ad_bids\" ORDER BY \"publisher\" DESC", + "select pub, dom from ad_bids_metrics order by pub desc, dom asc": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\" FROM \"ad_bids\" ORDER BY \"publisher\" DESC, \"domain\" ASC", + "select pub, dom from ad_bids_metrics order by pub desc, dom asc limit 10": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\" FROM \"ad_bids\" ORDER BY \"publisher\" DESC, \"domain\" ASC LIMIT 10", + "select pub, dom from ad_bids_metrics where tld = 'Yahoo'": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\" FROM \"ad_bids\" WHERE regexp_extract(domain, '(.*\\.)?(.*\\.com)', 2) = 'Yahoo'", + "select pub, dom from ad_bids_metrics where dom like '%google%'": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\" FROM \"ad_bids\" WHERE \"domain\" LIKE '%google%'", + "select pub, dom from ad_bids_metrics where tld = 'Yahoo' LIMIT 5": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\" FROM \"ad_bids\" WHERE regexp_extract(domain, '(.*\\.)?(.*\\.com)', 2) = 'Yahoo' LIMIT 5", + "select pub, dom from ad_bids_metrics where tld = 'Yahoo' order by pub desc": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\" FROM \"ad_bids\" WHERE regexp_extract(domain, '(.*\\.)?(.*\\.com)', 2) = 'Yahoo' ORDER BY \"publisher\" DESC", + "select pub, dom from ad_bids_metrics where tld = 'Yahoo' order by pub desc, dom asc": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\" FROM \"ad_bids\" WHERE regexp_extract(domain, '(.*\\.)?(.*\\.com)', 2) = 'Yahoo' ORDER BY \"publisher\" DESC, \"domain\" ASC", + "select pub, dom from ad_bids_metrics where tld = 'Yahoo' order by pub desc, dom asc limit 10": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\" FROM \"ad_bids\" WHERE regexp_extract(domain, '(.*\\.)?(.*\\.com)', 2) = 'Yahoo' ORDER BY \"publisher\" DESC, \"domain\" ASC LIMIT 10", + "select pub, dom,measure_0,measure_1 from ad_bids_metrics": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\", count(*) AS \"measure_0\", avg(bid_price) AS \"measure_1\" FROM \"ad_bids\" GROUP BY \"domain\", \"publisher\"", + "select pub, dom,measure_0 from ad_bids_metrics LIMIT 5": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\", count(*) AS \"measure_0\" FROM \"ad_bids\" GROUP BY \"domain\", \"publisher\" LIMIT 5", + "select pub, dom,measure_0 from ad_bids_metrics order by pub desc": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\", count(*) AS \"measure_0\" FROM \"ad_bids\" GROUP BY \"domain\", \"publisher\" ORDER BY \"publisher\" DESC", + "select pub, dom,measure_0 from ad_bids_metrics order by pub desc, dom asc": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\", count(*) AS \"measure_0\" FROM \"ad_bids\" GROUP BY \"domain\", \"publisher\" ORDER BY \"publisher\" DESC, \"domain\" ASC", + "select pub, dom,measure_0 from ad_bids_metrics order by pub desc, dom asc limit 10": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\", count(*) AS \"measure_0\" FROM \"ad_bids\" GROUP BY \"domain\", \"publisher\" ORDER BY \"publisher\" DESC, \"domain\" ASC LIMIT 10", + "select pub, dom,measure_0 from ad_bids_metrics where tld = 'Yahoo'": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\", count(*) AS \"measure_0\" FROM \"ad_bids\" WHERE regexp_extract(domain, '(.*\\.)?(.*\\.com)', 2) = 'Yahoo' GROUP BY \"domain\", \"publisher\"", + "select pub, dom,measure_0 from ad_bids_metrics where tld = 'Yahoo' LIMIT 5": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\", count(*) AS \"measure_0\" FROM \"ad_bids\" WHERE regexp_extract(domain, '(.*\\.)?(.*\\.com)', 2) = 'Yahoo' GROUP BY \"domain\", \"publisher\" LIMIT 5", + "select pub, dom,measure_0 from ad_bids_metrics where tld = 'Yahoo' order by pub desc": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\", count(*) AS \"measure_0\" FROM \"ad_bids\" WHERE regexp_extract(domain, '(.*\\.)?(.*\\.com)', 2) = 'Yahoo' GROUP BY \"domain\", \"publisher\" ORDER BY \"publisher\" DESC", + "select pub, dom,measure_0 from ad_bids_metrics where tld = 'Yahoo' order by pub desc, dom asc": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\", count(*) AS \"measure_0\" FROM \"ad_bids\" WHERE regexp_extract(domain, '(.*\\.)?(.*\\.com)', 2) = 'Yahoo' GROUP BY \"domain\", \"publisher\" ORDER BY \"publisher\" DESC, \"domain\" ASC", + "select pub, dom,measure_0 from ad_bids_metrics where tld = 'Yahoo' order by pub desc, dom asc limit 10": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\", count(*) AS \"measure_0\" FROM \"ad_bids\" WHERE regexp_extract(domain, '(.*\\.)?(.*\\.com)', 2) = 'Yahoo' GROUP BY \"domain\", \"publisher\" ORDER BY \"publisher\" DESC, \"domain\" ASC LIMIT 10", + "select pub, dom,measure_0 from ad_bids_metrics where tld = 'Yahoo' having measure_0 > 10 order by pub desc, dom asc limit 10": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\", count(*) AS \"measure_0\" FROM \"ad_bids\" WHERE regexp_extract(domain, '(.*\\.)?(.*\\.com)', 2) = 'Yahoo' GROUP BY \"domain\", \"publisher\" HAVING count(*) > 10 ORDER BY \"publisher\" DESC, \"domain\" ASC LIMIT 10", + "select pub, dom from ad_bids_metrics where tld = '{{.user.domain}}'": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\" FROM \"ad_bids\" WHERE regexp_extract(domain, '(.*\\.)?(.*\\.com)', 2) = '{{.user.domain}}'", + "select pub, dom as site from ad_bids_metrics": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"site\" FROM \"ad_bids\"", + "select pub, dom, date_trunc('second', timestamp) from ad_bids_metrics": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\", date_trunc('second', timestamp) FROM \"ad_bids\"", + "select publisher, domain, measure_2, measure_3 as \"click rate\" from ad_bids_mini_metrics where publisher is not null": "SELECT \"publisher\" AS \"publisher\", \"domain\" AS \"domain\", sum(impressions) AS \"measure_2\", sum(clicks) AS \"click rate\" FROM \"ad_bids_mini\" WHERE \"publisher\" IS NOT NULL GROUP BY \"domain\", \"publisher\"", + "select distinct pub from ad_bids_metrics": "SELECT DISTINCT \"publisher\" AS \"pub\" FROM \"ad_bids\"", + "select pub, dom, date_trunc('hour', timestamp) as day, measure_0 from ad_bids_metrics order by date_trunc('hour', timestamp) desc limit 10": "SELECT \"publisher\" AS \"pub\", \"domain\" AS \"dom\", date_trunc('hour', timestamp) AS \"day\", count(*) AS \"measure_0\" FROM \"ad_bids\" GROUP BY \"domain\", \"publisher\", date_trunc('hour', timestamp) ORDER BY date_trunc('hour', timestamp) DESC LIMIT 10", } for inSQL, outSQL := range passTests { got, _, _, err := compiler.Compile(context.Background(), inSQL)