github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/norm/fold_constants_funcs.go (about) 1 // Copyright 2019 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 norm 12 13 import ( 14 "github.com/cockroachdb/cockroach/pkg/sql/opt" 15 "github.com/cockroachdb/cockroach/pkg/sql/opt/cat" 16 "github.com/cockroachdb/cockroach/pkg/sql/opt/memo" 17 "github.com/cockroachdb/cockroach/pkg/sql/parser" 18 "github.com/cockroachdb/cockroach/pkg/sql/privilege" 19 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 20 "github.com/cockroachdb/cockroach/pkg/sql/types" 21 ) 22 23 // FoldNullUnary replaces the unary operator with a typed null value having the 24 // same type as the unary operator would have. 25 func (c *CustomFuncs) FoldNullUnary(op opt.Operator, input opt.ScalarExpr) opt.ScalarExpr { 26 return c.f.ConstructNull(memo.InferUnaryType(op, input.DataType())) 27 } 28 29 // FoldNullBinary replaces the binary operator with a typed null value having 30 // the same type as the binary operator would have. 31 func (c *CustomFuncs) FoldNullBinary(op opt.Operator, left, right opt.ScalarExpr) opt.ScalarExpr { 32 return c.f.ConstructNull(memo.InferBinaryType(op, left.DataType(), right.DataType())) 33 } 34 35 // AllowNullArgs returns true if the binary operator with the given inputs 36 // allows one of those inputs to be null. If not, then the binary operator will 37 // simply be replaced by null. 38 func (c *CustomFuncs) AllowNullArgs(op opt.Operator, left, right opt.ScalarExpr) bool { 39 return memo.BinaryAllowsNullArgs(op, left.DataType(), right.DataType()) 40 } 41 42 // IsListOfConstants returns true if elems is a list of constant values or 43 // tuples. 44 func (c *CustomFuncs) IsListOfConstants(elems memo.ScalarListExpr) bool { 45 for _, elem := range elems { 46 if !c.IsConstValueOrTuple(elem) { 47 return false 48 } 49 } 50 return true 51 } 52 53 // FoldArray evaluates an Array expression with constant inputs. It returns the 54 // array as a Const datum with type TArray. 55 func (c *CustomFuncs) FoldArray(elems memo.ScalarListExpr, typ *types.T) opt.ScalarExpr { 56 elemType := typ.ArrayContents() 57 a := tree.NewDArray(elemType) 58 a.Array = make(tree.Datums, len(elems)) 59 for i := range a.Array { 60 a.Array[i] = memo.ExtractConstDatum(elems[i]) 61 if a.Array[i] == tree.DNull { 62 a.HasNulls = true 63 } else { 64 a.HasNonNulls = true 65 } 66 } 67 return c.f.ConstructConst(a, typ) 68 } 69 70 // IsConstValueOrTuple returns true if the input is a constant or a tuple of 71 // constants. 72 func (c *CustomFuncs) IsConstValueOrTuple(input opt.ScalarExpr) bool { 73 return memo.CanExtractConstDatum(input) 74 } 75 76 // HasNullElement returns true if the input tuple has at least one constant, 77 // null element. Note that it only returns true if one element is known to be 78 // null. For example, given the tuple (1, x), it will return false because x is 79 // not guaranteed to be null. 80 func (c *CustomFuncs) HasNullElement(input opt.ScalarExpr) bool { 81 tup := input.(*memo.TupleExpr) 82 for _, e := range tup.Elems { 83 if e.Op() == opt.NullOp { 84 return true 85 } 86 } 87 return false 88 } 89 90 // HasAllNullElements returns true if the input tuple has only constant, null 91 // elements, or if the tuple is empty (has 0 elements). Note that it only 92 // returns true if all elements are known to be null. For example, given the 93 // tuple (NULL, x), it will return false because x is not guaranteed to be 94 // null. 95 func (c *CustomFuncs) HasAllNullElements(input opt.ScalarExpr) bool { 96 tup := input.(*memo.TupleExpr) 97 for _, e := range tup.Elems { 98 if e.Op() != opt.NullOp { 99 return false 100 } 101 } 102 return true 103 } 104 105 // HasNonNullElement returns true if the input tuple has at least one constant, 106 // non-null element. Note that it only returns true if one element is known to 107 // be non-null. For example, given the tuple (NULL, x), it will return false 108 // because x is not guaranteed to be non-null. 109 func (c *CustomFuncs) HasNonNullElement(input opt.ScalarExpr) bool { 110 tup := input.(*memo.TupleExpr) 111 for _, e := range tup.Elems { 112 // It is guaranteed that the input has at least one non-null element if 113 // e is not null and it is either a constant value, array, or tuple. 114 // Note that it doesn't matter whether a nested tuple has non-null 115 // elements or not. For example, (NULL, (NULL, NULL)) IS NULL evaluates 116 // to false because one first-level element is not null - the second is 117 // a tuple. 118 if e.Op() != opt.NullOp && (opt.IsConstValueOp(e) || e.Op() == opt.TupleOp || e.Op() == opt.ArrayOp) { 119 return true 120 } 121 } 122 return false 123 } 124 125 // HasAllNonNullElements returns true if the input tuple has all constant, 126 // non-null elements, or if the tuple is empty (has 0 elements). Note that it 127 // only returns true if all elements are known to be non-null. For example, 128 // given the tuple (1, x), it will return false because x is not guaranteed to 129 // be non-null. 130 func (c *CustomFuncs) HasAllNonNullElements(input opt.ScalarExpr) bool { 131 tup := input.(*memo.TupleExpr) 132 for _, e := range tup.Elems { 133 // It is not guaranteed that the input has all non-null elements if e 134 // is null or it is neither a constant value, array, nor tuple. Note 135 // that it doesn't matter whether a nested tuple has non-null elements 136 // or not. For example, (1, (NULL, NULL)) IS NOT NULL evaluates to true 137 // because all first-level elements are not null. 138 if e.Op() == opt.NullOp || !(opt.IsConstValueOp(e) || e.Op() == opt.TupleOp || e.Op() == opt.ArrayOp) { 139 return false 140 } 141 } 142 return true 143 } 144 145 // FoldBinary evaluates a binary expression with constant inputs. It returns 146 // a constant expression as long as it finds an appropriate overload function 147 // for the given operator and input types, and the evaluation causes no error. 148 func (c *CustomFuncs) FoldBinary(op opt.Operator, left, right opt.ScalarExpr) opt.ScalarExpr { 149 lDatum, rDatum := memo.ExtractConstDatum(left), memo.ExtractConstDatum(right) 150 151 o, ok := memo.FindBinaryOverload(op, left.DataType(), right.DataType()) 152 if !ok { 153 return nil 154 } 155 156 result, err := o.Fn(c.f.evalCtx, lDatum, rDatum) 157 if err != nil { 158 return nil 159 } 160 return c.f.ConstructConstVal(result, o.ReturnType) 161 } 162 163 // FoldUnary evaluates a unary expression with a constant input. It returns 164 // a constant expression as long as it finds an appropriate overload function 165 // for the given operator and input type, and the evaluation causes no error. 166 func (c *CustomFuncs) FoldUnary(op opt.Operator, input opt.ScalarExpr) opt.ScalarExpr { 167 datum := memo.ExtractConstDatum(input) 168 169 o, ok := memo.FindUnaryOverload(op, input.DataType()) 170 if !ok { 171 return nil 172 } 173 174 result, err := o.Fn(c.f.evalCtx, datum) 175 if err != nil { 176 return nil 177 } 178 return c.f.ConstructConstVal(result, o.ReturnType) 179 } 180 181 // foldStringToRegclassCast resolves a string that is a table name into an OID 182 // by resolving the table name and returning its table ID. This permits the 183 // optimizer to do intelligent things like push down filters that look like: 184 // ... WHERE oid = 'my_table'::REGCLASS 185 func (c *CustomFuncs) foldStringToRegclassCast( 186 input opt.ScalarExpr, typ *types.T, 187 ) (opt.ScalarExpr, error) { 188 // Special case: we're casting a string to a REGCLASS oid, which is a 189 // table id lookup. 190 flags := cat.Flags{AvoidDescriptorCaches: false, NoTableStats: true} 191 datum := memo.ExtractConstDatum(input) 192 s := tree.MustBeDString(datum) 193 tn, err := parser.ParseQualifiedTableName(string(s)) 194 if err != nil { 195 return nil, err 196 } 197 ds, resName, err := c.f.catalog.ResolveDataSource(c.f.evalCtx.Context, flags, tn) 198 if err != nil { 199 return nil, err 200 } 201 202 c.mem.Metadata().AddDependency(opt.DepByName(&resName), ds, privilege.SELECT) 203 204 regclassOid := tree.NewDOidWithName(tree.DInt(ds.PostgresDescriptorID()), types.RegClass, string(tn.ObjectName)) 205 return c.f.ConstructConstVal(regclassOid, typ), nil 206 207 } 208 209 // FoldCast evaluates a cast expression with a constant input. It returns 210 // a constant expression as long as the evaluation causes no error. 211 func (c *CustomFuncs) FoldCast(input opt.ScalarExpr, typ *types.T) opt.ScalarExpr { 212 if typ.Family() == types.OidFamily { 213 if typ.Oid() == types.RegClass.Oid() && input.DataType().Family() == types.StringFamily { 214 expr, err := c.foldStringToRegclassCast(input, typ) 215 if err == nil { 216 return expr 217 } 218 } 219 // Save this cast for the execbuilder. 220 return nil 221 } 222 223 datum := memo.ExtractConstDatum(input) 224 texpr := tree.NewTypedCastExpr(datum, typ) 225 226 result, err := texpr.Eval(c.f.evalCtx) 227 if err != nil { 228 return nil 229 } 230 231 return c.f.ConstructConstVal(result, typ) 232 } 233 234 // isMonotonicConversion returns true if conversion of a value from FROM to 235 // TO is monotonic. 236 // That is, if a and b are values of type FROM, then 237 // 238 // 1. a = b implies a::TO = b::TO and 239 // 2. a < b implies a::TO <= b::TO 240 // 241 // Property (1) can be violated by cases like: 242 // 243 // '-0'::FLOAT = '0'::FLOAT, but '-0'::FLOAT::STRING != '0'::FLOAT::STRING 244 // 245 // Property (2) can be violated by cases like: 246 // 247 // 2 < 10, but 2::STRING > 10::STRING. 248 // 249 // Note that the stronger version of (2), 250 // 251 // a < b implies a::TO < b::TO 252 // 253 // is not required, for instance this is not generally true of conversion from 254 // a TIMESTAMP to a DATE, but certain such conversions can still generate spans 255 // in some cases where values under FROM and TO are "the same" (such as where a 256 // TIMESTAMP precisely falls on a date boundary). We don't need this property 257 // because we will subsequently check that the values can round-trip to ensure 258 // that we don't lose any information by doing the conversion. 259 // TODO(justin): fill this out with the complete set of such conversions. 260 func isMonotonicConversion(from, to *types.T) bool { 261 switch from.Family() { 262 case types.TimestampFamily, types.TimestampTZFamily, types.DateFamily: 263 switch to.Family() { 264 case types.TimestampFamily, types.TimestampTZFamily, types.DateFamily: 265 return true 266 } 267 return false 268 269 case types.IntFamily, types.FloatFamily, types.DecimalFamily: 270 switch to.Family() { 271 case types.IntFamily, types.FloatFamily, types.DecimalFamily: 272 return true 273 } 274 return false 275 } 276 277 return false 278 } 279 280 // UnifyComparison attempts to convert a constant expression to the type of the 281 // variable expression, if that conversion can round-trip and is monotonic. 282 func (c *CustomFuncs) UnifyComparison(left, right opt.ScalarExpr) opt.ScalarExpr { 283 v := left.(*memo.VariableExpr) 284 cnst := right.(*memo.ConstExpr) 285 286 desiredType := v.DataType() 287 originalType := cnst.DataType() 288 289 // Don't bother if they're already the same. 290 if desiredType.Equivalent(originalType) { 291 return nil 292 } 293 294 if !isMonotonicConversion(originalType, desiredType) { 295 return nil 296 } 297 298 // Check that the datum can round-trip between the types. If this is true, it 299 // means we don't lose any information needed to generate spans, and combined 300 // with monotonicity means that it's safe to convert the RHS to the type of 301 // the LHS. 302 convertedDatum, err := tree.PerformCast(c.f.evalCtx, cnst.Value, desiredType) 303 if err != nil { 304 return nil 305 } 306 307 convertedBack, err := tree.PerformCast(c.f.evalCtx, convertedDatum, originalType) 308 if err != nil { 309 return nil 310 } 311 312 if convertedBack.Compare(c.f.evalCtx, cnst.Value) != 0 { 313 return nil 314 } 315 316 return c.f.ConstructConst(convertedDatum, desiredType) 317 } 318 319 // FoldComparison evaluates a comparison expression with constant inputs. It 320 // returns a constant expression as long as it finds an appropriate overload 321 // function for the given operator and input types, and the evaluation causes 322 // no error. 323 func (c *CustomFuncs) FoldComparison(op opt.Operator, left, right opt.ScalarExpr) opt.ScalarExpr { 324 lDatum, rDatum := memo.ExtractConstDatum(left), memo.ExtractConstDatum(right) 325 326 var flipped, not bool 327 o, flipped, not, ok := memo.FindComparisonOverload(op, left.DataType(), right.DataType()) 328 if !ok { 329 return nil 330 } 331 332 if flipped { 333 lDatum, rDatum = rDatum, lDatum 334 } 335 336 result, err := o.Fn(c.f.evalCtx, lDatum, rDatum) 337 if err != nil { 338 return nil 339 } 340 if b, ok := result.(*tree.DBool); ok && not { 341 result = tree.MakeDBool(!*b) 342 } 343 return c.f.ConstructConstVal(result, types.Bool) 344 } 345 346 // FoldIndirection evaluates an array indirection operator with constant inputs. 347 // It returns the referenced array element as a constant value, or nil if the 348 // evaluation results in an error. 349 func (c *CustomFuncs) FoldIndirection(input, index opt.ScalarExpr) opt.ScalarExpr { 350 // Index is 1-based, so convert to 0-based. 351 indexD := memo.ExtractConstDatum(index) 352 353 // Case 1: The input is a static array constructor. 354 if arr, ok := input.(*memo.ArrayExpr); ok { 355 if indexInt, ok := indexD.(*tree.DInt); ok { 356 indexI := int(*indexInt) - 1 357 if indexI >= 0 && indexI < len(arr.Elems) { 358 return arr.Elems[indexI] 359 } 360 return c.f.ConstructNull(arr.Typ.ArrayContents()) 361 } 362 if indexD == tree.DNull { 363 return c.f.ConstructNull(arr.Typ.ArrayContents()) 364 } 365 return nil 366 } 367 368 // Case 2: The input is a constant DArray. 369 if memo.CanExtractConstDatum(input) { 370 inputD := memo.ExtractConstDatum(input) 371 texpr := tree.NewTypedIndirectionExpr(inputD, indexD, input.DataType().ArrayContents()) 372 result, err := texpr.Eval(c.f.evalCtx) 373 if err == nil { 374 return c.f.ConstructConstVal(result, texpr.ResolvedType()) 375 } 376 } 377 378 return nil 379 } 380 381 // FoldColumnAccess tries to evaluate a tuple column access operator with a 382 // constant tuple input (though tuple field values do not need to be constant). 383 // It returns the referenced tuple field value, or nil if folding is not 384 // possible or results in an error. 385 func (c *CustomFuncs) FoldColumnAccess(input opt.ScalarExpr, idx memo.TupleOrdinal) opt.ScalarExpr { 386 // Case 1: The input is a static tuple constructor. 387 if tup, ok := input.(*memo.TupleExpr); ok { 388 return tup.Elems[idx] 389 } 390 391 // Case 2: The input is a constant DTuple. 392 if memo.CanExtractConstDatum(input) { 393 datum := memo.ExtractConstDatum(input) 394 395 texpr := tree.NewTypedColumnAccessExpr(datum, "" /* by-index access */, int(idx)) 396 result, err := texpr.Eval(c.f.evalCtx) 397 if err == nil { 398 return c.f.ConstructConstVal(result, texpr.ResolvedType()) 399 } 400 } 401 402 return nil 403 } 404 405 // FoldFunction evaluates a function expression with constant inputs. It 406 // returns a constant expression as long as the function is contained in the 407 // FoldFunctionWhitelist, and the evaluation causes no error. 408 func (c *CustomFuncs) FoldFunction( 409 args memo.ScalarListExpr, private *memo.FunctionPrivate, 410 ) opt.ScalarExpr { 411 // Non-normal function classes (aggregate, window, generator) cannot be 412 // folded into a single constant. 413 if private.Properties.Class != tree.NormalClass { 414 return nil 415 } 416 // Functions that aren't immutable and also not in the whitelist cannot 417 // be folded. 418 if _, ok := FoldFunctionWhitelist[private.Name]; !ok && private.Overload.Volatility > tree.VolatilityImmutable { 419 return nil 420 } 421 422 exprs := make(tree.TypedExprs, len(args)) 423 for i := range exprs { 424 exprs[i] = memo.ExtractConstDatum(args[i]) 425 } 426 funcRef := tree.WrapFunction(private.Name) 427 fn := tree.NewTypedFuncExpr( 428 funcRef, 429 0, /* aggQualifier */ 430 exprs, 431 nil, /* filter */ 432 nil, /* windowDef */ 433 private.Typ, 434 private.Properties, 435 private.Overload, 436 ) 437 438 result, err := fn.Eval(c.f.evalCtx) 439 if err != nil { 440 return nil 441 } 442 return c.f.ConstructConstVal(result, private.Typ) 443 } 444 445 // FoldFunctionWhitelist contains non-immutable functions that are nevertheless 446 // known to be safe for folding. 447 var FoldFunctionWhitelist = map[string]struct{}{ 448 // The SQL statement is generated in the optbuilder phase, so the remaining 449 // function execution is immutable. 450 "addgeometrycolumn": {}, 451 452 // Query plan cache is invalidated on location changes. 453 "crdb_internal.locality_value": {}, 454 }