github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/memo/logical_props_builder.go (about)

     1  // Copyright 2018 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 memo
    12  
    13  import (
    14  	"math"
    15  
    16  	"github.com/cockroachdb/cockroach/pkg/sql/opt"
    17  	"github.com/cockroachdb/cockroach/pkg/sql/opt/constraint"
    18  	"github.com/cockroachdb/cockroach/pkg/sql/opt/props"
    19  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    20  	"github.com/cockroachdb/cockroach/pkg/util/log"
    21  	"github.com/cockroachdb/errors"
    22  )
    23  
    24  var fdAnnID = opt.NewTableAnnID()
    25  
    26  // logicalPropsBuilder is a helper class that consolidates the code that derives
    27  // a parent expression's logical properties from those of its children.
    28  //
    29  // buildProps is called by the memo group construction code in order to
    30  // initialize the new group's logical properties.
    31  // NOTE: When deriving properties from children, be sure to keep the child
    32  //       properties immutable by copying them if necessary.
    33  // NOTE: The parent expression is passed as an expression for convenient access
    34  //       to children, but certain properties on it are not yet defined (like
    35  //       its logical properties!).
    36  type logicalPropsBuilder struct {
    37  	evalCtx *tree.EvalContext
    38  	mem     *Memo
    39  	sb      statisticsBuilder
    40  
    41  	// When set to true, disableStats disables stat generation during
    42  	// logical prop building. Useful in checkExpr when we don't want
    43  	// to create stats for non-normalized expressions and potentially
    44  	// mutate opt_tester output compared to cases where checkExpr is
    45  	// not run.
    46  	disableStats bool
    47  }
    48  
    49  func (b *logicalPropsBuilder) init(evalCtx *tree.EvalContext, mem *Memo) {
    50  	b.evalCtx = evalCtx
    51  	b.mem = mem
    52  	b.sb.init(evalCtx, mem.Metadata())
    53  }
    54  
    55  func (b *logicalPropsBuilder) clear() {
    56  	b.evalCtx = nil
    57  	b.mem = nil
    58  	b.sb.clear()
    59  }
    60  
    61  func (b *logicalPropsBuilder) buildScanProps(scan *ScanExpr, rel *props.Relational) {
    62  	md := scan.Memo().Metadata()
    63  	hardLimit := scan.HardLimit.RowCount()
    64  
    65  	// Side Effects
    66  	// ------------
    67  	// A Locking option is a side-effect (we don't want to elide this scan).
    68  	if scan.Locking != nil {
    69  		rel.CanHaveSideEffects = true
    70  		rel.VolatilitySet.AddVolatile()
    71  	}
    72  
    73  	// Output Columns
    74  	// --------------
    75  	// Scan output columns are stored in the definition.
    76  	rel.OutputCols = scan.Cols
    77  
    78  	// Not Null Columns
    79  	// ----------------
    80  	// Initialize not-NULL columns from the table schema.
    81  	rel.NotNullCols = tableNotNullCols(md, scan.Table)
    82  	if scan.Constraint != nil {
    83  		rel.NotNullCols.UnionWith(scan.Constraint.ExtractNotNullCols(b.evalCtx))
    84  	}
    85  	rel.NotNullCols.IntersectionWith(rel.OutputCols)
    86  
    87  	// Outer Columns
    88  	// -------------
    89  	// Scan operator never has outer columns.
    90  
    91  	// Functional Dependencies
    92  	// -----------------------
    93  	// Check the hard limit to determine whether there is at most one row. Note
    94  	// that def.HardLimit = 0 indicates there is no known limit.
    95  	if hardLimit == 1 {
    96  		rel.FuncDeps.MakeMax1Row(rel.OutputCols)
    97  	} else {
    98  		// Initialize key FD's from the table schema, including constant columns from
    99  		// the constraint, minus any columns that are not projected by the Scan
   100  		// operator.
   101  		rel.FuncDeps.CopyFrom(MakeTableFuncDep(md, scan.Table))
   102  		if scan.Constraint != nil {
   103  			rel.FuncDeps.AddConstants(scan.Constraint.ExtractConstCols(b.evalCtx))
   104  		}
   105  		if tabMeta := md.TableMeta(scan.Table); tabMeta.Constraints != nil {
   106  			b.addFiltersToFuncDep(*tabMeta.Constraints.(*FiltersExpr), &rel.FuncDeps)
   107  		}
   108  		rel.FuncDeps.MakeNotNull(rel.NotNullCols)
   109  		rel.FuncDeps.ProjectCols(rel.OutputCols)
   110  	}
   111  
   112  	// Cardinality
   113  	// -----------
   114  	// Restrict cardinality based on constraint, FDs, and hard limit.
   115  	rel.Cardinality = props.AnyCardinality
   116  	if scan.Constraint != nil && scan.Constraint.IsContradiction() {
   117  		rel.Cardinality = props.ZeroCardinality
   118  	} else if rel.FuncDeps.HasMax1Row() {
   119  		rel.Cardinality = rel.Cardinality.Limit(1)
   120  	} else {
   121  		if hardLimit > 0 && hardLimit < math.MaxUint32 {
   122  			rel.Cardinality = rel.Cardinality.Limit(uint32(hardLimit))
   123  		}
   124  		if scan.Constraint != nil {
   125  			b.updateCardinalityFromConstraint(scan.Constraint, rel)
   126  		}
   127  	}
   128  
   129  	// Statistics
   130  	// ----------
   131  	if !b.disableStats {
   132  		b.sb.buildScan(scan, rel)
   133  	}
   134  }
   135  
   136  func (b *logicalPropsBuilder) buildSequenceSelectProps(
   137  	seq *SequenceSelectExpr, rel *props.Relational,
   138  ) {
   139  	// Output Columns
   140  	// --------------
   141  	// Output columns are stored in the definition.
   142  	rel.OutputCols = seq.Cols.ToSet()
   143  
   144  	// Not Null Columns
   145  	// ----------------
   146  	// Every column is not null.
   147  	rel.NotNullCols = rel.OutputCols
   148  
   149  	// Outer Columns
   150  	// -------------
   151  	// The operator never has outer columns.
   152  
   153  	// Functional Dependencies
   154  	// -----------------------
   155  	rel.FuncDeps.MakeMax1Row(rel.OutputCols)
   156  
   157  	// Cardinality
   158  	// -----------
   159  	rel.Cardinality = props.OneCardinality
   160  
   161  	// Statistics
   162  	// ----------
   163  	if !b.disableStats {
   164  		b.sb.buildSequenceSelect(rel)
   165  	}
   166  }
   167  
   168  func (b *logicalPropsBuilder) buildSelectProps(sel *SelectExpr, rel *props.Relational) {
   169  	BuildSharedProps(sel, &rel.Shared)
   170  
   171  	inputProps := sel.Input.Relational()
   172  
   173  	// Output Columns
   174  	// --------------
   175  	// Inherit output columns from input.
   176  	rel.OutputCols = inputProps.OutputCols
   177  
   178  	// Not Null Columns
   179  	// ----------------
   180  	// A column can become not null due to a null rejecting filter expression:
   181  	//
   182  	//   SELECT y FROM xy WHERE y=5
   183  	//
   184  	// "y" cannot be null because the SQL equality operator rejects nulls.
   185  	rel.NotNullCols = b.rejectNullCols(sel.Filters)
   186  	rel.NotNullCols.UnionWith(inputProps.NotNullCols)
   187  	rel.NotNullCols.IntersectionWith(rel.OutputCols)
   188  
   189  	// Outer Columns
   190  	// -------------
   191  	// Outer columns were derived by BuildSharedProps; remove any that are bound
   192  	// by input columns.
   193  	rel.OuterCols.DifferenceWith(inputProps.OutputCols)
   194  
   195  	// Functional Dependencies
   196  	// -----------------------
   197  	// Start with copy of FuncDepSet from input, add FDs from the WHERE clause
   198  	// and outer columns, modify with any additional not-null columns, then
   199  	// possibly simplify by calling ProjectCols.
   200  	rel.FuncDeps.CopyFrom(&inputProps.FuncDeps)
   201  	b.addFiltersToFuncDep(sel.Filters, &rel.FuncDeps)
   202  	addOuterColsToFuncDep(rel.OuterCols, &rel.FuncDeps)
   203  	rel.FuncDeps.MakeNotNull(rel.NotNullCols)
   204  	rel.FuncDeps.ProjectCols(rel.OutputCols)
   205  
   206  	// Cardinality
   207  	// -----------
   208  	// Select filter can filter any or all rows.
   209  	rel.Cardinality = inputProps.Cardinality.AsLowAs(0)
   210  	if sel.Filters.IsFalse() {
   211  		rel.Cardinality = props.ZeroCardinality
   212  	} else if rel.FuncDeps.HasMax1Row() {
   213  		rel.Cardinality = rel.Cardinality.Limit(1)
   214  	} else {
   215  		b.updateCardinalityFromFilters(sel.Filters, rel)
   216  	}
   217  
   218  	// Statistics
   219  	// ----------
   220  	if !b.disableStats {
   221  		b.sb.buildSelect(sel, rel)
   222  	}
   223  }
   224  
   225  func (b *logicalPropsBuilder) buildProjectProps(prj *ProjectExpr, rel *props.Relational) {
   226  	BuildSharedProps(prj, &rel.Shared)
   227  
   228  	inputProps := prj.Input.Relational()
   229  
   230  	// Output Columns
   231  	// --------------
   232  	// Output columns are the union of synthesized columns and passthrough columns.
   233  	for i := range prj.Projections {
   234  		rel.OutputCols.Add(prj.Projections[i].Col)
   235  	}
   236  	rel.OutputCols.UnionWith(prj.Passthrough)
   237  
   238  	// Not Null Columns
   239  	// ----------------
   240  	// Not null columns were derived by initUnexportedFields; just intersect them
   241  	// with the output columns.
   242  	rel.NotNullCols = prj.notNullCols.Intersection(rel.OutputCols)
   243  
   244  	// Outer Columns
   245  	// -------------
   246  	// Outer columns were derived by BuildSharedProps; remove any that are bound
   247  	// by input columns.
   248  	rel.OuterCols.DifferenceWith(inputProps.OutputCols)
   249  
   250  	// Functional Dependencies
   251  	// -----------------------
   252  	// The functional dependencies were derived by initUnexportedFields; just
   253  	// remove columns that are not projected.
   254  	rel.FuncDeps.CopyFrom(&prj.internalFuncDeps)
   255  	rel.FuncDeps.ProjectCols(rel.OutputCols)
   256  
   257  	// Cardinality
   258  	// -----------
   259  	// Inherit cardinality from input.
   260  	rel.Cardinality = inputProps.Cardinality
   261  
   262  	// Statistics
   263  	// ----------
   264  	if !b.disableStats {
   265  		b.sb.buildProject(prj, rel)
   266  	}
   267  }
   268  
   269  func (b *logicalPropsBuilder) buildInnerJoinProps(join *InnerJoinExpr, rel *props.Relational) {
   270  	b.buildJoinProps(join, rel)
   271  }
   272  
   273  func (b *logicalPropsBuilder) buildLeftJoinProps(join *LeftJoinExpr, rel *props.Relational) {
   274  	b.buildJoinProps(join, rel)
   275  }
   276  
   277  func (b *logicalPropsBuilder) buildRightJoinProps(join *RightJoinExpr, rel *props.Relational) {
   278  	b.buildJoinProps(join, rel)
   279  }
   280  
   281  func (b *logicalPropsBuilder) buildFullJoinProps(join *FullJoinExpr, rel *props.Relational) {
   282  	b.buildJoinProps(join, rel)
   283  }
   284  
   285  func (b *logicalPropsBuilder) buildSemiJoinProps(join *SemiJoinExpr, rel *props.Relational) {
   286  	b.buildJoinProps(join, rel)
   287  }
   288  
   289  func (b *logicalPropsBuilder) buildAntiJoinProps(join *AntiJoinExpr, rel *props.Relational) {
   290  	b.buildJoinProps(join, rel)
   291  }
   292  
   293  func (b *logicalPropsBuilder) buildInnerJoinApplyProps(
   294  	join *InnerJoinApplyExpr, rel *props.Relational,
   295  ) {
   296  	b.buildJoinProps(join, rel)
   297  }
   298  
   299  func (b *logicalPropsBuilder) buildLeftJoinApplyProps(
   300  	join *LeftJoinApplyExpr, rel *props.Relational,
   301  ) {
   302  	b.buildJoinProps(join, rel)
   303  }
   304  
   305  func (b *logicalPropsBuilder) buildSemiJoinApplyProps(
   306  	join *SemiJoinApplyExpr, rel *props.Relational,
   307  ) {
   308  	b.buildJoinProps(join, rel)
   309  }
   310  
   311  func (b *logicalPropsBuilder) buildAntiJoinApplyProps(
   312  	join *AntiJoinApplyExpr, rel *props.Relational,
   313  ) {
   314  	b.buildJoinProps(join, rel)
   315  }
   316  
   317  func (b *logicalPropsBuilder) buildJoinProps(join RelExpr, rel *props.Relational) {
   318  	BuildSharedProps(join, &rel.Shared)
   319  
   320  	var h joinPropsHelper
   321  	h.init(b, join)
   322  
   323  	// Output Columns
   324  	// --------------
   325  	rel.OutputCols = h.outputCols()
   326  
   327  	// Not Null Columns
   328  	// ----------------
   329  	rel.NotNullCols = h.notNullCols()
   330  	rel.NotNullCols.IntersectionWith(rel.OutputCols)
   331  
   332  	// Outer Columns
   333  	// -------------
   334  	// Outer columns were initially set by BuildSharedProps. Remove any that are
   335  	// bound by the input columns.
   336  	inputCols := h.leftProps.OutputCols.Union(h.rightProps.OutputCols)
   337  	rel.OuterCols.DifferenceWith(inputCols)
   338  
   339  	// Functional Dependencies
   340  	// -----------------------
   341  	h.setFuncDeps(rel)
   342  
   343  	// Cardinality
   344  	// -----------
   345  	// Calculate cardinality, depending on join type.
   346  	rel.Cardinality = h.cardinality()
   347  	if rel.FuncDeps.HasMax1Row() {
   348  		rel.Cardinality = rel.Cardinality.Limit(1)
   349  	}
   350  
   351  	// Statistics
   352  	// ----------
   353  	if !b.disableStats {
   354  		b.sb.buildJoin(join, rel, &h)
   355  	}
   356  }
   357  
   358  func (b *logicalPropsBuilder) buildIndexJoinProps(indexJoin *IndexJoinExpr, rel *props.Relational) {
   359  	BuildSharedProps(indexJoin, &rel.Shared)
   360  
   361  	inputProps := indexJoin.Input.Relational()
   362  	md := b.mem.Metadata()
   363  
   364  	// Output Columns
   365  	// --------------
   366  	rel.OutputCols = indexJoin.Cols
   367  
   368  	// Not Null Columns
   369  	// ----------------
   370  	// Add not-NULL columns from the table schema, and filter out any not-NULL
   371  	// columns from the input that are not projected by the index join.
   372  	rel.NotNullCols = tableNotNullCols(md, indexJoin.Table)
   373  	rel.NotNullCols.IntersectionWith(rel.OutputCols)
   374  
   375  	// Outer Columns
   376  	// -------------
   377  	// Outer columns were already derived by BuildSharedProps.
   378  
   379  	// Functional Dependencies
   380  	// -----------------------
   381  	// Start with the input FD set, and join that with the table's FD.
   382  	rel.FuncDeps.CopyFrom(&inputProps.FuncDeps)
   383  	rel.FuncDeps.AddFrom(MakeTableFuncDep(md, indexJoin.Table))
   384  	rel.FuncDeps.MakeNotNull(rel.NotNullCols)
   385  	rel.FuncDeps.ProjectCols(rel.OutputCols)
   386  
   387  	// Cardinality
   388  	// -----------
   389  	// Inherit cardinality from input.
   390  	rel.Cardinality = inputProps.Cardinality
   391  
   392  	// Statistics
   393  	// ----------
   394  	if !b.disableStats {
   395  		b.sb.buildIndexJoin(indexJoin, rel)
   396  	}
   397  }
   398  
   399  func (b *logicalPropsBuilder) buildLookupJoinProps(join *LookupJoinExpr, rel *props.Relational) {
   400  	b.buildJoinProps(join, rel)
   401  }
   402  
   403  func (b *logicalPropsBuilder) buildGeoLookupJoinProps(
   404  	join *GeoLookupJoinExpr, rel *props.Relational,
   405  ) {
   406  	b.buildJoinProps(join, rel)
   407  }
   408  
   409  func (b *logicalPropsBuilder) buildZigzagJoinProps(join *ZigzagJoinExpr, rel *props.Relational) {
   410  	b.buildJoinProps(join, rel)
   411  }
   412  
   413  func (b *logicalPropsBuilder) buildMergeJoinProps(join *MergeJoinExpr, rel *props.Relational) {
   414  	b.buildJoinProps(join, rel)
   415  }
   416  
   417  func (b *logicalPropsBuilder) buildGroupByProps(groupBy *GroupByExpr, rel *props.Relational) {
   418  	b.buildGroupingExprProps(groupBy, rel)
   419  }
   420  
   421  func (b *logicalPropsBuilder) buildScalarGroupByProps(
   422  	scalarGroupBy *ScalarGroupByExpr, rel *props.Relational,
   423  ) {
   424  	b.buildGroupingExprProps(scalarGroupBy, rel)
   425  }
   426  
   427  func (b *logicalPropsBuilder) buildDistinctOnProps(
   428  	distinctOn *DistinctOnExpr, rel *props.Relational,
   429  ) {
   430  	b.buildGroupingExprProps(distinctOn, rel)
   431  }
   432  
   433  func (b *logicalPropsBuilder) buildEnsureDistinctOnProps(
   434  	distinctOn *EnsureDistinctOnExpr, rel *props.Relational,
   435  ) {
   436  	b.buildGroupingExprProps(distinctOn, rel)
   437  }
   438  
   439  func (b *logicalPropsBuilder) buildUpsertDistinctOnProps(
   440  	distinctOn *UpsertDistinctOnExpr, rel *props.Relational,
   441  ) {
   442  	b.buildGroupingExprProps(distinctOn, rel)
   443  }
   444  
   445  func (b *logicalPropsBuilder) buildEnsureUpsertDistinctOnProps(
   446  	distinctOn *EnsureUpsertDistinctOnExpr, rel *props.Relational,
   447  ) {
   448  	b.buildGroupingExprProps(distinctOn, rel)
   449  }
   450  
   451  func (b *logicalPropsBuilder) buildGroupingExprProps(groupExpr RelExpr, rel *props.Relational) {
   452  	BuildSharedProps(groupExpr, &rel.Shared)
   453  
   454  	inputProps := groupExpr.Child(0).(RelExpr).Relational()
   455  	aggs := *groupExpr.Child(1).(*AggregationsExpr)
   456  	groupPrivate := groupExpr.Private().(*GroupingPrivate)
   457  	groupingCols := groupPrivate.GroupingCols
   458  
   459  	// Output Columns
   460  	// --------------
   461  	// Output columns are the union of grouping columns with columns from the
   462  	// aggregate projection list.
   463  	rel.OutputCols = groupingCols.Copy()
   464  	for i := range aggs {
   465  		rel.OutputCols.Add(aggs[i].Col)
   466  	}
   467  
   468  	// Not Null Columns
   469  	// ----------------
   470  	// Propagate not null setting from input columns that are being grouped.
   471  	rel.NotNullCols = inputProps.NotNullCols.Intersection(groupingCols)
   472  
   473  	for i := range aggs {
   474  		item := &aggs[i]
   475  		agg := ExtractAggFunc(item.Agg)
   476  
   477  		// Some aggregates never return NULL, regardless of input.
   478  		if opt.AggregateIsNeverNull(agg.Op()) {
   479  			rel.NotNullCols.Add(item.Col)
   480  			continue
   481  		}
   482  
   483  		// If there is a possibility that the aggregate function has zero input
   484  		// rows, then it may return NULL. This is possible with ScalarGroupBy and
   485  		// with AggFilter.
   486  		if groupExpr.Op() == opt.ScalarGroupByOp || item.Agg.Op() == opt.AggFilterOp {
   487  			continue
   488  		}
   489  
   490  		// Most aggregate functions return a non-NULL result if they have at least
   491  		// one input row with non-NULL argument value, and if all argument values are non-NULL.
   492  		if opt.AggregateIsNeverNullOnNonNullInput(agg.Op()) {
   493  			inputCols := ExtractAggInputColumns(agg)
   494  			if inputCols.SubsetOf(inputProps.NotNullCols) {
   495  				rel.NotNullCols.Add(item.Col)
   496  			}
   497  		}
   498  	}
   499  
   500  	// Outer Columns
   501  	// -------------
   502  	// Outer columns were derived by BuildSharedProps; remove any that are bound
   503  	// by input columns.
   504  	rel.OuterCols.DifferenceWith(inputProps.OutputCols)
   505  
   506  	// Functional Dependencies
   507  	// -----------------------
   508  	rel.FuncDeps.CopyFrom(&inputProps.FuncDeps)
   509  	if groupingCols.Empty() {
   510  		// When there are no grouping columns, then there is a single group, and
   511  		// therefore at most one output row.
   512  		rel.FuncDeps.MakeMax1Row(rel.OutputCols)
   513  	} else {
   514  		// Start by eliminating input columns that aren't projected.
   515  		rel.FuncDeps.ProjectCols(rel.OutputCols)
   516  
   517  		// The output of most of the grouping operators forms a strict key because
   518  		// they eliminate all duplicates in the grouping columns. However, the
   519  		// UpsertDistinctOn and EnsureUpsertDistinctOn operators do not group
   520  		// NULL values together, so they only form a lax key when NULL values
   521  		// are possible.
   522  		if groupPrivate.NullsAreDistinct && !groupingCols.SubsetOf(rel.NotNullCols) {
   523  			rel.FuncDeps.AddLaxKey(groupingCols, rel.OutputCols)
   524  		} else {
   525  			rel.FuncDeps.AddStrictKey(groupingCols, rel.OutputCols)
   526  		}
   527  	}
   528  
   529  	// Cardinality
   530  	// -----------
   531  	if groupExpr.Op() == opt.ScalarGroupByOp {
   532  		// Scalar GroupBy returns exactly one row.
   533  		rel.Cardinality = props.OneCardinality
   534  	} else {
   535  		// GroupBy and DistinctOn act like a filter, never returning more rows
   536  		// than the input has. However, if the input has at least one row, then
   537  		// at least one row will also be returned by GroupBy and DistinctOn.
   538  		rel.Cardinality = inputProps.Cardinality.AsLowAs(1)
   539  		if rel.FuncDeps.HasMax1Row() {
   540  			rel.Cardinality = rel.Cardinality.Limit(1)
   541  		}
   542  	}
   543  
   544  	// Statistics
   545  	// ----------
   546  	if !b.disableStats {
   547  		b.sb.buildGroupBy(groupExpr, rel)
   548  	}
   549  }
   550  
   551  func (b *logicalPropsBuilder) buildUnionProps(union *UnionExpr, rel *props.Relational) {
   552  	b.buildSetProps(union, rel)
   553  }
   554  
   555  func (b *logicalPropsBuilder) buildIntersectProps(isect *IntersectExpr, rel *props.Relational) {
   556  	b.buildSetProps(isect, rel)
   557  }
   558  
   559  func (b *logicalPropsBuilder) buildExceptProps(except *ExceptExpr, rel *props.Relational) {
   560  	b.buildSetProps(except, rel)
   561  }
   562  
   563  func (b *logicalPropsBuilder) buildUnionAllProps(union *UnionAllExpr, rel *props.Relational) {
   564  	b.buildSetProps(union, rel)
   565  }
   566  
   567  func (b *logicalPropsBuilder) buildIntersectAllProps(
   568  	isect *IntersectAllExpr, rel *props.Relational,
   569  ) {
   570  	b.buildSetProps(isect, rel)
   571  }
   572  
   573  func (b *logicalPropsBuilder) buildExceptAllProps(except *ExceptAllExpr, rel *props.Relational) {
   574  	b.buildSetProps(except, rel)
   575  }
   576  
   577  func (b *logicalPropsBuilder) buildSetProps(setNode RelExpr, rel *props.Relational) {
   578  	BuildSharedProps(setNode, &rel.Shared)
   579  
   580  	leftProps := setNode.Child(0).(RelExpr).Relational()
   581  	rightProps := setNode.Child(1).(RelExpr).Relational()
   582  	setPrivate := setNode.Private().(*SetPrivate)
   583  	if len(setPrivate.OutCols) != len(setPrivate.LeftCols) ||
   584  		len(setPrivate.OutCols) != len(setPrivate.RightCols) {
   585  		panic(errors.AssertionFailedf(
   586  			"lists in SetPrivate are not all the same length. new:%d, left:%d, right:%d",
   587  			log.Safe(len(setPrivate.OutCols)), log.Safe(len(setPrivate.LeftCols)), log.Safe(len(setPrivate.RightCols)),
   588  		))
   589  	}
   590  
   591  	// Output Columns
   592  	// --------------
   593  	// Output columns are stored in the definition.
   594  	rel.OutputCols = setPrivate.OutCols.ToSet()
   595  
   596  	// Not Null Columns
   597  	// ----------------
   598  	// Columns have to be not-null on both sides to be not-null in result.
   599  	// setPrivate matches columns on the left and right sides of the operator
   600  	// with the output columns, since OutputCols are not ordered and may
   601  	// not correspond to each other.
   602  	for i := range setPrivate.OutCols {
   603  		if leftProps.NotNullCols.Contains((setPrivate.LeftCols)[i]) &&
   604  			rightProps.NotNullCols.Contains((setPrivate.RightCols)[i]) {
   605  			rel.NotNullCols.Add((setPrivate.OutCols)[i])
   606  		}
   607  	}
   608  
   609  	// Outer Columns
   610  	// -------------
   611  	// Outer columns were already derived by BuildSharedProps.
   612  
   613  	// Functional Dependencies
   614  	// -----------------------
   615  	switch setNode.Op() {
   616  	case opt.UnionOp, opt.IntersectOp, opt.ExceptOp:
   617  		// These operators eliminate duplicates, so a strict key exists.
   618  		rel.FuncDeps.AddStrictKey(rel.OutputCols, rel.OutputCols)
   619  	}
   620  
   621  	// Cardinality
   622  	// -----------
   623  	// Calculate cardinality of the set operator.
   624  	rel.Cardinality = b.makeSetCardinality(
   625  		setNode.Op(), leftProps.Cardinality, rightProps.Cardinality)
   626  
   627  	// Statistics
   628  	// ----------
   629  	if !b.disableStats {
   630  		b.sb.buildSetNode(setNode, rel)
   631  	}
   632  }
   633  
   634  func (b *logicalPropsBuilder) buildValuesProps(values *ValuesExpr, rel *props.Relational) {
   635  	BuildSharedProps(values, &rel.Shared)
   636  
   637  	card := uint32(len(values.Rows))
   638  
   639  	// Output Columns
   640  	// --------------
   641  	// Use output columns that are attached to the values op.
   642  	rel.OutputCols = values.Cols.ToSet()
   643  
   644  	// Not Null Columns
   645  	// ----------------
   646  	// All columns are assumed to be nullable, unless they contain only constant
   647  	// non-null values.
   648  
   649  	for colIdx, col := range values.Cols {
   650  		notNull := true
   651  		for rowIdx := range values.Rows {
   652  			val := values.Rows[rowIdx].(*TupleExpr).Elems[colIdx]
   653  			if !opt.IsConstValueOp(val) || val.Op() == opt.NullOp {
   654  				// Null or not a constant.
   655  				notNull = false
   656  				break
   657  			}
   658  		}
   659  		if notNull {
   660  			rel.NotNullCols.Add(col)
   661  		}
   662  	}
   663  
   664  	// Outer Columns
   665  	// -------------
   666  	// Outer columns were already derived by BuildSharedProps.
   667  
   668  	// Functional Dependencies
   669  	// -----------------------
   670  	if card <= 1 {
   671  		rel.FuncDeps.MakeMax1Row(rel.OutputCols)
   672  	}
   673  
   674  	// Cardinality
   675  	// -----------
   676  	// Cardinality is number of tuples in the Values operator.
   677  	rel.Cardinality = props.Cardinality{Min: card, Max: card}
   678  
   679  	// Statistics
   680  	// ----------
   681  	if !b.disableStats {
   682  		b.sb.buildValues(values, rel)
   683  	}
   684  }
   685  
   686  func (b *logicalPropsBuilder) buildBasicProps(e opt.Expr, cols opt.ColList, rel *props.Relational) {
   687  	BuildSharedProps(e, &rel.Shared)
   688  
   689  	// Output Columns
   690  	// --------------
   691  	rel.OutputCols = cols.ToSet()
   692  
   693  	// Not Null Columns
   694  	// ----------------
   695  	// All columns are assumed to be nullable.
   696  
   697  	// Outer Columns
   698  	// -------------
   699  	// No outer columns.
   700  
   701  	// Functional Dependencies
   702  	// -----------------------
   703  	// Empty FD set.
   704  
   705  	// Cardinality
   706  	// -----------
   707  	// Don't make any assumptions about cardinality of output.
   708  	rel.Cardinality = props.AnyCardinality
   709  
   710  	// Statistics
   711  	// ----------
   712  	if !b.disableStats {
   713  		b.sb.buildUnknown(rel)
   714  	}
   715  }
   716  
   717  func (b *logicalPropsBuilder) buildWithProps(with *WithExpr, rel *props.Relational) {
   718  	// Copy over the props from the input.
   719  	inputProps := with.Main.Relational()
   720  
   721  	BuildSharedProps(with, &rel.Shared)
   722  
   723  	// Side Effects
   724  	// ------------
   725  	// This expression has side effects if either Binding or Input has side
   726  	// effects, which is what is computed by the call to BuildSharedProps.
   727  
   728  	// Output Columns
   729  	// --------------
   730  	// Inherited from the input expression.
   731  	rel.OutputCols = inputProps.OutputCols
   732  
   733  	// Not Null Columns
   734  	// ----------------
   735  	// Inherited from the input expression.
   736  	rel.NotNullCols = inputProps.NotNullCols
   737  
   738  	// Outer Columns
   739  	// -------------
   740  	// The outer columns are the union of the outer columns from Binding or Input,
   741  	// which is what is computed by the call to BuildSharedProps.
   742  
   743  	// Functional Dependencies
   744  	// -----------------------
   745  	rel.FuncDeps = inputProps.FuncDeps
   746  
   747  	// Cardinality
   748  	// -----------
   749  	rel.Cardinality = inputProps.Cardinality
   750  
   751  	// Statistics
   752  	// ----------
   753  	// Inherited from the input expression.
   754  	rel.Stats = inputProps.Stats
   755  }
   756  
   757  func (b *logicalPropsBuilder) buildWithScanProps(withScan *WithScanExpr, rel *props.Relational) {
   758  	BuildSharedProps(withScan, &rel.Shared)
   759  	bindingProps := withScan.BindingProps
   760  
   761  	// Side Effects
   762  	// ------------
   763  	// WithScan has no side effects (even if the original expression had them).
   764  
   765  	// Output Columns
   766  	// --------------
   767  	rel.OutputCols = withScan.OutCols.ToSet()
   768  
   769  	// Not Null Columns
   770  	// ----------------
   771  	rel.NotNullCols = opt.TranslateColSet(bindingProps.NotNullCols, withScan.InCols, withScan.OutCols)
   772  
   773  	// Outer Columns
   774  	// -------------
   775  	// No outer columns.
   776  
   777  	// Functional Dependencies
   778  	// -----------------------
   779  	// Inherit dependencies from the referenced expression (remapping the
   780  	// columns).
   781  	rel.FuncDeps.CopyFrom(&bindingProps.FuncDeps)
   782  	for i := range withScan.InCols {
   783  		rel.FuncDeps.AddEquivalency(withScan.InCols[i], withScan.OutCols[i])
   784  	}
   785  	rel.FuncDeps.ProjectCols(withScan.OutCols.ToSet())
   786  
   787  	// Cardinality
   788  	// -----------
   789  	// Inherit from the referenced expression.
   790  	rel.Cardinality = bindingProps.Cardinality
   791  
   792  	// Statistics
   793  	// ----------
   794  	if !b.disableStats {
   795  		b.sb.buildWithScan(withScan, rel)
   796  	}
   797  }
   798  
   799  func (b *logicalPropsBuilder) buildRecursiveCTEProps(rec *RecursiveCTEExpr, rel *props.Relational) {
   800  	BuildSharedProps(rec, &rel.Shared)
   801  
   802  	// Output Columns
   803  	// --------------
   804  	rel.OutputCols = rec.OutCols.ToSet()
   805  
   806  	// Not Null Columns
   807  	// ----------------
   808  	// All columns are assumed to be nullable.
   809  
   810  	// Outer Columns
   811  	// -------------
   812  	// No outer columns.
   813  
   814  	// Functional Dependencies
   815  	// -----------------------
   816  	// No known FDs.
   817  
   818  	// Cardinality
   819  	// -----------
   820  	// At least the cardinality of the initial buffer.
   821  	rel.Cardinality = props.AnyCardinality.AtLeast(rec.Initial.Relational().Cardinality)
   822  
   823  	// Statistics
   824  	// ----------
   825  	if !b.disableStats {
   826  		b.sb.buildUnknown(rel)
   827  	}
   828  }
   829  
   830  func (b *logicalPropsBuilder) buildExplainProps(explain *ExplainExpr, rel *props.Relational) {
   831  	b.buildBasicProps(explain, explain.ColList, rel)
   832  }
   833  
   834  func (b *logicalPropsBuilder) buildShowTraceForSessionProps(
   835  	showTrace *ShowTraceForSessionExpr, rel *props.Relational,
   836  ) {
   837  	b.buildBasicProps(showTrace, showTrace.ColList, rel)
   838  }
   839  
   840  func (b *logicalPropsBuilder) buildOpaqueRelProps(op *OpaqueRelExpr, rel *props.Relational) {
   841  	b.buildBasicProps(op, op.Columns, rel)
   842  }
   843  
   844  func (b *logicalPropsBuilder) buildOpaqueMutationProps(
   845  	op *OpaqueMutationExpr, rel *props.Relational,
   846  ) {
   847  	b.buildBasicProps(op, op.Columns, rel)
   848  }
   849  
   850  func (b *logicalPropsBuilder) buildOpaqueDDLProps(op *OpaqueDDLExpr, rel *props.Relational) {
   851  	b.buildBasicProps(op, op.Columns, rel)
   852  }
   853  
   854  func (b *logicalPropsBuilder) buildAlterTableSplitProps(
   855  	split *AlterTableSplitExpr, rel *props.Relational,
   856  ) {
   857  	b.buildBasicProps(split, split.Columns, rel)
   858  }
   859  
   860  func (b *logicalPropsBuilder) buildAlterTableUnsplitProps(
   861  	unsplit *AlterTableUnsplitExpr, rel *props.Relational,
   862  ) {
   863  	b.buildBasicProps(unsplit, unsplit.Columns, rel)
   864  }
   865  
   866  func (b *logicalPropsBuilder) buildAlterTableUnsplitAllProps(
   867  	unsplitAll *AlterTableUnsplitAllExpr, rel *props.Relational,
   868  ) {
   869  	b.buildBasicProps(unsplitAll, unsplitAll.Columns, rel)
   870  }
   871  
   872  func (b *logicalPropsBuilder) buildAlterTableRelocateProps(
   873  	relocate *AlterTableRelocateExpr, rel *props.Relational,
   874  ) {
   875  	b.buildBasicProps(relocate, relocate.Columns, rel)
   876  }
   877  
   878  func (b *logicalPropsBuilder) buildControlJobsProps(ctl *ControlJobsExpr, rel *props.Relational) {
   879  	b.buildBasicProps(ctl, opt.ColList{}, rel)
   880  }
   881  
   882  func (b *logicalPropsBuilder) buildCancelQueriesProps(
   883  	cancel *CancelQueriesExpr, rel *props.Relational,
   884  ) {
   885  	b.buildBasicProps(cancel, opt.ColList{}, rel)
   886  }
   887  
   888  func (b *logicalPropsBuilder) buildCancelSessionsProps(
   889  	cancel *CancelSessionsExpr, rel *props.Relational,
   890  ) {
   891  	b.buildBasicProps(cancel, opt.ColList{}, rel)
   892  }
   893  
   894  func (b *logicalPropsBuilder) buildExportProps(export *ExportExpr, rel *props.Relational) {
   895  	b.buildBasicProps(export, export.Columns, rel)
   896  }
   897  
   898  func (b *logicalPropsBuilder) buildLimitProps(limit *LimitExpr, rel *props.Relational) {
   899  	BuildSharedProps(limit, &rel.Shared)
   900  
   901  	inputProps := limit.Input.Relational()
   902  
   903  	haveConstLimit := false
   904  	constLimit := int64(math.MaxUint32)
   905  	if cnst, ok := limit.Limit.(*ConstExpr); ok {
   906  		haveConstLimit = true
   907  		constLimit = int64(*cnst.Value.(*tree.DInt))
   908  	}
   909  
   910  	// Side Effects
   911  	// ------------
   912  	// Negative limits can trigger a runtime error.
   913  	if constLimit < 0 || !haveConstLimit {
   914  		rel.CanHaveSideEffects = true
   915  		rel.VolatilitySet.AddImmutable()
   916  	}
   917  
   918  	// Output Columns
   919  	// --------------
   920  	// Output columns are inherited from input.
   921  	rel.OutputCols = inputProps.OutputCols
   922  
   923  	// Not Null Columns
   924  	// ----------------
   925  	// Not null columns are inherited from input.
   926  	rel.NotNullCols = inputProps.NotNullCols
   927  
   928  	// Outer Columns
   929  	// -------------
   930  	// Outer columns were already derived by BuildSharedProps.
   931  
   932  	// Functional Dependencies
   933  	// -----------------------
   934  	// Inherit functional dependencies from input if limit is > 1, else just use
   935  	// single row dependencies.
   936  	if constLimit > 1 {
   937  		rel.FuncDeps.CopyFrom(&inputProps.FuncDeps)
   938  	} else {
   939  		rel.FuncDeps.MakeMax1Row(rel.OutputCols)
   940  	}
   941  
   942  	// Cardinality
   943  	// -----------
   944  	// Limit puts a cap on the number of rows returned by input.
   945  	rel.Cardinality = inputProps.Cardinality
   946  	if constLimit <= 0 {
   947  		rel.Cardinality = props.ZeroCardinality
   948  	} else if constLimit < math.MaxUint32 {
   949  		rel.Cardinality = rel.Cardinality.Limit(uint32(constLimit))
   950  	}
   951  
   952  	// Statistics
   953  	// ----------
   954  	if !b.disableStats {
   955  		b.sb.buildLimit(limit, rel)
   956  	}
   957  }
   958  
   959  func (b *logicalPropsBuilder) buildOffsetProps(offset *OffsetExpr, rel *props.Relational) {
   960  	BuildSharedProps(offset, &rel.Shared)
   961  
   962  	inputProps := offset.Input.Relational()
   963  
   964  	// Output Columns
   965  	// --------------
   966  	// Output columns are inherited from input.
   967  	rel.OutputCols = inputProps.OutputCols
   968  
   969  	// Not Null Columns
   970  	// ----------------
   971  	// Not null columns are inherited from input.
   972  	rel.NotNullCols = inputProps.NotNullCols
   973  
   974  	// Outer Columns
   975  	// -------------
   976  	// Outer columns were already derived by BuildSharedProps.
   977  
   978  	// Functional Dependencies
   979  	// -----------------------
   980  	// Inherit functional dependencies from input.
   981  	rel.FuncDeps.CopyFrom(&inputProps.FuncDeps)
   982  
   983  	// Cardinality
   984  	// -----------
   985  	// Offset decreases the number of rows that are passed through from input.
   986  	rel.Cardinality = inputProps.Cardinality
   987  	if cnst, ok := offset.Offset.(*ConstExpr); ok {
   988  		constOffset := int64(*cnst.Value.(*tree.DInt))
   989  		if constOffset > 0 {
   990  			if constOffset > math.MaxUint32 {
   991  				constOffset = math.MaxUint32
   992  			}
   993  			rel.Cardinality = inputProps.Cardinality.Skip(uint32(constOffset))
   994  		}
   995  	}
   996  
   997  	// Statistics
   998  	// ----------
   999  	if !b.disableStats {
  1000  		b.sb.buildOffset(offset, rel)
  1001  	}
  1002  }
  1003  
  1004  func (b *logicalPropsBuilder) buildMax1RowProps(max1Row *Max1RowExpr, rel *props.Relational) {
  1005  	BuildSharedProps(max1Row, &rel.Shared)
  1006  
  1007  	inputProps := max1Row.Input.Relational()
  1008  
  1009  	// Output Columns
  1010  	// --------------
  1011  	// Output columns are inherited from input.
  1012  	rel.OutputCols = inputProps.OutputCols
  1013  
  1014  	// Not Null Columns
  1015  	// ----------------
  1016  	// Not null columns are inherited from input.
  1017  	rel.NotNullCols = inputProps.NotNullCols
  1018  
  1019  	// Outer Columns
  1020  	// -------------
  1021  	// Outer columns were already derived by BuildSharedProps.
  1022  
  1023  	// Functional Dependencies
  1024  	// -----------------------
  1025  	// Max1Row always returns zero or one rows.
  1026  	rel.FuncDeps.MakeMax1Row(rel.OutputCols)
  1027  
  1028  	// Cardinality
  1029  	// -----------
  1030  	// Max1Row ensures that zero or one row is returned from input.
  1031  	rel.Cardinality = inputProps.Cardinality.Limit(1)
  1032  
  1033  	// Statistics
  1034  	// ----------
  1035  	if !b.disableStats {
  1036  		b.sb.buildMax1Row(max1Row, rel)
  1037  	}
  1038  }
  1039  
  1040  func (b *logicalPropsBuilder) buildOrdinalityProps(ord *OrdinalityExpr, rel *props.Relational) {
  1041  	BuildSharedProps(ord, &rel.Shared)
  1042  
  1043  	inputProps := ord.Input.Relational()
  1044  
  1045  	// Output Columns
  1046  	// --------------
  1047  	// An extra output column is added to those projected by input operator.
  1048  	rel.OutputCols = inputProps.OutputCols.Copy()
  1049  	rel.OutputCols.Add(ord.ColID)
  1050  
  1051  	// Not Null Columns
  1052  	// ----------------
  1053  	// The new output column is not null, and other columns inherit not null
  1054  	// property from input.
  1055  	rel.NotNullCols = inputProps.NotNullCols.Copy()
  1056  	rel.NotNullCols.Add(ord.ColID)
  1057  
  1058  	// Outer Columns
  1059  	// -------------
  1060  	// Outer columns were already derived by BuildSharedProps.
  1061  
  1062  	// Functional Dependencies
  1063  	// -----------------------
  1064  	// Inherit functional dependencies from input, and add strict key FD for the
  1065  	// additional key column.
  1066  	rel.FuncDeps.CopyFrom(&inputProps.FuncDeps)
  1067  	if key, ok := rel.FuncDeps.StrictKey(); ok {
  1068  		// Any existing keys are still keys.
  1069  		rel.FuncDeps.AddStrictKey(key, rel.OutputCols)
  1070  	}
  1071  	rel.FuncDeps.AddStrictKey(opt.MakeColSet(ord.ColID), rel.OutputCols)
  1072  
  1073  	// Cardinality
  1074  	// -----------
  1075  	// Inherit cardinality from input.
  1076  	rel.Cardinality = inputProps.Cardinality
  1077  
  1078  	// Statistics
  1079  	// ----------
  1080  	if !b.disableStats {
  1081  		b.sb.buildOrdinality(ord, rel)
  1082  	}
  1083  }
  1084  
  1085  func (b *logicalPropsBuilder) buildWindowProps(window *WindowExpr, rel *props.Relational) {
  1086  	BuildSharedProps(window, &rel.Shared)
  1087  
  1088  	inputProps := window.Input.Relational()
  1089  
  1090  	// Output Columns
  1091  	// --------------
  1092  	// Output columns are all the passthrough columns with the addition of the
  1093  	// window function column.
  1094  	rel.OutputCols = inputProps.OutputCols.Copy()
  1095  	for _, w := range window.Windows {
  1096  		rel.OutputCols.Add(w.Col)
  1097  	}
  1098  
  1099  	// Not Null Columns
  1100  	// ----------------
  1101  	// Inherit not null columns from input.
  1102  	// TODO(justin): in many cases the added column may not be nullable.
  1103  	rel.NotNullCols = inputProps.NotNullCols.Intersection(rel.OutputCols)
  1104  
  1105  	// Outer Columns
  1106  	// -------------
  1107  	// Outer columns were derived by BuildSharedProps; remove any that are bound
  1108  	// by input columns.
  1109  	rel.OuterCols.DifferenceWith(inputProps.OutputCols)
  1110  
  1111  	// Functional Dependencies
  1112  	// -----------------------
  1113  	// Functional dependencies are the same as the input.
  1114  	// TODO(justin): in many cases there are more FDs to be derived, some
  1115  	// examples include:
  1116  	// * row_number+the partition is a key.
  1117  	// * rank is determined by the partition and the value being ordered by.
  1118  	// * aggregations/first_value/last_value are determined by the partition.
  1119  	rel.FuncDeps.CopyFrom(&inputProps.FuncDeps)
  1120  
  1121  	// Cardinality
  1122  	// -----------
  1123  	// Window functions never change the cardinality of their input.
  1124  	rel.Cardinality = inputProps.Cardinality
  1125  
  1126  	// Statistics
  1127  	// ----------
  1128  	if !b.disableStats {
  1129  		b.sb.buildWindow(window, rel)
  1130  	}
  1131  }
  1132  
  1133  func (b *logicalPropsBuilder) buildProjectSetProps(
  1134  	projectSet *ProjectSetExpr, rel *props.Relational,
  1135  ) {
  1136  	BuildSharedProps(projectSet, &rel.Shared)
  1137  
  1138  	inputProps := projectSet.Input.Relational()
  1139  
  1140  	// Output Columns
  1141  	// --------------
  1142  	// Output columns are the union between the output columns from the Zip and
  1143  	// the input.
  1144  	rel.OutputCols = projectSet.Zip.OutputCols()
  1145  	rel.OutputCols.UnionWith(inputProps.OutputCols)
  1146  
  1147  	// Not Null Columns
  1148  	// ----------------
  1149  	// Inherit not null columns from input. All other columns are assumed to be
  1150  	// nullable.
  1151  	rel.NotNullCols = inputProps.NotNullCols.Copy()
  1152  
  1153  	// Outer Columns
  1154  	// -------------
  1155  	// Outer columns were derived by BuildSharedProps; remove any that are bound
  1156  	// by input columns.
  1157  	rel.OuterCols.DifferenceWith(inputProps.OutputCols)
  1158  
  1159  	// Functional Dependencies
  1160  	// -----------------------
  1161  	// Start with copy of FuncDepSet. Since ProjectSet is a lateral cross join
  1162  	// between the input and the functional zip (which has an empty FD set), call
  1163  	// MakeApply with an empty FD set. Then add outer columns, modify with
  1164  	// any additional not-null columns, and possibly simplify by calling
  1165  	// ProjectCols.
  1166  	rel.FuncDeps.CopyFrom(&inputProps.FuncDeps)
  1167  	rel.FuncDeps.MakeApply(&props.FuncDepSet{})
  1168  	addOuterColsToFuncDep(rel.OuterCols, &rel.FuncDeps)
  1169  	rel.FuncDeps.MakeNotNull(rel.NotNullCols)
  1170  	rel.FuncDeps.ProjectCols(rel.OutputCols)
  1171  
  1172  	// Cardinality
  1173  	// -----------
  1174  	// Don't make any assumptions about cardinality of ProjectSet unless the
  1175  	// input cardinality is zero.
  1176  	if inputProps.Cardinality == props.ZeroCardinality {
  1177  		rel.Cardinality = props.ZeroCardinality
  1178  	} else {
  1179  		rel.Cardinality = props.AnyCardinality
  1180  	}
  1181  
  1182  	// Statistics
  1183  	// ----------
  1184  	if !b.disableStats {
  1185  		b.sb.buildProjectSet(projectSet, rel)
  1186  	}
  1187  }
  1188  
  1189  func (b *logicalPropsBuilder) buildInsertProps(ins *InsertExpr, rel *props.Relational) {
  1190  	b.buildMutationProps(ins, rel)
  1191  }
  1192  
  1193  func (b *logicalPropsBuilder) buildUpdateProps(upd *UpdateExpr, rel *props.Relational) {
  1194  	b.buildMutationProps(upd, rel)
  1195  }
  1196  
  1197  func (b *logicalPropsBuilder) buildUpsertProps(ups *UpsertExpr, rel *props.Relational) {
  1198  	b.buildMutationProps(ups, rel)
  1199  }
  1200  
  1201  func (b *logicalPropsBuilder) buildDeleteProps(del *DeleteExpr, rel *props.Relational) {
  1202  	b.buildMutationProps(del, rel)
  1203  }
  1204  
  1205  func (b *logicalPropsBuilder) buildMutationProps(mutation RelExpr, rel *props.Relational) {
  1206  	BuildSharedProps(mutation, &rel.Shared)
  1207  
  1208  	private := mutation.Private().(*MutationPrivate)
  1209  
  1210  	// If no rows are output by the operator, then all other properties retain
  1211  	// default values.
  1212  	if !private.NeedResults() {
  1213  		return
  1214  	}
  1215  
  1216  	inputProps := mutation.Child(0).(RelExpr).Relational()
  1217  	md := b.mem.Metadata()
  1218  	tab := md.Table(private.Table)
  1219  
  1220  	// Output Columns
  1221  	// --------------
  1222  	// Only non-mutation columns are output columns.
  1223  	for i, n := 0, tab.ColumnCount(); i < n; i++ {
  1224  		if private.IsColumnOutput(i) {
  1225  			colID := private.Table.ColumnID(i)
  1226  			rel.OutputCols.Add(colID)
  1227  		}
  1228  	}
  1229  
  1230  	// The output columns of the mutation will also include all
  1231  	// columns it allowed to pass through.
  1232  	for _, col := range private.PassthroughCols {
  1233  		if col != 0 {
  1234  			rel.OutputCols.Add(col)
  1235  		}
  1236  	}
  1237  
  1238  	// Not Null Columns
  1239  	// ----------------
  1240  	// A column should be marked as not-null if the target table column is not
  1241  	// null or the corresponding insert and fetch/update columns are not null. In
  1242  	// other words, if either the source or destination column is not null, then
  1243  	// the column must be not null.
  1244  	for i, n := 0, tab.ColumnCount(); i < n; i++ {
  1245  		tabColID := private.Table.ColumnID(i)
  1246  		if !rel.OutputCols.Contains(tabColID) {
  1247  			continue
  1248  		}
  1249  
  1250  		// If the target table column is not null, then mark the column as not null.
  1251  		if !tab.Column(i).IsNullable() {
  1252  			rel.NotNullCols.Add(tabColID)
  1253  			continue
  1254  		}
  1255  
  1256  		// If the input column is not null, then the result will be not null.
  1257  		if inputProps.NotNullCols.Contains(private.ReturnCols[i]) {
  1258  			rel.NotNullCols.Add(private.Table.ColumnID(i))
  1259  		}
  1260  	}
  1261  
  1262  	// Outer Columns
  1263  	// -------------
  1264  	// Outer columns were already derived by BuildSharedProps.
  1265  
  1266  	// Functional Dependencies
  1267  	// -----------------------
  1268  	// Start with copy of FuncDepSet from input. Map the FDs of each source column
  1269  	// to the corresponding destination column by making the columns equivalent
  1270  	// and then filtering out the source columns via a call to ProjectCols.
  1271  	rel.FuncDeps.CopyFrom(&inputProps.FuncDeps)
  1272  	private.AddEquivTableCols(md, &rel.FuncDeps)
  1273  	rel.FuncDeps.ProjectCols(rel.OutputCols)
  1274  
  1275  	// Cardinality
  1276  	// -----------
  1277  	// Inherit cardinality from input.
  1278  	rel.Cardinality = inputProps.Cardinality
  1279  
  1280  	// Statistics
  1281  	// ----------
  1282  	if !b.disableStats {
  1283  		b.sb.buildMutation(mutation, rel)
  1284  	}
  1285  }
  1286  
  1287  func (b *logicalPropsBuilder) buildCreateTableProps(ct *CreateTableExpr, rel *props.Relational) {
  1288  	BuildSharedProps(ct, &rel.Shared)
  1289  }
  1290  
  1291  func (b *logicalPropsBuilder) buildCreateViewProps(cv *CreateViewExpr, rel *props.Relational) {
  1292  	BuildSharedProps(cv, &rel.Shared)
  1293  }
  1294  
  1295  func (b *logicalPropsBuilder) buildFiltersItemProps(item *FiltersItem, scalar *props.Scalar) {
  1296  	BuildSharedProps(item.Condition, &scalar.Shared)
  1297  
  1298  	// Constraints
  1299  	// -----------
  1300  	cb := constraintsBuilder{md: b.mem.Metadata(), evalCtx: b.evalCtx}
  1301  	// TODO(rytaft): Using local variables here to avoid a data race. It would be
  1302  	// better to avoid lazy building of props altogether.
  1303  	constraints, tightConstraints := cb.buildConstraints(item.Condition)
  1304  	if constraints.IsUnconstrained() {
  1305  		scalar.Constraints, scalar.TightConstraints = nil, false
  1306  	} else {
  1307  		scalar.Constraints, scalar.TightConstraints = constraints, tightConstraints
  1308  	}
  1309  
  1310  	// Functional Dependencies
  1311  	// -----------------------
  1312  	// Add constant columns. No need to add not null columns, because they
  1313  	// are only relevant if there are lax FDs that can be made strict.
  1314  	if scalar.Constraints != nil {
  1315  		constCols := scalar.Constraints.ExtractConstCols(b.evalCtx)
  1316  		scalar.FuncDeps.AddConstants(constCols)
  1317  	}
  1318  
  1319  	// Check for filter conjunct of the form: x = y.
  1320  	if eq, ok := item.Condition.(*EqExpr); ok {
  1321  		if leftVar, ok := eq.Left.(*VariableExpr); ok {
  1322  			if rightVar, ok := eq.Right.(*VariableExpr); ok {
  1323  				scalar.FuncDeps.AddEquivalency(leftVar.Col, rightVar.Col)
  1324  			}
  1325  		}
  1326  	}
  1327  }
  1328  
  1329  func (b *logicalPropsBuilder) buildProjectionsItemProps(
  1330  	item *ProjectionsItem, scalar *props.Scalar,
  1331  ) {
  1332  	item.Typ = item.Element.DataType()
  1333  	BuildSharedProps(item.Element, &scalar.Shared)
  1334  }
  1335  
  1336  func (b *logicalPropsBuilder) buildAggregationsItemProps(
  1337  	item *AggregationsItem, scalar *props.Scalar,
  1338  ) {
  1339  	item.Typ = item.Agg.DataType()
  1340  	BuildSharedProps(item.Agg, &scalar.Shared)
  1341  }
  1342  
  1343  func (b *logicalPropsBuilder) buildWindowsItemProps(item *WindowsItem, scalar *props.Scalar) {
  1344  	item.Typ = item.Function.DataType()
  1345  	BuildSharedProps(item.Function, &scalar.Shared)
  1346  }
  1347  
  1348  func (b *logicalPropsBuilder) buildZipItemProps(item *ZipItem, scalar *props.Scalar) {
  1349  	item.Typ = item.Fn.DataType()
  1350  	BuildSharedProps(item.Fn, &scalar.Shared)
  1351  }
  1352  
  1353  // BuildSharedProps fills in the shared properties derived from the given
  1354  // expression's subtree. It will only recurse into a child when it is not
  1355  // already caching properties.
  1356  //
  1357  // Note that shared is an "input-output" argument, and should be assumed
  1358  // to be partially filled in already. Boolean fields such as HasPlaceholder,
  1359  // CanHaveSideEffects and HasCorrelatedSubquery should never be reset to false
  1360  // once set to true.
  1361  func BuildSharedProps(e opt.Expr, shared *props.Shared) {
  1362  	switch t := e.(type) {
  1363  	case *VariableExpr:
  1364  		// Variable introduces outer column.
  1365  		shared.OuterCols.Add(t.Col)
  1366  		return
  1367  
  1368  	case *PlaceholderExpr:
  1369  		shared.HasPlaceholder = true
  1370  		return
  1371  
  1372  	case *DivExpr:
  1373  		// Division by zero error is possible, unless the right-hand side is a
  1374  		// non-zero constant.
  1375  		var nonZero bool
  1376  		if c, ok := t.Right.(*ConstExpr); ok {
  1377  			switch v := c.Value.(type) {
  1378  			case *tree.DInt:
  1379  				nonZero = (*v != 0)
  1380  			case *tree.DFloat:
  1381  				nonZero = (*v != 0.0)
  1382  			case *tree.DDecimal:
  1383  				nonZero = !v.IsZero()
  1384  			}
  1385  		}
  1386  		if !nonZero {
  1387  			shared.CanHaveSideEffects = true
  1388  			shared.VolatilitySet.AddImmutable()
  1389  		}
  1390  
  1391  	case *SubqueryExpr, *ExistsExpr, *AnyExpr, *ArrayFlattenExpr:
  1392  		shared.HasSubquery = true
  1393  		if hasOuterCols(e.Child(0)) {
  1394  			shared.HasCorrelatedSubquery = true
  1395  		}
  1396  		if t.Op() == opt.AnyOp && hasOuterCols(e.Child(1)) {
  1397  			shared.HasCorrelatedSubquery = true
  1398  		}
  1399  
  1400  	case *FunctionExpr:
  1401  		if t.Properties.Impure {
  1402  			// Impure functions can return different value on each call.
  1403  			shared.CanHaveSideEffects = true
  1404  		}
  1405  		shared.VolatilitySet.Add(t.Overload.Volatility)
  1406  
  1407  	default:
  1408  		if opt.IsMutationOp(e) {
  1409  			shared.CanHaveSideEffects = true
  1410  			shared.CanMutate = true
  1411  			shared.VolatilitySet.AddVolatile()
  1412  		}
  1413  	}
  1414  
  1415  	// Recursively build the shared properties.
  1416  	for i, n := 0, e.ChildCount(); i < n; i++ {
  1417  		child := e.Child(i)
  1418  
  1419  		// Some expressions cache shared properties.
  1420  		var cached *props.Shared
  1421  		switch t := child.(type) {
  1422  		case RelExpr:
  1423  			cached = &t.Relational().Shared
  1424  		case ScalarPropsExpr:
  1425  			cached = &t.ScalarProps().Shared
  1426  		}
  1427  
  1428  		// Don't need to recurse if properties are cached.
  1429  		if cached != nil {
  1430  			shared.OuterCols.UnionWith(cached.OuterCols)
  1431  			if cached.HasPlaceholder {
  1432  				shared.HasPlaceholder = true
  1433  			}
  1434  			shared.VolatilitySet.UnionWith(cached.VolatilitySet)
  1435  			if cached.CanHaveSideEffects {
  1436  				shared.CanHaveSideEffects = true
  1437  			}
  1438  			if cached.CanMutate {
  1439  				shared.CanMutate = true
  1440  			}
  1441  			if cached.HasSubquery {
  1442  				shared.HasSubquery = true
  1443  			}
  1444  			if cached.HasCorrelatedSubquery {
  1445  				shared.HasCorrelatedSubquery = true
  1446  			}
  1447  		} else {
  1448  			BuildSharedProps(e.Child(i), shared)
  1449  		}
  1450  	}
  1451  }
  1452  
  1453  // hasOuterCols returns true if the given expression has outer columns (i.e.
  1454  // columns that are referenced by the expression but not bound by it).
  1455  func hasOuterCols(e opt.Expr) bool {
  1456  	switch t := e.(type) {
  1457  	case *VariableExpr:
  1458  		return true
  1459  	case RelExpr:
  1460  		return !t.Relational().OuterCols.Empty()
  1461  	case ScalarPropsExpr:
  1462  		return !t.ScalarProps().Shared.OuterCols.Empty()
  1463  	}
  1464  
  1465  	for i, n := 0, e.ChildCount(); i < n; i++ {
  1466  		if hasOuterCols(e.Child(i)) {
  1467  			return true
  1468  		}
  1469  	}
  1470  
  1471  	return false
  1472  }
  1473  
  1474  // MakeTableFuncDep returns the set of functional dependencies derived from the
  1475  // given base table. The set is derived lazily and is cached in the metadata,
  1476  // since it may be accessed multiple times during query optimization. For more
  1477  // details, see Relational.FuncDepSet.
  1478  func MakeTableFuncDep(md *opt.Metadata, tabID opt.TableID) *props.FuncDepSet {
  1479  	fd, ok := md.TableAnnotation(tabID, fdAnnID).(*props.FuncDepSet)
  1480  	if ok {
  1481  		// Already made.
  1482  		return fd
  1483  	}
  1484  
  1485  	// Make now and annotate the metadata table with it for next time.
  1486  	var allCols opt.ColSet
  1487  	tab := md.Table(tabID)
  1488  	for i := 0; i < tab.DeletableColumnCount(); i++ {
  1489  		allCols.Add(tabID.ColumnID(i))
  1490  	}
  1491  
  1492  	fd = &props.FuncDepSet{}
  1493  	for i := 0; i < tab.IndexCount(); i++ {
  1494  		var keyCols opt.ColSet
  1495  		index := tab.Index(i)
  1496  
  1497  		if index.IsInverted() {
  1498  			// Skip inverted indexes for now.
  1499  			continue
  1500  		}
  1501  
  1502  		// If index has a separate lax key, add a lax key FD. Otherwise, add a
  1503  		// strict key. See the comment for cat.Index.LaxKeyColumnCount.
  1504  		for col := 0; col < index.LaxKeyColumnCount(); col++ {
  1505  			ord := index.Column(col).Ordinal
  1506  			keyCols.Add(tabID.ColumnID(ord))
  1507  		}
  1508  		if index.LaxKeyColumnCount() < index.KeyColumnCount() {
  1509  			// This case only occurs for a UNIQUE index having a NULL-able column.
  1510  			fd.AddLaxKey(keyCols, allCols)
  1511  		} else {
  1512  			fd.AddStrictKey(keyCols, allCols)
  1513  		}
  1514  	}
  1515  	md.SetTableAnnotation(tabID, fdAnnID, fd)
  1516  	return fd
  1517  }
  1518  
  1519  func (b *logicalPropsBuilder) makeSetCardinality(
  1520  	nt opt.Operator, left, right props.Cardinality,
  1521  ) props.Cardinality {
  1522  	var card props.Cardinality
  1523  	switch nt {
  1524  	case opt.UnionOp, opt.UnionAllOp:
  1525  		// Add cardinality of left and right inputs.
  1526  		card = left.Add(right)
  1527  
  1528  	case opt.IntersectOp, opt.IntersectAllOp:
  1529  		// Use minimum of left and right Max cardinality.
  1530  		card = props.Cardinality{Min: 0, Max: left.Max}
  1531  		card = card.Limit(right.Max)
  1532  
  1533  	case opt.ExceptOp, opt.ExceptAllOp:
  1534  		// Use left Max cardinality.
  1535  		card = props.Cardinality{Min: 0, Max: left.Max}
  1536  		if left.Min > right.Max {
  1537  			card.Min = left.Min - right.Max
  1538  		}
  1539  	}
  1540  	switch nt {
  1541  	case opt.UnionOp, opt.IntersectOp, opt.ExceptOp:
  1542  		// Removing distinct values results in at least one row if input has at
  1543  		// least one row.
  1544  		card = card.AsLowAs(1)
  1545  	}
  1546  	return card
  1547  }
  1548  
  1549  // rejectNullCols returns the set of all columns that are inferred to be not-
  1550  // null, based on the filter conditions.
  1551  func (b *logicalPropsBuilder) rejectNullCols(filters FiltersExpr) opt.ColSet {
  1552  	var notNullCols opt.ColSet
  1553  	for i := range filters {
  1554  		filterProps := filters[i].ScalarProps()
  1555  		if filterProps.Constraints != nil {
  1556  			notNullCols.UnionWith(filterProps.Constraints.ExtractNotNullCols(b.evalCtx))
  1557  		}
  1558  	}
  1559  	return notNullCols
  1560  }
  1561  
  1562  // addFiltersToFuncDep returns the union of all functional dependencies from
  1563  // each condition in the filters.
  1564  func (b *logicalPropsBuilder) addFiltersToFuncDep(filters FiltersExpr, fdset *props.FuncDepSet) {
  1565  	for i := range filters {
  1566  		filterProps := filters[i].ScalarProps()
  1567  		fdset.AddFrom(&filterProps.FuncDeps)
  1568  	}
  1569  
  1570  	if len(filters) <= 1 {
  1571  		return
  1572  	}
  1573  
  1574  	// Some columns can only be determined to be constant from multiple
  1575  	// constraints (e.g. x <= 1 AND x >= 1); we intersect the constraints and
  1576  	// extract const columns from the intersection. But intersection is expensive
  1577  	// so we first do a quick check to rule out cases where each constraint refers
  1578  	// to a different set of columns.
  1579  	var cols opt.ColSet
  1580  	possibleIntersection := false
  1581  	for i := range filters {
  1582  		if c := filters[i].ScalarProps().Constraints; c != nil {
  1583  			s := c.ExtractCols()
  1584  			if cols.Intersects(s) {
  1585  				possibleIntersection = true
  1586  				break
  1587  			}
  1588  			cols.UnionWith(s)
  1589  		}
  1590  	}
  1591  
  1592  	if possibleIntersection {
  1593  		intersection := constraint.Unconstrained
  1594  		for i := range filters {
  1595  			if c := filters[i].ScalarProps().Constraints; c != nil {
  1596  				intersection = intersection.Intersect(b.evalCtx, c)
  1597  			}
  1598  		}
  1599  		constCols := intersection.ExtractConstCols(b.evalCtx)
  1600  		fdset.AddConstants(constCols)
  1601  	}
  1602  }
  1603  
  1604  // updateCardinalityFromFilters determines whether a tight cardinality bound
  1605  // can be determined from the filters, and updates the cardinality accordingly.
  1606  // Specifically, it may be possible to determine a tight bound if the key
  1607  // column(s) are constrained to a finite number of values.
  1608  func (b *logicalPropsBuilder) updateCardinalityFromFilters(
  1609  	filters FiltersExpr, rel *props.Relational,
  1610  ) {
  1611  	for i := range filters {
  1612  		filterProps := filters[i].ScalarProps()
  1613  		if filterProps.Constraints == nil {
  1614  			continue
  1615  		}
  1616  
  1617  		for j, n := 0, filterProps.Constraints.Length(); j < n; j++ {
  1618  			c := filterProps.Constraints.Constraint(j)
  1619  			b.updateCardinalityFromConstraint(c, rel)
  1620  		}
  1621  	}
  1622  }
  1623  
  1624  // updateCardinalityFromConstraint determines whether a tight cardinality
  1625  // bound can be determined from the constraint, and updates the cardinality
  1626  // accordingly. Specifically, it may be possible to determine a tight bound
  1627  // if the key column(s) are constrained to a finite number of values.
  1628  func (b *logicalPropsBuilder) updateCardinalityFromConstraint(
  1629  	c *constraint.Constraint, rel *props.Relational,
  1630  ) {
  1631  	cols := c.Columns.ColSet()
  1632  	if !rel.FuncDeps.ColsAreLaxKey(cols) {
  1633  		return
  1634  	}
  1635  
  1636  	count := c.CalculateMaxResults(b.evalCtx, cols, rel.NotNullCols)
  1637  	if count != 0 && count < math.MaxUint32 {
  1638  		rel.Cardinality = rel.Cardinality.Limit(uint32(count))
  1639  	}
  1640  }
  1641  
  1642  // ensureLookupJoinInputProps lazily populates the relational properties that
  1643  // apply to the lookup side of the join, as if it were a Scan operator.
  1644  func ensureLookupJoinInputProps(join *LookupJoinExpr, sb *statisticsBuilder) *props.Relational {
  1645  	relational := &join.lookupProps
  1646  	if relational.OutputCols.Empty() {
  1647  		md := join.Memo().Metadata()
  1648  		relational.OutputCols = join.Cols.Difference(join.Input.Relational().OutputCols)
  1649  
  1650  		// Include the key columns in the output columns.
  1651  		index := md.Table(join.Table).Index(join.Index)
  1652  		for i := range join.KeyCols {
  1653  			indexColID := join.Table.ColumnID(index.Column(i).Ordinal)
  1654  			relational.OutputCols.Add(indexColID)
  1655  		}
  1656  
  1657  		relational.NotNullCols = tableNotNullCols(md, join.Table)
  1658  		relational.NotNullCols.IntersectionWith(relational.OutputCols)
  1659  		relational.Cardinality = props.AnyCardinality
  1660  		relational.FuncDeps.CopyFrom(MakeTableFuncDep(md, join.Table))
  1661  		relational.FuncDeps.ProjectCols(relational.OutputCols)
  1662  		relational.Stats = *sb.makeTableStatistics(join.Table)
  1663  	}
  1664  	return relational
  1665  }
  1666  
  1667  // ensureGeoLookupJoinInputProps lazily populates the relational properties
  1668  // that apply to the lookup side of the join, as if it were a Scan operator.
  1669  func ensureGeoLookupJoinInputProps(
  1670  	join *GeoLookupJoinExpr, sb *statisticsBuilder,
  1671  ) *props.Relational {
  1672  	relational := &join.lookupProps
  1673  	if relational.OutputCols.Empty() {
  1674  		md := join.Memo().Metadata()
  1675  		relational.OutputCols = join.Cols.Difference(join.Input.Relational().OutputCols)
  1676  		relational.NotNullCols = tableNotNullCols(md, join.Table)
  1677  		relational.NotNullCols.IntersectionWith(relational.OutputCols)
  1678  		relational.Cardinality = props.AnyCardinality
  1679  
  1680  		// TODO(rytaft): See if we need to use different functional dependencies
  1681  		// for the inverted index.
  1682  		relational.FuncDeps.CopyFrom(MakeTableFuncDep(md, join.Table))
  1683  		relational.FuncDeps.ProjectCols(relational.OutputCols)
  1684  
  1685  		// TODO(rytaft): Change this to use inverted index stats once available.
  1686  		relational.Stats = *sb.makeTableStatistics(join.Table)
  1687  	}
  1688  	return relational
  1689  }
  1690  
  1691  // ensureZigzagJoinInputProps lazily populates the relational properties that
  1692  // apply to the two sides of the join, as if it were a Scan operator.
  1693  func ensureZigzagJoinInputProps(join *ZigzagJoinExpr, sb *statisticsBuilder) {
  1694  	ensureInputPropsForIndex(
  1695  		join.Memo().Metadata(),
  1696  		join.LeftTable,
  1697  		join.LeftIndex,
  1698  		join.Cols,
  1699  		&join.leftProps,
  1700  		sb,
  1701  	)
  1702  	// For stats purposes, ensure left and right column sets are disjoint.
  1703  	ensureInputPropsForIndex(
  1704  		join.Memo().Metadata(),
  1705  		join.RightTable,
  1706  		join.RightIndex,
  1707  		join.Cols.Difference(join.leftProps.OutputCols),
  1708  		&join.rightProps,
  1709  		sb,
  1710  	)
  1711  }
  1712  
  1713  // ensureInputPropsForIndex populates relational properties for the specified
  1714  // table and index at the specified logical properties pointer.
  1715  func ensureInputPropsForIndex(
  1716  	md *opt.Metadata,
  1717  	tabID opt.TableID,
  1718  	indexOrd int,
  1719  	outputCols opt.ColSet,
  1720  	relProps *props.Relational,
  1721  	sb *statisticsBuilder,
  1722  ) {
  1723  	if relProps.OutputCols.Empty() {
  1724  		relProps.OutputCols = md.TableMeta(tabID).IndexColumns(indexOrd)
  1725  		relProps.OutputCols.IntersectionWith(outputCols)
  1726  		relProps.NotNullCols = tableNotNullCols(md, tabID)
  1727  		relProps.NotNullCols.IntersectionWith(relProps.OutputCols)
  1728  		relProps.Cardinality = props.AnyCardinality
  1729  		relProps.FuncDeps.CopyFrom(MakeTableFuncDep(md, tabID))
  1730  		relProps.FuncDeps.ProjectCols(relProps.OutputCols)
  1731  		relProps.Stats = *sb.makeTableStatistics(tabID)
  1732  	}
  1733  }
  1734  
  1735  // tableNotNullCols returns the set of not-NULL columns from the given table.
  1736  func tableNotNullCols(md *opt.Metadata, tabID opt.TableID) opt.ColSet {
  1737  	cs := opt.ColSet{}
  1738  	tab := md.Table(tabID)
  1739  
  1740  	// Only iterate over non-mutation columns, since even non-null mutation
  1741  	// columns can be null during backfill.
  1742  	for i := 0; i < tab.ColumnCount(); i++ {
  1743  		// Non-null mutation columns can be null during backfill.
  1744  		if !tab.Column(i).IsNullable() {
  1745  			cs.Add(tabID.ColumnID(i))
  1746  		}
  1747  	}
  1748  	return cs
  1749  }
  1750  
  1751  // addOuterColsToFuncDep adds the given outer columns and columns equivalent to
  1752  // them to the FD set. References to outer columns act like constants, since
  1753  // they are the same for all rows in the inner relation.
  1754  func addOuterColsToFuncDep(outerCols opt.ColSet, fdset *props.FuncDepSet) {
  1755  	equivCols := fdset.ComputeEquivClosure(outerCols)
  1756  	fdset.AddConstants(equivCols)
  1757  }
  1758  
  1759  // joinPropsHelper is a helper that calculates and stores properties related to
  1760  // joins that are used internally when deriving logical properties and
  1761  // statistics.
  1762  type joinPropsHelper struct {
  1763  	join     RelExpr
  1764  	joinType opt.Operator
  1765  
  1766  	leftProps  *props.Relational
  1767  	rightProps *props.Relational
  1768  
  1769  	filters           FiltersExpr
  1770  	filtersFD         props.FuncDepSet
  1771  	filterNotNullCols opt.ColSet
  1772  	filterIsTrue      bool
  1773  	filterIsFalse     bool
  1774  
  1775  	selfJoinCols opt.ColSet
  1776  }
  1777  
  1778  func (h *joinPropsHelper) init(b *logicalPropsBuilder, joinExpr RelExpr) {
  1779  	h.join = joinExpr
  1780  
  1781  	switch join := joinExpr.(type) {
  1782  	case *LookupJoinExpr:
  1783  		h.leftProps = joinExpr.Child(0).(RelExpr).Relational()
  1784  		ensureLookupJoinInputProps(join, &b.sb)
  1785  		h.joinType = join.JoinType
  1786  		h.rightProps = &join.lookupProps
  1787  		h.filters = join.On
  1788  		b.addFiltersToFuncDep(h.filters, &h.filtersFD)
  1789  		h.filterNotNullCols = b.rejectNullCols(h.filters)
  1790  
  1791  		// Apply the lookup join equalities.
  1792  		md := join.Memo().Metadata()
  1793  		index := md.Table(join.Table).Index(join.Index)
  1794  		for i, colID := range join.KeyCols {
  1795  			indexColID := join.Table.ColumnID(index.Column(i).Ordinal)
  1796  			h.filterNotNullCols.Add(colID)
  1797  			h.filterNotNullCols.Add(indexColID)
  1798  			h.filtersFD.AddEquivalency(colID, indexColID)
  1799  			if colID == indexColID {
  1800  				// This can happen if an index join was converted into a lookup join.
  1801  				h.selfJoinCols.Add(colID)
  1802  			}
  1803  		}
  1804  
  1805  		// Lookup join has implicit equality conditions on KeyCols.
  1806  		h.filterIsTrue = false
  1807  		h.filterIsFalse = h.filters.IsFalse()
  1808  
  1809  	case *GeoLookupJoinExpr:
  1810  		h.leftProps = joinExpr.Child(0).(RelExpr).Relational()
  1811  		ensureGeoLookupJoinInputProps(join, &b.sb)
  1812  		h.joinType = join.JoinType
  1813  		h.rightProps = &join.lookupProps
  1814  		h.filters = join.On
  1815  		b.addFiltersToFuncDep(h.filters, &h.filtersFD)
  1816  		h.filterNotNullCols = b.rejectNullCols(h.filters)
  1817  
  1818  		// Geospatial lookup join always has a filter condition on the index keys.
  1819  		h.filterIsTrue = false
  1820  		h.filterIsFalse = h.filters.IsFalse()
  1821  
  1822  	case *MergeJoinExpr:
  1823  		h.joinType = join.JoinType
  1824  		h.leftProps = join.Left.Relational()
  1825  		h.rightProps = join.Right.Relational()
  1826  		h.filters = join.On
  1827  		b.addFiltersToFuncDep(h.filters, &h.filtersFD)
  1828  		h.filterNotNullCols = b.rejectNullCols(h.filters)
  1829  
  1830  		// Apply the merge join equalities.
  1831  		for i := range join.LeftEq {
  1832  			l := join.LeftEq[i].ID()
  1833  			r := join.RightEq[i].ID()
  1834  			h.filterNotNullCols.Add(l)
  1835  			h.filterNotNullCols.Add(r)
  1836  			h.filtersFD.AddEquivalency(l, r)
  1837  		}
  1838  
  1839  		// Merge join has implicit equality conditions on the merge columns.
  1840  		h.filterIsTrue = false
  1841  		h.filterIsFalse = h.filters.IsFalse()
  1842  
  1843  	case *ZigzagJoinExpr:
  1844  		ensureZigzagJoinInputProps(join, &b.sb)
  1845  		h.joinType = opt.InnerJoinOp
  1846  		h.leftProps = &join.leftProps
  1847  		h.rightProps = &join.rightProps
  1848  		h.filters = join.On
  1849  		b.addFiltersToFuncDep(h.filters, &h.filtersFD)
  1850  		h.filterNotNullCols = b.rejectNullCols(h.filters)
  1851  
  1852  		// Apply the zigzag join equalities.
  1853  		for i := range join.LeftEqCols {
  1854  			leftColID := join.LeftEqCols[i]
  1855  			rightColID := join.RightEqCols[i]
  1856  
  1857  			h.filterNotNullCols.Add(leftColID)
  1858  			h.filterNotNullCols.Add(rightColID)
  1859  			h.filtersFD.AddEquivalency(leftColID, rightColID)
  1860  		}
  1861  
  1862  	default:
  1863  		h.joinType = join.Op()
  1864  		h.leftProps = join.Child(0).(RelExpr).Relational()
  1865  		h.rightProps = join.Child(1).(RelExpr).Relational()
  1866  
  1867  		h.filters = *join.Child(2).(*FiltersExpr)
  1868  		b.addFiltersToFuncDep(h.filters, &h.filtersFD)
  1869  		h.filterNotNullCols = b.rejectNullCols(h.filters)
  1870  		h.filterIsTrue = h.filters.IsTrue()
  1871  		h.filterIsFalse = h.filters.IsFalse()
  1872  	}
  1873  }
  1874  
  1875  func (h *joinPropsHelper) outputCols() opt.ColSet {
  1876  	// Output columns are union of columns from left and right inputs, except
  1877  	// in case of:
  1878  	//
  1879  	//   1. semi and anti joins, which only project the left columns
  1880  	//   2. lookup joins, which can project a subset of input columns
  1881  	//
  1882  	var cols opt.ColSet
  1883  	switch h.joinType {
  1884  	case opt.SemiJoinOp, opt.AntiJoinOp, opt.SemiJoinApplyOp, opt.AntiJoinApplyOp:
  1885  		cols = h.leftProps.OutputCols.Copy()
  1886  
  1887  	default:
  1888  		cols = h.leftProps.OutputCols.Union(h.rightProps.OutputCols)
  1889  	}
  1890  
  1891  	if lookup, ok := h.join.(*LookupJoinExpr); ok {
  1892  		// Remove any columns that are not projected by the lookup join.
  1893  		cols.IntersectionWith(lookup.Cols)
  1894  	}
  1895  
  1896  	return cols
  1897  }
  1898  
  1899  func (h *joinPropsHelper) notNullCols() opt.ColSet {
  1900  	var notNullCols opt.ColSet
  1901  
  1902  	// Left/full outer joins can result in right columns becoming null.
  1903  	// Otherwise, propagate not null setting from right child.
  1904  	switch h.joinType {
  1905  	case opt.LeftJoinOp, opt.FullJoinOp, opt.LeftJoinApplyOp,
  1906  		opt.SemiJoinOp, opt.SemiJoinApplyOp, opt.AntiJoinOp, opt.AntiJoinApplyOp:
  1907  
  1908  	default:
  1909  		notNullCols = h.rightProps.NotNullCols.Copy()
  1910  	}
  1911  
  1912  	// Right/full outer joins can result in left columns becoming null.
  1913  	// Otherwise, propagate not null setting from left child.
  1914  	switch h.joinType {
  1915  	case opt.RightJoinOp, opt.FullJoinOp:
  1916  
  1917  	default:
  1918  		notNullCols.UnionWith(h.leftProps.NotNullCols)
  1919  	}
  1920  
  1921  	// Add not-null constraints from ON predicate for inner and semi-joins.
  1922  	switch h.joinType {
  1923  	case opt.InnerJoinOp, opt.SemiJoinApplyOp:
  1924  		notNullCols.UnionWith(h.filterNotNullCols)
  1925  	}
  1926  
  1927  	return notNullCols
  1928  }
  1929  
  1930  func (h *joinPropsHelper) setFuncDeps(rel *props.Relational) {
  1931  	// Start with FDs from left side, and modify based on join type.
  1932  	rel.FuncDeps.CopyFrom(&h.leftProps.FuncDeps)
  1933  
  1934  	// Anti and semi joins only inherit FDs from left side, since right side
  1935  	// simply acts like a filter.
  1936  	switch h.joinType {
  1937  	case opt.SemiJoinOp, opt.SemiJoinApplyOp:
  1938  		// Add FDs from the ON predicate, which include equivalent columns and
  1939  		// constant columns. Any outer columns become constants as well.
  1940  		rel.FuncDeps.AddFrom(&h.filtersFD)
  1941  		addOuterColsToFuncDep(rel.OuterCols, &rel.FuncDeps)
  1942  		rel.FuncDeps.MakeNotNull(rel.NotNullCols)
  1943  
  1944  		// Call ProjectCols to remove any FDs involving columns from the right side.
  1945  		rel.FuncDeps.ProjectCols(rel.OutputCols)
  1946  
  1947  	case opt.AntiJoinOp, opt.AntiJoinApplyOp:
  1948  		// Anti-joins inherit FDs from left input, and nothing more, since the
  1949  		// right input is not projected, and the ON predicate doesn't filter rows
  1950  		// in the usual way.
  1951  
  1952  	default:
  1953  		// Joins are modeled as consisting of several steps:
  1954  		//   1. Compute cartesian product of left and right inputs.
  1955  		//   2. For inner joins, apply ON predicate filter on resulting rows.
  1956  		//   3. For outer joins, add non-matching rows, extended with NULL values
  1957  		//      for the null-supplying side of the join.
  1958  		if opt.IsJoinApplyOp(h.join) {
  1959  			rel.FuncDeps.MakeApply(&h.rightProps.FuncDeps)
  1960  		} else {
  1961  			rel.FuncDeps.MakeProduct(&h.rightProps.FuncDeps)
  1962  		}
  1963  
  1964  		notNullInputCols := h.leftProps.NotNullCols.Union(h.rightProps.NotNullCols)
  1965  
  1966  		switch h.joinType {
  1967  		case opt.InnerJoinOp, opt.InnerJoinApplyOp:
  1968  			// Add FDs from the ON predicate, which include equivalent columns and
  1969  			// constant columns.
  1970  			rel.FuncDeps.AddFrom(&h.filtersFD)
  1971  			addOuterColsToFuncDep(rel.OuterCols, &rel.FuncDeps)
  1972  
  1973  		case opt.LeftJoinOp, opt.LeftJoinApplyOp:
  1974  			rel.FuncDeps.MakeLeftOuter(
  1975  				&h.leftProps.FuncDeps, &h.filtersFD,
  1976  				h.leftProps.OutputCols, h.rightProps.OutputCols, notNullInputCols,
  1977  			)
  1978  
  1979  		case opt.RightJoinOp:
  1980  			rel.FuncDeps.MakeLeftOuter(
  1981  				&h.rightProps.FuncDeps, &h.filtersFD,
  1982  				h.rightProps.OutputCols, h.leftProps.OutputCols, notNullInputCols,
  1983  			)
  1984  
  1985  		case opt.FullJoinOp:
  1986  			rel.FuncDeps.MakeFullOuter(h.leftProps.OutputCols, h.rightProps.OutputCols, notNullInputCols)
  1987  
  1988  		default:
  1989  			panic(errors.AssertionFailedf("unhandled join type %s", h.joinType))
  1990  		}
  1991  
  1992  		rel.FuncDeps.MakeNotNull(rel.NotNullCols)
  1993  
  1994  		// Call ProjectCols to trigger simplification, since outer joins may have
  1995  		// created new possibilities for simplifying removed columns.
  1996  		rel.FuncDeps.ProjectCols(rel.OutputCols)
  1997  	}
  1998  }
  1999  
  2000  func (h *joinPropsHelper) cardinality() props.Cardinality {
  2001  	left := h.leftProps.Cardinality
  2002  
  2003  	switch h.joinType {
  2004  	case opt.SemiJoinOp, opt.SemiJoinApplyOp, opt.AntiJoinOp, opt.AntiJoinApplyOp:
  2005  		// Semi/Anti join cardinality never exceeds left input cardinality, and
  2006  		// allows zero rows.
  2007  		return left.AsLowAs(0)
  2008  	}
  2009  
  2010  	// Other join types can return up to cross product of rows.
  2011  	right := h.rightProps.Cardinality
  2012  	innerJoinCard := left.Product(right)
  2013  
  2014  	// Apply filter to cardinality.
  2015  	if !h.filterIsTrue {
  2016  		if h.filterIsFalse {
  2017  			innerJoinCard = props.ZeroCardinality
  2018  		} else {
  2019  			innerJoinCard = innerJoinCard.AsLowAs(0)
  2020  		}
  2021  	}
  2022  
  2023  	// Outer joins return minimum number of rows, depending on type.
  2024  	switch h.joinType {
  2025  	case opt.LeftJoinOp, opt.LeftJoinApplyOp:
  2026  		return innerJoinCard.AtLeast(left)
  2027  
  2028  	case opt.RightJoinOp:
  2029  		return innerJoinCard.AtLeast(right)
  2030  
  2031  	case opt.FullJoinOp:
  2032  		if innerJoinCard.IsZero() {
  2033  			// In this case, we know that each left or right row will generate an
  2034  			// output row.
  2035  			return left.Add(right)
  2036  		}
  2037  		var c props.Cardinality
  2038  		// We get at least MAX(left.Min, right.Min) rows.
  2039  		c.Min = left.Min
  2040  		if c.Min < right.Min {
  2041  			c.Min = right.Min
  2042  		}
  2043  		// We could get left.Max + right.Max rows (if the filter doesn't match
  2044  		// anything). We use Add here because it handles overflow.
  2045  		c.Max = left.Add(right).Max
  2046  		return innerJoinCard.AtLeast(c)
  2047  
  2048  	default:
  2049  		return innerJoinCard
  2050  	}
  2051  }
  2052  
  2053  func (b *logicalPropsBuilder) buildFakeRelProps(fake *FakeRelExpr, rel *props.Relational) {
  2054  	*rel = *fake.Props
  2055  }