Skip to content

Commit 62d693d

Browse files
committed
sql: still more routine dependency improvements
I missed in #158935 that check constraints and partial index predicates are additional places where we unnecessarily track transitive column dependencies when creating a routine. The fix is gated behind the same session setting as #158935. Fixes #158154 Release note (sql change): Fixed two cases where creation of a routine resolves unnecessary column dependencies, which can prevent drop of the column without first dropping the routine. Here, the unnecessary dependencies are due to references within CHECK constraints, including those for RLS policies and hash-sharded indexes, as well as those in partial index predicates. The fix is gated behind the session setting `use_improved_routine_deps_triggers_and_computed_cols`, which is off by default before 26.1.
1 parent 74ebd71 commit 62d693d

8 files changed

Lines changed: 286 additions & 4 deletions

File tree

pkg/sql/logictest/testdata/logic_test/check_constraints

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,3 +494,33 @@ ALTER TABLE t_91697 ADD CHECK (a < 123);
494494

495495
statement error pgcode 23514 pq: failed to satisfy CHECK constraint \(a < 'public.t67100a'::REGCLASS\)
496496
INSERT INTO t_91697 VALUES (321);
497+
498+
# Regression test for #158154. Don't add transitive column dependencies to
499+
# routines that build check constraints.
500+
subtest regression_158154
501+
502+
statement ok
503+
CREATE TABLE t158154 (a INT, b INT, CONSTRAINT foo CHECK (b > 0));
504+
505+
statement ok
506+
CREATE PROCEDURE p158154() LANGUAGE SQL AS $$
507+
INSERT INTO t158154 (a) VALUES (1);
508+
UPDATE t158154 SET a = a + 1;
509+
$$;
510+
511+
statement ok
512+
CALL p158154();
513+
514+
# The procedure does not directly depend on column b, so dropping column b
515+
# should be allowed after dropping the check constraint.
516+
statement ok
517+
ALTER TABLE t158154 DROP CONSTRAINT foo, DROP COLUMN b;
518+
519+
statement ok
520+
CALL p158154();
521+
522+
statement ok
523+
DROP PROCEDURE p158154;
524+
DROP TABLE t158154;
525+
526+
subtest end

pkg/sql/logictest/testdata/logic_test/enums

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2116,3 +2116,35 @@ WHERE stat->>'columns' = '["a"]'
21162116
"null_count": 0,
21172117
"row_count": 5
21182118
}
2119+
2120+
# Regression test for #158154. Don't add transitive column dependencies to
2121+
# routines that build check constraints for enum columns.
2122+
subtest regression_158154
2123+
2124+
statement ok
2125+
CREATE TYPE typ158154 AS ENUM ('foo', 'bar');
2126+
2127+
statement ok
2128+
CREATE TABLE t158154 (a INT, b typ158154);
2129+
2130+
statement ok
2131+
CREATE PROCEDURE p158154() LANGUAGE SQL AS $$
2132+
INSERT INTO t158154 (a) VALUES (1);
2133+
UPDATE t158154 SET a = a + 1;
2134+
$$;
2135+
2136+
statement ok
2137+
CALL p158154();
2138+
2139+
statement ok
2140+
ALTER TABLE t158154 DROP COLUMN b;
2141+
2142+
statement ok
2143+
CALL p158154();
2144+
2145+
statement ok
2146+
DROP PROCEDURE p158154;
2147+
DROP TABLE t158154;
2148+
DROP TYPE typ158154;
2149+
2150+
subtest end

pkg/sql/logictest/testdata/logic_test/hash_sharded_index

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1435,3 +1435,37 @@ SELECT crdb_internal_d_shard_16 AS shard, count(*) < 20 FROM decimals GROUP BY 1
14351435
15 false
14361436

