Skip to content

Commit

Permalink
Merge #129080
Browse files Browse the repository at this point in the history
129080: opt: fix resolution of CHECK expressions for ENUM expression indexes r=mgartner a=mgartner

Fixes #127365

Release note (bug fix): A bug has been fixed that caused errors like
"ERROR: column 'crdb_internal_idx_expr' does not exist" when accessing a
table with an expression index where the expression evaluates to an
ENUM type, e.g., `CREATE INDEX ON t ((col::an_enum))`.

Co-authored-by: Marcus Gartner <[email protected]>
  • Loading branch information
craig[bot] and mgartner committed Aug 15, 2024
2 parents ab496aa + 0a82776 commit 5907123
Show file tree
Hide file tree
Showing 2 changed files with 167 additions and 0 deletions.
10 changes: 10 additions & 0 deletions pkg/sql/opt/optbuilder/select.go
Original file line number Diff line number Diff line change
Expand Up @@ -884,6 +884,16 @@ func (b *Builder) addCheckConstraintsForTable(tabMeta *opt.TableMeta) {
// Create a scope that can be used for building the scalar expressions.
tableScope := b.allocScope()
tableScope.appendOrdinaryColumnsFromTable(tabMeta, &tabMeta.Alias)
// Synthesized CHECK expressions, e.g., for columns of ENUM types, may
// reference inaccessible columns. This can happen when the type of an
// indexed expression is an ENUM. We make these columns visible so that they
// can be resolved while building the CHECK expressions. This is safe to do
// for all CHECK expressions, not just synthesized ones, because
// user-created CHECK expressions will never reference inaccessible columns
// - this is enforced during CREATE TABLE and ALTER TABLE statements.
for i := range tableScope.cols {
tableScope.cols[i].visibility = columnVisibility(cat.Visible)
}

// Find the non-nullable table columns. Mutation columns can be NULL during
// backfill, so they should be excluded.
Expand Down
157 changes: 157 additions & 0 deletions pkg/sql/opt/optbuilder/testdata/select
Original file line number Diff line number Diff line change
Expand Up @@ -1583,3 +1583,160 @@ project
│ └── (3, 4)
└── projections
└── ((column1:1, column2:2) AS x, y) [as=t:3]

# Regression test for #127365. Synthesized CHECK expressions should be allowed
# to reference inaccessible columns.
exec-ddl
CREATE TABLE t127365 (
k INT PRIMARY KEY,
a INT,
-- Simulate an expression index on an ENUM column by creating an
-- inaccessible virtual column with a check constraint.
"crdb_internal_idx_expr:inaccessible" INT AS (a + 1) VIRTUAL,
CHECK ("crdb_internal_idx_expr" IN (10, 20, 30)),
INDEX ("crdb_internal_idx_expr")
)
----

build
SELECT * FROM t127365
----
project
├── columns: k:1!null a:2
└── project
├── columns: crdb_internal_idx_expr:3 k:1!null a:2 crdb_internal_mvcc_timestamp:4 tableoid:5
├── scan t127365
│ ├── columns: k:1!null a:2 crdb_internal_mvcc_timestamp:4 tableoid:5
│ └── computed column expressions
│ └── crdb_internal_idx_expr:3
│ └── a:2 + 1
└── projections
└── a:2 + 1 [as=crdb_internal_idx_expr:3]

build
INSERT INTO t127365 VALUES (1, 10)
----
insert t127365
├── columns: <none>
├── insert-mapping:
│ ├── column1:6 => k:1
│ ├── column2:7 => a:2
│ └── crdb_internal_idx_expr_comp:8 => crdb_internal_idx_expr:3
├── check columns: check1:9
└── project
├── columns: check1:9!null column1:6!null column2:7!null crdb_internal_idx_expr_comp:8!null
├── project
│ ├── columns: crdb_internal_idx_expr_comp:8!null column1:6!null column2:7!null
│ ├── values
│ │ ├── columns: column1:6!null column2:7!null
│ │ └── (1, 10)
│ └── projections
│ └── column2:7 + 1 [as=crdb_internal_idx_expr_comp:8]
└── projections
└── crdb_internal_idx_expr_comp:8 IN (10, 20, 30) [as=check1:9]

build
UPSERT INTO t127365 VALUES (1, 10)
----
upsert t127365
├── arbiter indexes: t127365_pkey
├── columns: <none>
├── canary column: k:9
├── fetch columns: k:9 a:10 crdb_internal_idx_expr:11
├── insert-mapping:
│ ├── column1:6 => k:1
│ ├── column2:7 => a:2
│ └── crdb_internal_idx_expr_comp:8 => crdb_internal_idx_expr:3
├── update-mapping:
│ ├── column2:7 => a:2
│ └── crdb_internal_idx_expr_comp:8 => crdb_internal_idx_expr:3
├── check columns: check1:15
└── project
├── columns: check1:15!null column1:6!null column2:7!null crdb_internal_idx_expr_comp:8!null k:9 a:10 crdb_internal_idx_expr:11 crdb_internal_mvcc_timestamp:12 tableoid:13 upsert_k:14
├── project
│ ├── columns: upsert_k:14 column1:6!null column2:7!null crdb_internal_idx_expr_comp:8!null k:9 a:10 crdb_internal_idx_expr:11 crdb_internal_mvcc_timestamp:12 tableoid:13
│ ├── left-join (hash)
│ │ ├── columns: column1:6!null column2:7!null crdb_internal_idx_expr_comp:8!null k:9 a:10 crdb_internal_idx_expr:11 crdb_internal_mvcc_timestamp:12 tableoid:13
│ │ ├── ensure-upsert-distinct-on
│ │ │ ├── columns: column1:6!null column2:7!null crdb_internal_idx_expr_comp:8!null
│ │ │ ├── grouping columns: column1:6!null
│ │ │ ├── project
│ │ │ │ ├── columns: crdb_internal_idx_expr_comp:8!null column1:6!null column2:7!null
│ │ │ │ ├── values
│ │ │ │ │ ├── columns: column1:6!null column2:7!null
│ │ │ │ │ └── (1, 10)
│ │ │ │ └── projections
│ │ │ │ └── column2:7 + 1 [as=crdb_internal_idx_expr_comp:8]
│ │ │ └── aggregations
│ │ │ ├── first-agg [as=column2:7]
│ │ │ │ └── column2:7
│ │ │ └── first-agg [as=crdb_internal_idx_expr_comp:8]
│ │ │ └── crdb_internal_idx_expr_comp:8
│ │ ├── project
│ │ │ ├── columns: crdb_internal_idx_expr:11 k:9!null a:10 crdb_internal_mvcc_timestamp:12 tableoid:13
│ │ │ ├── scan t127365
│ │ │ │ ├── columns: k:9!null a:10 crdb_internal_mvcc_timestamp:12 tableoid:13
│ │ │ │ ├── computed column expressions
│ │ │ │ │ └── crdb_internal_idx_expr:11
│ │ │ │ │ └── a:10 + 1
│ │ │ │ └── flags: disabled not visible index feature
│ │ │ └── projections
│ │ │ └── a:10 + 1 [as=crdb_internal_idx_expr:11]
│ │ └── filters
│ │ └── column1:6 = k:9
│ └── projections
│ └── CASE WHEN k:9 IS NULL THEN column1:6 ELSE k:9 END [as=upsert_k:14]
└── projections
└── crdb_internal_idx_expr_comp:8 IN (10, 20, 30) [as=check1:15]

build
UPDATE t127365 SET a = 20 WHERE k = 1
----
update t127365
├── columns: <none>
├── fetch columns: k:6 a:7 crdb_internal_idx_expr:8
├── update-mapping:
│ ├── a_new:11 => a:2
│ └── crdb_internal_idx_expr_comp:12 => crdb_internal_idx_expr:3
├── check columns: check1:13
└── project
├── columns: check1:13!null k:6!null a:7 crdb_internal_idx_expr:8 crdb_internal_mvcc_timestamp:9 tableoid:10 a_new:11!null crdb_internal_idx_expr_comp:12!null
├── project
│ ├── columns: crdb_internal_idx_expr_comp:12!null k:6!null a:7 crdb_internal_idx_expr:8 crdb_internal_mvcc_timestamp:9 tableoid:10 a_new:11!null
│ ├── project
│ │ ├── columns: a_new:11!null k:6!null a:7 crdb_internal_idx_expr:8 crdb_internal_mvcc_timestamp:9 tableoid:10
│ │ ├── select
│ │ │ ├── columns: k:6!null a:7 crdb_internal_idx_expr:8 crdb_internal_mvcc_timestamp:9 tableoid:10
│ │ │ ├── project
│ │ │ │ ├── columns: crdb_internal_idx_expr:8 k:6!null a:7 crdb_internal_mvcc_timestamp:9 tableoid:10
│ │ │ │ ├── scan t127365
│ │ │ │ │ ├── columns: k:6!null a:7 crdb_internal_mvcc_timestamp:9 tableoid:10
│ │ │ │ │ └── computed column expressions
│ │ │ │ │ └── crdb_internal_idx_expr:8
│ │ │ │ │ └── a:7 + 1
│ │ │ │ └── projections
│ │ │ │ └── a:7 + 1 [as=crdb_internal_idx_expr:8]
│ │ │ └── filters
│ │ │ └── k:6 = 1
│ │ └── projections
│ │ └── 20 [as=a_new:11]
│ └── projections
│ └── a_new:11 + 1 [as=crdb_internal_idx_expr_comp:12]
└── projections
└── crdb_internal_idx_expr_comp:12 IN (10, 20, 30) [as=check1:13]

build
DELETE FROM t127365
----
delete t127365
├── columns: <none>
├── fetch columns: k:6 a:7 crdb_internal_idx_expr:8
└── project
├── columns: crdb_internal_idx_expr:8 k:6!null a:7 crdb_internal_mvcc_timestamp:9 tableoid:10
├── scan t127365
│ ├── columns: k:6!null a:7 crdb_internal_mvcc_timestamp:9 tableoid:10
│ └── computed column expressions
│ └── crdb_internal_idx_expr:8
│ └── a:7 + 1
└── projections
└── a:7 + 1 [as=crdb_internal_idx_expr:8]

0 comments on commit 5907123

Please sign in to comment.