Skip to content

Commit a5e9fbe

Browse files
committed
sql: avoid unnecessary column dependencies in routines
Previously, there were several cases where routines would add unnecessary column dependencies: * Columns referenced by a computed column expression would be added to the list of columns referenced by the routine. * BEFORE triggers would confuse the logic used to distinguish explicit from implicit INSERT columns, because triggers change the column IDs to be inserted. This could cause the routine to add implicitly referenced columns to its dependencies. * Dependencies from statements within a trigger would transitively apply to a routine that invoked trigger on a mutation. This commit prevents newly-created routines from adding unnecessary column dependencies in those cases, gated behind the session setting `use_improved_routine_deps_triggers_and_computed_cols`. Fixes #158154 Release note (bug fix): Fixed a bug that caused routines to prevent dropping more columns than necessary, most notably columns referenced by computed column expressions. The fix is gated behind the session setting `use_improved_routine_deps_triggers_and_computed_cols`, which is off by default prior to v26.1.
1 parent 9c106f8 commit a5e9fbe

File tree

17 files changed

+215
-32
lines changed

17 files changed

+215
-32
lines changed

pkg/ccl/logictestccl/testdata/logic_test/triggers

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5121,6 +5121,67 @@ DROP TRIGGER tr1 ON sc_tr_tbl;
51215121

51225122
subtest end
51235123

5124+
# Regression test for unnecessary column dependencies added to a routine with a
5125+
# mutation that involves a trigger.
5126+
subtest regression_158154
5127+
5128+
statement ok
5129+
SET use_improved_routine_deps_triggers_and_computed_cols = true;
5130+
5131+
statement ok
5132+
CREATE TABLE xy_158154 (x INT, y INT);
5133+
5134+
statement ok
5135+
CREATE FUNCTION g() RETURNS TRIGGER LANGUAGE PLpgSQL AS $$
5136+
BEGIN
5137+
SELECT x, y FROM xy_158154;
5138+
RAISE NOTICE '%: old: %, new: %', TG_OP, OLD, NEW;
5139+
RETURN COALESCE(NEW, OLD);
5140+
END
5141+
$$;
5142+
5143+
statement ok
5144+
CREATE TRIGGER foo BEFORE INSERT ON xy_158154 FOR EACH ROW EXECUTE FUNCTION g();
5145+
5146+
statement ok
5147+
CREATE PROCEDURE p(blah INT) LANGUAGE SQL AS $$
5148+
INSERT INTO xy_158154 (x) VALUES (blah);
5149+
$$;
5150+
5151+
statement ok
5152+
CALL p(100)
5153+
5154+
statement error pgcode 2BP01 pq: cannot drop column "y" because trigger "foo" on table "xy_158154" depends on it
5155+
ALTER TABLE xy_158154 DROP COLUMN y;
5156+
5157+
statement ok
5158+
DROP TRIGGER foo ON xy_158154;
5159+
DROP FUNCTION g();
5160+
5161+
# The procedure should not prevent the column drop.
5162+
statement ok
5163+
ALTER TABLE xy_158154 DROP COLUMN y;
5164+
5165+
statement ok
5166+
CALL p(200)
5167+
5168+
query I rowsort
5169+
SELECT * FROM xy_158154;
5170+
----
5171+
100
5172+
200
5173+
5174+
statement ok
5175+
DROP PROCEDURE p
5176+
5177+
statement ok
5178+
DROP TABLE xy_158154
5179+
5180+
statement ok
5181+
RESET use_improved_routine_deps_triggers_and_computed_cols;
5182+
5183+
subtest end
5184+
51245185
# ==============================================================================
51255186
# End of regression tests. Add new features above the regression test section.
51265187
# ==============================================================================

pkg/sql/logictest/testdata/logic_test/information_schema

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4285,6 +4285,7 @@ unconstrained_non_covering_index_scan_enabled off
42854285
unsafe_allow_triggers_modifying_cascades off
42864286
use_cputs_on_non_unique_indexes off
42874287
use_improved_routine_dependency_tracking on
4288+
use_improved_routine_deps_triggers_and_computed_cols off
42884289
use_pre_25_2_variadic_builtins off
42894290
use_proc_txn_control_extended_protocol_fix on
42904291
use_soft_limit_for_distribute_scan on

