From bde9ef1ae22f1b9100a4bbedfd12f226b292a1b3 Mon Sep 17 00:00:00 2001 From: Marcus Gartner Date: Tue, 29 Dec 2020 16:30:58 -0800 Subject: [PATCH] opt: normalize partial index PUT/DEL projections to false The `SimplifyPartialIndexProjections` normalization rule has been added that normalizes synthesized partial index PUT and DEL columns to False when it is guaranteed that a mutation will not require changed to the associated partial index. This normalization can lead to further normalizations, such as pruning columns that the synthesized projections relied on. The motivation for this change is to allow fully disjoint updates to different columns in the same row, when the columns are split across different families. By pruning columns not needed to maintain a partial index, we're not forced to scan all column families. This can ultimately reduce contention during updates. Release note (performance improvement): UPDATE and UPSERT operations on tables with partial indexes no longer evaluate partial index predicate expressions when it is guaranteed that the operation will not alter the state of the partial index. In some cases, this can eliminate fetching the existing value of columns that are referenced in partial index predicates. --- pkg/sql/opt/norm/mutation_funcs.go | 116 +++++++ pkg/sql/opt/norm/prune_cols_funcs.go | 15 +- pkg/sql/opt/norm/rules/mutation.opt | 39 +++ pkg/sql/opt/norm/testdata/rules/mutation | 332 +++++++++++++++++++++ pkg/sql/opt/norm/testdata/rules/prune_cols | 48 +-- 5 files changed, 524 insertions(+), 26 deletions(-) create mode 100644 pkg/sql/opt/norm/mutation_funcs.go create mode 100644 pkg/sql/opt/norm/testdata/rules/mutation diff --git a/pkg/sql/opt/norm/mutation_funcs.go b/pkg/sql/opt/norm/mutation_funcs.go new file mode 100644 index 000000000000..f0cc82e57e2f --- /dev/null +++ b/pkg/sql/opt/norm/mutation_funcs.go @@ -0,0 +1,116 @@ +// Copyright 2020 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package norm + +import ( + "github.com/cockroachdb/cockroach/pkg/sql/opt" + "github.com/cockroachdb/cockroach/pkg/sql/opt/memo" +) + +// SimplifiablePartialIndexProjectCols returns the set of projected partial +// index PUT and DEL columns with expressions that can be simplified to false. +// These projected expressions can only be simplified to false when an UPDATE or +// UPSERT mutates neither the associated index's columns nor the columns +// referenced in the partial index predicate. +func (c *CustomFuncs) SimplifiablePartialIndexProjectCols( + private *memo.MutationPrivate, + uniqueChecks memo.UniqueChecksExpr, + fkChecks memo.FKChecksExpr, + projections memo.ProjectionsExpr, +) opt.ColSet { + tabMeta := c.mem.Metadata().TableMeta(private.Table) + + // Determine the set of target table columns that need to be updated. + var updateCols opt.ColSet + for ord, col := range private.UpdateCols { + if col != 0 { + updateCols.Add(tabMeta.MetaID.ColumnID(ord)) + } + } + + // Determine the set of columns needed for the mutation operator, excluding + // the partial index PUT and DEL columns. + neededMutationCols := c.neededMutationCols(private, uniqueChecks, fkChecks, false /* includePartialIndexCols */) + + // Determine the set of project columns that are already simplified to + // false. + var simplifiedProjectCols opt.ColSet + for i := range projections { + project := &projections[i] + if project.Element == memo.FalseSingleton { + simplifiedProjectCols.Add(project.Col) + } + } + + // Columns that are required by the mutation operator and columns that + // have already been simplified to false are ineligible to be simplified. + ineligibleCols := neededMutationCols.Union(simplifiedProjectCols) + + var cols opt.ColSet + ord := 0 + for i, n := 0, tabMeta.Table.DeletableIndexCount(); i < n; i++ { + pred, isPartialIndex := tabMeta.PartialIndexPredicate(i) + + // Skip non-partial indexes. + if !isPartialIndex { + continue + } + + // If the columns being updated are part of the index or referenced in + // the partial index predicate, then the update requires may update the + // index. Therefore, the partial index PUT and DEL columns cannot be + // simplified. + // + // Note that we use the set of index columns where the virtual + // columns have been mapped to their source columns. Virtual columns + // are never part of the updated columns. Updates to source columns + // trigger index changes. + predFilters := *pred.(*memo.FiltersExpr) + indexAndPredCols := tabMeta.IndexColumnsMapVirtual(i) + indexAndPredCols.UnionWith(predFilters.OuterCols()) + if indexAndPredCols.Intersects(updateCols) { + continue + } + + // Add the projected PUT column if it is eligible to be simplified. + putCol := private.PartialIndexPutCols[ord] + if !ineligibleCols.Contains(putCol) { + cols.Add(putCol) + } + + // Add the projected DEL column if it is eligible to be simplified. + delCol := private.PartialIndexDelCols[ord] + if !ineligibleCols.Contains(delCol) { + cols.Add(delCol) + } + + ord++ + } + + return cols +} + +// SimplifyPartialIndexProjections returns a new projection expression with any +// projected column's expression simplified to false if the column exists in +// simplifiableCols. +func (c *CustomFuncs) SimplifyPartialIndexProjections( + projections memo.ProjectionsExpr, simplifiableCols opt.ColSet, +) memo.ProjectionsExpr { + simplified := make(memo.ProjectionsExpr, len(projections)) + for i := range projections { + if col := projections[i].Col; simplifiableCols.Contains(col) { + simplified[i] = c.f.ConstructProjectionsItem(memo.FalseSingleton, col) + } else { + simplified[i] = projections[i] + } + } + return simplified +} diff --git a/pkg/sql/opt/norm/prune_cols_funcs.go b/pkg/sql/opt/norm/prune_cols_funcs.go index eae66d98cd90..e7325cd6c62b 100644 --- a/pkg/sql/opt/norm/prune_cols_funcs.go +++ b/pkg/sql/opt/norm/prune_cols_funcs.go @@ -44,6 +44,15 @@ func (c *CustomFuncs) NeededExplainCols(private *memo.ExplainPrivate) opt.ColSet // in turn trigger the PruneMutationInputCols rule. func (c *CustomFuncs) NeededMutationCols( private *memo.MutationPrivate, uniqueChecks memo.UniqueChecksExpr, fkChecks memo.FKChecksExpr, +) opt.ColSet { + return c.neededMutationCols(private, uniqueChecks, fkChecks, true /* includePartialIndexCols */) +} + +func (c *CustomFuncs) neededMutationCols( + private *memo.MutationPrivate, + uniqueChecks memo.UniqueChecksExpr, + fkChecks memo.FKChecksExpr, + includePartialIndexCols bool, ) opt.ColSet { var cols opt.ColSet @@ -60,8 +69,10 @@ func (c *CustomFuncs) NeededMutationCols( addCols(private.FetchCols) addCols(private.UpdateCols) addCols(private.CheckCols) - addCols(private.PartialIndexPutCols) - addCols(private.PartialIndexDelCols) + if includePartialIndexCols { + addCols(private.PartialIndexPutCols) + addCols(private.PartialIndexDelCols) + } addCols(private.ReturnCols) addCols(opt.OptionalColList(private.PassthroughCols)) if private.CanaryCol != 0 { diff --git a/pkg/sql/opt/norm/rules/mutation.opt b/pkg/sql/opt/norm/rules/mutation.opt index e69de29bb2d1..cb1f8d264373 100644 --- a/pkg/sql/opt/norm/rules/mutation.opt +++ b/pkg/sql/opt/norm/rules/mutation.opt @@ -0,0 +1,39 @@ +# ============================================================================= +# mutation.opt contains normalization rules for the mutation operators. +# ============================================================================= + +# SimplifyPartialIndexProjections converts partial index PUT and DEL projected +# expressions to false when it is guaranteed that the mutation will not require +# changes to the associated partial index. These projected expressions can only +# be simplified to false when an UPDATE or UPSERT mutates neither the +# associated index's columns nor the columns referenced in the partial index +# predicate. +[SimplifyPartialIndexProjections, Normalize] +(Update | Upsert + $project:(Project $input:* $projections:* $passthrough:*) + $uniqueChecks:* + $fkChecks:* + $mutationPrivate:* & + ^(ColsAreEmpty + $simplifiableCols:(SimplifiablePartialIndexProjectCols + $mutationPrivate + $uniqueChecks + $fkChecks + $projections + ) + ) +) +=> +((OpName) + (Project + $input + (SimplifyPartialIndexProjections + $projections + $simplifiableCols + ) + $passthrough + ) + $uniqueChecks + $fkChecks + $mutationPrivate +) diff --git a/pkg/sql/opt/norm/testdata/rules/mutation b/pkg/sql/opt/norm/testdata/rules/mutation new file mode 100644 index 000000000000..18534be9d94a --- /dev/null +++ b/pkg/sql/opt/norm/testdata/rules/mutation @@ -0,0 +1,332 @@ +exec-ddl +CREATE TABLE t ( + k INT PRIMARY KEY, + a INT, + b INT, + c INT, + d INT, + e INT, + f INT, + g INT, + h BOOL, + INDEX (c) WHERE d > 1, + INDEX (e) WHERE f > 1 AND g > 1 +) +---- + +# Simplify UPDATE partial index put/del column to false when the indexed columns +# and columns referenced in predicates are not mutating. +norm expect=SimplifyPartialIndexProjections +UPDATE t SET a = 2, b = 2 WHERE k = 1 +---- +update t + ├── columns: + ├── fetch columns: k:11 a:12 b:13 c:14 d:15 e:16 f:17 g:18 h:19 + ├── update-mapping: + │ ├── a_new:21 => a:2 + │ └── a_new:21 => b:3 + ├── partial index put columns: partial_index_put1:22 partial_index_put2:23 + ├── partial index del columns: partial_index_put1:22 partial_index_put2:23 + ├── cardinality: [0 - 0] + ├── volatile, mutations + └── project + ├── columns: partial_index_put1:22!null partial_index_put2:23!null a_new:21!null k:11!null a:12 b:13 c:14 d:15 e:16 f:17 g:18 h:19 + ├── cardinality: [0 - 1] + ├── key: () + ├── fd: ()-->(11-19,21-23) + ├── select + │ ├── columns: k:11!null a:12 b:13 c:14 d:15 e:16 f:17 g:18 h:19 + │ ├── cardinality: [0 - 1] + │ ├── key: () + │ ├── fd: ()-->(11-19) + │ ├── scan t + │ │ ├── columns: k:11!null a:12 b:13 c:14 d:15 e:16 f:17 g:18 h:19 + │ │ ├── partial index predicates + │ │ │ ├── secondary: filters + │ │ │ │ └── d:15 > 1 [outer=(15), constraints=(/15: [/2 - ]; tight)] + │ │ │ └── secondary: filters + │ │ │ ├── f:17 > 1 [outer=(17), constraints=(/17: [/2 - ]; tight)] + │ │ │ └── g:18 > 1 [outer=(18), constraints=(/18: [/2 - ]; tight)] + │ │ ├── key: (11) + │ │ └── fd: (11)-->(12-19) + │ └── filters + │ └── k:11 = 1 [outer=(11), constraints=(/11: [/1 - /1]; tight), fd=()-->(11)] + └── projections + ├── false [as=partial_index_put1:22] + ├── false [as=partial_index_put2:23] + └── 2 [as=a_new:21] + +# Simplify UPSERT partial index put/del column to false when the indexed columns +# and columns referenced in predicates are not mutating. +norm expect=SimplifyPartialIndexProjections +UPSERT INTO t (k, a, b) VALUES (1, 2, 3) +---- +upsert t + ├── columns: + ├── arbiter indexes: primary + ├── canary column: k:16 + ├── fetch columns: k:16 a:17 b:18 c:19 d:20 e:21 f:22 g:23 h:24 + ├── insert-mapping: + │ ├── column1:11 => k:1 + │ ├── column2:12 => a:2 + │ ├── column3:13 => b:3 + │ ├── column14:14 => c:4 + │ ├── column14:14 => d:5 + │ ├── column14:14 => e:6 + │ ├── column14:14 => f:7 + │ ├── column14:14 => g:8 + │ └── column15:15 => h:9 + ├── update-mapping: + │ ├── column2:12 => a:2 + │ └── column3:13 => b:3 + ├── partial index put columns: partial_index_put1:33 partial_index_put2:35 + ├── partial index del columns: partial_index_del1:34 partial_index_del2:36 + ├── cardinality: [0 - 0] + ├── volatile, mutations + └── project + ├── columns: partial_index_put1:33!null partial_index_del1:34!null partial_index_put2:35!null partial_index_del2:36!null column1:11!null column2:12!null column3:13!null column14:14 column15:15 k:16 a:17 b:18 c:19 d:20 e:21 f:22 g:23 h:24 + ├── cardinality: [1 - 1] + ├── key: () + ├── fd: ()-->(11-24,33-36) + ├── left-join (cross) + │ ├── columns: column1:11!null column2:12!null column3:13!null column14:14 column15:15 k:16 a:17 b:18 c:19 d:20 e:21 f:22 g:23 h:24 + │ ├── cardinality: [1 - 1] + │ ├── multiplicity: left-rows(exactly-one), right-rows(exactly-one) + │ ├── key: () + │ ├── fd: ()-->(11-24) + │ ├── values + │ │ ├── columns: column1:11!null column2:12!null column3:13!null column14:14 column15:15 + │ │ ├── cardinality: [1 - 1] + │ │ ├── key: () + │ │ ├── fd: ()-->(11-15) + │ │ └── (1, 2, 3, NULL, NULL) + │ ├── select + │ │ ├── columns: k:16!null a:17 b:18 c:19 d:20 e:21 f:22 g:23 h:24 + │ │ ├── cardinality: [0 - 1] + │ │ ├── key: () + │ │ ├── fd: ()-->(16-24) + │ │ ├── scan t + │ │ │ ├── columns: k:16!null a:17 b:18 c:19 d:20 e:21 f:22 g:23 h:24 + │ │ │ ├── partial index predicates + │ │ │ │ ├── secondary: filters + │ │ │ │ │ └── d:20 > 1 [outer=(20), constraints=(/20: [/2 - ]; tight)] + │ │ │ │ └── secondary: filters + │ │ │ │ ├── f:22 > 1 [outer=(22), constraints=(/22: [/2 - ]; tight)] + │ │ │ │ └── g:23 > 1 [outer=(23), constraints=(/23: [/2 - ]; tight)] + │ │ │ ├── key: (16) + │ │ │ └── fd: (16)-->(17-24) + │ │ └── filters + │ │ └── k:16 = 1 [outer=(16), constraints=(/16: [/1 - /1]; tight), fd=()-->(16)] + │ └── filters (true) + └── projections + ├── false [as=partial_index_put1:33] + ├── false [as=partial_index_del1:34] + ├── false [as=partial_index_put2:35] + └── false [as=partial_index_del2:36] + +# Simplify INSERT ... ON CONFLICT ... DO UPDATE partial index put/del column to +# false when the indexed columns and columns referenced in predicates are not +# mutating. +norm expect=SimplifyPartialIndexProjections +INSERT INTO t (k, a, b) VALUES (1, 2, 3) ON CONFLICT (k) DO UPDATE SET a = t.a + 1, b = t.b + 1 +---- +upsert t + ├── columns: + ├── arbiter indexes: primary + ├── canary column: k:16 + ├── fetch columns: k:16 a:17 b:18 c:19 d:20 e:21 f:22 g:23 h:24 + ├── insert-mapping: + │ ├── column1:11 => k:1 + │ ├── column2:12 => a:2 + │ ├── column3:13 => b:3 + │ ├── column14:14 => c:4 + │ ├── column14:14 => d:5 + │ ├── column14:14 => e:6 + │ ├── column14:14 => f:7 + │ ├── column14:14 => g:8 + │ └── column15:15 => h:9 + ├── update-mapping: + │ ├── upsert_a:29 => a:2 + │ └── upsert_b:30 => b:3 + ├── partial index put columns: partial_index_put1:37 partial_index_put2:39 + ├── partial index del columns: partial_index_del1:38 partial_index_del2:40 + ├── cardinality: [0 - 0] + ├── volatile, mutations + └── project + ├── columns: partial_index_put1:37!null partial_index_del1:38!null partial_index_put2:39!null partial_index_del2:40!null upsert_a:29 upsert_b:30 column1:11!null column2:12!null column3:13!null column14:14 column15:15 k:16 a:17 b:18 c:19 d:20 e:21 f:22 g:23 h:24 + ├── cardinality: [1 - 1] + ├── immutable + ├── key: () + ├── fd: ()-->(11-24,29,30,37-40) + ├── left-join (cross) + │ ├── columns: column1:11!null column2:12!null column3:13!null column14:14 column15:15 k:16 a:17 b:18 c:19 d:20 e:21 f:22 g:23 h:24 + │ ├── cardinality: [1 - 1] + │ ├── multiplicity: left-rows(exactly-one), right-rows(exactly-one) + │ ├── key: () + │ ├── fd: ()-->(11-24) + │ ├── values + │ │ ├── columns: column1:11!null column2:12!null column3:13!null column14:14 column15:15 + │ │ ├── cardinality: [1 - 1] + │ │ ├── key: () + │ │ ├── fd: ()-->(11-15) + │ │ └── (1, 2, 3, NULL, NULL) + │ ├── select + │ │ ├── columns: k:16!null a:17 b:18 c:19 d:20 e:21 f:22 g:23 h:24 + │ │ ├── cardinality: [0 - 1] + │ │ ├── key: () + │ │ ├── fd: ()-->(16-24) + │ │ ├── scan t + │ │ │ ├── columns: k:16!null a:17 b:18 c:19 d:20 e:21 f:22 g:23 h:24 + │ │ │ ├── partial index predicates + │ │ │ │ ├── secondary: filters + │ │ │ │ │ └── d:20 > 1 [outer=(20), constraints=(/20: [/2 - ]; tight)] + │ │ │ │ └── secondary: filters + │ │ │ │ ├── f:22 > 1 [outer=(22), constraints=(/22: [/2 - ]; tight)] + │ │ │ │ └── g:23 > 1 [outer=(23), constraints=(/23: [/2 - ]; tight)] + │ │ │ ├── key: (16) + │ │ │ └── fd: (16)-->(17-24) + │ │ └── filters + │ │ └── k:16 = 1 [outer=(16), constraints=(/16: [/1 - /1]; tight), fd=()-->(16)] + │ └── filters (true) + └── projections + ├── false [as=partial_index_put1:37] + ├── false [as=partial_index_del1:38] + ├── false [as=partial_index_put2:39] + ├── false [as=partial_index_del2:40] + ├── CASE WHEN k:16 IS NULL THEN column2:12 ELSE a:17 + 1 END [as=upsert_a:29, outer=(12,16,17), immutable] + └── CASE WHEN k:16 IS NULL THEN column3:13 ELSE b:18 + 1 END [as=upsert_b:30, outer=(13,16,18), immutable] + +# Do not simplify partial index put/del column to false when the indexed columns +# are mutating. +norm expect-not=SimplifyPartialIndexProjections +UPDATE t SET c = 1, e = 1 WHERE k = 1 +---- +update t + ├── columns: + ├── fetch columns: k:11 a:12 b:13 c:14 d:15 e:16 f:17 g:18 h:19 + ├── update-mapping: + │ ├── c_new:21 => c:4 + │ └── c_new:21 => e:6 + ├── partial index put columns: partial_index_put1:22 partial_index_put2:23 + ├── partial index del columns: partial_index_put1:22 partial_index_put2:23 + ├── cardinality: [0 - 0] + ├── volatile, mutations + └── project + ├── columns: partial_index_put1:22 partial_index_put2:23 c_new:21!null k:11!null a:12 b:13 c:14 d:15 e:16 f:17 g:18 h:19 + ├── cardinality: [0 - 1] + ├── key: () + ├── fd: ()-->(11-19,21-23) + ├── select + │ ├── columns: k:11!null a:12 b:13 c:14 d:15 e:16 f:17 g:18 h:19 + │ ├── cardinality: [0 - 1] + │ ├── key: () + │ ├── fd: ()-->(11-19) + │ ├── scan t + │ │ ├── columns: k:11!null a:12 b:13 c:14 d:15 e:16 f:17 g:18 h:19 + │ │ ├── partial index predicates + │ │ │ ├── secondary: filters + │ │ │ │ └── d:15 > 1 [outer=(15), constraints=(/15: [/2 - ]; tight)] + │ │ │ └── secondary: filters + │ │ │ ├── f:17 > 1 [outer=(17), constraints=(/17: [/2 - ]; tight)] + │ │ │ └── g:18 > 1 [outer=(18), constraints=(/18: [/2 - ]; tight)] + │ │ ├── key: (11) + │ │ └── fd: (11)-->(12-19) + │ └── filters + │ └── k:11 = 1 [outer=(11), constraints=(/11: [/1 - /1]; tight), fd=()-->(11)] + └── projections + ├── d:15 > 1 [as=partial_index_put1:22, outer=(15)] + ├── (f:17 > 1) AND (g:18 > 1) [as=partial_index_put2:23, outer=(17,18)] + └── 1 [as=c_new:21] + +# Do not simplify partial index put/del column to false when the columns +# referenced in partial index predicates are mutating. +norm expect-not=SimplifyPartialIndexProjections +UPDATE t SET d = d + 1, g = g + 1 WHERE k = 1 +---- +update t + ├── columns: + ├── fetch columns: k:11 a:12 b:13 c:14 d:15 e:16 f:17 g:18 h:19 + ├── update-mapping: + │ ├── d_new:21 => d:5 + │ └── g_new:22 => g:8 + ├── partial index put columns: partial_index_put1:23 partial_index_put2:25 + ├── partial index del columns: partial_index_del1:24 partial_index_del2:26 + ├── cardinality: [0 - 0] + ├── volatile, mutations + └── project + ├── columns: partial_index_put1:23 partial_index_del1:24 partial_index_put2:25 partial_index_del2:26 k:11!null a:12 b:13 c:14 d:15 e:16 f:17 g:18 h:19 d_new:21 g_new:22 + ├── cardinality: [0 - 1] + ├── immutable + ├── key: () + ├── fd: ()-->(11-19,21-26) + ├── project + │ ├── columns: d_new:21 g_new:22 k:11!null a:12 b:13 c:14 d:15 e:16 f:17 g:18 h:19 + │ ├── cardinality: [0 - 1] + │ ├── immutable + │ ├── key: () + │ ├── fd: ()-->(11-19,21,22) + │ ├── select + │ │ ├── columns: k:11!null a:12 b:13 c:14 d:15 e:16 f:17 g:18 h:19 + │ │ ├── cardinality: [0 - 1] + │ │ ├── key: () + │ │ ├── fd: ()-->(11-19) + │ │ ├── scan t + │ │ │ ├── columns: k:11!null a:12 b:13 c:14 d:15 e:16 f:17 g:18 h:19 + │ │ │ ├── partial index predicates + │ │ │ │ ├── secondary: filters + │ │ │ │ │ └── d:15 > 1 [outer=(15), constraints=(/15: [/2 - ]; tight)] + │ │ │ │ └── secondary: filters + │ │ │ │ ├── f:17 > 1 [outer=(17), constraints=(/17: [/2 - ]; tight)] + │ │ │ │ └── g:18 > 1 [outer=(18), constraints=(/18: [/2 - ]; tight)] + │ │ │ ├── key: (11) + │ │ │ └── fd: (11)-->(12-19) + │ │ └── filters + │ │ └── k:11 = 1 [outer=(11), constraints=(/11: [/1 - /1]; tight), fd=()-->(11)] + │ └── projections + │ ├── d:15 + 1 [as=d_new:21, outer=(15), immutable] + │ └── g:18 + 1 [as=g_new:22, outer=(18), immutable] + └── projections + ├── d_new:21 > 1 [as=partial_index_put1:23, outer=(21)] + ├── d:15 > 1 [as=partial_index_del1:24, outer=(15)] + ├── (f:17 > 1) AND (g_new:22 > 1) [as=partial_index_put2:25, outer=(17,22)] + └── (f:17 > 1) AND (g:18 > 1) [as=partial_index_del2:26, outer=(17,18)] + +# Do not simplify partial index put/del column to false when it is also an +# update column. +norm +UPDATE t SET h = d > 1 WHERE k = 1 +---- +update t + ├── columns: + ├── fetch columns: k:11 a:12 b:13 c:14 d:15 e:16 f:17 g:18 h:19 + ├── update-mapping: + │ └── h_new:21 => h:9 + ├── partial index put columns: h_new:21 partial_index_put2:22 + ├── partial index del columns: h_new:21 partial_index_put2:22 + ├── cardinality: [0 - 0] + ├── volatile, mutations + └── project + ├── columns: partial_index_put2:22!null h_new:21 k:11!null a:12 b:13 c:14 d:15 e:16 f:17 g:18 h:19 + ├── cardinality: [0 - 1] + ├── key: () + ├── fd: ()-->(11-19,21,22) + ├── select + │ ├── columns: k:11!null a:12 b:13 c:14 d:15 e:16 f:17 g:18 h:19 + │ ├── cardinality: [0 - 1] + │ ├── key: () + │ ├── fd: ()-->(11-19) + │ ├── scan t + │ │ ├── columns: k:11!null a:12 b:13 c:14 d:15 e:16 f:17 g:18 h:19 + │ │ ├── partial index predicates + │ │ │ ├── secondary: filters + │ │ │ │ └── d:15 > 1 [outer=(15), constraints=(/15: [/2 - ]; tight)] + │ │ │ └── secondary: filters + │ │ │ ├── f:17 > 1 [outer=(17), constraints=(/17: [/2 - ]; tight)] + │ │ │ └── g:18 > 1 [outer=(18), constraints=(/18: [/2 - ]; tight)] + │ │ ├── key: (11) + │ │ └── fd: (11)-->(12-19) + │ └── filters + │ └── k:11 = 1 [outer=(11), constraints=(/11: [/1 - /1]; tight), fd=()-->(11)] + └── projections + ├── false [as=partial_index_put2:22] + └── d:15 > 1 [as=h_new:21, outer=(15)] diff --git a/pkg/sql/opt/norm/testdata/rules/prune_cols b/pkg/sql/opt/norm/testdata/rules/prune_cols index ef54a006d307..001be4a90991 100644 --- a/pkg/sql/opt/norm/testdata/rules/prune_cols +++ b/pkg/sql/opt/norm/testdata/rules/prune_cols @@ -2076,27 +2076,27 @@ update partial_indexes ├── cardinality: [0 - 0] ├── volatile, mutations └── project - ├── columns: partial_index_put1:12 d_new:11 a:6!null d:9 + ├── columns: partial_index_put1:12!null d_new:11 a:6!null d:9 ├── cardinality: [0 - 1] ├── immutable ├── key: () ├── fd: ()-->(6,9,11,12) ├── select - │ ├── columns: a:6!null b:7 d:9 + │ ├── columns: a:6!null d:9 │ ├── cardinality: [0 - 1] │ ├── key: () - │ ├── fd: ()-->(6,7,9) + │ ├── fd: ()-->(6,9) │ ├── scan partial_indexes - │ │ ├── columns: a:6!null b:7 d:9 + │ │ ├── columns: a:6!null d:9 │ │ ├── partial index predicates │ │ │ └── secondary: filters │ │ │ └── b:7 > 1 [outer=(7), constraints=(/7: [/2 - ]; tight)] │ │ ├── key: (6) - │ │ └── fd: (6)-->(7,9) + │ │ └── fd: (6)-->(9) │ └── filters │ └── a:6 = 1 [outer=(6), constraints=(/6: [/1 - /1]; tight), fd=()-->(6)] └── projections - ├── b:7 > 1 [as=partial_index_put1:12, outer=(7)] + ├── false [as=partial_index_put1:12] └── d:9 + 1 [as=d_new:11, outer=(9), immutable] # Do not prune the indexed column c when a column in the partial index @@ -2163,16 +2163,16 @@ upsert partial_indexes ├── cardinality: [0 - 0] ├── volatile, mutations └── project - ├── columns: partial_index_put1:18 partial_index_del1:19 column1:6!null column2:7!null column8:8 column9:9 a:10 d:13 + ├── columns: partial_index_put1:18!null partial_index_del1:19!null column1:6!null column2:7!null column8:8 column9:9 a:10 d:13 ├── cardinality: [1 - 1] ├── key: () ├── fd: ()-->(6-10,13,18,19) ├── left-join (cross) - │ ├── columns: column1:6!null column2:7!null column8:8 column9:9 a:10 b:11 d:13 + │ ├── columns: column1:6!null column2:7!null column8:8 column9:9 a:10 d:13 │ ├── cardinality: [1 - 1] │ ├── multiplicity: left-rows(exactly-one), right-rows(exactly-one) │ ├── key: () - │ ├── fd: ()-->(6-11,13) + │ ├── fd: ()-->(6-10,13) │ ├── values │ │ ├── columns: column1:6!null column2:7!null column8:8 column9:9 │ │ ├── cardinality: [1 - 1] @@ -2180,23 +2180,23 @@ upsert partial_indexes │ │ ├── fd: ()-->(6-9) │ │ └── (1, 2, NULL, NULL) │ ├── select - │ │ ├── columns: a:10!null b:11 d:13 + │ │ ├── columns: a:10!null d:13 │ │ ├── cardinality: [0 - 1] │ │ ├── key: () - │ │ ├── fd: ()-->(10,11,13) + │ │ ├── fd: ()-->(10,13) │ │ ├── scan partial_indexes - │ │ │ ├── columns: a:10!null b:11 d:13 + │ │ │ ├── columns: a:10!null d:13 │ │ │ ├── partial index predicates │ │ │ │ └── secondary: filters │ │ │ │ └── b:11 > 1 [outer=(11), constraints=(/11: [/2 - ]; tight)] │ │ │ ├── key: (10) - │ │ │ └── fd: (10)-->(11,13) + │ │ │ └── fd: (10)-->(13) │ │ └── filters │ │ └── a:10 = 1 [outer=(10), constraints=(/10: [/1 - /1]; tight), fd=()-->(10)] │ └── filters (true) └── projections - ├── CASE WHEN a:10 IS NULL THEN column8:8 ELSE b:11 END > 1 [as=partial_index_put1:18, outer=(8,10,11)] - └── b:11 > 1 [as=partial_index_del1:19, outer=(11)] + ├── false [as=partial_index_put1:18] + └── false [as=partial_index_del1:19] # Do not prune the indexed column c when a column in the partial index # predicate, b, is being updated. @@ -2279,16 +2279,16 @@ upsert partial_indexes ├── cardinality: [0 - 0] ├── volatile, mutations └── project - ├── columns: partial_index_put1:20 partial_index_del1:21 upsert_d:19!null column1:6!null column2:7!null column8:8 column9:9 a:10 d:13 + ├── columns: partial_index_put1:20!null partial_index_del1:21!null upsert_d:19!null column1:6!null column2:7!null column8:8 column9:9 a:10 d:13 ├── cardinality: [1 - 1] ├── key: () ├── fd: ()-->(6-10,13,19-21) ├── left-join (cross) - │ ├── columns: column1:6!null column2:7!null column8:8 column9:9 a:10 b:11 d:13 + │ ├── columns: column1:6!null column2:7!null column8:8 column9:9 a:10 d:13 │ ├── cardinality: [1 - 1] │ ├── multiplicity: left-rows(exactly-one), right-rows(exactly-one) │ ├── key: () - │ ├── fd: ()-->(6-11,13) + │ ├── fd: ()-->(6-10,13) │ ├── values │ │ ├── columns: column1:6!null column2:7!null column8:8 column9:9 │ │ ├── cardinality: [1 - 1] @@ -2296,23 +2296,23 @@ upsert partial_indexes │ │ ├── fd: ()-->(6-9) │ │ └── (1, 2, NULL, NULL) │ ├── select - │ │ ├── columns: a:10!null b:11 d:13 + │ │ ├── columns: a:10!null d:13 │ │ ├── cardinality: [0 - 1] │ │ ├── key: () - │ │ ├── fd: ()-->(10,11,13) + │ │ ├── fd: ()-->(10,13) │ │ ├── scan partial_indexes - │ │ │ ├── columns: a:10!null b:11 d:13 + │ │ │ ├── columns: a:10!null d:13 │ │ │ ├── partial index predicates │ │ │ │ └── secondary: filters │ │ │ │ └── b:11 > 1 [outer=(11), constraints=(/11: [/2 - ]; tight)] │ │ │ ├── key: (10) - │ │ │ └── fd: (10)-->(11,13) + │ │ │ └── fd: (10)-->(13) │ │ └── filters │ │ └── a:10 = 1 [outer=(10), constraints=(/10: [/1 - /1]; tight), fd=()-->(10)] │ └── filters (true) └── projections - ├── CASE WHEN a:10 IS NULL THEN column8:8 ELSE b:11 END > 1 [as=partial_index_put1:20, outer=(8,10,11)] - ├── b:11 > 1 [as=partial_index_del1:21, outer=(11)] + ├── false [as=partial_index_put1:20] + ├── false [as=partial_index_del1:21] └── CASE WHEN a:10 IS NULL THEN column2:7 ELSE 3 END [as=upsert_d:19, outer=(7,10)] # Do not prune the indexed column c when a column in the partial index