14371437
subtest end
1438+
1439+
# Regression test for #158154. Don't add transitive column dependencies to
1440+
# routines that build check constraints.
1441+
subtest regression_158154
1442+
1443+
statement ok
1444+
CREATE TABLE t158154 (col1 INT8 NOT NULL, col2 TIMESTAMP NOT NULL);
1445+
1446+
statement ok
1447+
CREATE INDEX idx1 ON t158154 (col2 ASC) USING HASH WITH (bucket_count = 6);
1448+
1449+
statement ok
1450+
CREATE OR REPLACE PROCEDURE p158154 () LANGUAGE PLpgSQL AS $proc$
1451+
BEGIN
1452+
UPDATE t158154 SET col1 = col1 + 1;
1453+
END;
1454+
$proc$;
1455+
1456+
statement ok
1457+
CALL p158154();
1458+
1459+
# The procedure does not directly depend on the hash-sharded index computed
1460+
# column, so dropping the index should succeed.
1461+
statement ok
1462+
DROP INDEX t158154@idx1;
1463+
1464+
statement ok
1465+
CALL p158154();
1466+
1467+
statement ok
1468+
DROP PROCEDURE p158154;
1469+
DROP TABLE t158154;
1470+
1471+
subtest end

pkg/sql/logictest/testdata/logic_test/partial_index

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2497,3 +2497,97 @@ DROP INDEX idx97551;
24972497
statement ok
24982498
DROP TABLE t97551;
24992499
DROP TYPE enum97551;
2500+
2501+
subtest end
2502+
2503+
# Regression test for #158154. Don't add transitive column dependencies to
2504+
# routines that build partial index predicate expressions.
2505+
subtest regression_158154
2506+
2507+
# Simple case with INSERT and UPDATE into a table with a partial index.
2508+
statement ok
2509+
CREATE TABLE t158154 (a INT, b INT, INDEX b_idx (b) WHERE (b > 0));
2510+
2511+
statement ok
2512+
CREATE PROCEDURE p158154() LANGUAGE SQL AS $$
2513+
INSERT INTO t158154 (a) VALUES (1);
2514+
UPDATE t158154 SET a = a + 1;
2515+
$$;
2516+
2517+
statement ok
2518+
CALL p158154();
2519+
2520+
statement ok
2521+
DROP INDEX t158154@b_idx;
2522+
2523+
statement ok
2524+
ALTER TABLE t158154 DROP COLUMN b;
2525+
2526+
statement ok
2527+
CALL p158154();
2528+
2529+
statement ok
2530+
DROP PROCEDURE p158154;
2531+
DROP TABLE t158154;
2532+
2533+
# Case with a partial index used as an arbiter.
2534+
statement ok
2535+
CREATE TABLE t158154 (a INT, b INT);
2536+
2537+
statement ok
2538+
CREATE UNIQUE INDEX a_idx ON t158154 (a) WHERE (b > 0);
2539+
2540+
# WHERE false implies any partial index predicate.
2541+
statement ok
2542+
CREATE PROCEDURE p158154() LANGUAGE SQL AS $$
2543+
INSERT INTO t158154 (a) VALUES (1) ON CONFLICT (a) WHERE (false) DO NOTHING;
2544+
INSERT INTO t158154 (a) VALUES (2) ON CONFLICT (a) WHERE (false) DO UPDATE SET a = EXCLUDED.a + 1;
2545+
$$;
2546+
2547+
statement ok
2548+
CALL p158154();
2549+
2550+
statement ok
2551+
DROP INDEX t158154@a_idx;
2552+
2553+
statement ok
2554+
ALTER TABLE t158154 DROP COLUMN b;
2555+
2556+
statement ok
2557+
CREATE UNIQUE INDEX a_idx ON t158154 (a);
2558+
2559+
statement ok
2560+
CALL p158154();
2561+
2562+
statement ok
2563+
DROP PROCEDURE p158154;
2564+
DROP TABLE t158154;
2565+
2566+
# Verify that a SELECT statement inside a procedure does not add transitive
2567+
# dependencies from the partial index predicate.
2568+
statement ok
2569+
CREATE TABLE t158154 (a INT, b INT, INDEX b_idx (b) WHERE (b > 0));
2570+
2571+
statement ok
2572+
CREATE PROCEDURE p158154() LANGUAGE SQL AS $$
2573+
SELECT a FROM t158154;
2574+
SELECT count(*) FROM t158154 WHERE a > 5;
2575+
$$;
2576+
2577+
statement ok
2578+
CALL p158154();
2579+
2580+
statement ok
2581+
DROP INDEX t158154@b_idx;
2582+
2583+
statement ok
2584+
ALTER TABLE t158154 DROP COLUMN b;
2585+
2586+
statement ok
2587+
CALL p158154();
2588+
2589+
statement ok
2590+
DROP PROCEDURE p158154;
2591+
DROP TABLE t158154;
2592+
2593+
subtest end

