// processAliasedTable produces a builder subtree for the given AliasedTableExpr. // If the expression is a subquery, then the the route built for it will contain // the entire subquery tree in the from clause, as if it was a table. // The symtab entry for the query will be a tabsym where the columns // will be built from the select expressions of the subquery. // Since the table aliases only contain vindex columns, we'll follow // the same rule: only columns from the subquery that are identified as // vindex columns will be added to the tabsym. // A symtab symbol can only point to a route. This means that we canoot // support complex joins in subqueries yet. func processAliasedTable(tableExpr *sqlparser.AliasedTableExpr, vschema VSchema) (builder, error) { switch expr := tableExpr.Expr.(type) { case *sqlparser.TableName: eroute, table, err := getTablePlan(expr, vschema) if err != nil { return nil, err } alias := sqlparser.TableIdent(sqlparser.String(expr)) astName := expr.Name if tableExpr.As != "" { alias = tableExpr.As astName = alias } return newRoute( sqlparser.TableExprs([]sqlparser.TableExpr{tableExpr}), eroute, table, vschema, alias, astName, ), nil case *sqlparser.Subquery: sel, ok := expr.Select.(*sqlparser.Select) if !ok { return nil, errors.New("unsupported: union operator in subqueries") } subplan, err := processSelect(sel, vschema, nil) if err != nil { return nil, err } subroute, ok := subplan.(*route) if !ok { return nil, errors.New("unsupported: complex join in subqueries") } table := &vindexes.Table{ Keyspace: subroute.ERoute.Keyspace, } for _, colsyms := range subroute.Colsyms { if colsyms.Vindex == nil { continue } table.ColumnVindexes = append(table.ColumnVindexes, &vindexes.ColumnVindex{ Column: cistring.CIString(colsyms.Alias), Vindex: colsyms.Vindex, }) } rtb := newRoute( sqlparser.TableExprs([]sqlparser.TableExpr{tableExpr}), subroute.ERoute, table, vschema, tableExpr.As, tableExpr.As, ) subroute.Redirect = rtb return rtb, nil } panic("unreachable") }
// Find returns the route for the symbol referenced by col. // If a reference is found, the column's Metadata is set to point // it. Subsequent searches will reuse this meatadata. // If autoResolve is true, and there is only one table in the symbol table, // then an unqualified reference is assumed to be implicitly against // that table. The table info doesn't contain the full list of columns. // So, any column reference is presumed valid. If a Colsyms scope is // present, then the table scope is not searched. If a symbol is found // in the current symtab, then isLocal is set to true. Otherwise, the // search is continued in the outer symtab. If so, isLocal will be set // to false. If the symbol was not found, an error is returned. // isLocal must be checked before you can push-down (or pull-out) // a construct. // If a symbol was found in an outer scope, then the column reference // is added to the Externs field. func (st *symtab) Find(col *sqlparser.ColName, autoResolve bool) (rb *route, isLocal bool, err error) { if m, ok := col.Metadata.(sym); ok { return m.Route(), m.Symtab() == st, nil } if len(st.Colsyms) != 0 { name := sqlparser.String(col) starname := sqlparser.String(&sqlparser.ColName{ Name: sqlparser.NewColIdent("*"), Qualifier: col.Qualifier, }) for _, colsym := range st.Colsyms { if colsym.Alias.EqualString(name) || colsym.Alias.EqualString(starname) || colsym.Alias.EqualString("*") { col.Metadata = colsym return colsym.Route(), true, nil } } if st.Outer != nil { // autoResolve only allowed for innermost scope. rb, _, err = st.Outer.Find(col, false) if err == nil { st.Externs = append(st.Externs, col) } return rb, false, err } return nil, false, fmt.Errorf("symbol %s not found", sqlparser.String(col)) } qualifier := sqlparser.TableIdent(sqlparser.String(col.Qualifier)) if qualifier == "" && autoResolve && len(st.tables) == 1 { for _, t := range st.tables { qualifier = t.Alias break } } alias := st.findTable(qualifier) if alias == nil { if st.Outer != nil { // autoResolve only allowed for innermost scope. rb, _, err = st.Outer.Find(col, false) if err == nil { st.Externs = append(st.Externs, col) } return rb, false, err } return nil, false, fmt.Errorf("symbol %s not found", sqlparser.String(col)) } col.Metadata = alias return alias.Route(), true, nil }