// 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) }
// 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 }