pkg/sql/logictest/testdata/logic_test/pg_catalog

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3241,6 +3241,7 @@ unsafe_allow_triggers_modifying_cascades off
32413241
use_cputs_on_non_unique_indexes off NULL NULL NULL string
32423242
use_declarative_schema_changer on NULL NULL NULL string
32433243
use_improved_routine_dependency_tracking on NULL NULL NULL string
3244+
use_improved_routine_deps_triggers_and_computed_cols off NULL NULL NULL string
32443245
use_pre_25_2_variadic_builtins off NULL NULL NULL string
32453246
use_proc_txn_control_extended_protocol_fix on NULL NULL NULL string
32463247
use_soft_limit_for_distribute_scan on NULL NULL NULL string
@@ -3492,6 +3493,7 @@ unsafe_allow_triggers_modifying_cascades off
34923493
use_cputs_on_non_unique_indexes off NULL user NULL off off
34933494
use_declarative_schema_changer on NULL user NULL on on
34943495
use_improved_routine_dependency_tracking on NULL user NULL on on
3496+
use_improved_routine_deps_triggers_and_computed_cols off NULL user NULL off off
34953497
use_pre_25_2_variadic_builtins off NULL user NULL off off
34963498
use_proc_txn_control_extended_protocol_fix on NULL user NULL on on
34973499
use_soft_limit_for_distribute_scan on NULL user NULL on on
@@ -3735,6 +3737,7 @@ unsafe_allow_triggers_modifying_cascades NULL NULL
37353737
use_cputs_on_non_unique_indexes NULL NULL NULL NULL NULL
37363738
use_declarative_schema_changer NULL NULL NULL NULL NULL
37373739
use_improved_routine_dependency_tracking NULL NULL NULL NULL NULL
3740+
use_improved_routine_deps_triggers_and_computed_cols NULL NULL NULL NULL NULL
37383741
use_pre_25_2_variadic_builtins NULL NULL NULL NULL NULL
37393742
use_proc_txn_control_extended_protocol_fix NULL NULL NULL NULL NULL
37403743
use_soft_limit_for_distribute_scan NULL NULL NULL NULL NULL

pkg/sql/logictest/testdata/logic_test/show_source

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ unsafe_allow_triggers_modifying_cascades off
258258
use_cputs_on_non_unique_indexes off
259259
use_declarative_schema_changer on
260260
use_improved_routine_dependency_tracking on
261+
use_improved_routine_deps_triggers_and_computed_cols off
261262
use_pre_25_2_variadic_builtins off
262263
use_proc_txn_control_extended_protocol_fix on
263264
use_soft_limit_for_distribute_scan on

pkg/sql/logictest/testdata/logic_test/udf_insert

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,9 +235,20 @@ SELECT f_drop(5,100,101);
235235
----
236236
(5,100,101)
237237

238-
statement error pgcode 2BP01 cannot drop column \"c\" because function \"f_drop\" depends on it
238+
statement error pgcode 2BP01 pq: cannot drop column "c" because function "f_drop" depends on it
239239
ALTER TABLE t_alter DROP COLUMN c;
240240

241+
statement ok
242+
DROP FUNCTION f_drop;
243+
244+
# Columns that aren't explicitly referenced by a routine can be dropped.
245+
statement ok
246+
ALTER TABLE t_alter DROP COLUMN c;
247+
ALTER TABLE t_alter DROP COLUMN b;
248+
249+
statement error pgcode 2BP01 pq: cannot drop column "a" because function "f_int" depends on it
250+
ALTER TABLE t_alter DROP COLUMN a;
251+
241252
query T
242253
SELECT f_record(6);
243254
----

pkg/sql/logictest/testdata/logic_test/udf_update

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,3 +412,46 @@ statement ok
412412
RESET prevent_update_set_column_drop;
413413

414414
subtest end
415+
416+
# Allow drop of columns referenced only in computed column expressions.
417+
subtest regression_158154
418+
419+
statement ok
420+
SET use_improved_routine_deps_triggers_and_computed_cols = true;
421+
422+
statement ok
423+
CREATE TABLE t158154 (col1 INT8 NOT NULL, col2 TIMESTAMP NOT NULL, computed TIMESTAMP NOT NULL AS (col2) STORED);
424+
INSERT INTO t158154 (col1, col2) VALUES (1, '2024-01-01');
425+
426+
statement ok
427+
CREATE OR REPLACE PROCEDURE p158154() LANGUAGE PLpgSQL AS $proc$
428+
BEGIN UPDATE t158154 SET col1 = col1 + 1; END;
429+
$proc$;
430+
431+
statement ok
432+
CALL p158154()
433+
434+
query ITT
435+
SELECT * FROM t158154
436+
----
437+
2 2024-01-01 00:00:00 +0000 +0000 2024-01-01 00:00:00 +0000 +0000
438+
439+
statement ok
440+
ALTER TABLE t158154 DROP COLUMN computed, DROP COLUMN col2;
441+
442+
statement ok
443+
CALL p158154()
444+
445+
query I
446+
SELECT * FROM t158154
447+
----
448+
3
449+
450+
statement ok
451+
DROP PROCEDURE p158154;
452+
DROP TABLE t158154;
453+
454+
statement ok
455+
RESET use_improved_routine_deps_triggers_and_computed_cols;
456+
457+
subtest end

