// 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.SQLName(sqlparser.String(col)) starname := sqlparser.SQLName(sqlparser.String(&sqlparser.ColName{ Name: "*", Qualifier: col.Qualifier, })) for _, colsym := range st.Colsyms { if name == colsym.Alias || starname == colsym.Alias || colsym.Alias == "*" { 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.SQLName(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 }
// PushStar pushes the '*' expression into the route. func (rb *route) PushStar(expr *sqlparser.StarExpr) *colsym { colsym := newColsym(rb, rb.Symtab()) colsym.Alias = sqlparser.SQLName(sqlparser.String(expr)) rb.Select.SelectExprs = append(rb.Select.SelectExprs, expr) rb.Colsyms = append(rb.Colsyms, colsym) return colsym }
func convertColumnNamesToValExpr(colNames []string) []sqlparser.ValExpr { valExprs := make([]sqlparser.ValExpr, 0, len(colNames)) for _, colName := range colNames { valExprs = append(valExprs, &sqlparser.ColName{Name: sqlparser.SQLName(colName)}) } return valExprs }
// 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.SQLName(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.ColVindexes = append(table.ColVindexes, &vindexes.ColVindex{ Col: string(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") }
func buildOrderByClause(splitColumns []string) sqlparser.OrderBy { result := make(sqlparser.OrderBy, 0, len(splitColumns)) for _, splitColumn := range splitColumns { result = append(result, &sqlparser.Order{ Expr: &sqlparser.ColName{Name: sqlparser.SQLName(splitColumn)}, Direction: sqlparser.AscScr, }, ) } return result }
func convertColumnNamesToSelectExprs(columnNames []string) sqlparser.SelectExprs { result := make([]sqlparser.SelectExpr, 0, len(columnNames)) for _, columnName := range columnNames { result = append(result, &sqlparser.NonStarExpr{ Expr: &sqlparser.ColName{ Name: sqlparser.SQLName(columnName), }, }) } return result }
// getWhereClause returns a whereClause based on desired upper and lower // bounds for primary key. func (qs *QuerySplitter) getWhereClause(whereClause *sqlparser.Where, bindVars map[string]interface{}, start, end sqltypes.Value) *sqlparser.Where { var startClause *sqlparser.ComparisonExpr var endClause *sqlparser.ComparisonExpr var clauses sqlparser.BoolExpr // No upper or lower bound, just return the where clause of original query if start.IsNull() && end.IsNull() { return whereClause } pk := &sqlparser.ColName{ Name: sqlparser.SQLName(qs.splitColumn), } if !start.IsNull() { startClause = &sqlparser.ComparisonExpr{ Operator: sqlparser.GreaterEqualStr, Left: pk, Right: sqlparser.ValArg([]byte(":" + startBindVarName)), } bindVars[startBindVarName] = start.ToNative() } // splitColumn < end if !end.IsNull() { endClause = &sqlparser.ComparisonExpr{ Operator: sqlparser.LessThanStr, Left: pk, Right: sqlparser.ValArg([]byte(":" + endBindVarName)), } bindVars[endBindVarName] = end.ToNative() } if startClause == nil { clauses = endClause } else { if endClause == nil { clauses = startClause } else { // splitColumn >= start AND splitColumn < end clauses = &sqlparser.AndExpr{ Left: startClause, Right: endClause, } } } if whereClause != nil { clauses = &sqlparser.AndExpr{ Left: &sqlparser.ParenBoolExpr{Expr: whereClause.Expr}, Right: &sqlparser.ParenBoolExpr{Expr: clauses}, } } return &sqlparser.Where{ Type: sqlparser.WhereStr, Expr: clauses, } }
// getWhereClause returns a whereClause based on desired upper and lower // bounds for primary key. func (qs *QuerySplitter) getWhereClause(start, end sqltypes.Value) *sqlparser.Where { var startClause *sqlparser.ComparisonExpr var endClause *sqlparser.ComparisonExpr var clauses sqlparser.BoolExpr // No upper or lower bound, just return the where clause of original query if start.IsNull() && end.IsNull() { return qs.sel.Where } pk := &sqlparser.ColName{ Name: sqlparser.SQLName(qs.splitColumn), } // splitColumn >= start if !start.IsNull() { startClause = &sqlparser.ComparisonExpr{ Operator: sqlparser.AST_GE, Left: pk, Right: sqlparser.NumVal((start).Raw()), } } // splitColumn < end if !end.IsNull() { endClause = &sqlparser.ComparisonExpr{ Operator: sqlparser.AST_LT, Left: pk, Right: sqlparser.NumVal((end).Raw()), } } if startClause == nil { clauses = endClause } else { if endClause == nil { clauses = startClause } else { // splitColumn >= start AND splitColumn < end clauses = &sqlparser.AndExpr{ Left: startClause, Right: endClause, } } } if qs.sel.Where != nil { clauses = &sqlparser.AndExpr{ Left: qs.sel.Where.Expr, Right: clauses, } } return &sqlparser.Where{ Type: sqlparser.AST_WHERE, Expr: clauses, } }
// GenerateSelectSubquery generates the subquery for selects. func GenerateSelectSubquery(sel *sqlparser.Select, tableInfo *schema.Table, index string) *sqlparser.ParsedQuery { hint := &sqlparser.IndexHints{Type: sqlparser.UseStr, Indexes: []sqlparser.SQLName{sqlparser.SQLName(index)}} tableExpr := sel.From[0].(*sqlparser.AliasedTableExpr) savedHint := tableExpr.Hints tableExpr.Hints = hint defer func() { tableExpr.Hints = savedHint }() return GenerateSubquery( tableInfo.Indexes[0].Columns, tableExpr, sel.Where, sel.OrderBy, sel.Limit, false, ) }
// PushSelect pushes the select expression into the route. func (rb *route) PushSelect(expr *sqlparser.NonStarExpr, _ *route) (colsym *colsym, colnum int, err error) { colsym = newColsym(rb, rb.Symtab()) if expr.As != "" { colsym.Alias = expr.As } if col, ok := expr.Expr.(*sqlparser.ColName); ok { if colsym.Alias == "" { colsym.Alias = sqlparser.SQLName(sqlparser.String(col)) } colsym.Vindex = rb.Symtab().Vindex(col, rb, true) colsym.Underlying = newColref(col) } else { if rb.IsRHS { return nil, 0, errors.New("unsupported: complex left join and column expressions") } } rb.Select.SelectExprs = append(rb.Select.SelectExprs, expr) rb.Colsyms = append(rb.Colsyms, colsym) return colsym, len(rb.Colsyms) - 1, nil }
func buildIndexPlan(ins *sqlparser.Insert, tablename string, colVindex *ColVindex, plan *Plan) error { pos := -1 for i, column := range ins.Columns { if colVindex.Col == sqlparser.GetColName(column.(*sqlparser.NonStarExpr).Expr) { pos = i break } } if pos == -1 { pos = len(ins.Columns) ins.Columns = append(ins.Columns, &sqlparser.NonStarExpr{Expr: &sqlparser.ColName{Name: sqlparser.SQLName(colVindex.Col)}}) ins.Rows.(sqlparser.Values)[0] = append(ins.Rows.(sqlparser.Values)[0].(sqlparser.ValTuple), &sqlparser.NullVal{}) } row := ins.Rows.(sqlparser.Values)[0].(sqlparser.ValTuple) val, err := asInterface(row[pos]) if err != nil { return fmt.Errorf("could not convert val: %s, pos: %d: %v", sqlparser.String(row[pos]), pos, err) } plan.Values = append(plan.Values.([]interface{}), val) row[pos] = sqlparser.ValArg([]byte(fmt.Sprintf(":_%s", colVindex.Col))) return nil }
func findOrInsertPos(ins *sqlparser.Insert, col string) (row sqlparser.ValTuple, pos int) { pos = -1 for i, column := range ins.Columns { if col == sqlparser.GetColName(column.(*sqlparser.NonStarExpr).Expr) { pos = i break } } if pos == -1 { pos = len(ins.Columns) ins.Columns = append(ins.Columns, &sqlparser.NonStarExpr{Expr: &sqlparser.ColName{Name: sqlparser.SQLName(col)}}) ins.Rows.(sqlparser.Values)[0] = append(ins.Rows.(sqlparser.Values)[0].(sqlparser.ValTuple), &sqlparser.NullVal{}) } return ins.Rows.(sqlparser.Values)[0].(sqlparser.ValTuple), pos }
// getWhereClause returns a whereClause based on desired upper and lower // bounds for primary key. func (qs *QuerySplitter) getWhereClause(whereClause *sqlparser.Where, bindVars map[string]interface{}, start, end sqltypes.Value) *sqlparser.Where { var startClause *sqlparser.ComparisonExpr var endClause *sqlparser.ComparisonExpr var clauses sqlparser.BoolExpr // No upper or lower bound, just return the where clause of original query if start.IsNull() && end.IsNull() { return whereClause } pk := &sqlparser.ColName{ Name: sqlparser.SQLName(qs.splitColumn), } if !start.IsNull() { startClause = &sqlparser.ComparisonExpr{ Operator: sqlparser.AST_GE, Left: pk, Right: sqlparser.ValArg([]byte(":" + startBindVarName)), } if start.IsNumeric() { v, _ := start.ParseInt64() bindVars[startBindVarName] = v } else if start.IsString() { bindVars[startBindVarName] = start.Raw() } else if start.IsFractional() { v, _ := start.ParseFloat64() bindVars[startBindVarName] = v } } // splitColumn < end if !end.IsNull() { endClause = &sqlparser.ComparisonExpr{ Operator: sqlparser.AST_LT, Left: pk, Right: sqlparser.ValArg([]byte(":" + endBindVarName)), } if end.IsNumeric() { v, _ := end.ParseInt64() bindVars[endBindVarName] = v } else if end.IsString() { bindVars[endBindVarName] = end.Raw() } else if end.IsFractional() { v, _ := end.ParseFloat64() bindVars[endBindVarName] = v } } if startClause == nil { clauses = endClause } else { if endClause == nil { clauses = startClause } else { // splitColumn >= start AND splitColumn < end clauses = &sqlparser.AndExpr{ Left: startClause, Right: endClause, } } } if whereClause != nil { clauses = &sqlparser.AndExpr{ Left: &sqlparser.ParenBoolExpr{Expr: whereClause.Expr}, Right: &sqlparser.ParenBoolExpr{Expr: clauses}, } } return &sqlparser.Where{ Type: sqlparser.AST_WHERE, Expr: clauses, } }