github.com/cockroachdb/cockroachdb-parser@v0.23.3-0.20240213214944-911057d40c9a/pkg/sql/sem/cast/cast.go (about) 1 // Copyright 2022 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 cast defines the semantically allowed casts and their information. 12 // 13 // Note that it does not provide the mechanism to perform the evaluation of 14 // these casts. 15 package cast 16 17 import ( 18 "github.com/cockroachdb/cockroachdb-parser/pkg/sql/sem/volatility" 19 "github.com/cockroachdb/cockroachdb-parser/pkg/sql/types" 20 "github.com/lib/pq/oid" 21 ) 22 23 // Context represents the contexts in which a cast can be performed. There 24 // are three types of cast contexts: explicit, assignment, and implicit. Not all 25 // casts can be performed in all contexts. See the description of each context 26 // below for more details. 27 // 28 // The concept of cast contexts is taken directly from Postgres's cast behavior. 29 // More information can be found in the Postgres documentation on type 30 // conversion: https://www.postgresql.org/docs/current/typeconv.html 31 type Context uint8 32 33 const ( 34 _ Context = iota 35 // ContextExplicit is a cast performed explicitly with the syntax 36 // CAST(x AS T) or x::T. 37 ContextExplicit 38 // ContextAssignment is a cast implicitly performed during an INSERT, 39 // UPSERT, or UPDATE statement. 40 ContextAssignment 41 // ContextImplicit is a cast performed implicitly. For example, the DATE 42 // below is implicitly cast to a TIMESTAMPTZ so that the values can be 43 // compared. 44 // 45 // SELECT '2021-01-10'::DATE < now() 46 // 47 ContextImplicit 48 ) 49 50 // String returns the representation of Context as a string. 51 func (cc Context) String() string { 52 switch cc { 53 case ContextExplicit: 54 return "explicit" 55 case ContextAssignment: 56 return "assignment" 57 case ContextImplicit: 58 return "implicit" 59 default: 60 return "invalid" 61 } 62 } 63 64 // PGString returns the representation of Context as an abbreviated string. 65 func (cc Context) PGString() string { 66 switch cc { 67 case ContextExplicit: 68 return "e" 69 case ContextAssignment: 70 return "a" 71 case ContextImplicit: 72 return "i" 73 } 74 return "" 75 } 76 77 // ContextOrigin indicates the source of information for a cast's maximum 78 // context (see cast.MaxContext below). It is only used to annotate entries in 79 // castMap and to perform assertions on cast entries in the init function. It 80 // has no effect on the behavior of a cast. 81 type ContextOrigin uint8 82 83 const ( 84 _ ContextOrigin = iota 85 // ContextOriginPgCast specifies that a cast's maximum context is based on 86 // information in Postgres's pg_cast table. 87 ContextOriginPgCast 88 // ContextOriginAutomaticIOConversion specifies that a cast's maximum 89 // context is not included in Postgres's pg_cast table. In Postgres's 90 // internals, these casts are evaluated by each data type's input and output 91 // functions. 92 // 93 // Automatic casts can only convert to or from string types [1]. Conversions 94 // to string types are assignment casts and conversions from string types 95 // are explicit casts [2]. These rules are asserted in the init function. 96 // 97 // [1] https://www.postgresql.org/docs/13/catalog-pg-cast.html#CATALOG-PG-CAST 98 // [2] https://www.postgresql.org/docs/13/sql-createcast.html#SQL-CREATECAST-NOTES 99 ContextOriginAutomaticIOConversion 100 // ContextOriginLegacyConversion is used for casts that are not supported by 101 // Postgres, but are supported by CockroachDB and continue to be supported 102 // for backwards compatibility. 103 ContextOriginLegacyConversion 104 ) 105 106 // Cast includes details about a cast from one OID to another. 107 // 108 // TODO(mgartner, otan): Move PerformCast logic to this struct. 109 type Cast struct { 110 // MaxContext is the maximum context in which the cast is allowed. A cast 111 // can only be performed in a context that is at or below the specified 112 // maximum context. 113 // 114 // ContextExplicit casts can only be performed in an explicit context. 115 // 116 // ContextAssignment casts can be performed in an explicit context or in 117 // an assignment context in an INSERT, UPSERT, or UPDATE statement. 118 // 119 // ContextImplicit casts can be performed in any context. 120 MaxContext Context 121 // origin is the source of truth for the cast's context. It is used to 122 // annotate entries in castMap and to perform assertions on cast entries in 123 // the init function. It has no effect on the behavior of a cast. 124 origin ContextOrigin 125 // Volatility indicates whether the result of the cast is dependent only on 126 // the source value, or dependent on outside factors (such as parameter 127 // variables or table contents). 128 Volatility volatility.V 129 // VolatilityHint is an optional string for volatility.Stable casts. When 130 // set, it is used as an error hint suggesting a possible workaround when 131 // stable casts are not allowed. 132 VolatilityHint string 133 } 134 135 // ForEachCast calls fn for every valid cast from a source type to a target 136 // type. 137 func ForEachCast( 138 fn func( 139 src oid.Oid, tgt oid.Oid, castCtx Context, ctxOrigin ContextOrigin, v volatility.V, 140 ), 141 ) { 142 for src, tgts := range castMap { 143 for tgt, cast := range tgts { 144 fn(src, tgt, cast.MaxContext, cast.origin, cast.Volatility) 145 } 146 } 147 } 148 149 // ValidCast returns true if a valid cast exists from src to tgt in the given 150 // context. 151 func ValidCast(src, tgt *types.T, ctx Context) bool { 152 srcFamily := src.Family() 153 tgtFamily := tgt.Family() 154 155 // If src and tgt are array types, check for a valid cast between their 156 // content types. 157 if srcFamily == types.ArrayFamily && tgtFamily == types.ArrayFamily { 158 return ValidCast(src.ArrayContents(), tgt.ArrayContents(), ctx) 159 } 160 161 // If src and tgt are tuple types, check for a valid cast between each 162 // corresponding tuple element. 163 // 164 // Casts from a tuple type to AnyTuple are a no-op so they are always valid. 165 // If tgt is AnyTuple, we continue to LookupCast below which contains a 166 // special case for these casts. 167 if srcFamily == types.TupleFamily && tgtFamily == types.TupleFamily && tgt != types.AnyTuple { 168 srcTypes := src.TupleContents() 169 tgtTypes := tgt.TupleContents() 170 // The tuple types must have the same number of elements. 171 if len(srcTypes) != len(tgtTypes) { 172 return false 173 } 174 for i := range srcTypes { 175 if ok := ValidCast(srcTypes[i], tgtTypes[i], ctx); !ok { 176 return false 177 } 178 } 179 return true 180 } 181 182 // If src and tgt are not both array or tuple types, check castMap for a 183 // valid cast. 184 c, ok := LookupCast(src, tgt) 185 if ok { 186 return c.MaxContext >= ctx 187 } 188 189 return false 190 } 191 192 // OIDInCastMap checks to see if the cast is in the cast map. This bypasses 193 // a few false equivalences found in LookupCast. 194 // You are more likely using to use LookupCast. 195 func OIDInCastMap(src, tgt oid.Oid) bool { 196 _, ok := castMap[src][tgt] 197 return ok 198 } 199 200 // LookupCast returns a cast that describes the cast from src to tgt if it 201 // exists. If it does not exist, ok=false is returned. 202 func LookupCast(src, tgt *types.T) (Cast, bool) { 203 srcFamily := src.Family() 204 tgtFamily := tgt.Family() 205 206 // Unknown is the type given to an expression that statically evaluates 207 // to NULL. NULL can be immutably cast to any type in any context. 208 if srcFamily == types.UnknownFamily { 209 return Cast{ 210 MaxContext: ContextImplicit, 211 Volatility: volatility.Immutable, 212 }, true 213 } 214 215 // Enums have dynamic OIDs, so they can't be populated in castMap. Instead, 216 // we dynamically create cast structs for valid enum casts. 217 if srcFamily == types.EnumFamily && tgtFamily == types.StringFamily { 218 // Casts from enum types to strings are immutable and allowed in 219 // assignment contexts. 220 return Cast{ 221 MaxContext: ContextAssignment, 222 Volatility: volatility.Immutable, 223 }, true 224 } 225 if tgtFamily == types.EnumFamily { 226 switch srcFamily { 227 case types.StringFamily: 228 // Casts from string types to enums are immutable and allowed in 229 // explicit contexts. 230 return Cast{ 231 MaxContext: ContextExplicit, 232 Volatility: volatility.Immutable, 233 }, true 234 case types.UnknownFamily: 235 // Casts from unknown to enums are immutable and allowed in implicit 236 // contexts. 237 return Cast{ 238 MaxContext: ContextImplicit, 239 Volatility: volatility.Immutable, 240 }, true 241 } 242 } 243 244 // Casts from array types to string types are stable and allowed in 245 // assignment contexts. 246 if srcFamily == types.ArrayFamily && tgtFamily == types.StringFamily { 247 return Cast{ 248 MaxContext: ContextAssignment, 249 Volatility: volatility.Stable, 250 }, true 251 } 252 253 // Casts from array and tuple types to string types are immutable and 254 // allowed in assignment contexts. 255 // TODO(mgartner): Tuple to string casts should be stable. They are 256 // immutable to avoid backward incompatibility with previous versions, but 257 // this is incorrect and can causes corrupt indexes, corrupt tables, and 258 // incorrect query results. 259 if srcFamily == types.TupleFamily && tgtFamily == types.StringFamily { 260 return Cast{ 261 MaxContext: ContextAssignment, 262 Volatility: volatility.Immutable, 263 }, true 264 } 265 266 // Casts from any tuple type to AnyTuple are no-ops, so they are implicit 267 // and immutable. 268 if srcFamily == types.TupleFamily && tgt == types.AnyTuple { 269 return Cast{ 270 MaxContext: ContextImplicit, 271 Volatility: volatility.Immutable, 272 }, true 273 } 274 275 // Casts from string types to array and tuple types are stable and allowed 276 // in explicit contexts. 277 if srcFamily == types.StringFamily && 278 (tgtFamily == types.ArrayFamily || tgtFamily == types.TupleFamily) { 279 return Cast{ 280 MaxContext: ContextExplicit, 281 Volatility: volatility.Stable, 282 }, true 283 } 284 285 // Casts from int types to bit and varbit types are allowed only if the the 286 // length of the bit or varbit is defined 287 if srcFamily == types.IntFamily && 288 tgtFamily == types.BitFamily && 289 tgt.Width() != 0 { 290 return Cast{ 291 MaxContext: ContextExplicit, 292 Volatility: volatility.Immutable, 293 }, true 294 } 295 296 if tgts, ok := castMap[src.Oid()]; ok { 297 if c, ok := tgts[tgt.Oid()]; ok { 298 return c, true 299 } 300 } 301 302 // If src and tgt are the same type, the immutable cast is valid in any 303 // context. This logic is intentionally after the lookup into castMap so 304 // that entries in castMap are preferred. 305 if src.Oid() == tgt.Oid() { 306 return Cast{ 307 MaxContext: ContextImplicit, 308 Volatility: volatility.Immutable, 309 }, true 310 } 311 312 return Cast{}, false 313 } 314 315 // LookupCastVolatility returns the Volatility of a valid cast. 316 func LookupCastVolatility(from, to *types.T) (_ volatility.V, ok bool) { 317 fromFamily := from.Family() 318 toFamily := to.Family() 319 // Special case for casting between arrays. 320 if fromFamily == types.ArrayFamily && toFamily == types.ArrayFamily { 321 return LookupCastVolatility(from.ArrayContents(), to.ArrayContents()) 322 } 323 // Special case for casting between tuples. 324 if fromFamily == types.TupleFamily && toFamily == types.TupleFamily { 325 fromTypes := from.TupleContents() 326 toTypes := to.TupleContents() 327 // Handle case where an overload makes a tuple get casted to tuple{}. 328 if len(toTypes) == 1 && toTypes[0].Family() == types.AnyFamily { 329 return volatility.Stable, true 330 } 331 if len(fromTypes) != len(toTypes) { 332 return 0, false 333 } 334 maxVolatility := volatility.Leakproof 335 for i := range fromTypes { 336 v, lookupOk := LookupCastVolatility(fromTypes[i], toTypes[i]) 337 if !lookupOk { 338 return 0, false 339 } 340 if v > maxVolatility { 341 maxVolatility = v 342 } 343 } 344 return maxVolatility, true 345 } 346 347 cast, ok := LookupCast(from, to) 348 if !ok { 349 return 0, false 350 } 351 return cast.Volatility, true 352 }