github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/norm/rules/prune_cols.opt (about) 1 # ============================================================================= 2 # prune_cols.opt contains normalization rules that eliminate columns that are 3 # never used. For example: 4 # 5 # SELECT x FROM (SELECT x, y, z FROM a) WHERE y = 10 6 # 7 # The "z" column is never referenced, either by the filter condition or by the 8 # final projection. Therefore, the query can be rewritten as: 9 # 10 # SELECT x FROM (SELECT x, y FROM a) WHERE y = 10 11 # 12 # Unused columns are very common, especially when the SQL * symbol is used to 13 # select all columns in a table. They can be expensive to fetch and transfer, so 14 # it's important to retain only columns that are actually needed by the query. 15 # 16 # The rules work by "pushing down" a new Project operator into any child inputs 17 # that have unused columns. The Project only includes columns that are needed by 18 # some operator in its subtree. The new Project may then spawn a sub-Project of 19 # its own that gets pushed even further down the tree. Eventually, a Project 20 # recursively reaches an operator that is capable of filtering columns, like 21 # Scan or Project, and it will be merged into that operator. 22 # 23 # Pruning unused columns is not desirable if the Project operator "gets stuck" 24 # during push down, and is unable to merge with another column filtering 25 # operator. This situation causes the expression tree to become littered with 26 # extra Project operators that impede pattern matching. To avoid this problem, 27 # the RelationalProps.Rules.PruneCols property tracks columns which can be 28 # pruned without requiring extra Project operators. The Prune rules use this to 29 # only push down Project operators that are sure to merge into another operator 30 # at the end of their journey. See the PruneCols comment for more details. 31 # ============================================================================= 32 33 # PruneProjectCols discards columns from a nested project which are not used by 34 # the outer project. 35 [PruneProjectCols, Normalize] 36 (Project 37 $project:(Project) 38 $projections:* 39 $passthrough:* & 40 (CanPruneCols 41 $project 42 $needed:(UnionCols 43 (ProjectionOuterCols $projections) 44 $passthrough 45 ) 46 ) 47 ) 48 => 49 (Project (PruneCols $project $needed) $projections $passthrough) 50 51 # PruneScanCols discards Scan operator columns that are never used. The needed 52 # columns are pushed down into the Scan's opt.ScanOpDef private. 53 [PruneScanCols, Normalize] 54 (Project 55 $input:(Scan) 56 $projections:* 57 $passthrough:* & 58 (CanPruneCols 59 $input 60 $needed:(UnionCols 61 (ProjectionOuterCols $projections) 62 $passthrough 63 ) 64 ) 65 ) 66 => 67 (Project (PruneCols $input $needed) $projections $passthrough) 68 69 # PruneSelectCols discards Select input columns that are never used. 70 # 71 # The PruneCols property should prevent this rule (which pushes Project below 72 # Select) from cycling with the PushSelectIntoProject rule (which pushes Select 73 # below Project). 74 [PruneSelectCols, Normalize] 75 (Project 76 (Select $input:* $filters:*) 77 $projections:* 78 $passthrough:* & 79 (CanPruneCols 80 $input 81 $needed:(UnionCols3 82 (FilterOuterCols $filters) 83 (ProjectionOuterCols $projections) 84 $passthrough 85 ) 86 ) 87 ) 88 => 89 (Project 90 (Select (PruneCols $input $needed) $filters) 91 $projections 92 $passthrough 93 ) 94 95 # PruneLimitCols discards Limit input columns that are never used. 96 # 97 # The PruneCols property should prevent this rule (which pushes Project below 98 # Limit) from cycling with the PushLimitIntoProject rule (which pushes Limit 99 # below Project). 100 [PruneLimitCols, Normalize] 101 (Project 102 (Limit $input:* $limit:* $ordering:*) 103 $projections:* 104 $passthrough:* & 105 (CanPruneCols 106 $input 107 $needed:(UnionCols3 108 (OrderingCols $ordering) 109 (ProjectionOuterCols $projections) 110 $passthrough 111 ) 112 ) 113 ) 114 => 115 (Project 116 (Limit 117 (PruneCols $input $needed) 118 $limit 119 (PruneOrdering $ordering $needed) 120 ) 121 $projections 122 $passthrough 123 ) 124 125 # PruneOffsetCols discards Offset input columns that are never used. 126 # 127 # The PruneCols property should prevent this rule (which pushes Project below 128 # Offset) from cycling with the PushOffsetIntoProject rule (which pushes Offset 129 # below Project). 130 [PruneOffsetCols, Normalize] 131 (Project 132 (Offset $input:* $offset:* $ordering:*) 133 $projections:* 134 $passthrough:* & 135 (CanPruneCols 136 $input 137 $needed:(UnionCols3 138 (OrderingCols $ordering) 139 (ProjectionOuterCols $projections) 140 $passthrough 141 ) 142 ) 143 ) 144 => 145 (Project 146 (Offset 147 (PruneCols $input $needed) 148 $offset 149 (PruneOrdering $ordering $needed) 150 ) 151 $projections 152 $passthrough 153 ) 154 155 # PruneJoinLeftCols discards columns on the left side of a join that are never 156 # used. 157 [PruneJoinLeftCols, Normalize] 158 (Project 159 $input:(Join $left:* $right:* $on:* $private:*) 160 $projections:* 161 $passthrough:* & 162 (CanPruneCols 163 $left 164 $needed:(UnionCols4 165 (OuterCols $right) 166 (FilterOuterCols $on) 167 (ProjectionOuterCols $projections) 168 $passthrough 169 ) 170 ) 171 ) 172 => 173 (Project 174 ((OpName $input) 175 (PruneCols $left $needed) 176 $right 177 $on 178 $private 179 ) 180 $projections 181 $passthrough 182 ) 183 184 # PruneJoinRightCols discards columns on the right side of a join that are never 185 # used. 186 # 187 # The PruneCols property should prevent this rule (which pushes Project below 188 # Join) from cycling with the TryDecorrelateProject rule (which pushes Join 189 # below Project). 190 [PruneJoinRightCols, Normalize] 191 (Project 192 $input:(Join $left:* $right:* $on:* $private:*) 193 $projections:* 194 $passthrough:* & 195 (CanPruneCols 196 $right 197 $needed:(UnionCols3 198 (FilterOuterCols $on) 199 (ProjectionOuterCols $projections) 200 $passthrough 201 ) 202 ) 203 ) 204 => 205 (Project 206 ((OpName $input) 207 $left 208 (PruneCols $right $needed) 209 $on 210 $private 211 ) 212 $projections 213 $passthrough 214 ) 215 216 # PruneSemiAntiJoinRightCols discards columns on the right side of a 217 # Semi or Anti join that are never used. This is similar to PruneJoinRightCols. 218 # PruneJoinRightCols normally prunes the RHS of a join but it can't do that 219 # in the case of Semi/Anti joins because the projection is eliminated after 220 # the LHS is pruned. This rule doesn't require a projection over the Semi/Anti 221 # join in order to prune the RHS. 222 [PruneSemiAntiJoinRightCols, Normalize] 223 (SemiJoin | SemiJoinApply | AntiJoin | AntiJoinApply 224 $left:* 225 $right:* 226 $on:* 227 $private:* & 228 (CanPruneCols $right $needed:(FilterOuterCols $on)) 229 ) 230 => 231 ((OpName) $left (PruneCols $right $needed) $on $private) 232 233 # PruneAggCols discards aggregation columns in a GroupBy that are never used. 234 # Note that UpsertDistinctOn is not included here because its columns are always 235 # used. 236 [PruneAggCols, Normalize] 237 (Project 238 $input:(GroupBy | ScalarGroupBy | DistinctOn 239 | EnsureDistinctOn 240 $innerInput:* 241 $aggregations:* 242 $groupingPrivate:* 243 ) 244 $projections:* 245 $passthrough:* & 246 (CanPruneAggCols 247 $aggregations 248 $needed:(UnionCols 249 (ProjectionOuterCols $projections) 250 $passthrough 251 ) 252 ) 253 ) 254 => 255 (Project 256 ((OpName $input) 257 $innerInput 258 (PruneAggCols $aggregations $needed) 259 $groupingPrivate 260 ) 261 $projections 262 $passthrough 263 ) 264 265 # PruneGroupByCols discards GroupBy input columns that are never used. Note that 266 # UpsertDistinctOn is not included here because its columns are always used. 267 [PruneGroupByCols, Normalize] 268 (GroupBy | ScalarGroupBy | DistinctOn | EnsureDistinctOn 269 $input:* 270 $aggregations:* 271 $groupingPrivate:* & 272 (CanPruneCols 273 $input 274 $needed:(UnionCols 275 (AggregationOuterCols $aggregations) 276 (NeededGroupingCols $groupingPrivate) 277 ) 278 ) 279 ) 280 => 281 ((OpName) 282 (PruneCols $input $needed) 283 $aggregations 284 (PruneOrderingGroupBy $groupingPrivate $needed) 285 ) 286 287 # PruneValuesCols discards Values columns that are never used. 288 [PruneValuesCols, Normalize] 289 (Project 290 $input:(Values) 291 $projections:* 292 $passthrough:* & 293 (CanPruneCols 294 $input 295 $needed:(UnionCols 296 (ProjectionOuterCols $projections) 297 $passthrough 298 ) 299 ) 300 ) 301 => 302 (Project (PruneCols $input $needed) $projections $passthrough) 303 304 # PruneOrdinalityCols discards Ordinality input columns that are never used. 305 [PruneOrdinalityCols, Normalize] 306 (Project 307 (Ordinality $input:* $ordinalityPrivate:*) 308 $projections:* 309 $passthrough:* & 310 (CanPruneCols 311 $input 312 $needed:(UnionCols3 313 (NeededOrdinalityCols $ordinalityPrivate) 314 (ProjectionOuterCols $projections) 315 $passthrough 316 ) 317 ) 318 ) 319 => 320 (Project 321 (Ordinality 322 (PruneCols $input $needed) 323 (PruneOrderingOrdinality $ordinalityPrivate $needed) 324 ) 325 $projections 326 $passthrough 327 ) 328 329 # PruneExplainCols discards Explain input columns that are never used by its 330 # required physical properties. 331 [PruneExplainCols, Normalize] 332 (Explain 333 $input:* 334 $explainPrivate:* & 335 (CanPruneCols 336 $input 337 $needed:(NeededExplainCols $explainPrivate) 338 ) 339 ) 340 => 341 (Explain (PruneCols $input $needed) $explainPrivate) 342 343 # PruneProjectSetCols discards ProjectSet columns that are never used. 344 [PruneProjectSetCols, Normalize] 345 (Project 346 $input:(ProjectSet $innerInput:* $zip:*) 347 $projections:* 348 $passthrough:* & 349 (CanPruneCols 350 $input 351 $needed:(UnionCols3 352 (ZipOuterCols $zip) 353 (ProjectionOuterCols $projections) 354 $passthrough 355 ) 356 ) 357 ) 358 => 359 (Project 360 (ProjectSet (PruneCols $innerInput $needed) $zip) 361 $projections 362 $passthrough 363 ) 364 365 # PruneWindowOutputCols eliminates unused window functions from a Window 366 # expression. 367 [PruneWindowOutputCols, Normalize] 368 (Project 369 (Window $input:* $windows:* $private:*) 370 $projections:* 371 $passthrough:* & 372 (CanPruneWindows 373 $needed:(UnionCols 374 (ProjectionOuterCols $projections) 375 $passthrough 376 ) 377 $windows 378 ) 379 ) 380 => 381 (Project 382 (Window $input (PruneWindows $needed $windows) $private) 383 $projections 384 $passthrough 385 ) 386 387 # PruneWindowInputCols discards window passthrough columns which are never used. 388 # NB: This rule should go after PruneWindowOutputCols, or else this rule can get 389 # into a cycle. 390 [PruneWindowInputCols, Normalize] 391 (Project 392 $input:(Window $innerInput:* $fn:* $private:*) 393 $projections:* 394 $passthrough:* & 395 (CanPruneCols 396 $input 397 $needed:(UnionCols3 398 (NeededWindowCols $fn $private) 399 (ProjectionOuterCols $projections) 400 $passthrough 401 ) 402 ) 403 ) 404 => 405 (Project 406 (Window (PruneCols $innerInput $needed) $fn $private) 407 $projections 408 $passthrough 409 ) 410 411 # PruneMutationFetchCols removes columns from the mutation operator's FetchCols 412 # set if they are never used. Removing FetchCols can in turn can trigger the 413 # PruneMutationInputCols rule, which can prune any input columns which are now 414 # unreferenced. 415 [PruneMutationFetchCols, Normalize] 416 (Update | Upsert | Delete 417 $input:* 418 $checks:* 419 $mutationPrivate:* & 420 (CanPruneMutationFetchCols 421 $mutationPrivate 422 $needed:(NeededMutationFetchCols 423 (OpName) 424 $mutationPrivate 425 ) 426 ) 427 ) 428 => 429 ((OpName) 430 $input 431 $checks 432 (PruneMutationFetchCols $mutationPrivate $needed) 433 ) 434 435 # PruneMutationInputCols discards input columns that are never used by the 436 # mutation operator. 437 [PruneMutationInputCols, Normalize] 438 (Insert | Update | Upsert | Delete 439 $input:* 440 $checks:* 441 $mutationPrivate:* & 442 (CanPruneCols 443 $input 444 $needed:(NeededMutationCols $mutationPrivate $checks) 445 ) 446 ) 447 => 448 ((OpName) (PruneCols $input $needed) $checks $mutationPrivate) 449 450 # PruneReturningCols removes columns from the mutation operator's ReturnCols 451 # set if they are not used in the RETURNING clause of the mutation. 452 # Removing ReturnCols will then allow the PruneMutationFetchCols to be more 453 # conservative with the fetch columns. 454 # TODO(ridwanmsharif): Mutations shouldn't need to return the primary key 455 # columns. Make appropriate changes to SQL execution to accommodate this. 456 [PruneMutationReturnCols, Normalize] 457 (Project 458 $input:(Insert | Update | Upsert | Delete 459 $innerInput:* 460 $checks:* 461 $mutationPrivate:* 462 ) 463 $projections:* 464 $passthrough:* & 465 (CanPruneMutationReturnCols 466 $mutationPrivate 467 $needed:(UnionCols3 468 (PrimaryKeyCols (MutationTable $mutationPrivate)) 469 (ProjectionOuterCols $projections) 470 $passthrough 471 ) 472 ) 473 ) 474 => 475 (Project 476 ((OpName $input) 477 $innerInput 478 $checks 479 (PruneMutationReturnCols $mutationPrivate $needed) 480 ) 481 $projections 482 $passthrough 483 ) 484 485 # PruneWithScanCols discards columns scanned from the WithScan that are never 486 # used. 487 [PruneWithScanCols, Normalize] 488 (Project 489 $input:(WithScan) 490 $projections:* 491 $passthrough:* & 492 (CanPruneCols 493 $input 494 $needed:(UnionCols 495 (ProjectionOuterCols $projections) 496 $passthrough 497 ) 498 ) 499 ) 500 => 501 (Project (PruneCols $input $needed) $projections $passthrough) 502 503 # PruneWithCols pushes a Project operator beneath a With. It's ok to 504 # unconditionally push this Project down, since in the pruning case, we're 505 # getting the project closer to the source of any prune requests, and if we 506 # just end up with a Project incidentally, it's safe to just always push it 507 # down. 508 [PruneWithCols, Normalize] 509 (Project 510 (With $binding:* $input:* $private:*) 511 $projections:* 512 $passthrough:* 513 ) 514 => 515 (With 516 $binding 517 (Project $input $projections $passthrough) 518 $private 519 ) 520 521 # PruneUnionAllCols prunes columns from the left and right input relations that 522 # are never used. Since UNION ALL preserves duplicates, any column may be pruned 523 # if it is not needed, which is not generally true of set operators. 524 # 525 # Since UnionAll requires that both inputs have an equal number of columns, 526 # rather than using PruneCols to prune the left and right sides, this rule 527 # pushes down Projects on both sides to ensure that exactly the needed columns 528 # are passed as input to the UnionAll, to prevent situations where one side has 529 # more columns left over after PruneCols than the other (for instance, if $left 530 # is a normal scan where all columns may be pruned, but $right is a scan with a 531 # filter, leading to an additional column being kept on just the right side). 532 # If extraneous, these Projects may be cleaned up later by rules like 533 # EliminateProject. 534 [PruneUnionAllCols, Normalize] 535 (Project 536 $union:(UnionAll $left:* $right:* $colmap:*) 537 $projections:* 538 $passthrough:* & 539 (CanPruneCols 540 $union 541 $needed:(UnionCols 542 (ProjectionOuterCols $projections) 543 $passthrough 544 ) 545 ) 546 ) 547 => 548 (Project 549 (UnionAll 550 (Project $left [] (NeededColMapLeft $needed $colmap)) 551 (Project $right [] (NeededColMapRight $needed $colmap)) 552 (PruneSetPrivate $needed $colmap) 553 ) 554 $projections 555 $passthrough 556 )