diff --git a/pkg/sql/opt/colset.go b/pkg/sql/opt/colset.go index 409092fbe625..b9c72723213c 100644 --- a/pkg/sql/opt/colset.go +++ b/pkg/sql/opt/colset.go @@ -99,6 +99,15 @@ func (s ColSet) SingleColumn() ColumnID { return col } +// ToList converts the set to a ColList, in column ID order. +func (s ColSet) ToList() ColList { + res := make(ColList, 0, s.Len()) + s.ForEach(func(x ColumnID) { + res = append(res, x) + }) + return res +} + // TranslateColSet is used to translate a ColSet from one set of column IDs // to an equivalent set. This is relevant for set operations such as UNION, // INTERSECT and EXCEPT, and can be used to map a ColSet defined on the left diff --git a/pkg/sql/opt/column_meta.go b/pkg/sql/opt/column_meta.go index f1fd143b99aa..a01c61c3853d 100644 --- a/pkg/sql/opt/column_meta.go +++ b/pkg/sql/opt/column_meta.go @@ -26,33 +26,12 @@ func (c ColumnID) index() int { return int(c - 1) } -// ColList is a list of column ids. +// ColList is a list of column IDs. // // TODO(radu): perhaps implement a FastIntList with the same "small" // representation as FastIntMap but with a slice for large cases. type ColList []ColumnID -// ColumnMeta stores information about one of the columns stored in the -// metadata. -type ColumnMeta struct { - // MetaID is the identifier for this column that is unique within the query - // metadata. - MetaID ColumnID - - // Alias is the best-effort name of this column. Since the same column in a - // query can have multiple names (using aliasing), one of those is chosen to - // be used for pretty-printing and debugging. This might be different than - // what is stored in the physical properties and is presented to end users. - Alias string - - // Type is the scalar SQL type of this column. - Type *types.T - - // Table is the base table to which this column belongs. - // If the column was synthesized (i.e. no base table), then it is 0. - Table TableID -} - // ToSet converts a column id list to a column id set. func (cl ColList) ToSet() ColSet { var r ColSet @@ -87,13 +66,68 @@ func (cl ColList) Equals(other ColList) bool { return true } -// ColSetToList converts a column id set to a list, in column id order. -func ColSetToList(set ColSet) ColList { - res := make(ColList, 0, set.Len()) - set.ForEach(func(x ColumnID) { - res = append(res, x) - }) - return res +// OptionalColList is a list of column IDs where some of the IDs can be unset. +// It is used when the columns map 1-to-1 to a known list of objects (e.g. table +// columns). +type OptionalColList []ColumnID + +// IsEmpty returns true if all columns in the list are unset. +func (ocl OptionalColList) IsEmpty() bool { + for i := range ocl { + if ocl[i] != 0 { + return false + } + } + return true +} + +// Equals returns true if this column list is identical to another list. +func (ocl OptionalColList) Equals(other OptionalColList) bool { + if len(ocl) != len(other) { + return false + } + for i := range ocl { + if ocl[i] != other[i] { + return false + } + } + return true +} + +// Find searches for a column in the list and returns its index in the list (if +// successful). +func (ocl OptionalColList) Find(col ColumnID) (idx int, ok bool) { + if col == 0 { + // An OptionalColList cannot contain zero column IDs. + return -1, false + } + for i := range ocl { + if ocl[i] == col { + return i, true + } + } + return -1, false +} + +// ColumnMeta stores information about one of the columns stored in the +// metadata. +type ColumnMeta struct { + // MetaID is the identifier for this column that is unique within the query + // metadata. + MetaID ColumnID + + // Alias is the best-effort name of this column. Since the same column in a + // query can have multiple names (using aliasing), one of those is chosen to + // be used for pretty-printing and debugging. This might be different than + // what is stored in the physical properties and is presented to end users. + Alias string + + // Type is the scalar SQL type of this column. + Type *types.T + + // Table is the base table to which this column belongs. + // If the column was synthesized (i.e. no base table), then it is 0. + Table TableID } // ColMap provides a 1:1 mapping from one column id to another. It is used by diff --git a/pkg/sql/opt/exec/execbuilder/mutation.go b/pkg/sql/opt/exec/execbuilder/mutation.go index 7a035cbf79ba..56709ed26338 100644 --- a/pkg/sql/opt/exec/execbuilder/mutation.go +++ b/pkg/sql/opt/exec/execbuilder/mutation.go @@ -193,7 +193,7 @@ func (b *Builder) tryBuildFastPathInsert(ins *memo.InsertExpr) (_ execPlan, ok b out := &fkChecks[i] out.InsertCols = make([]exec.TableColumnOrdinal, len(lookupJoin.KeyCols)) - findCol := func(cols opt.ColList, col opt.ColumnID) int { + findCol := func(cols opt.OptionalColList, col opt.ColumnID) int { res, ok := cols.Find(col) if !ok { panic(errors.AssertionFailedf("cannot find column %d", col)) @@ -203,7 +203,7 @@ func (b *Builder) tryBuildFastPathInsert(ins *memo.InsertExpr) (_ execPlan, ok b for i, keyCol := range lookupJoin.KeyCols { // The keyCol comes from the WithScan operator. We must find the matching // column in the mutation input. - withColOrd := findCol(withScan.OutCols, keyCol) + withColOrd := findCol(opt.OptionalColList(withScan.OutCols), keyCol) inputCol := withScan.InCols[withColOrd] out.InsertCols[i] = exec.TableColumnOrdinal(findCol(ins.InsertCols, inputCol)) } @@ -302,7 +302,7 @@ func (b *Builder) buildUpdate(upd *memo.UpdateExpr) (execPlan, error) { // to passthrough those columns so the projection above can use // them. if upd.NeedResults() { - colList = appendColsWhenPresent(colList, upd.PassthroughCols) + colList = append(colList, upd.PassthroughCols...) } colList = appendColsWhenPresent(colList, upd.CheckCols) colList = appendColsWhenPresent(colList, upd.PartialIndexPutCols) @@ -724,7 +724,7 @@ func (b *Builder) buildDeleteRange( // appendColsWhenPresent appends non-zero column IDs from the src list into the // dst list, and returns the possibly grown list. -func appendColsWhenPresent(dst, src opt.ColList) opt.ColList { +func appendColsWhenPresent(dst opt.ColList, src opt.OptionalColList) opt.ColList { for _, col := range src { if col != 0 { dst = append(dst, col) @@ -737,11 +737,8 @@ func appendColsWhenPresent(dst, src opt.ColList) opt.ColList { // column ID in the given list. This is used with mutation operators, which // maintain lists that correspond to the target table, with zero column IDs // indicating columns that are not involved in the mutation. -func ordinalSetFromColList(colList opt.ColList) util.FastIntSet { +func ordinalSetFromColList(colList opt.OptionalColList) util.FastIntSet { var res util.FastIntSet - if colList == nil { - return res - } for i, col := range colList { if col != 0 { res.Add(i) diff --git a/pkg/sql/opt/exec/execbuilder/relational.go b/pkg/sql/opt/exec/execbuilder/relational.go index 7a3337187b06..47871f9cffaa 100644 --- a/pkg/sql/opt/exec/execbuilder/relational.go +++ b/pkg/sql/opt/exec/execbuilder/relational.go @@ -1197,7 +1197,7 @@ func (b *Builder) buildDistinct(distinct memo.RelExpr) (execPlan, error) { if input.outputCols.Len() == outCols.Len() { return ep, nil } - return b.ensureColumns(ep, opt.ColSetToList(outCols), distinct.ProvidedPhysical().Ordering) + return b.ensureColumns(ep, outCols.ToList(), distinct.ProvidedPhysical().Ordering) } func (b *Builder) buildGroupByInput(groupBy memo.RelExpr) (execPlan, error) { diff --git a/pkg/sql/opt/memo/check_expr.go b/pkg/sql/opt/memo/check_expr.go index 4f3158e89661..c45f26a2c1c4 100644 --- a/pkg/sql/opt/memo/check_expr.go +++ b/pkg/sql/opt/memo/check_expr.go @@ -285,7 +285,7 @@ func (m *Memo) CheckExpr(e opt.Expr) { } } -func (m *Memo) checkColListLen(colList opt.ColList, expectedLen int, listName string) { +func (m *Memo) checkColListLen(colList opt.OptionalColList, expectedLen int, listName string) { if len(colList) != expectedLen { panic(errors.AssertionFailedf("column list %s expected length = %d, actual length = %d", listName, log.Safe(expectedLen), len(colList))) diff --git a/pkg/sql/opt/memo/expr_format.go b/pkg/sql/opt/memo/expr_format.go index 3ff58842c904..38c8ac33ea1d 100644 --- a/pkg/sql/opt/memo/expr_format.go +++ b/pkg/sql/opt/memo/expr_format.go @@ -282,7 +282,7 @@ func (f *ExprFmtCtx) formatRelational(e RelExpr, tp treeprinter.Node) { default: // Fall back to writing output columns in column id order. - colList = opt.ColSetToList(e.Relational().OutputCols) + colList = e.Relational().OutputCols.ToList() } f.formatColumns(e, tp, colList, required.Presentation) @@ -294,7 +294,7 @@ func (f *ExprFmtCtx) formatRelational(e RelExpr, tp treeprinter.Node) { *UpsertDistinctOnExpr, *EnsureUpsertDistinctOnExpr: private := e.Private().(*GroupingPrivate) if !f.HasFlags(ExprFmtHideColumns) && !private.GroupingCols.Empty() { - f.formatColList(e, tp, "grouping columns:", opt.ColSetToList(private.GroupingCols)) + f.formatColList(e, tp, "grouping columns:", private.GroupingCols.ToList()) } if !f.HasFlags(ExprFmtHidePhysProps) && !private.Ordering.Any() { tp.Childf("internal-ordering: %s", private.Ordering) @@ -510,8 +510,8 @@ func (f *ExprFmtCtx) formatRelational(e RelExpr, tp treeprinter.Node) { } f.formatArbiters(tp, t.Arbiters, t.Table) f.formatMutationCols(e, tp, "insert-mapping:", t.InsertCols, t.Table) - f.formatColList(e, tp, "check columns:", t.CheckCols) - f.formatColList(e, tp, "partial index put columns:", t.PartialIndexPutCols) + f.formatOptionalColList(e, tp, "check columns:", t.CheckCols) + f.formatOptionalColList(e, tp, "partial index put columns:", t.PartialIndexPutCols) f.formatMutationCommon(tp, &t.MutationPrivate) } @@ -520,11 +520,11 @@ func (f *ExprFmtCtx) formatRelational(e RelExpr, tp treeprinter.Node) { if len(colList) == 0 { tp.Child("columns: ") } - f.formatColList(e, tp, "fetch columns:", t.FetchCols) + f.formatOptionalColList(e, tp, "fetch columns:", t.FetchCols) f.formatMutationCols(e, tp, "update-mapping:", t.UpdateCols, t.Table) - f.formatColList(e, tp, "check columns:", t.CheckCols) - f.formatColList(e, tp, "partial index put columns:", t.PartialIndexPutCols) - f.formatColList(e, tp, "partial index del columns:", t.PartialIndexDelCols) + f.formatOptionalColList(e, tp, "check columns:", t.CheckCols) + f.formatOptionalColList(e, tp, "partial index put columns:", t.PartialIndexPutCols) + f.formatOptionalColList(e, tp, "partial index del columns:", t.PartialIndexDelCols) f.formatMutationCommon(tp, &t.MutationPrivate) } @@ -536,16 +536,16 @@ func (f *ExprFmtCtx) formatRelational(e RelExpr, tp treeprinter.Node) { if t.CanaryCol != 0 { f.formatArbiters(tp, t.Arbiters, t.Table) f.formatColList(e, tp, "canary column:", opt.ColList{t.CanaryCol}) - f.formatColList(e, tp, "fetch columns:", t.FetchCols) + f.formatOptionalColList(e, tp, "fetch columns:", t.FetchCols) f.formatMutationCols(e, tp, "insert-mapping:", t.InsertCols, t.Table) f.formatMutationCols(e, tp, "update-mapping:", t.UpdateCols, t.Table) f.formatMutationCols(e, tp, "return-mapping:", t.ReturnCols, t.Table) } else { f.formatMutationCols(e, tp, "upsert-mapping:", t.InsertCols, t.Table) } - f.formatColList(e, tp, "check columns:", t.CheckCols) - f.formatColList(e, tp, "partial index put columns:", t.PartialIndexPutCols) - f.formatColList(e, tp, "partial index del columns:", t.PartialIndexDelCols) + f.formatOptionalColList(e, tp, "check columns:", t.CheckCols) + f.formatOptionalColList(e, tp, "partial index put columns:", t.PartialIndexPutCols) + f.formatOptionalColList(e, tp, "partial index del columns:", t.PartialIndexDelCols) f.formatMutationCommon(tp, &t.MutationPrivate) } @@ -554,8 +554,8 @@ func (f *ExprFmtCtx) formatRelational(e RelExpr, tp treeprinter.Node) { if len(colList) == 0 { tp.Child("columns: ") } - f.formatColList(e, tp, "fetch columns:", t.FetchCols) - f.formatColList(e, tp, "partial index del columns:", t.PartialIndexDelCols) + f.formatOptionalColList(e, tp, "fetch columns:", t.FetchCols) + f.formatOptionalColList(e, tp, "partial index del columns:", t.PartialIndexDelCols) f.formatMutationCommon(tp, &t.MutationPrivate) } @@ -1160,6 +1160,23 @@ func (f *ExprFmtCtx) formatColList( nd RelExpr, tp treeprinter.Node, heading string, colList opt.ColList, ) { if len(colList) > 0 { + notNullCols := nd.Relational().NotNullCols + f.Buffer.Reset() + f.Buffer.WriteString(heading) + for _, col := range colList { + f.space() + f.formatCol("" /* label */, col, notNullCols) + } + tp.Child(f.Buffer.String()) + } +} + +// formatOptionalColList constructs a new treeprinter child containing the +// specified list of optional columns formatted using the formatCol method. +func (f *ExprFmtCtx) formatOptionalColList( + nd RelExpr, tp treeprinter.Node, heading string, colList opt.OptionalColList, +) { + if !colList.IsEmpty() { notNullCols := nd.Relational().NotNullCols f.Buffer.Reset() f.Buffer.WriteString(heading) @@ -1180,9 +1197,9 @@ func (f *ExprFmtCtx) formatColList( // a:1 => x:4 // func (f *ExprFmtCtx) formatMutationCols( - nd RelExpr, tp treeprinter.Node, heading string, colList opt.ColList, tabID opt.TableID, + nd RelExpr, tp treeprinter.Node, heading string, colList opt.OptionalColList, tabID opt.TableID, ) { - if len(colList) == 0 { + if colList.IsEmpty() { return } diff --git a/pkg/sql/opt/memo/interner.go b/pkg/sql/opt/memo/interner.go index 533ba9e3403e..2be73defe867 100644 --- a/pkg/sql/opt/memo/interner.go +++ b/pkg/sql/opt/memo/interner.go @@ -437,6 +437,15 @@ func (h *hasher) HashColList(val opt.ColList) { h.hash = hash } +func (h *hasher) HashOptionalColList(val opt.OptionalColList) { + hash := h.hash + for _, id := range val { + hash ^= internHash(id) + hash *= prime64 + } + h.hash = hash +} + func (h *hasher) HashOrdering(val opt.Ordering) { hash := h.hash for _, id := range val { @@ -836,6 +845,10 @@ func (h *hasher) IsColListEqual(l, r opt.ColList) bool { return l.Equals(r) } +func (h *hasher) IsOptionalColListEqual(l, r opt.OptionalColList) bool { + return l.Equals(r) +} + func (h *hasher) IsOrderingEqual(l, r opt.Ordering) bool { return l.Equals(r) } diff --git a/pkg/sql/opt/norm/prune_cols_funcs.go b/pkg/sql/opt/norm/prune_cols_funcs.go index b1bc7004978d..ca0cf56b424b 100644 --- a/pkg/sql/opt/norm/prune_cols_funcs.go +++ b/pkg/sql/opt/norm/prune_cols_funcs.go @@ -48,7 +48,7 @@ func (c *CustomFuncs) NeededMutationCols( var cols opt.ColSet // Add all input columns referenced by the mutation private. - addCols := func(list opt.ColList) { + addCols := func(list opt.OptionalColList) { for _, id := range list { if id != 0 { cols.Add(id) @@ -63,7 +63,7 @@ func (c *CustomFuncs) NeededMutationCols( addCols(private.PartialIndexPutCols) addCols(private.PartialIndexDelCols) addCols(private.ReturnCols) - addCols(private.PassthroughCols) + addCols(opt.OptionalColList(private.PassthroughCols)) if private.CanaryCol != 0 { cols.Add(private.CanaryCol) } @@ -318,13 +318,11 @@ func (c *CustomFuncs) PruneMutationFetchCols( // that are not in the neededCols set to zero. This indicates that those input // columns are not needed by this mutation list. func (c *CustomFuncs) filterMutationList( - tabID opt.TableID, inList opt.ColList, neededCols opt.ColSet, -) opt.ColList { - newList := make(opt.ColList, len(inList)) + tabID opt.TableID, inList opt.OptionalColList, neededCols opt.ColSet, +) opt.OptionalColList { + newList := make(opt.OptionalColList, len(inList)) for i, c := range inList { - if !neededCols.Contains(tabID.ColumnID(i)) { - newList[i] = 0 - } else { + if c != 0 && neededCols.Contains(tabID.ColumnID(i)) { newList[i] = c } } @@ -585,7 +583,7 @@ func DerivePruneCols(e memo.RelExpr) opt.ColSet { // Find the columns that would need to be fetched, if no returning // clause were present. withoutReturningPrivate := *e.Private().(*memo.MutationPrivate) - withoutReturningPrivate.ReturnCols = opt.ColList{} + withoutReturningPrivate.ReturnCols = opt.OptionalColList{} neededCols := neededMutationFetchCols(e.Memo(), e.Op(), &withoutReturningPrivate) // Only the "free" RETURNING columns can be pruned away (i.e. the columns @@ -639,17 +637,12 @@ func (c *CustomFuncs) PruneMutationReturnCols( private *memo.MutationPrivate, needed opt.ColSet, ) *memo.MutationPrivate { newPrivate := *private - newReturnCols := make(opt.ColList, len(private.ReturnCols)) - newPassthroughCols := make(opt.ColList, 0, len(private.PassthroughCols)) tabID := c.mem.Metadata().TableMeta(private.Table).MetaID // Prune away the ReturnCols that are unused. - for i := range private.ReturnCols { - if needed.Contains(tabID.ColumnID(i)) { - newReturnCols[i] = private.ReturnCols[i] - } - } + newPrivate.ReturnCols = c.filterMutationList(tabID, private.ReturnCols, needed) + newPassthroughCols := make(opt.ColList, 0, len(private.PassthroughCols)) // Prune away the PassthroughCols that are unused. for _, passthroughCol := range private.PassthroughCols { if passthroughCol != 0 && needed.Contains(passthroughCol) { @@ -657,7 +650,6 @@ func (c *CustomFuncs) PruneMutationReturnCols( } } - newPrivate.ReturnCols = newReturnCols newPrivate.PassthroughCols = newPassthroughCols return &newPrivate } diff --git a/pkg/sql/opt/ops/mutation.opt b/pkg/sql/opt/ops/mutation.opt index f81319c9e56c..77db533285f1 100644 --- a/pkg/sql/opt/ops/mutation.opt +++ b/pkg/sql/opt/ops/mutation.opt @@ -44,7 +44,7 @@ define MutationPrivate { # # If there is a delete-only mutation column "c", then InsertCols would contain # [a_colid, b_colid, 0]. - InsertCols ColList + InsertCols OptionalColList # FetchCols are columns from the Input expression that will be fetched from # the target table. They must be a subset of the Input expression's output @@ -69,7 +69,7 @@ define MutationPrivate { # updated value, and the "d" column is needed because it's in the same family # as "c". Taking all this into account, FetchCols would contain this list: # [a_colid, 0, c_colid, d_colid, 0]. - FetchCols ColList + FetchCols OptionalColList # UpdateCols are columns from the Input expression that contain updated values # for columns of the target table. They must be a subset of the Input @@ -85,7 +85,7 @@ define MutationPrivate { # # Since column "b" is updated, and "c" is a computed column dependent on "b", # then UpdateCols would contain [0, b_colid, c_colid]. - UpdateCols ColList + UpdateCols OptionalColList # CheckCols are columns from the Input expression containing the results of # evaluating the check constraints from the target table. Evaluating a check @@ -103,7 +103,7 @@ define MutationPrivate { # Since the check constraint for column "a" can be statically proven to be # true, CheckCols would contain [0, b_colid]. # TODO(radu): we don't actually implement this optimization currently. - CheckCols ColList + CheckCols OptionalColList # PartialIndexPutCols are columns from the Input expression containing the # results of evaluating each partial index predicate from the target table @@ -125,13 +125,13 @@ define MutationPrivate { # evaluating the predicate of the index on c. The index on b is not a # partial index, because it has no predicate, so it is not included in # PartialIndexPutCols. - PartialIndexPutCols ColList + PartialIndexPutCols OptionalColList # PartialIndexDelCols is similar to PartialIndexPutCols, but instead # indicates when the previous version of a row must be deleted from a # partial index during updates or deletes in order to maintain the state of # the index. - PartialIndexDelCols ColList + PartialIndexDelCols OptionalColList # CanaryCol is used only with the Upsert operator. It identifies the column # that the execution engine uses to decide whether to insert or to update. @@ -153,7 +153,7 @@ define MutationPrivate { # including any columns that are undergoing mutation (being added or dropped # as part of online schema change). If no RETURNING clause was specified, # then ReturnCols is nil. - ReturnCols ColList + ReturnCols OptionalColList # PassthroughCols are columns that the mutation needs to passthrough from # its input. It's similar to the passthrough columns in projections. This diff --git a/pkg/sql/opt/optbuilder/mutation_builder.go b/pkg/sql/opt/optbuilder/mutation_builder.go index e5608dc3ffd9..44cbd684d5a2 100644 --- a/pkg/sql/opt/optbuilder/mutation_builder.go +++ b/pkg/sql/opt/optbuilder/mutation_builder.go @@ -72,7 +72,7 @@ type mutationBuilder struct { // including mutation columns. Table columns which will not have values // inserted are set to 0 (e.g. delete-only mutation columns). insertColIDs // is empty if this is not an Insert/Upsert operator. - insertColIDs opt.ColList + insertColIDs opt.OptionalColList // fetchColIDs lists the input column IDs storing values which are fetched // from the target table in order to provide existing values that will form @@ -80,13 +80,13 @@ type mutationBuilder struct { // columns in the target table, including mutation columns. Table columns // which do not need to be fetched are set to 0. fetchColIDs is empty if // this is an Insert operator. - fetchColIDs opt.ColList + fetchColIDs opt.OptionalColList // updateColIDs lists the input column IDs providing update values. Its // length is always equal to the number of columns in the target table, // including mutation columns. Table columns which do not need to be // updated are set to 0. - updateColIDs opt.ColList + updateColIDs opt.OptionalColList // upsertColIDs lists the input column IDs that choose between an insert or // update column using a CASE expression: @@ -98,13 +98,13 @@ type mutationBuilder struct { // the target table, including mutation columns. Table columns which do not // need to be updated are set to 0. upsertColIDs is empty if this is not // an Upsert operator. - upsertColIDs opt.ColList + upsertColIDs opt.OptionalColList // checkColIDs lists the input column IDs storing the boolean results of // evaluating check constraint expressions defined on the target table. Its // length is always equal to the number of check constraints on the table // (see opt.Table.CheckCount). - checkColIDs opt.ColList + checkColIDs opt.OptionalColList // partialIndexPutColIDs lists the input column IDs storing the boolean // results of evaluating partial index predicate expressions of the target @@ -114,7 +114,7 @@ type mutationBuilder struct { // added to the corresponding partial index. The length of // partialIndexPutColIDs is always equal to the number of partial indexes on // the table. - partialIndexPutColIDs opt.ColList + partialIndexPutColIDs opt.OptionalColList // partialIndexDelColIDs lists the input column IDs storing the boolean // results of evaluating partial index predicate expressions of the target @@ -124,7 +124,7 @@ type mutationBuilder struct { // should be removed from the corresponding partial index. The length of // partialIndexPutColIDs is always equal to the number of partial indexes on // the table. - partialIndexDelColIDs opt.ColList + partialIndexDelColIDs opt.OptionalColList // canaryColID is the ID of the column that is used to decide whether to // insert or update each row. If the canary column's value is null, then it's @@ -190,7 +190,7 @@ func (mb *mutationBuilder) init(b *Builder, opName string, tab cat.Table, alias // Allocate segmented array of column IDs. numPartialIndexes := partialIndexCount(tab) - colIDs := make(opt.ColList, n*4+tab.CheckCount()+2*numPartialIndexes) + colIDs := make(opt.OptionalColList, n*4+tab.CheckCount()+2*numPartialIndexes) mb.insertColIDs = colIDs[:n] mb.fetchColIDs = colIDs[n : n*2] mb.updateColIDs = colIDs[n*2 : n*3] @@ -557,7 +557,7 @@ func (mb *mutationBuilder) replaceDefaultExprs(inRows *tree.Select) (outRows *tr // NOTE: colIDs is updated with the column IDs of any synthesized columns which // are added to outScope. func (mb *mutationBuilder) addSynthesizedCols( - colIDs opt.ColList, addCol func(col *cat.Column) bool, + colIDs opt.OptionalColList, addCol func(col *cat.Column) bool, ) { var projectionsScope *scope @@ -641,7 +641,7 @@ func (mb *mutationBuilder) addSynthesizedCols( // roundDecimalValues will only round decimal columns that are part of the // colIDs list (i.e. are not 0). If a column is rounded, then the list will be // updated with the column ID of the new synthesized column. -func (mb *mutationBuilder) roundDecimalValues(colIDs opt.ColList, roundComputedCols bool) { +func (mb *mutationBuilder) roundDecimalValues(colIDs opt.OptionalColList, roundComputedCols bool) { var projectionsScope *scope for i, id := range colIDs { @@ -918,13 +918,11 @@ func (mb *mutationBuilder) makeMutationPrivate(needResults bool) *memo.MutationP // Helper function that returns nil if there are no non-zero column IDs in a // given list. A zero column ID indicates that column does not participate // in this mutation operation. - checkEmptyList := func(colIDs opt.ColList) opt.ColList { - for _, id := range colIDs { - if id != 0 { - return colIDs - } + checkEmptyList := func(colIDs opt.OptionalColList) opt.OptionalColList { + if colIDs.IsEmpty() { + return nil } - return nil + return colIDs } private := &memo.MutationPrivate{ @@ -946,7 +944,7 @@ func (mb *mutationBuilder) makeMutationPrivate(needResults bool) *memo.MutationP } if needResults { - private.ReturnCols = make(opt.ColList, mb.tab.ColumnCount()) + private.ReturnCols = make(opt.OptionalColList, mb.tab.ColumnCount()) for i, n := 0, mb.tab.ColumnCount(); i < n; i++ { if kind := mb.tab.Column(i).Kind(); kind != cat.Ordinary { // Only non-mutation and non-system columns are output columns. diff --git a/pkg/sql/opt/optgen/cmd/optgen/metadata.go b/pkg/sql/opt/optgen/cmd/optgen/metadata.go index e154aa2ea546..8ec6d4488d63 100644 --- a/pkg/sql/opt/optgen/cmd/optgen/metadata.go +++ b/pkg/sql/opt/optgen/cmd/optgen/metadata.go @@ -192,6 +192,7 @@ func newMetadata(compiled *lang.CompiledExpr, pkg string) *metadata { "ColumnID": {fullName: "opt.ColumnID", passByVal: true}, "ColSet": {fullName: "opt.ColSet", passByVal: true}, "ColList": {fullName: "opt.ColList", passByVal: true}, + "OptionalColList": {fullName: "opt.OptionalColList", passByVal: true}, "TableID": {fullName: "opt.TableID", passByVal: true}, "SchemaID": {fullName: "opt.SchemaID", passByVal: true}, "SequenceID": {fullName: "opt.SequenceID", passByVal: true}, diff --git a/pkg/sql/opt/xform/limit_funcs.go b/pkg/sql/opt/xform/limit_funcs.go index 154bf40aac67..0d33994ffe70 100644 --- a/pkg/sql/opt/xform/limit_funcs.go +++ b/pkg/sql/opt/xform/limit_funcs.go @@ -231,13 +231,13 @@ func (c *CustomFuncs) SplitScanIntoUnionScans( // Construct a new ScanExpr for each span and union them all together. We // output the old ColumnIDs from each union. - oldColList := opt.ColSetToList(scan.Relational().OutputCols) + oldColList := scan.Relational().OutputCols.ToList() last := c.makeNewScan(sp, cons.Columns, newHardLimit, newSpans.Get(0)) for i, cnt := 1, newSpans.Count(); i < cnt; i++ { newScan := c.makeNewScan(sp, cons.Columns, newHardLimit, newSpans.Get(i)) last = c.e.f.ConstructUnion(last, newScan, &memo.SetPrivate{ - LeftCols: opt.ColSetToList(last.Relational().OutputCols), - RightCols: opt.ColSetToList(newScan.Relational().OutputCols), + LeftCols: last.Relational().OutputCols.ToList(), + RightCols: newScan.Relational().OutputCols.ToList(), OutCols: oldColList, }) } diff --git a/pkg/sql/opt/xform/select_funcs.go b/pkg/sql/opt/xform/select_funcs.go index 13be5a43923a..1d624e1f557b 100644 --- a/pkg/sql/opt/xform/select_funcs.go +++ b/pkg/sql/opt/xform/select_funcs.go @@ -1729,10 +1729,10 @@ func (c *CustomFuncs) mapFilterCols( func (c *CustomFuncs) MakeSetPrivateForSplitDisjunction( left, right *memo.ScanPrivate, ) *memo.SetPrivate { - leftAndOutCols := opt.ColSetToList(left.Cols) + leftAndOutCols := left.Cols.ToList() return &memo.SetPrivate{ LeftCols: leftAndOutCols, - RightCols: opt.ColSetToList(right.Cols), + RightCols: right.Cols.ToList(), OutCols: leftAndOutCols, } }