Skip to content

Commit 80173b2

Browse files
committed
sql: track routine dependencies on columns in RETURNING clause
Previously, while building a routine, we did not explicitly add dependencies to columns referenced in the RETURNING clause of mutations within the body statements. This was not a problem for INSERT or UPSERT due to #145098, but could cause a routine with UPDATE or DELETE statements to be broken after a referenced column was dropped. This commit fixes the bug by tracking column references in the RETURNING clause. Fixes #146414 Release note (bug fix): Fixed a bug that allowed a column to be dropped from a table even if it was referenced in the RETURNING clause of an UPDATE or DELETE statement in a routine. In releases prior to v25.3, the fix will be off by default, and can be enabled by setting the session variable `use_improved_routine_dependency_tracking`.
1 parent 7b71d93 commit 80173b2

File tree

9 files changed

+130
-27
lines changed

9 files changed

+130
-27
lines changed

pkg/sql/logictest/testdata/logic_test/udf_delete

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,3 +302,25 @@ SELECT * FROM u_a;
302302
2 b 20
303303

304304
subtest end
305+
306+
subtest regression_146414
307+
308+
statement ok
309+
CREATE TABLE t146414 (
310+
a INT NOT NULL,
311+
b INT AS (a + 1) VIRTUAL
312+
)
313+
314+
statement ok
315+
CREATE FUNCTION f146414() RETURNS INT LANGUAGE SQL AS $$
316+
DELETE FROM t146414 WHERE a = 1 RETURNING b;
317+
SELECT 1;
318+
$$;
319+
320+
statement error pgcode 2BP01 pq: cannot drop column "b" because function "f146414" depends on it
321+
ALTER TABLE t146414 DROP COLUMN b;
322+
323+
statement ok
324+
SELECT f146414()
325+
326+
subtest end

