// SanitizeVarFreeExpr verifies a default expression is valid, has the // correct type and contains no variable expressions. func SanitizeVarFreeExpr(expr parser.Expr, expectedType parser.Type, context string) error { if parser.ContainsVars(expr) { return exprContainsVarsError(context, expr) } typedExpr, err := parser.TypeCheck(expr, nil, expectedType) if err != nil { return err } if defaultType := typedExpr.ResolvedType(); !expectedType.Equal(defaultType) { return incompatibleExprTypeError(context, expectedType, defaultType) } return nil }
func (p *planner) SetTimeZone(n *parser.SetTimeZone) (planNode, error) { typedValue, err := parser.TypeCheck(n.Value, nil, parser.TypeInt) if err != nil { return nil, err } d, err := typedValue.Eval(&p.evalCtx) if err != nil { return nil, err } var loc *time.Location var offset int64 switch v := d.(type) { case *parser.DString: location := string(*v) loc, err = timeutil.LoadLocation(location) if err != nil { return nil, fmt.Errorf("cannot find time zone %q: %v", location, err) } case *parser.DInterval: offset, _, _, err = v.Duration.Div(time.Second.Nanoseconds()).Encode() if err != nil { return nil, err } case *parser.DInt: offset = int64(*v) * 60 * 60 case *parser.DFloat: offset = int64(float64(*v) * 60.0 * 60.0) case *parser.DDecimal: sixty := inf.NewDec(60, 0) sixty.Mul(sixty, sixty).Mul(sixty, &v.Dec) sixty.Round(sixty, 0, inf.RoundDown) var ok bool if offset, ok = sixty.Unscaled(); !ok { return nil, fmt.Errorf("time zone value %s would overflow an int64", sixty) } default: return nil, fmt.Errorf("bad time zone value: %v", n.Value) } if loc == nil { loc = time.FixedZone(d.String(), int(offset)) } p.session.Location = loc return &emptyNode{}, nil }
func makeDefaultExprs( cols []sqlbase.ColumnDescriptor, parse *parser.Parser, evalCtx *parser.EvalContext, ) ([]parser.TypedExpr, error) { // Check to see if any of the columns have DEFAULT expressions. If there // are no DEFAULT expressions, we don't bother with constructing the // defaults map as the defaults are all NULL. haveDefaults := false for _, col := range cols { if col.DefaultExpr != nil { haveDefaults = true break } } if !haveDefaults { return nil, nil } // Build the default expressions map from the parsed SELECT statement. defaultExprs := make([]parser.TypedExpr, 0, len(cols)) exprStrings := make([]string, 0, len(cols)) for _, col := range cols { if col.DefaultExpr != nil { exprStrings = append(exprStrings, *col.DefaultExpr) } } exprs, err := parser.ParseExprsTraditional(exprStrings) if err != nil { return nil, err } defExprIdx := 0 for _, col := range cols { if col.DefaultExpr == nil { defaultExprs = append(defaultExprs, parser.DNull) continue } expr := exprs[defExprIdx] typedExpr, err := parser.TypeCheck(expr, nil, col.Type.ToDatumType()) if err != nil { return nil, err } if typedExpr, err = parse.NormalizeExpr(evalCtx, typedExpr); err != nil { return nil, err } defaultExprs = append(defaultExprs, typedExpr) defExprIdx++ } return defaultExprs, nil }
// analyzeExpr performs semantic analysis of an axpression, including: // - replacing sub-queries by a sql.subquery node; // - resolving names (optional); // - type checking (with optional type enforcement); // - normalization. // The parameters sources and IndexedVars, if both are non-nil, indicate // name resolution should be performed. The IndexedVars map will be filled // as a result. func (p *planner) analyzeExpr( raw parser.Expr, sources multiSourceInfo, ivarHelper parser.IndexedVarHelper, expectedType parser.Type, requireType bool, typingContext string, ) (parser.TypedExpr, error) { // Replace the sub-queries. // In all contexts that analyze a single expression, a single value // is expected. Tell this to replaceSubqueries. (See UPDATE for a // counter-example; cases where a subquery is an operand of a // comparison are handled specially in the subqueryVisitor already.) replaced, err := p.replaceSubqueries(raw, 1 /* one value expected */) if err != nil { return nil, err } // Perform optional name resolution. var resolved parser.Expr if sources == nil { resolved = replaced } else { resolved, err = p.resolveNames(replaced, sources, ivarHelper) if err != nil { return nil, err } } // Type check. var typedExpr parser.TypedExpr if requireType { typedExpr, err = parser.TypeCheckAndRequire(resolved, &p.semaCtx, expectedType, typingContext) } else { typedExpr, err = parser.TypeCheck(resolved, &p.semaCtx, expectedType) } if err != nil { return nil, err } // Normalize. return p.parser.NormalizeExpr(&p.evalCtx, typedExpr) }
func parseAndNormalizeExpr(t *testing.T, sql string, sel *selectNode) parser.TypedExpr { expr, err := parser.ParseExprTraditional(sql) if err != nil { t.Fatalf("%s: %v", sql, err) } // Perform name resolution because {analyze,simplify}Expr want // expressions containing IndexedVars. if expr, err = sel.resolveNames(expr); err != nil { t.Fatalf("%s: %v", sql, err) } typedExpr, err := parser.TypeCheck(expr, nil, parser.NoTypePreference) if err != nil { t.Fatalf("%s: %v", sql, err) } ctx := &parser.EvalContext{} if typedExpr, err = ctx.NormalizeExpr(typedExpr); err != nil { t.Fatalf("%s: %v", sql, err) } return typedExpr }
// processExpression parses the string expression inside an Expression, // interpreting $0, $1, etc as indexed variables. func processExpression(exprSpec Expression, h *parser.IndexedVarHelper) (parser.TypedExpr, error) { if exprSpec.Expr == "" { return nil, nil } expr, err := parser.ParseExprTraditional(exprSpec.Expr) if err != nil { return nil, err } // Convert Placeholders to IndexedVars v := valArgsConvert{h: h, err: nil} expr, _ = parser.WalkExpr(&v, expr) if v.err != nil { return nil, v.err } // Convert to a fully typed expression. typedExpr, err := parser.TypeCheck(expr, nil, parser.NoTypePreference) if err != nil { return nil, err } return typedExpr, nil }
// processExpression parses the string expression inside an Expression, // and associates ordinal references (@1, @2, etc) with the given helper. func processExpression(exprSpec Expression, h *parser.IndexedVarHelper) (parser.TypedExpr, error) { if exprSpec.Expr == "" { return nil, nil } expr, err := parser.ParseExprTraditional(exprSpec.Expr) if err != nil { return nil, err } // Bind IndexedVars to our eh.vars. v := ivarBinder{h: h, err: nil} parser.WalkExprConst(&v, expr) if v.err != nil { return nil, v.err } // Convert to a fully typed expression. typedExpr, err := parser.TypeCheck(expr, nil, parser.NoTypePreference) if err != nil { return nil, err } return typedExpr, nil }
// Update updates columns for a selection of rows from a table. // Privileges: UPDATE and SELECT on table. We currently always use a select statement. // Notes: postgres requires UPDATE. Requires SELECT with WHERE clause with table. // mysql requires UPDATE. Also requires SELECT with WHERE clause with table. // TODO(guanqun): need to support CHECK in UPDATE func (p *planner) Update( n *parser.Update, desiredTypes []parser.Type, autoCommit bool, ) (planNode, error) { tracing.AnnotateTrace() tn, err := p.getAliasedTableName(n.Table) if err != nil { return nil, err } en, err := p.makeEditNode(tn, autoCommit, privilege.UPDATE) if err != nil { return nil, err } exprs := make([]*parser.UpdateExpr, len(n.Exprs)) for i, expr := range n.Exprs { // Replace the sub-query nodes. newExpr, err := p.replaceSubqueries(expr.Expr, len(expr.Names)) if err != nil { return nil, err } exprs[i] = &parser.UpdateExpr{Tuple: expr.Tuple, Expr: newExpr, Names: expr.Names} } // Determine which columns we're inserting into. names, err := p.namesForExprs(exprs) if err != nil { return nil, err } updateCols, err := p.processColumns(en.tableDesc, names) if err != nil { return nil, err } defaultExprs, err := makeDefaultExprs(updateCols, &p.parser, &p.evalCtx) if err != nil { return nil, err } var requestedCols []sqlbase.ColumnDescriptor if len(n.Returning) > 0 || len(en.tableDesc.Checks) > 0 { // TODO(dan): This could be made tighter, just the rows needed for RETURNING // exprs. requestedCols = en.tableDesc.Columns } fkTables := tablesNeededForFKs(*en.tableDesc, CheckUpdates) if err := p.fillFKTableMap(fkTables); err != nil { return nil, err } ru, err := makeRowUpdater(p.txn, en.tableDesc, fkTables, updateCols, requestedCols, rowUpdaterDefault) if err != nil { return nil, err } tw := tableUpdater{ru: ru, autoCommit: autoCommit} tracing.AnnotateTrace() // Generate the list of select targets. We need to select all of the columns // plus we select all of the update expressions in case those expressions // reference columns (e.g. "UPDATE t SET v = v + 1"). Note that we flatten // expressions for tuple assignments just as we flattened the column names // above. So "UPDATE t SET (a, b) = (1, 2)" translates into select targets of // "*, 1, 2", not "*, (1, 2)". targets := sqlbase.ColumnsSelectors(ru.fetchCols) i := 0 // Remember the index where the targets for exprs start. exprTargetIdx := len(targets) desiredTypesFromSelect := make([]parser.Type, len(targets), len(targets)+len(exprs)) for i := range targets { desiredTypesFromSelect[i] = parser.TypeAny } for _, expr := range exprs { if expr.Tuple { switch t := expr.Expr.(type) { case (*parser.Tuple): for _, e := range t.Exprs { typ := updateCols[i].Type.ToDatumType() e := fillDefault(e, typ, i, defaultExprs) targets = append(targets, parser.SelectExpr{Expr: e}) desiredTypesFromSelect = append(desiredTypesFromSelect, typ) i++ } default: return nil, fmt.Errorf("cannot use this expression to assign multiple columns: %s", expr.Expr) } } else { typ := updateCols[i].Type.ToDatumType() e := fillDefault(expr.Expr, typ, i, defaultExprs) targets = append(targets, parser.SelectExpr{Expr: e}) desiredTypesFromSelect = append(desiredTypesFromSelect, typ) i++ } } rows, err := p.SelectClause(&parser.SelectClause{ Exprs: targets, From: &parser.From{Tables: []parser.TableExpr{n.Table}}, Where: n.Where, }, nil, nil, desiredTypesFromSelect, publicAndNonPublicColumns) if err != nil { return nil, err } // Placeholders have their types populated in the above Select if they are part // of an expression ("SET a = 2 + $1") in the type check step where those // types are inferred. For the simpler case ("SET a = $1"), populate them // using checkColumnType. This step also verifies that the expression // types match the column types. sel := rows.(*selectTopNode).source.(*selectNode) for i, target := range sel.render[exprTargetIdx:] { // DefaultVal doesn't implement TypeCheck if _, ok := target.(parser.DefaultVal); ok { continue } // TODO(nvanbenschoten) isn't this TypeCheck redundant with the call to SelectClause? typedTarget, err := parser.TypeCheck(target, &p.semaCtx, updateCols[i].Type.ToDatumType()) if err != nil { return nil, err } err = sqlbase.CheckColumnType(updateCols[i], typedTarget.ResolvedType(), p.semaCtx.Placeholders) if err != nil { return nil, err } } updateColsIdx := make(map[sqlbase.ColumnID]int, len(ru.updateCols)) for i, col := range ru.updateCols { updateColsIdx[col.ID] = i } un := &updateNode{ n: n, editNodeBase: en, updateCols: ru.updateCols, updateColsIdx: updateColsIdx, tw: tw, } if err := un.checkHelper.init(p, tn, en.tableDesc); err != nil { return nil, err } if err := un.run.initEditNode(&un.editNodeBase, rows, n.Returning, desiredTypes); err != nil { return nil, err } return un, nil }
// Set sets session variables. // Privileges: None. // Notes: postgres/mysql do not require privileges for session variables (some exceptions). func (p *planner) Set(n *parser.Set) (planNode, error) { if n.Name == nil { // A client has sent the reserved internal syntax SET ROW ... // Reject it. return nil, errors.New("invalid statement: SET ROW") } // By using VarName.String() here any variables that are keywords will // be double quoted. name := strings.ToUpper(n.Name.String()) typedValues := make([]parser.TypedExpr, len(n.Values)) for i, expr := range n.Values { typedValue, err := parser.TypeCheck(expr, nil, parser.TypeString) if err != nil { return nil, err } typedValues[i] = typedValue } switch name { case `DATABASE`: dbName, err := p.getStringVal(name, typedValues) if err != nil { return nil, err } if len(dbName) != 0 { // Verify database descriptor exists. if _, err := p.mustGetDatabaseDesc(dbName); err != nil { return nil, err } } p.session.Database = dbName p.evalCtx.Database = dbName case `SYNTAX`: s, err := p.getStringVal(name, typedValues) if err != nil { return nil, err } switch parser.Name(s).Normalize() { case parser.ReNormalizeName(parser.Modern.String()): p.session.Syntax = int32(parser.Modern) case parser.ReNormalizeName(parser.Traditional.String()): p.session.Syntax = int32(parser.Traditional) default: return nil, fmt.Errorf("%s: \"%s\" is not in (%q, %q)", name, s, parser.Modern, parser.Traditional) } case `EXTRA_FLOAT_DIGITS`: // These settings are sent by the JDBC driver but we silently ignore them. case `APPLICATION_NAME`: // These settings are sent by the clients to improve query logging on the server, // but we silently ignore them. case `DEFAULT_TRANSACTION_ISOLATION`: // It's unfortunate that clients want us to support both SET // SESSION CHARACTERISTICS AS TRANSACTION ..., which takes the // isolation level as keywords/identifiers (e.g. JDBC), and SET // DEFAULT_TRANSACTION_ISOLATION TO '...', which takes an // expression (e.g. psycopg2). But that's how it is. Just ensure // this code keeps in sync with SetDefaultIsolation() below. s, err := p.getStringVal(name, typedValues) if err != nil { return nil, err } switch strings.ToUpper(s) { case `READ UNCOMMITTED`, `READ COMMITTED`, `SNAPSHOT`: p.session.DefaultIsolationLevel = enginepb.SNAPSHOT case `REPEATABLE READ`, `SERIALIZABLE`: p.session.DefaultIsolationLevel = enginepb.SERIALIZABLE default: return nil, fmt.Errorf("%s: unknown isolation level: %q", name, s) } case `DIST_SQL`: s, err := p.getStringVal(name, typedValues) if err != nil { return nil, err } switch parser.Name(s).Normalize() { case parser.ReNormalizeName("sync"): p.session.DistSQLMode = distSQLSync case parser.ReNormalizeName("async"): p.session.DistSQLMode = distSQLAsync default: return nil, fmt.Errorf("%s: \"%s\" not supported", name, s) } default: return nil, fmt.Errorf("unknown variable: %q", name) } return &emptyNode{}, nil }
// Set sets session variables. // Privileges: None. // Notes: postgres/mysql do not require privileges for session variables (some exceptions). func (p *planner) Set(n *parser.Set) (planNode, error) { if n.Name == nil { // A client has sent the reserved internal syntax SET ROW ... // Reject it. return nil, errors.New("invalid statement: SET ROW") } // By using VarName.String() here any variables that are keywords will // be double quoted. name := strings.ToUpper(n.Name.String()) typedValues := make([]parser.TypedExpr, len(n.Values)) for i, expr := range n.Values { typedValue, err := parser.TypeCheck(expr, nil, parser.TypeString) if err != nil { return nil, err } typedValues[i] = typedValue } switch name { case `DATABASE`: dbName, err := p.getStringVal(name, typedValues) if err != nil { return nil, err } if len(dbName) != 0 { // Verify database descriptor exists. if _, err := p.mustGetDatabaseDesc(dbName); err != nil { return nil, err } } p.session.Database = dbName p.evalCtx.Database = dbName case `SYNTAX`: s, err := p.getStringVal(name, typedValues) if err != nil { return nil, err } switch parser.Name(s).Normalize() { case parser.ReNormalizeName(parser.Modern.String()): p.session.Syntax = int32(parser.Modern) case parser.ReNormalizeName(parser.Traditional.String()): p.session.Syntax = int32(parser.Traditional) default: return nil, fmt.Errorf("%s: \"%s\" is not in (%q, %q)", name, s, parser.Modern, parser.Traditional) } case `DEFAULT_TRANSACTION_ISOLATION`: // It's unfortunate that clients want us to support both SET // SESSION CHARACTERISTICS AS TRANSACTION ..., which takes the // isolation level as keywords/identifiers (e.g. JDBC), and SET // DEFAULT_TRANSACTION_ISOLATION TO '...', which takes an // expression (e.g. psycopg2). But that's how it is. Just ensure // this code keeps in sync with SetDefaultIsolation() below. s, err := p.getStringVal(name, typedValues) if err != nil { return nil, err } switch strings.ToUpper(s) { case `READ UNCOMMITTED`, `READ COMMITTED`, `SNAPSHOT`: p.session.DefaultIsolationLevel = enginepb.SNAPSHOT case `REPEATABLE READ`, `SERIALIZABLE`: p.session.DefaultIsolationLevel = enginepb.SERIALIZABLE default: return nil, fmt.Errorf("%s: unknown isolation level: %q", name, s) } case `DIST_SQL`: s, err := p.getStringVal(name, typedValues) if err != nil { return nil, err } switch parser.Name(s).Normalize() { case parser.ReNormalizeName("off"): p.session.DistSQLMode = distSQLOff case parser.ReNormalizeName("on"): p.session.DistSQLMode = distSQLOn case parser.ReNormalizeName("always"): p.session.DistSQLMode = distSQLAlways default: return nil, fmt.Errorf("%s: \"%s\" not supported", name, s) } // These settings are sent by various client drivers. We don't support // changing them, so we either silently ignore them or throw an error given // a setting that we do not respect. case `EXTRA_FLOAT_DIGITS`: // See https://www.postgresql.org/docs/9.6/static/runtime-config-client.html case `APPLICATION_NAME`: // Set by clients to improve query logging. // See https://www.postgresql.org/docs/9.6/static/runtime-config-logging.html#GUC-APPLICATION-NAME case `CLIENT_ENCODING`: // See https://www.postgresql.org/docs/9.6/static/multibyte.html s, err := p.getStringVal(name, typedValues) if err != nil { return nil, err } if strings.ToUpper(s) != "UTF8" { return nil, fmt.Errorf("non-UTF8 encoding %s not supported", s) } case `SEARCH_PATH`: // Controls the schema search order. We don't really support this as we // don't have first-class support for schemas. // TODO(jordan) can we hook this up to EvalContext.SearchPath without // breaking things? // See https://www.postgresql.org/docs/9.6/static/runtime-config-client.html case `STANDARD_CONFORMING_STRINGS`: // If true, escape backslash literals in strings. We do this by default, // and we do not support the opposite behavior. // See https://www.postgresql.org/docs/9.1/static/runtime-config-compatible.html#GUC-STANDARD-CONFORMING-STRINGS s, err := p.getStringVal(name, typedValues) if err != nil { return nil, err } if parser.Name(s).Normalize() != parser.ReNormalizeName("on") { return nil, fmt.Errorf("%s: \"%s\" not supported", name, s) } case `CLIENT_MIN_MESSAGES`: // Controls returned message verbosity. We don't support this. // See https://www.postgresql.org/docs/9.6/static/runtime-config-compatible.html default: return nil, fmt.Errorf("unknown variable: %q", name) } return &emptyNode{}, nil }