Analyze a SQL select expression which may have arbitrary nested subqueries and may or may not have its sources embedded. The output of a select expression is a record that wraps its input and selected columns in a record {in:any,out:any}. The schema returned represents the observable scope of the s
(sel *ast.SQLSelect, demand []ast.Expr, seq sem.Seq, parentType super.Type)
| 23 | // and also sort by the out elements. It's up to the caller to unwrap the in/out |
| 24 | // record when returning to pipeline context. |
| 25 | func (t *translator) sqlSelect(sel *ast.SQLSelect, demand []ast.Expr, seq sem.Seq, parentType super.Type) (sem.Seq, tableScope) { |
| 26 | if len(sel.Selection.Args) == 0 { |
| 27 | t.error(sel, errors.New("SELECT clause has no selection")) |
| 28 | return seq, badTable |
| 29 | } |
| 30 | seq, fromScope := t.selectFrom(sel.Loc, sel.From, seq, parentType) |
| 31 | if fromScope == nil || fromScope == badTable { |
| 32 | return seq, badTable |
| 33 | } |
| 34 | if t.scope.sql != nil { |
| 35 | fromScope = &subqueryScope{ |
| 36 | outer: t.scope.sql, |
| 37 | inner: fromScope, |
| 38 | } |
| 39 | } |
| 40 | save := t.scope.sql |
| 41 | scope := &selectScope{ |
| 42 | in: fromScope, |
| 43 | } |
| 44 | t.scope.sql = scope |
| 45 | defer func() { |
| 46 | t.scope.sql = save |
| 47 | }() |
| 48 | // We first form column slots (and expand *'s) but delay analysis of expressions. |
| 49 | // This will let us properly resolve lateral column aliases from any |
| 50 | // GROUP BY expressions that reference them. |
| 51 | fromType := fromScope.superType(t.sctx, t.checker.unknown) |
| 52 | inType := t.sctx.MustLookupTypeRecord([]super.Field{super.NewField("in", fromType)}) |
| 53 | scope.columns = t.formProjection(scope, sel.Selection.Args, inType) |
| 54 | var where sem.Expr |
| 55 | if sel.Where != nil { |
| 56 | var typ super.Type |
| 57 | where, typ = t.expr(sel.Where, inType) |
| 58 | t.checker.boolean(sel.Where, typ) |
| 59 | } |
| 60 | scope.lateral = true |
| 61 | scope.groupings = t.groupBy(scope, sel.GroupBy, inType) |
| 62 | scope.aggOk = true |
| 63 | // Make sure any aggs needed by ORDER BY and HAVING are computed and |
| 64 | // placed into the scope.aggs table. |
| 65 | // This is a little tricky because we need to find the agg functions |
| 66 | // and type check their args against the input table, but we can't |
| 67 | // type check the outer parts of the expression yet because the output |
| 68 | // scope is not available. So we just call expr() here and ignore |
| 69 | // any type errors. This will work out ok because we descend into |
| 70 | // expressions before type checking them so all agg funcs will be |
| 71 | // encountered and entered into the scope.aggs table and the real |
| 72 | // type checking will be handled above by the ORDER BY logic. |
| 73 | t.checker.pushErrs() |
| 74 | for _, e := range demand { |
| 75 | t.expr(e, inType) |
| 76 | } |
| 77 | if sel.Having != nil { |
| 78 | scope.lateral = false |
| 79 | t.expr(sel.Having, inType) |
| 80 | scope.lateral = true |
| 81 | } |
| 82 | t.checker.popErrs() |
no test coverage detected