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 }
func (eh *exprHelper) init( expr Expression, types []sqlbase.ColumnType_Kind, evalCtx *parser.EvalContext, ) error { if expr.Expr == "" { return nil } eh.types = types eh.evalCtx = evalCtx eh.vars = parser.MakeIndexedVarHelper(eh, len(types)) var err error eh.expr, err = processExpression(expr, &eh.vars) if err != nil { return err } var p parser.Parser if p.AggregateInExpr(eh.expr, evalCtx.SearchPath) { return errors.Errorf("expression '%s' has aggregate", eh.expr) } return nil }
// Prepare returns the result types of the given statement. pinfo may // contain partial type information for placeholders. Prepare will // populate the missing types. The column result types are returned (or // nil if there are no results). func (e *Executor) Prepare( query string, session *Session, pinfo parser.PlaceholderTypes, ) (ResultColumns, error) { log.VEventf(session.Ctx(), 2, "preparing: %s", query) var p parser.Parser stmts, err := p.Parse(query, parser.Syntax(session.Syntax)) if err != nil { return nil, err } switch len(stmts) { case 0: return nil, nil case 1: // ignore default: return nil, errors.Errorf("expected 1 statement, but found %d", len(stmts)) } stmt := stmts[0] if err = pinfo.ProcessPlaceholderAnnotations(stmt); err != nil { return nil, err } protoTS, err := isAsOf(&session.planner, stmt, e.cfg.Clock.Now()) if err != nil { return nil, err } session.planner.resetForBatch(e) session.planner.semaCtx.Placeholders.SetTypes(pinfo) session.planner.evalCtx.PrepareOnly = true // Prepare needs a transaction because it needs to retrieve db/table // descriptors for type checking. // TODO(andrei): is this OK? If we're preparing as part of a SQL txn, how do // we check that they're reading descriptors consistent with the txn in which // they'll be used? txn := client.NewTxn(session.Ctx(), *e.cfg.DB) txn.Proto.Isolation = session.DefaultIsolationLevel session.planner.setTxn(txn) defer session.planner.setTxn(nil) if protoTS != nil { session.planner.avoidCachedDescriptors = true defer func() { session.planner.avoidCachedDescriptors = false }() setTxnTimestamps(txn, *protoTS) } plan, err := session.planner.prepare(stmt) if err != nil { return nil, err } if plan == nil { return nil, nil } defer plan.Close() cols := plan.Columns() for _, c := range cols { if err := checkResultType(c.Typ); err != nil { return nil, err } } return cols, nil }
// MakeColumnDefDescs creates the column descriptor for a column, as well as the // index descriptor if the column is a primary key or unique. // The search path is used for name resolution for DEFAULT expressions. func MakeColumnDefDescs( d *parser.ColumnTableDef, searchPath parser.SearchPath, ) (*ColumnDescriptor, *IndexDescriptor, error) { col := &ColumnDescriptor{ Name: string(d.Name), Nullable: d.Nullable.Nullability != parser.NotNull && !d.PrimaryKey, } var colDatumType parser.Type switch t := d.Type.(type) { case *parser.BoolColType: col.Type.Kind = ColumnType_BOOL colDatumType = parser.TypeBool case *parser.IntColType: col.Type.Kind = ColumnType_INT col.Type.Width = int32(t.N) colDatumType = parser.TypeInt if t.IsSerial() { if d.HasDefaultExpr() { return nil, nil, fmt.Errorf("SERIAL column %q cannot have a default value", col.Name) } s := "unique_rowid()" col.DefaultExpr = &s } case *parser.FloatColType: col.Type.Kind = ColumnType_FLOAT col.Type.Precision = int32(t.Prec) colDatumType = parser.TypeFloat case *parser.DecimalColType: col.Type.Kind = ColumnType_DECIMAL col.Type.Width = int32(t.Scale) col.Type.Precision = int32(t.Prec) colDatumType = parser.TypeDecimal case *parser.DateColType: col.Type.Kind = ColumnType_DATE colDatumType = parser.TypeDate case *parser.TimestampColType: col.Type.Kind = ColumnType_TIMESTAMP colDatumType = parser.TypeTimestamp case *parser.TimestampTZColType: col.Type.Kind = ColumnType_TIMESTAMPTZ colDatumType = parser.TypeTimestampTZ case *parser.IntervalColType: col.Type.Kind = ColumnType_INTERVAL colDatumType = parser.TypeInterval case *parser.StringColType: col.Type.Kind = ColumnType_STRING col.Type.Width = int32(t.N) colDatumType = parser.TypeString case *parser.BytesColType: col.Type.Kind = ColumnType_BYTES colDatumType = parser.TypeBytes case *parser.ArrayColType: if _, ok := t.ParamType.(*parser.IntColType); ok { col.Type.Kind = ColumnType_INT_ARRAY } else { return nil, nil, errors.Errorf("arrays of type %s are unsupported", t.ParamType) } colDatumType = parser.TypeIntArray for i, e := range t.BoundsExprs { ctx := parser.SemaContext{SearchPath: searchPath} te, err := parser.TypeCheckAndRequire(e, &ctx, parser.TypeInt, "array bounds") if err != nil { return nil, nil, errors.Wrapf(err, "couldn't get bound %d", i) } d, err := te.Eval(nil) if err != nil { return nil, nil, errors.Wrapf(err, "couldn't Eval bound %d", i) } b := d.(*parser.DInt) col.Type.ArrayDimensions = append(col.Type.ArrayDimensions, int32(*b)) } default: return nil, nil, errors.Errorf("unexpected type %T", t) } if col.Type.Kind == ColumnType_DECIMAL { switch { case col.Type.Precision == 0 && col.Type.Width > 0: // TODO (seif): Find right range for error message. return nil, nil, errors.New("invalid NUMERIC precision 0") case col.Type.Precision < col.Type.Width: return nil, nil, fmt.Errorf("NUMERIC scale %d must be between 0 and precision %d", col.Type.Width, col.Type.Precision) } } if len(d.CheckExprs) > 0 { // Should never happen since `hoistConstraints` moves these to table level return nil, nil, errors.New("unexpected column CHECK constraint") } if d.HasFKConstraint() { // Should never happen since `hoistConstraints` moves these to table level return nil, nil, errors.New("unexpected column REFERENCED constraint") } if d.HasDefaultExpr() { // Verify the default expression type is compatible with the column type. if err := SanitizeVarFreeExpr( d.DefaultExpr.Expr, colDatumType, "DEFAULT", searchPath, ); err != nil { return nil, nil, err } var p parser.Parser if err := p.AssertNoAggregationOrWindowing( d.DefaultExpr.Expr, "DEFAULT expressions", searchPath, ); err != nil { return nil, nil, err } s := d.DefaultExpr.Expr.String() col.DefaultExpr = &s } var idx *IndexDescriptor if d.PrimaryKey || d.Unique { idx = &IndexDescriptor{ Unique: true, ColumnNames: []string{string(d.Name)}, ColumnDirections: []IndexDescriptor_Direction{IndexDescriptor_ASC}, } if d.UniqueConstraintName != "" { idx.Name = string(d.UniqueConstraintName) } } return col, idx, nil }
func (c *cliState) doCheckStatement(startState, contState, execState cliStateEnum) cliStateEnum { // From here on, client-side syntax checking is enabled. var parser parser.Parser parsedStmts, err := parser.Parse(c.concatLines, c.syntax) if err != nil { _ = c.invalidSyntax(0, "statement ignored: %v", err) // Even on failure, add the last (erroneous) lines as-is to the // history, so that the user can recall them later to fix them. for i := c.partialStmtsLen; i < len(c.partialLines); i++ { c.addHistory(c.partialLines[i]) } // Stop here if exiterr is set. if c.errExit { return cliStop } // Otherwise, remove the erroneous lines from the buffered input, // then try again. c.partialLines = c.partialLines[:c.partialStmtsLen] if len(c.partialLines) == 0 { return startState } return contState } if !isInteractive { return execState } if c.normalizeHistory { // Add statements, not lines, to the history. for i := c.partialStmtsLen; i < len(parsedStmts); i++ { c.addHistory(parsedStmts[i].String() + ";") } } else { // Add the last lines received to the history. for i := c.partialStmtsLen; i < len(c.partialLines); i++ { c.addHistory(c.partialLines[i]) } } // Replace the last entered lines by the last entered statements. c.partialLines = c.partialLines[:c.partialStmtsLen] for i := c.partialStmtsLen; i < len(parsedStmts); i++ { c.partialLines = append(c.partialLines, parsedStmts[i].String()+";") } nextState := execState // In interactive mode, we make some additional effort to help the user: // if the entry so far is starting an incomplete transaction, push // the user to enter input over multiple lines. if endsWithIncompleteTxn(parsedStmts) && c.lastInputLine != "" { if c.partialStmtsLen == 0 { fmt.Fprintln(osStderr, "Now adding input for a multi-line SQL transaction client-side.\n"+ "Press Enter two times to send the SQL text collected so far to the server, or Ctrl+C to cancel.") } nextState = contState } c.partialStmtsLen = len(parsedStmts) return nextState }