github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/norm/project_funcs.go (about) 1 // Copyright 2020 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 "fmt" 15 16 "github.com/cockroachdb/cockroach/pkg/sql/opt" 17 "github.com/cockroachdb/cockroach/pkg/sql/opt/memo" 18 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 19 "github.com/cockroachdb/cockroach/pkg/sql/types" 20 "github.com/cockroachdb/errors" 21 ) 22 23 // CanMergeProjections returns true if the outer Projections operator never 24 // references any of the inner Projections columns. If true, then the outer does 25 // not depend on the inner, and the two can be merged into a single set. 26 func (c *CustomFuncs) CanMergeProjections(outer, inner memo.ProjectionsExpr) bool { 27 innerCols := c.ProjectionCols(inner) 28 for i := range outer { 29 if outer[i].ScalarProps().OuterCols.Intersects(innerCols) { 30 return false 31 } 32 } 33 return true 34 } 35 36 // MergeProjections concatenates the synthesized columns from the outer 37 // Projections operator, and the synthesized columns from the inner Projections 38 // operator that are passed through by the outer. Note that the outer 39 // synthesized columns must never contain references to the inner synthesized 40 // columns; this can be verified by first calling CanMergeProjections. 41 func (c *CustomFuncs) MergeProjections( 42 outer, inner memo.ProjectionsExpr, passthrough opt.ColSet, 43 ) memo.ProjectionsExpr { 44 // No need to recompute properties on the new projections, since they should 45 // still be valid. 46 newProjections := make(memo.ProjectionsExpr, len(outer), len(outer)+len(inner)) 47 copy(newProjections, outer) 48 for i := range inner { 49 item := &inner[i] 50 if passthrough.Contains(item.Col) { 51 newProjections = append(newProjections, *item) 52 } 53 } 54 return newProjections 55 } 56 57 // MergeProjectWithValues merges a Project operator with its input Values 58 // operator. This is only possible in certain circumstances, which are described 59 // in the MergeProjectWithValues rule comment. 60 // 61 // Values columns that are part of the Project passthrough columns are retained 62 // in the final Values operator, and Project synthesized columns are added to 63 // it. Any unreferenced Values columns are discarded. For example: 64 // 65 // SELECT column1, 3 FROM (VALUES (1, 2)) 66 // => 67 // (VALUES (1, 3)) 68 // 69 func (c *CustomFuncs) MergeProjectWithValues( 70 projections memo.ProjectionsExpr, passthrough opt.ColSet, input memo.RelExpr, 71 ) memo.RelExpr { 72 newExprs := make(memo.ScalarListExpr, 0, len(projections)+passthrough.Len()) 73 newTypes := make([]*types.T, 0, len(newExprs)) 74 newCols := make(opt.ColList, 0, len(newExprs)) 75 76 values := input.(*memo.ValuesExpr) 77 tuple := values.Rows[0].(*memo.TupleExpr) 78 for i, colID := range values.Cols { 79 if passthrough.Contains(colID) { 80 newExprs = append(newExprs, tuple.Elems[i]) 81 newTypes = append(newTypes, tuple.Elems[i].DataType()) 82 newCols = append(newCols, colID) 83 } 84 } 85 86 for i := range projections { 87 item := &projections[i] 88 newExprs = append(newExprs, item.Element) 89 newTypes = append(newTypes, item.Element.DataType()) 90 newCols = append(newCols, item.Col) 91 } 92 93 tupleTyp := types.MakeTuple(newTypes) 94 rows := memo.ScalarListExpr{c.f.ConstructTuple(newExprs, tupleTyp)} 95 return c.f.ConstructValues(rows, &memo.ValuesPrivate{ 96 Cols: newCols, 97 ID: values.ID, 98 }) 99 } 100 101 // CanUnnestTuplesFromValues returns true if the Values operator has a single 102 // column containing tuples that can be unfolded into multiple columns. 103 // 104 // This is the case if: 105 // 106 // 1. The Values operator has exactly one output column. 107 // 108 // 2. The single output column is of type tuple. 109 // 110 // 3. There is at least one row. 111 // 112 // 4. All tuples in the single column are either TupleExpr's or ConstExpr's 113 // that wrap DTuples, as opposed to dynamically generated tuples. 114 // 115 func (c *CustomFuncs) CanUnnestTuplesFromValues(expr memo.RelExpr) bool { 116 values := expr.(*memo.ValuesExpr) 117 if !c.HasOneCol(expr) { 118 return false 119 } 120 colTypeFam := c.mem.Metadata().ColumnMeta(values.Cols[0]).Type.Family() 121 if colTypeFam != types.TupleFamily { 122 return false 123 } 124 if len(values.Rows) < 1 { 125 return false 126 } 127 for _, row := range values.Rows { 128 if !c.IsStaticTuple(row.(*memo.TupleExpr).Elems[0]) { 129 return false 130 } 131 } 132 return true 133 } 134 135 // OnlyTupleColumnsAccessed ensures that the input ProjectionsExpr contains no 136 // direct references to the tuple represented by the given ColumnID. 137 func (c *CustomFuncs) OnlyTupleColumnsAccessed( 138 projections memo.ProjectionsExpr, tupleCol opt.ColumnID, 139 ) bool { 140 var check func(expr opt.Expr) bool 141 check = func(expr opt.Expr) bool { 142 switch t := expr.(type) { 143 case *memo.ColumnAccessExpr: 144 switch t.Input.(type) { 145 case *memo.VariableExpr: 146 return true 147 } 148 149 case *memo.VariableExpr: 150 return t.Col != tupleCol 151 } 152 for i, n := 0, expr.ChildCount(); i < n; i++ { 153 if !check(expr.Child(i)) { 154 return false 155 } 156 } 157 return true 158 } 159 160 for i := range projections { 161 if !check(projections[i].Element) { 162 return false 163 } 164 } 165 166 return true 167 } 168 169 // MakeColsForUnnestTuples takes in the ColumnID of a tuple column and adds new 170 // columns to metadata corresponding to each field in the tuple. A ColList 171 // containing these new columns is returned. 172 func (c *CustomFuncs) MakeColsForUnnestTuples(tupleColID opt.ColumnID) opt.ColList { 173 mem := c.mem.Metadata() 174 tupleType := c.mem.Metadata().ColumnMeta(tupleColID).Type 175 176 // Create a new column for each position in the tuple. Add it to outColIDs. 177 tupleLen := len(tupleType.TupleContents()) 178 tupleAlias := mem.ColumnMeta(tupleColID).Alias 179 outColIDs := make(opt.ColList, tupleLen) 180 for i := 0; i < tupleLen; i++ { 181 newAlias := fmt.Sprintf("%s_%d", tupleAlias, i+1) 182 newColID := mem.AddColumn(newAlias, tupleType.TupleContents()[i]) 183 outColIDs[i] = newColID 184 } 185 return outColIDs 186 } 187 188 // UnnestTuplesFromValues takes in a Values operator that has a single column 189 // of tuples and a ColList corresponding to each tuple field. It returns a new 190 // Values operator with the tuple expanded out into the Values rows. 191 // For example, these rows: 192 // 193 // ((1, 2),) 194 // ((3, 4),) 195 // 196 // would be unnested as: 197 // 198 // (1, 2) 199 // (3, 4) 200 // 201 func (c *CustomFuncs) UnnestTuplesFromValues( 202 expr memo.RelExpr, valuesCols opt.ColList, 203 ) memo.RelExpr { 204 values := expr.(*memo.ValuesExpr) 205 tupleColID := values.Cols[0] 206 tupleType := c.mem.Metadata().ColumnMeta(tupleColID).Type 207 outTuples := make(memo.ScalarListExpr, len(values.Rows)) 208 209 // Pull the inner tuples out of the single column of the Values operator and 210 // put them into a ScalarListExpr to be used in the new Values operator. 211 for i, row := range values.Rows { 212 outerTuple := row.(*memo.TupleExpr) 213 switch t := outerTuple.Elems[0].(type) { 214 case *memo.TupleExpr: 215 outTuples[i] = t 216 217 case *memo.ConstExpr: 218 dTuple := t.Value.(*tree.DTuple) 219 tupleVals := make(memo.ScalarListExpr, len(dTuple.D)) 220 for i, v := range dTuple.D { 221 val := c.f.ConstructConstVal(v, tupleType.TupleContents()[i]) 222 tupleVals[i] = val 223 } 224 outTuples[i] = c.f.ConstructTuple(tupleVals, tupleType) 225 226 default: 227 panic(errors.AssertionFailedf("unhandled input op: %T", t)) 228 } 229 } 230 231 // Return new ValuesExpr with new tuples. 232 valuesPrivate := &memo.ValuesPrivate{Cols: valuesCols, ID: c.mem.Metadata().NextUniqueID()} 233 return c.f.ConstructValues(outTuples, valuesPrivate) 234 } 235 236 // FoldTupleColumnAccess constructs a new ProjectionsExpr from the old one with 237 // any ColumnAccess operators that refer to the original tuple column (oldColID) 238 // replaced by new columns from the output of the given ValuesExpr. 239 func (c *CustomFuncs) FoldTupleColumnAccess( 240 projections memo.ProjectionsExpr, valuesCols opt.ColList, oldColID opt.ColumnID, 241 ) memo.ProjectionsExpr { 242 newProjections := make(memo.ProjectionsExpr, len(projections)) 243 244 // Recursively traverses a ProjectionsItem element and replaces references to 245 // positions in the tuple rows with one of the newly constructed columns. 246 var replace ReplaceFunc 247 replace = func(nd opt.Expr) opt.Expr { 248 if colAccess, ok := nd.(*memo.ColumnAccessExpr); ok { 249 if variable, ok := colAccess.Input.(*memo.VariableExpr); ok { 250 // Skip past references to columns other than the input tuple column. 251 if variable.Col == oldColID { 252 return c.f.ConstructVariable(valuesCols[int(colAccess.Idx)]) 253 } 254 } 255 } 256 return c.f.Replace(nd, replace) 257 } 258 259 // Construct and return a new ProjectionsExpr using the new ColumnIDs. 260 for i := range projections { 261 projection := &projections[i] 262 newProjections[i] = c.f.ConstructProjectionsItem( 263 replace(projection.Element).(opt.ScalarExpr), projection.Col) 264 } 265 return newProjections 266 } 267 268 // CanPushColumnRemappingIntoValues returns true if there is at least one 269 // ProjectionsItem for which the following conditions hold: 270 // 271 // 1. The ProjectionsItem remaps an output column from the given ValuesExpr. 272 // 273 // 2. The Values output column being remapped is not in the passthrough set. 274 // 275 func (c *CustomFuncs) CanPushColumnRemappingIntoValues( 276 projections memo.ProjectionsExpr, passthrough opt.ColSet, values memo.RelExpr, 277 ) bool { 278 outputCols := values.(*memo.ValuesExpr).Relational().OutputCols 279 for i := range projections { 280 if variable, ok := projections[i].Element.(*memo.VariableExpr); ok { 281 if !passthrough.Contains(variable.Col) && outputCols.Contains(variable.Col) { 282 return true 283 } 284 } 285 } 286 return false 287 } 288 289 // PushColumnRemappingIntoValues folds ProjectionsItems into the passthrough set 290 // if all they do is remap output columns from the ValuesExpr input. The Values 291 // output columns are replaced by the corresponding columns from the folded 292 // ProjectionsItems. 293 // 294 // Example: 295 // project 296 // ├── columns: x:2!null 297 // ├── values 298 // │ ├── columns: column1:1!null 299 // │ ├── cardinality: [2 - 2] 300 // │ ├── (1,) 301 // │ └── (2,) 302 // └── projections 303 // └── column1:1 [as=x:2, outer=(1)] 304 // => 305 // project 306 // ├── columns: x:2!null 307 // └── values 308 // ├── columns: x:2!null 309 // ├── cardinality: [2 - 2] 310 // ├── (1,) 311 // └── (2,) 312 // 313 // This allows other rules to fire. In the above example, EliminateProject can 314 // now remove the Project altogether. 315 func (c *CustomFuncs) PushColumnRemappingIntoValues( 316 oldInput memo.RelExpr, oldProjections memo.ProjectionsExpr, oldPassthrough opt.ColSet, 317 ) memo.RelExpr { 318 oldValues := oldInput.(*memo.ValuesExpr) 319 oldValuesCols := oldValues.Relational().OutputCols 320 newPassthrough := oldPassthrough.Copy() 321 replacementCols := make(map[opt.ColumnID]opt.ColumnID) 322 var newProjections memo.ProjectionsExpr 323 324 // Construct the new ProjectionsExpr and passthrough columns. Keep track of 325 // which Values columns are to be replaced. 326 for i := range oldProjections { 327 oldItem := &oldProjections[i] 328 329 // A column can be replaced if the following conditions hold: 330 // 1. The current ProjectionsItem contains a VariableExpr. 331 // 2. The VariableExpr references a column from the ValuesExpr. 332 // 3. The column has not already been assigned a replacement. 333 // 4. The column is not a passthrough column. 334 if v, ok := oldItem.Element.(*memo.VariableExpr); ok { 335 if targetCol := v.Col; oldValuesCols.Contains(targetCol) { 336 if replacementCols[targetCol] == 0 { 337 if !newPassthrough.Contains(targetCol) { 338 // The conditions for column replacement have been met. Map the old 339 // Values output column to its replacement and add the replacement 340 // to newPassthrough so it will become a passthrough column. 341 // Continue so that no corresponding ProjectionsItem is added to 342 // newProjections. 343 replacementCols[targetCol] = oldItem.Col 344 newPassthrough.Add(oldItem.Col) 345 continue 346 } 347 } 348 } 349 } 350 // The current ProjectionsItem cannot be folded into newPassthrough because 351 // the above conditions do not hold. Simply add it to newProjections. Later, 352 // every ProjectionsItem will be recursively traversed and any references to 353 // columns that are in replacementCols will be replaced. 354 newProjections = append(newProjections, *oldItem) 355 } 356 357 // Recursively traverses a ProjectionsItem element and replaces references to 358 // old ValuesExpr columns with the replacement columns. This ensures that any 359 // remaining references to old columns are replaced. For example: 360 // 361 // WITH t AS (SELECT x, x FROM (VALUES (1)) f(x)) SELECT * FROM t; 362 // 363 // The "x" column of the Values operator will be mapped to the first column of 364 // t. This first column will become a passthrough column. Now, the remaining 365 // reference to "x" in the second column of t needs to be replaced by the new 366 // passthrough column. 367 var replace ReplaceFunc 368 replace = func(nd opt.Expr) opt.Expr { 369 switch t := nd.(type) { 370 case *memo.VariableExpr: 371 if replaceCol := replacementCols[t.Col]; replaceCol != 0 { 372 return c.f.ConstructVariable(replaceCol) 373 } 374 } 375 return c.f.Replace(nd, replace) 376 } 377 378 // Traverse each element in newProjections and replace col references as 379 // dictated by replacementCols. 380 for i := range newProjections { 381 item := &newProjections[i] 382 newProjections[i] = c.f.ConstructProjectionsItem( 383 replace(item.Element).(opt.ScalarExpr), item.Col) 384 } 385 386 // Replace all columns in newValuesColList that have been remapped by the old 387 // ProjectionsExpr. 388 oldValuesColList := oldValues.Cols 389 newValuesColList := make(opt.ColList, len(oldValuesColList)) 390 for i := range newValuesColList { 391 if replaceCol := replacementCols[oldValuesColList[i]]; replaceCol != 0 { 392 newValuesColList[i] = replaceCol 393 } else { 394 newValuesColList[i] = oldValuesColList[i] 395 } 396 } 397 398 // Construct a new ValuesExpr with the replaced cols. 399 newValues := c.f.ConstructValues( 400 oldValues.Rows, 401 &memo.ValuesPrivate{Cols: newValuesColList, ID: c.f.Metadata().NextUniqueID()}) 402 403 // Construct and return a new ProjectExpr with the new ValuesExpr as input. 404 return c.f.ConstructProject(newValues, newProjections, newPassthrough) 405 } 406 407 // IsStaticTuple returns true if the given ScalarExpr is either a TupleExpr or a 408 // ConstExpr wrapping a DTuple. Expressions within a static tuple can be 409 // determined during planning: 410 // 411 // (1, 2) 412 // (x, y) 413 // 414 // By contrast, expressions within a dynamic tuple can only be determined at 415 // run-time: 416 // 417 // SELECT (SELECT (x, y) FROM xy) 418 // 419 // Here, if there are 0 rows in xy, the tuple value will be NULL. Or, if there 420 // is more than one row in xy, a dynamic error will be raised. 421 func (c *CustomFuncs) IsStaticTuple(expr opt.ScalarExpr) bool { 422 switch t := expr.(type) { 423 case *memo.TupleExpr: 424 return true 425 426 case *memo.ConstExpr: 427 if _, ok := t.Value.(*tree.DTuple); ok { 428 return true 429 } 430 } 431 return false 432 }