pkg/sql/logictest/testdata/logic_test/row_level_security

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5930,4 +5930,61 @@ DROP TABLE accounts, non_rls;
59305930
statement ok
59315931
DROP USER alice;
59325932

5933+
statement ok
5934+
RESET SESSION row_security;
5935+
5936+
subtest end
5937+
5938+
# Regression test for #158154. Don't add transitive column dependencies to
5939+
# routines that build RLS constraints.
5940+
subtest regression_158154
5941+
5942+
statement ok
5943+
CREATE TABLE t158154 (c0 INT, c1 TEXT DEFAULT 'foobarbaz');
5944+
5945+
statement ok
5946+
CREATE ROLE user_158154
5947+
5948+
statement ok
5949+
GRANT ALL ON t158154 TO user_158154;
5950+
5951+
statement ok
5952+
ALTER TABLE t158154 ENABLE ROW LEVEL SECURITY, FORCE ROW LEVEL SECURITY;
5953+
5954+
statement ok
5955+
ALTER TABLE t158154 OWNER TO user_158154;
5956+
5957+
statement ok
5958+
SET ROLE user_158154;
5959+
5960+
statement ok
5961+
CREATE POLICY IF NOT EXISTS p1 on t158154 WITH CHECK (c0 > 0 AND c1 = 'foobarbaz');
5962+
5963+
statement ok
5964+
CREATE PROCEDURE p158154() LANGUAGE SQL AS $$
5965+
UPDATE t158154 SET c0 = 1 WHERE true;
5966+
$$;
5967+
5968+
statement ok
5969+
CALL p158154();
5970+
5971+
# The procedure does not directly depend on column b, so dropping column b
5972+
# should be allowed after dropping the RLS constraint.
5973+
statement ok
5974+
DROP POLICY p1 on t158154;
5975+
ALTER TABLE t158154 DROP COLUMN c1;
5976+
5977+
statement ok
5978+
CALL p158154();
5979+
5980+
statement ok
5981+
SET ROLE root;
5982+
5983+
statement ok
5984+
DROP PROCEDURE p158154;
5985+
DROP TABLE t158154;
5986+
5987+
statement ok
5988+
DROP ROLE user_158154;
5989+
59335990
subtest end

