github.com/cockroachdb/cockroachdb-parser@v0.23.3-0.20240213214944-911057d40c9a/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/cockroachdb-parser/pkg/sql/pgwire/pgcode" 17 "github.com/cockroachdb/cockroachdb-parser/pkg/sql/pgwire/pgerror" 18 "github.com/cockroachdb/cockroachdb-parser/pkg/sql/types" 19 "github.com/cockroachdb/errors" 20 "github.com/cockroachdb/redact" 21 ) 22 23 // IndexedVarContainer provides the implementation of TypeCheck, Eval, and 24 // String for IndexedVars. 25 type IndexedVarContainer interface { 26 IndexedVarResolvedType(idx int) *types.T 27 // IndexedVarNodeFormatter returns a NodeFormatter; if an object that 28 // wishes to implement this interface has lost the textual name that an 29 // IndexedVar originates from, this function can return nil (and the 30 // ordinal syntax "@1, @2, .." will be used). 31 IndexedVarNodeFormatter(idx int) NodeFormatter 32 } 33 34 // IndexedVar is a VariableExpr that can be used as a leaf in expressions; it 35 // represents a dynamic value. It defers calls to TypeCheck, Eval, String to an 36 // IndexedVarContainer. 37 type IndexedVar struct { 38 Idx int 39 Used bool 40 41 col NodeFormatter 42 43 typeAnnotation 44 } 45 46 var _ TypedExpr = &IndexedVar{} 47 48 // Variable is a dummy function part of the VariableExpr interface. 49 func (*IndexedVar) Variable() {} 50 51 // Walk is part of the Expr interface. 52 func (v *IndexedVar) Walk(_ Visitor) Expr { 53 return v 54 } 55 56 // TypeCheck is part of the Expr interface. 57 func (v *IndexedVar) TypeCheck( 58 _ context.Context, semaCtx *SemaContext, desired *types.T, 59 ) (TypedExpr, error) { 60 if semaCtx.IVarContainer == nil { 61 // A more technically correct message would be to say that the 62 // reference is unbound and thus cannot be typed. However this is 63 // a tad bit too technical for the average SQL use case and 64 // instead we acknowledge that we only get here if someone has 65 // used a column reference in a place where it's not allowed by 66 // the docs, so just say that instead. 67 return nil, pgerror.Newf( 68 pgcode.UndefinedColumn, "column reference @%d not allowed in this context", v.Idx+1) 69 } 70 v.typ = semaCtx.IVarContainer.IndexedVarResolvedType(v.Idx) 71 return v, nil 72 } 73 74 // ResolvedType is part of the TypedExpr interface. 75 func (v *IndexedVar) ResolvedType() *types.T { 76 if v.typ == nil { 77 panic(errors.AssertionFailedf("indexed var must be type checked first")) 78 } 79 return v.typ 80 } 81 82 // Format implements the NodeFormatter interface. 83 func (v *IndexedVar) Format(ctx *FmtCtx) { 84 f := ctx.flags 85 if ctx.indexedVarFormat != nil { 86 ctx.indexedVarFormat(ctx, v.Idx) 87 } else if f.HasFlags(fmtSymbolicVars) || v.col == nil { 88 ctx.Printf("@%d", v.Idx+1) 89 } else { 90 ctx.FormatNode(v.col) 91 } 92 } 93 94 // NewOrdinalReference is a helper routine to create a standalone 95 // IndexedVar with the given index value. This needs to undergo 96 // BindIfUnbound() below before it can be fully used. 97 func NewOrdinalReference(r int) *IndexedVar { 98 return &IndexedVar{Idx: r} 99 } 100 101 // NewTypedOrdinalReference returns a new IndexedVar with the given index value 102 // that is verified to be well-typed. 103 func NewTypedOrdinalReference(r int, typ *types.T) *IndexedVar { 104 return &IndexedVar{Idx: r, typeAnnotation: typeAnnotation{typ: typ}} 105 } 106 107 // IndexedVarHelper wraps an IndexedVarContainer (an interface) and creates 108 // IndexedVars bound to that container. 109 // 110 // It also keeps track of which indexes from the container are used by 111 // expressions. 112 type IndexedVarHelper struct { 113 vars []IndexedVar 114 container IndexedVarContainer 115 } 116 117 // Container returns the container associated with the helper. 118 func (h *IndexedVarHelper) Container() IndexedVarContainer { 119 return h.container 120 } 121 122 // BindIfUnbound ensures the IndexedVar is attached to this helper's container. 123 // - for freshly created IndexedVars (with a nil container) this will bind in-place. 124 // - for already bound IndexedVar, bound to this container, this will return the same ivar unchanged. 125 // - for ordinal references (with an explicit unboundContainer) this will return a new var. 126 // - for already bound IndexedVars, bound to another container, this will error out. 127 func (h *IndexedVarHelper) BindIfUnbound(ivar *IndexedVar) (*IndexedVar, error) { 128 // We perform the range check always, even if the ivar is already 129 // bound, as a form of safety assertion against misreuse of ivars 130 // across containers. 131 if ivar.Idx < 0 || ivar.Idx >= len(h.vars) { 132 return ivar, pgerror.Newf( 133 pgcode.UndefinedColumn, "invalid column ordinal: @%d", ivar.Idx+1) 134 } 135 136 if !ivar.Used { 137 return h.IndexedVar(ivar.Idx), nil 138 } 139 return ivar, nil 140 } 141 142 // MakeIndexedVarHelper initializes an IndexedVarHelper structure. 143 func MakeIndexedVarHelper(container IndexedVarContainer, numVars int) IndexedVarHelper { 144 return IndexedVarHelper{ 145 vars: make([]IndexedVar, numVars), 146 container: container, 147 } 148 } 149 150 // AppendSlot expands the capacity of this IndexedVarHelper by one and returns 151 // the index of the new slot. 152 func (h *IndexedVarHelper) AppendSlot() int { 153 h.vars = append(h.vars, IndexedVar{}) 154 return len(h.vars) - 1 155 } 156 157 func (h *IndexedVarHelper) checkIndex(idx int) { 158 if idx < 0 || idx >= len(h.vars) { 159 panic(errors.AssertionFailedf( 160 "invalid var index %d (columns: %d)", redact.Safe(idx), redact.Safe(len(h.vars)))) 161 } 162 } 163 164 // IndexedVar returns an IndexedVar for the given index. The index must be 165 // valid. 166 func (h *IndexedVarHelper) IndexedVar(idx int) *IndexedVar { 167 h.checkIndex(idx) 168 v := &h.vars[idx] 169 v.Idx = idx 170 v.Used = true 171 v.typ = h.container.IndexedVarResolvedType(idx) 172 v.col = h.container.IndexedVarNodeFormatter(idx) 173 return v 174 } 175 176 // IndexedVarWithType returns an IndexedVar for the given index, with the given 177 // type. The index must be valid. This should be used in the case where an 178 // indexed var is being added before its container has a corresponding entry 179 // for it. 180 func (h *IndexedVarHelper) IndexedVarWithType(idx int, typ *types.T) *IndexedVar { 181 h.checkIndex(idx) 182 v := &h.vars[idx] 183 v.Idx = idx 184 v.Used = true 185 v.typ = typ 186 return v 187 } 188 189 // IndexedVarUsed returns true if IndexedVar() was called for the given index. 190 // The index must be valid. 191 func (h *IndexedVarHelper) IndexedVarUsed(idx int) bool { 192 h.checkIndex(idx) 193 return h.vars[idx].Used 194 } 195 196 // GetIndexedVars returns the indexed var array of this helper. 197 // IndexedVars to the caller; unused vars are guaranteed to have 198 // a false Used field. 199 func (h *IndexedVarHelper) GetIndexedVars() []IndexedVar { 200 return h.vars 201 } 202 203 // Rebind collects all the IndexedVars in the given expression and re-binds them 204 // to this helper. 205 func (h *IndexedVarHelper) Rebind(expr TypedExpr) TypedExpr { 206 if expr == nil { 207 return nil 208 } 209 ret, _ := WalkExpr(h, expr) 210 return ret.(TypedExpr) 211 } 212 213 var _ Visitor = &IndexedVarHelper{} 214 215 // VisitPre implements the Visitor interface. 216 func (h *IndexedVarHelper) VisitPre(expr Expr) (recurse bool, newExpr Expr) { 217 if iv, ok := expr.(*IndexedVar); ok { 218 return false, h.IndexedVar(iv.Idx) 219 } 220 return true, expr 221 } 222 223 // VisitPost implements the Visitor interface. 224 func (*IndexedVarHelper) VisitPost(expr Expr) Expr { return expr } 225 226 type typeContainer struct { 227 types []*types.T 228 } 229 230 var _ IndexedVarContainer = &typeContainer{} 231 232 // IndexedVarResolvedType is part of the IndexedVarContainer interface. 233 func (tc *typeContainer) IndexedVarResolvedType(idx int) *types.T { 234 return tc.types[idx] 235 } 236 237 // IndexedVarNodeFormatter is part of the IndexedVarContainer interface. 238 func (tc *typeContainer) IndexedVarNodeFormatter(idx int) NodeFormatter { 239 return nil 240 } 241 242 // MakeTypesOnlyIndexedVarHelper creates an IndexedVarHelper which provides 243 // the given types for indexed vars. It does not support evaluation, unless 244 // Rebind is used with another container which supports evaluation. 245 func MakeTypesOnlyIndexedVarHelper(types []*types.T) IndexedVarHelper { 246 c := &typeContainer{types: types} 247 return MakeIndexedVarHelper(c, len(types)) 248 }