pkg/sql/opt/memo/memo.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ type Memo struct {
218218
usingHintInjection bool
219219
useSwapMutations bool
220220
preventUpdateSetColumnDrop bool
221+
useImprovedRoutineDepsTriggersComputedCols bool
221222

222223
// txnIsoLevel is the isolation level under which the plan was created. This
223224
// affects the planning of some locking operations, so it must be included in
@@ -336,6 +337,7 @@ func (m *Memo) Init(ctx context.Context, evalCtx *eval.Context) {
336337
usingHintInjection: evalCtx.Planner != nil && evalCtx.Planner.UsingHintInjection(),
337338
useSwapMutations: evalCtx.SessionData().UseSwapMutations,
338339
preventUpdateSetColumnDrop: evalCtx.SessionData().PreventUpdateSetColumnDrop,
340+
useImprovedRoutineDepsTriggersComputedCols: evalCtx.SessionData().UseImprovedRoutineDepsTriggersAndComputedCols,
339341
txnIsoLevel: evalCtx.TxnIsoLevel,
340342
}
341343
m.metadata.Init()
@@ -518,6 +520,7 @@ func (m *Memo) IsStale(
518520
m.usingHintInjection != (evalCtx.Planner != nil && evalCtx.Planner.UsingHintInjection()) ||
519521
m.useSwapMutations != evalCtx.SessionData().UseSwapMutations ||
520522
m.preventUpdateSetColumnDrop != evalCtx.SessionData().PreventUpdateSetColumnDrop ||
523+
m.useImprovedRoutineDepsTriggersComputedCols != evalCtx.SessionData().UseImprovedRoutineDepsTriggersAndComputedCols ||
521524
m.txnIsoLevel != evalCtx.TxnIsoLevel {
522525
return true, nil
523526
}

pkg/sql/opt/memo/memo_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,12 @@ func TestMemoIsStale(t *testing.T) {
617617
evalCtx.SessionData().PreventUpdateSetColumnDrop = false
618618
notStale()
619619

620+
// Stale use_improved_routine_deps_triggers_and_computed_cols.
621+
evalCtx.SessionData().UseImprovedRoutineDepsTriggersAndComputedCols = true
622+
stale()
623+
evalCtx.SessionData().UseImprovedRoutineDepsTriggersAndComputedCols = false
624+
notStale()
625+
620626
// User no longer has access to view.
621627
catalog.View(tree.NewTableNameWithSchema("t", catconstants.PublicSchemaName, "abcview")).Revoked = true
622628
_, err = o.Memo().IsStale(ctx, &evalCtx, catalog)

pkg/sql/opt/optbuilder/builder.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,27 @@ func (b *Builder) DisableUnsafeInternalCheck() func() {
601601
}
602602
}
603603

604+
// DisableSchemaDepTracking is used to disable dependency tracking for views and
605+
// routines, so that users don't have to face unnecessary restrictions during
606+
// schema changes.
607+
//
608+
// For example, we must prevent dropping a column that is referenced in the
609+
// WHERE clause or SET clause of an UPDATE statement. However, adding or
610+
// dropping columns that are synthesized with default or computed values is
611+
// perfectly safe, because those columns are determined from the table schema
612+
// rather than being user specified. If such a column is dropped, its value will
613+
// simply not be synthesized in mutation statements going forward.
614+
func (b *Builder) DisableSchemaDepTracking() func() {
615+
if !b.trackSchemaDeps {
616+
return func() {}
617+
}
618+
originalTrackSchemaDeps := b.trackSchemaDeps
619+
b.trackSchemaDeps = false
620+
return func() {
621+
b.trackSchemaDeps = originalTrackSchemaDeps
622+
}
623+
}
624+
604625
// optTrackingTypeResolver is a wrapper around a TypeReferenceResolver that
605626
// remembers all of the resolved types in the provided Metadata.
606627
type optTrackingTypeResolver struct {

pkg/sql/opt/optbuilder/insert.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,10 @@ func (b *Builder) buildInsert(ins *tree.Insert, inScope *scope) (outScope *scope
398398
mb.buildUpsert(returning)
399399
}
400400

401-
if b.trackSchemaDeps {
401+
mb.trackTargetColDeps()
402+
403+
// Legacy path for resolving insert target column dependencies.
404+
if b.trackSchemaDeps && !b.evalCtx.SessionData().UseImprovedRoutineDepsTriggersAndComputedCols {
402405
dep := opt.SchemaDep{DataSource: tab}
403406
// Track dependencies on insert columns.
404407
for i := range mb.insertColIDs {

0 commit comments

Comments
 (0)