pkg/sql/opt/optbuilder/insert.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,13 @@ func (mb *mutationBuilder) buildInputForDoNothing(
846846
// TODO(andyk): do we need to do more here?
847847
mb.outScope.ordering = nil
848848

849+
if !mb.arbiters.Empty() &&
850+
mb.b.evalCtx.SessionData().UseImprovedRoutineDepsTriggersAndComputedCols {
851+
// Avoid resolving transitive dependencies on columns and UDTs referenced by
852+
// any partial index predicates among the arbiters.
853+
defer mb.b.DisableSchemaDepTracking()()
854+
}
855+
849856
// Create an anti-join for each arbiter.
850857
mb.arbiters.ForEach(func(
851858
name string, conflictOrds intsets.Fast, pred tree.Expr, canaryOrd int, uniqueWithoutIndex bool, uniqueOrd int,
@@ -902,6 +909,13 @@ func (mb *mutationBuilder) buildInputForUpsert(
902909
// Ignore any ordering requested by the input.
903910
mb.outScope.ordering = nil
904911

912+
if !mb.arbiters.Empty() &&
913+
mb.b.evalCtx.SessionData().UseImprovedRoutineDepsTriggersAndComputedCols {
914+
// Avoid resolving transitive dependencies on columns and UDTs referenced by
915+
// any partial index predicates among the arbiters.
916+
defer mb.b.DisableSchemaDepTracking()()
917+
}
918+
905919
// Create an UpsertDistinctOn and a left-join for the single arbiter.
906920
mb.arbiters.ForEach(func(
907921
name string, conflictOrds intsets.Fast, pred tree.Expr, canaryOrd int, uniqueWithoutIndex bool, uniqueOrd int,

pkg/sql/opt/optbuilder/mutation_builder.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,6 +1097,13 @@ func (mb *mutationBuilder) maybeAddRegionColLookup(op opt.Operator) {
10971097
func (mb *mutationBuilder) addCheckConstraintCols(
10981098
isUpdate bool, policyCmdScope cat.PolicyCommandScope, includeSelectPolicies bool,
10991099
) {
1100+
if mb.b.evalCtx.SessionData().UseImprovedRoutineDepsTriggersAndComputedCols {
1101+
// Avoid adding unnecessary dependencies on columns that are referenced by
1102+
// check expressions. We only need to track columns that were explicitly
1103+
// specified by the user, e.g. those in SET, WHERE or RETURNING clause, or
1104+
// the target columns of an INSERT.
1105+
defer mb.b.DisableSchemaDepTracking()()
1106+
}
11001107
if mb.tab.CheckCount() != 0 {
11011108
projectionsScope := mb.outScope.replace()
11021109
projectionsScope.appendColumnsFromScope(mb.outScope)
@@ -1508,6 +1515,11 @@ func (mb *mutationBuilder) projectPartialIndexPutAndDelCols() {
15081515
// projectPartialIndexDelCols, or projectPartialIndexPutAndDelCols.
15091516
func (mb *mutationBuilder) projectPartialIndexColsImpl(putScope, delScope *scope) {
15101517
if partialIndexCount(mb.tab) > 0 {
1518+
if mb.b.evalCtx.SessionData().UseImprovedRoutineDepsTriggersAndComputedCols {
1519+
// Avoid unnecessary dependencies on columns and UDTs referenced by the
1520+
// partial index expression.
1521+
defer mb.b.DisableSchemaDepTracking()()
1522+
}
15111523
projectionScope := mb.outScope.replace()
15121524
projectionScope.appendColumnsFromScope(mb.outScope)
15131525

pkg/sql/opt/optbuilder/partial_index.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,12 @@ import (
2727
// scan. A scan and its logical properties are required in order to fully
2828
// normalize the partial index predicates.
2929
func (b *Builder) addPartialIndexPredicatesForTable(tabMeta *opt.TableMeta, scan memo.RelExpr) {
30-
// We do not want to track view/function deps here, otherwise a view/function
31-
// depending on a table with a partial index predicate using an UDT will
32-
// result in a type dependency being added between the view/function and the
33-
// UDT.
30+
// We do not want to track view/function deps here, otherwise a
31+
// view/function depending on a table with a partial index predicate using
32+
// a UDT will result in a type dependency being added between the
33+
// view/function and the UDT.
3434
defer b.DisableSchemaDepTracking()()
35+
3536
tab := tabMeta.Table
3637
numIndexes := tab.DeletableIndexCount()
3738

@@ -115,6 +116,14 @@ func (b *Builder) addPartialIndexPredicatesForTable(tabMeta *opt.TableMeta, scan
115116
func (b *Builder) buildPartialIndexPredicate(
116117
tabMeta *opt.TableMeta, tableScope *scope, expr tree.Expr, context string,
117118
) (memo.FiltersExpr, error) {
119+
if b.evalCtx.SessionData().UseImprovedRoutineDepsTriggersAndComputedCols {
120+
// We do not want to track view/function deps here, otherwise a view/function
121+
// depending on a table with a partial index predicate will add transitive
122+
// dependencies on any UDTs or columns referenced by the predicate. The
123+
// partial index will already prevent dropping such UDTs or columns.
124+
defer b.DisableSchemaDepTracking()()
125+
}
126+
118127
texpr := tableScope.resolveAndRequireType(expr, types.Bool)
119128

120129
var scalar opt.ScalarExpr

0 commit comments

Comments
 (0)