github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/sqlbase/computed_exprs.go (about) 1 // Copyright 2018 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package sqlbase 12 13 import ( 14 "context" 15 16 "github.com/cockroachdb/cockroach/pkg/sql/parser" 17 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" 18 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" 19 "github.com/cockroachdb/cockroach/pkg/sql/sem/transform" 20 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 21 "github.com/cockroachdb/cockroach/pkg/sql/types" 22 ) 23 24 // RowIndexedVarContainer is used to evaluate expressions over various rows. 25 type RowIndexedVarContainer struct { 26 CurSourceRow tree.Datums 27 28 // Because the rows we have might not be permuted in the same way as the 29 // original table, we need to store a mapping between them. 30 31 Cols []ColumnDescriptor 32 Mapping map[ColumnID]int 33 } 34 35 var _ tree.IndexedVarContainer = &RowIndexedVarContainer{} 36 37 // IndexedVarEval implements tree.IndexedVarContainer. 38 func (r *RowIndexedVarContainer) IndexedVarEval( 39 idx int, ctx *tree.EvalContext, 40 ) (tree.Datum, error) { 41 rowIdx, ok := r.Mapping[r.Cols[idx].ID] 42 if !ok { 43 return tree.DNull, nil 44 } 45 return r.CurSourceRow[rowIdx], nil 46 } 47 48 // IndexedVarResolvedType implements tree.IndexedVarContainer. 49 func (*RowIndexedVarContainer) IndexedVarResolvedType(idx int) *types.T { 50 panic("unsupported") 51 } 52 53 // IndexedVarNodeFormatter implements tree.IndexedVarContainer. 54 func (*RowIndexedVarContainer) IndexedVarNodeFormatter(idx int) tree.NodeFormatter { 55 return nil 56 } 57 58 // descContainer is a helper type that implements tree.IndexedVarContainer; it 59 // is used to type check computed columns and does not support evaluation. 60 type descContainer struct { 61 cols []ColumnDescriptor 62 } 63 64 func (j *descContainer) IndexedVarEval(idx int, ctx *tree.EvalContext) (tree.Datum, error) { 65 panic("unsupported") 66 } 67 68 func (j *descContainer) IndexedVarResolvedType(idx int) *types.T { 69 return j.cols[idx].Type 70 } 71 72 func (*descContainer) IndexedVarNodeFormatter(idx int) tree.NodeFormatter { 73 return nil 74 } 75 76 // CannotWriteToComputedColError constructs a write error for a computed column. 77 func CannotWriteToComputedColError(colName string) error { 78 return pgerror.Newf(pgcode.ObjectNotInPrerequisiteState, 79 "cannot write directly to computed column %q", tree.ErrNameString(colName)) 80 } 81 82 // MakeComputedExprs returns a slice of the computed expressions for the 83 // slice of input column descriptors, or nil if none of the input column 84 // descriptors have computed expressions. 85 // The length of the result slice matches the length of the input column 86 // descriptors. For every column that has no computed expression, a NULL 87 // expression is reported. 88 // addingCols indicates if the input column descriptors are being added 89 // and allows type checking of the compute expressions to reference 90 // input columns earlier in the slice. 91 func MakeComputedExprs( 92 ctx context.Context, 93 cols []ColumnDescriptor, 94 tableDesc *ImmutableTableDescriptor, 95 tn *tree.TableName, 96 txCtx *transform.ExprTransformContext, 97 evalCtx *tree.EvalContext, 98 addingCols bool, 99 ) ([]tree.TypedExpr, error) { 100 // Check to see if any of the columns have computed expressions. If there 101 // are none, we don't bother with constructing the map as the expressions 102 // are all NULL. 103 haveComputed := false 104 for i := range cols { 105 if cols[i].IsComputed() { 106 haveComputed = true 107 break 108 } 109 } 110 if !haveComputed { 111 return nil, nil 112 } 113 114 // Build the computed expressions map from the parsed statement. 115 computedExprs := make([]tree.TypedExpr, 0, len(cols)) 116 exprStrings := make([]string, 0, len(cols)) 117 for i := range cols { 118 col := &cols[i] 119 if col.IsComputed() { 120 exprStrings = append(exprStrings, *col.ComputeExpr) 121 } 122 } 123 exprs, err := parser.ParseExprs(exprStrings) 124 if err != nil { 125 return nil, err 126 } 127 128 // We need an ivarHelper and sourceInfo, unlike DEFAULT, since computed 129 // columns can reference other columns and thus need to be able to resolve 130 // column names (at this stage they only need to resolve the types so that 131 // the expressions can be typechecked - we have no need to evaluate them). 132 iv := &descContainer{tableDesc.Columns} 133 ivarHelper := tree.MakeIndexedVarHelper(iv, len(tableDesc.Columns)) 134 135 source := NewSourceInfoForSingleTable(*tn, ResultColumnsFromColDescs(tableDesc.GetID(), tableDesc.Columns)) 136 semaCtx := tree.MakeSemaContext() 137 semaCtx.IVarContainer = iv 138 139 addColumnInfo := func(col *ColumnDescriptor) { 140 ivarHelper.AppendSlot() 141 iv.cols = append(iv.cols, *col) 142 newCols := ResultColumnsFromColDescs(tableDesc.GetID(), []ColumnDescriptor{*col}) 143 source.SourceColumns = append(source.SourceColumns, newCols...) 144 } 145 146 compExprIdx := 0 147 for i := range cols { 148 col := &cols[i] 149 if !col.IsComputed() { 150 computedExprs = append(computedExprs, tree.DNull) 151 if addingCols { 152 addColumnInfo(col) 153 } 154 continue 155 } 156 expr, err := ResolveNames( 157 exprs[compExprIdx], source, ivarHelper, evalCtx.SessionData.SearchPath) 158 if err != nil { 159 return nil, err 160 } 161 162 typedExpr, err := tree.TypeCheck(ctx, expr, &semaCtx, col.Type) 163 if err != nil { 164 return nil, err 165 } 166 if typedExpr, err = txCtx.NormalizeExpr(evalCtx, typedExpr); err != nil { 167 return nil, err 168 } 169 computedExprs = append(computedExprs, typedExpr) 170 compExprIdx++ 171 if addingCols { 172 addColumnInfo(col) 173 } 174 } 175 return computedExprs, nil 176 }