예제 #1
0
파일: analyze.go 프로젝트: knz/cockroach
// 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)
}
예제 #2
0
// 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
}