github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/sem/tree/indexed_vars.go (about) 1 // Copyright 2016 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 tree 12 13 import ( 14 "context" 15 16 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" 17 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" 18 "github.com/cockroachdb/cockroach/pkg/sql/types" 19 "github.com/cockroachdb/cockroach/pkg/util/log" 20 "github.com/cockroachdb/errors" 21 ) 22 23 // IndexedVarContainer provides the implementation of TypeCheck, Eval, and 24 // String for IndexedVars. 25 type IndexedVarContainer interface { 26 IndexedVarEval(idx int, ctx *EvalContext) (Datum, error) 27 IndexedVarResolvedType(idx int) *types.T 28 // IndexedVarNodeFormatter returns a NodeFormatter; if an object that 29 // wishes to implement this interface has lost the textual name that an 30 // IndexedVar originates from, this function can return nil (and the 31 // ordinal syntax "@1, @2, .." will be used). 32 IndexedVarNodeFormatter(idx int) NodeFormatter 33 } 34 35 // IndexedVar is a VariableExpr that can be used as a leaf in expressions; it 36 // represents a dynamic value. It defers calls to TypeCheck, Eval, String to an 37 // IndexedVarContainer. 38 type IndexedVar struct { 39 Idx int 40 Used bool 41 42 col NodeFormatter 43 44 typeAnnotation 45 } 46 47 var _ TypedExpr = &IndexedVar{} 48 49 // Variable is a dummy function part of the VariableExpr interface. 50 func (*IndexedVar) Variable() {} 51 52 // Walk is part of the Expr interface. 53 func (v *IndexedVar) Walk(_ Visitor) Expr { 54 return v 55 } 56 57 // TypeCheck is part of the Expr interface. 58 func (v *IndexedVar) TypeCheck( 59 _ context.Context, semaCtx *SemaContext, desired *types.T, 60 ) (TypedExpr, error) { 61 if semaCtx.IVarContainer == nil || semaCtx.IVarContainer == unboundContainer { 62 // A more technically correct message would be to say that the 63 // reference is unbound and thus cannot be typed. However this is 64 // a tad bit too technical for the average SQL use case and 65 // instead we acknowledge that we only get here if someone has 66 // used a column reference in a place where it's not allowed by 67 // the docs, so just say that instead. 68 return nil, pgerror.Newf( 69 pgcode.UndefinedColumn, "column reference @%d not allowed in this context", v.Idx+1) 70 } 71 v.typ = semaCtx.IVarContainer.IndexedVarResolvedType(v.Idx) 72 return v, nil 73 } 74 75 // Eval is part of the TypedExpr interface. 76 func (v *IndexedVar) Eval(ctx *EvalContext) (Datum, error) { 77 if ctx.IVarContainer == nil || ctx.IVarContainer == unboundContainer { 78 return nil, errors.AssertionFailedf( 79 "indexed var must be bound to a container before evaluation") 80 } 81 return ctx.IVarContainer.IndexedVarEval(v.Idx, ctx) 82 } 83 84 // ResolvedType is part of the TypedExpr interface. 85 func (v *IndexedVar) ResolvedType() *types.T { 86 if v.typ == nil { 87 panic(errors.AssertionFailedf("indexed var must be type checked first")) 88 } 89 return v.typ 90 } 91 92 // Format implements the NodeFormatter interface. 93 func (v *IndexedVar) Format(ctx *FmtCtx) { 94 f := ctx.flags 95 if ctx.indexedVarFormat != nil { 96 ctx.indexedVarFormat(ctx, v.Idx) 97 } else if f.HasFlags(fmtSymbolicVars) || v.col == nil { 98 ctx.Printf("@%d", v.Idx+1) 99 } else { 100 v.col.Format(ctx) 101 } 102 } 103 104 // NewOrdinalReference is a helper routine to create a standalone 105 // IndexedVar with the given index value. This needs to undergo 106 // BindIfUnbound() below before it can be fully used. 107 func NewOrdinalReference(r int) *IndexedVar { 108 return &IndexedVar{Idx: r} 109 } 110 111 // NewTypedOrdinalReference returns a new IndexedVar with the given index value 112 // that is verified to be well-typed. 113 func NewTypedOrdinalReference(r int, typ *types.T) *IndexedVar { 114 return &IndexedVar{Idx: r, typeAnnotation: typeAnnotation{typ: typ}} 115 } 116 117 // IndexedVarHelper wraps an IndexedVarContainer (an interface) and creates 118 // IndexedVars bound to that container. 119 // 120 // It also keeps track of which indexes from the container are used by 121 // expressions. 122 type IndexedVarHelper struct { 123 vars []IndexedVar 124 container IndexedVarContainer 125 } 126 127 // Container returns the container associated with the helper. 128 func (h *IndexedVarHelper) Container() IndexedVarContainer { 129 return h.container 130 } 131 132 // BindIfUnbound ensures the IndexedVar is attached to this helper's container. 133 // - for freshly created IndexedVars (with a nil container) this will bind in-place. 134 // - for already bound IndexedVar, bound to this container, this will return the same ivar unchanged. 135 // - for ordinal references (with an explicit unboundContainer) this will return a new var. 136 // - for already bound IndexedVars, bound to another container, this will error out. 137 func (h *IndexedVarHelper) BindIfUnbound(ivar *IndexedVar) (*IndexedVar, error) { 138 // We perform the range check always, even if the ivar is already 139 // bound, as a form of safety assertion against misreuse of ivars 140 // across containers. 141 if ivar.Idx < 0 || ivar.Idx >= len(h.vars) { 142 return ivar, pgerror.Newf( 143 pgcode.UndefinedColumn, "invalid column ordinal: @%d", ivar.Idx+1) 144 } 145 146 if !ivar.Used { 147 return h.IndexedVar(ivar.Idx), nil 148 } 149 return ivar, nil 150 } 151 152 // MakeIndexedVarHelper initializes an IndexedVarHelper structure. 153 func MakeIndexedVarHelper(container IndexedVarContainer, numVars int) IndexedVarHelper { 154 return IndexedVarHelper{ 155 vars: make([]IndexedVar, numVars), 156 container: container, 157 } 158 } 159 160 // AppendSlot expands the capacity of this IndexedVarHelper by one and returns 161 // the index of the new slot. 162 func (h *IndexedVarHelper) AppendSlot() int { 163 h.vars = append(h.vars, IndexedVar{}) 164 return len(h.vars) - 1 165 } 166 167 func (h *IndexedVarHelper) checkIndex(idx int) { 168 if idx < 0 || idx >= len(h.vars) { 169 panic(errors.AssertionFailedf( 170 "invalid var index %d (columns: %d)", log.Safe(idx), log.Safe(len(h.vars)))) 171 } 172 } 173 174 // NumVars returns the number of variables the IndexedVarHelper was initialized 175 // for. 176 func (h *IndexedVarHelper) NumVars() int { 177 return len(h.vars) 178 } 179 180 // IndexedVar returns an IndexedVar for the given index. The index must be 181 // valid. 182 func (h *IndexedVarHelper) IndexedVar(idx int) *IndexedVar { 183 h.checkIndex(idx) 184 v := &h.vars[idx] 185 v.Idx = idx 186 v.Used = true 187 v.typ = h.container.IndexedVarResolvedType(idx) 188 v.col = h.container.IndexedVarNodeFormatter(idx) 189 return v 190 } 191 192 // IndexedVarWithType returns an IndexedVar for the given index, with the given 193 // type. The index must be valid. This should be used in the case where an 194 // indexed var is being added before its container has a corresponding entry 195 // for it. 196 func (h *IndexedVarHelper) IndexedVarWithType(idx int, typ *types.T) *IndexedVar { 197 h.checkIndex(idx) 198 v := &h.vars[idx] 199 v.Idx = idx 200 v.Used = true 201 v.typ = typ 202 return v 203 } 204 205 // IndexedVarUsed returns true if IndexedVar() was called for the given index. 206 // The index must be valid. 207 func (h *IndexedVarHelper) IndexedVarUsed(idx int) bool { 208 h.checkIndex(idx) 209 return h.vars[idx].Used 210 } 211 212 // GetIndexedVars returns the indexed var array of this helper. 213 // IndexedVars to the caller; unused vars are guaranteed to have 214 // a false Used field. 215 func (h *IndexedVarHelper) GetIndexedVars() []IndexedVar { 216 return h.vars 217 } 218 219 // Reset re-initializes an IndexedVarHelper structure with the same 220 // number of slots. After a helper has been reset, all the expressions 221 // that were linked to the helper before it was reset must be 222 // re-bound, e.g. using Rebind(). Resetting is useful to ensure that 223 // the helper's knowledge of which IndexedVars are actually used by 224 // linked expressions is up to date, especially after 225 // optimizations/transforms which eliminate sub-expressions. The 226 // optimizations performed by setNeededColumns() work then best. 227 // 228 // TODO(knz): groupNode and windowNode hold on to IndexedVar's after a Reset(). 229 func (h *IndexedVarHelper) Reset() { 230 h.vars = make([]IndexedVar, len(h.vars)) 231 } 232 233 // Rebind collects all the IndexedVars in the given expression 234 // and re-binds them to this helper. 235 func (h *IndexedVarHelper) Rebind(expr TypedExpr, alsoReset, normalizeToNonNil bool) TypedExpr { 236 if alsoReset { 237 h.Reset() 238 } 239 if expr == nil || expr == DBoolTrue { 240 if normalizeToNonNil { 241 return DBoolTrue 242 } 243 return nil 244 } 245 ret, _ := WalkExpr(h, expr) 246 return ret.(TypedExpr) 247 } 248 249 var _ Visitor = &IndexedVarHelper{} 250 251 // VisitPre implements the Visitor interface. 252 func (h *IndexedVarHelper) VisitPre(expr Expr) (recurse bool, newExpr Expr) { 253 if iv, ok := expr.(*IndexedVar); ok { 254 return false, h.IndexedVar(iv.Idx) 255 } 256 return true, expr 257 } 258 259 // VisitPost implements the Visitor interface. 260 func (*IndexedVarHelper) VisitPost(expr Expr) Expr { return expr } 261 262 type unboundContainerType struct{} 263 264 // unboundContainer is the marker used by ordinal references (@N) in 265 // the input syntax. It differs from `nil` in that calling 266 // BindIfUnbound on an indexed var that uses unboundContainer will 267 // cause a new IndexedVar object to be returned, to ensure that the 268 // original is left unchanged (to preserve the invariant that the AST 269 // is constant after parse). 270 var unboundContainer = &unboundContainerType{} 271 272 // IndexedVarEval is part of the IndexedVarContainer interface. 273 func (*unboundContainerType) IndexedVarEval(idx int, _ *EvalContext) (Datum, error) { 274 return nil, errors.AssertionFailedf("unbound ordinal reference @%d", log.Safe(idx+1)) 275 } 276 277 // IndexedVarResolvedType is part of the IndexedVarContainer interface. 278 func (*unboundContainerType) IndexedVarResolvedType(idx int) *types.T { 279 panic(errors.AssertionFailedf("unbound ordinal reference @%d", log.Safe(idx+1))) 280 } 281 282 // IndexedVarNodeFormatter is part of the IndexedVarContainer interface. 283 func (*unboundContainerType) IndexedVarNodeFormatter(idx int) NodeFormatter { 284 panic(errors.AssertionFailedf("unbound ordinal reference @%d", log.Safe(idx+1))) 285 } 286 287 type typeContainer struct { 288 types []*types.T 289 } 290 291 var _ IndexedVarContainer = &typeContainer{} 292 293 // IndexedVarEval is part of the IndexedVarContainer interface. 294 func (tc *typeContainer) IndexedVarEval(idx int, ctx *EvalContext) (Datum, error) { 295 return nil, errors.AssertionFailedf("no eval allowed in typeContainer") 296 } 297 298 // IndexedVarResolvedType is part of the IndexedVarContainer interface. 299 func (tc *typeContainer) IndexedVarResolvedType(idx int) *types.T { 300 return tc.types[idx] 301 } 302 303 // IndexedVarNodeFormatter is part of the IndexedVarContainer interface. 304 func (tc *typeContainer) IndexedVarNodeFormatter(idx int) NodeFormatter { 305 return nil 306 } 307 308 // MakeTypesOnlyIndexedVarHelper creates an IndexedVarHelper which provides 309 // the given types for indexed vars. It does not support evaluation, unless 310 // Rebind is used with another container which supports evaluation. 311 func MakeTypesOnlyIndexedVarHelper(types []*types.T) IndexedVarHelper { 312 c := &typeContainer{types: types} 313 return MakeIndexedVarHelper(c, len(types)) 314 }