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  )