pkg/sql/logictest/testdata/logic_test/udf_insert

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,3 +264,25 @@ statement error pgcode 23514 pq: failed to satisfy CHECK constraint \(b > 1:::IN
264264
SELECT f_checkb();
265265

266266
subtest end
267+
268+
subtest regression_146414
269+
270+
statement ok
271+
CREATE TABLE t146414 (
272+
a INT NOT NULL,
273+
b INT AS (a + 1) VIRTUAL
274+
)
275+
276+
statement ok
277+
CREATE FUNCTION f146414() RETURNS INT LANGUAGE SQL AS $$
278+
INSERT INTO t146414 (a) VALUES (100) RETURNING b;
279+
SELECT 1;
280+
$$;
281+
282+
statement error pgcode 2BP01 pq: cannot drop column "b" because function "f146414" depends on it
283+
ALTER TABLE t146414 DROP COLUMN b;
284+
285+
statement ok
286+
SELECT f146414()
287+
288+
subtest end

pkg/sql/logictest/testdata/logic_test/udf_update

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,3 +326,25 @@ SELECT * FROM kv;
326326
5 5
327327

328328
subtest end
329+
330+
subtest regression_146414
331+
332+
statement ok
333+
CREATE TABLE t146414 (
334+
a INT NOT NULL,
335+
b INT AS (a + 1) VIRTUAL
336+
)
337+
338+
statement ok
339+
CREATE FUNCTION f146414() RETURNS INT LANGUAGE SQL AS $$
340+
UPDATE t146414 SET a = a + 1 WHERE a = 1 RETURNING b;
341+
SELECT 1;
342+
$$;
343+
344+
statement error pgcode 2BP01 pq: cannot drop column "b" because function "f146414" depends on it
345+
ALTER TABLE t146414 DROP COLUMN b;
346+
347+
statement ok
348+
SELECT f146414()
349+
350+
subtest end

pkg/sql/logictest/testdata/logic_test/udf_upsert

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,3 +238,25 @@ statement error pgcode 22001 value too long for type CHAR\(3\)
238238
SELECT f_check_colerr_vals(NULL, 'abcd')
239239

240240
subtest end
241+
242+
subtest regression_146414
243+
244+
statement ok
245+
CREATE TABLE t146414 (
246+
a INT NOT NULL,
247+
b INT AS (a + 1) VIRTUAL
248+
)
249+
250+
statement ok
251+
CREATE FUNCTION f146414() RETURNS INT LANGUAGE SQL AS $$
252+
UPSERT INTO t146414 (a) VALUES (100) RETURNING b;
253+
SELECT 1;
254+
$$;
255+
256+
statement error pgcode 2BP01 pq: cannot drop column "b" because function "f146414" depends on it
257+
ALTER TABLE t146414 DROP COLUMN b;
258+
259+
statement ok
260+
SELECT f146414()
261+
262+
subtest end

pkg/sql/opt/optbuilder/mutation_builder.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1702,7 +1702,7 @@ func (mb *mutationBuilder) buildReturningScopes(
17021702
// 4. Project columns in same order as defined in table schema.
17031703
//
17041704
inScope = mb.outScope.replace()
1705-
inScope.appendOrdinaryColumnsFromTable(mb.md.TableMeta(mb.tabID), &mb.alias)
1705+
mb.b.appendOrdinaryColumnsFromTable(inScope, mb.md.TableMeta(mb.tabID), &mb.alias)
17061706

17071707
// extraAccessibleCols contains all the columns that the RETURNING
17081708
// clause can refer to in addition to the table columns. This is useful for

pkg/sql/opt/optbuilder/partial_index.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func (b *Builder) addPartialIndexPredicatesForTable(tabMeta *opt.TableMeta, scan
5757
// Construct a scan as the tableScope expr so that logical properties of the
5858
// scan can be used to fully normalize the index predicate.
5959
tableScope := b.allocScope()
60-
tableScope.appendOrdinaryColumnsFromTable(tabMeta, &tabMeta.Alias)
60+
b.appendOrdinaryColumnsFromTable(tableScope, tabMeta, &tabMeta.Alias)
6161

6262
// If the optional scan argument was provided and it outputs all of the
6363
// ordinary table columns, we use it as tableScope.expr. Otherwise, we must

pkg/sql/opt/optbuilder/scope.go

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import (
1414
"github.com/cockroachdb/cockroach/pkg/build"
1515
"github.com/cockroachdb/cockroach/pkg/sql/catalog/colinfo"
1616
"github.com/cockroachdb/cockroach/pkg/sql/opt"
17-
"github.com/cockroachdb/cockroach/pkg/sql/opt/cat"
1817
"github.com/cockroachdb/cockroach/pkg/sql/opt/memo"
1918
"github.com/cockroachdb/cockroach/pkg/sql/opt/props"
2019
"github.com/cockroachdb/cockroach/pkg/sql/opt/props/physical"
@@ -230,28 +229,6 @@ func (s *scope) appendColumnsFromScope(src *scope) {
230229
}
231230
}
232231

233-
// appendOrdinaryColumnsFromTable adds all non-mutation and non-system columns from the
234-
// given table metadata to this scope.
235-
func (s *scope) appendOrdinaryColumnsFromTable(tabMeta *opt.TableMeta, alias *tree.TableName) {
236-
tab := tabMeta.Table
237-
if s.cols == nil {
238-
s.cols = make([]scopeColumn, 0, tab.ColumnCount())
239-
}
240-
for i, n := 0, tab.ColumnCount(); i < n; i++ {
241-
tabCol := tab.Column(i)
242-
if tabCol.Kind() != cat.Ordinary {
243-
continue
244-
}
245-
s.cols = append(s.cols, scopeColumn{
246-
name: scopeColName(tabCol.ColName()),
247-
table: *alias,
248-
typ: tabCol.DatumType(),
249-
id: tabMeta.MetaID.ColumnID(i),
250-
visibility: columnVisibility(tabCol.Visibility()),
251-
})
252-
}
253-
}
254-
255232
// appendColumns adds newly bound variables to this scope.
256233
// The expressions in the new columns are reset to nil.
257234
func (s *scope) appendColumns(cols []scopeColumn) {

pkg/sql/opt/optbuilder/select.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -828,7 +828,7 @@ func (b *Builder) addCheckConstraintsForTable(tabMeta *opt.TableMeta) {
828828

829829
// Create a scope that can be used for building the scalar expressions.
830830
tableScope := b.allocScope()
831-
tableScope.appendOrdinaryColumnsFromTable(tabMeta, &tabMeta.Alias)
831+
b.appendOrdinaryColumnsFromTable(tableScope, tabMeta, &tabMeta.Alias)
832832
// Synthesized CHECK expressions, e.g., for columns of ENUM types, may
833833
// reference inaccessible columns. This can happen when the type of an
834834
// indexed expression is an ENUM. We make these columns visible so that they
@@ -925,7 +925,7 @@ func (b *Builder) addComputedColsForTable(
925925

926926
if tableScope == nil {
927927
tableScope = b.allocScope()
928-
tableScope.appendOrdinaryColumnsFromTable(tabMeta, &tabMeta.Alias)
928+
b.appendOrdinaryColumnsFromTable(tableScope, tabMeta, &tabMeta.Alias)
929929
}
930930

931931
colType := tabCol.DatumType()

pkg/sql/opt/optbuilder/util.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -851,3 +851,41 @@ func (b *Builder) makePLpgSQLRaiseFn(args memo.ScalarListExpr) opt.ScalarExpr {
851851
},
852852
)
853853
}
854+
855+
// appendOrdinaryColumnsFromTable adds all non-mutation and non-system columns
856+
// from the given table metadata to the given scope. References to these columns
857+
// will be tracked in the schema dependencies, if trackSchemaDeps is set.
858+
func (b *Builder) appendOrdinaryColumnsFromTable(
859+
s *scope, tabMeta *opt.TableMeta, alias *tree.TableName,
860+
) {
861+
tab := tabMeta.Table
862+
if s.cols == nil {
863+
s.cols = make([]scopeColumn, 0, tab.ColumnCount())
864+
}
865+
for i, n := 0, tab.ColumnCount(); i < n; i++ {
866+
tabCol := tab.Column(i)
867+
if tabCol.Kind() != cat.Ordinary {
868+
continue
869+
}
870+
s.cols = append(s.cols, scopeColumn{
871+
name: scopeColName(tabCol.ColName()),
872+
table: *alias,
873+
typ: tabCol.DatumType(),
874+
id: tabMeta.MetaID.ColumnID(i),
875+
visibility: columnVisibility(tabCol.Visibility()),
876+
})
877+
}
878+
if b.trackSchemaDeps {
879+
dep := opt.SchemaDep{DataSource: tab}
880+
for i, n := 0, tab.ColumnCount(); i < n; i++ {
881+
if tab.Column(i).Kind() != cat.Ordinary {
882+
continue
883+
}
884+
if dep.ColumnIDToOrd == nil {
885+
dep.ColumnIDToOrd = make(map[opt.ColumnID]int)
886+
}
887+
dep.ColumnIDToOrd[tabMeta.MetaID.ColumnID(i)] = i
888+
}
889+
b.schemaDeps = append(b.schemaDeps, dep)
890+
}
891+
}

0 commit comments

Comments
 (0)