diff --git a/pkg/sql/exec_util.go b/pkg/sql/exec_util.go index ceff7c4015a5..6287443b98dc 100644 --- a/pkg/sql/exec_util.go +++ b/pkg/sql/exec_util.go @@ -3197,6 +3197,10 @@ func (m *sessionDataMutator) SetExpectAndIgnoreNotVisibleColumnsInCopy(val bool) m.data.ExpectAndIgnoreNotVisibleColumnsInCopy = val } +func (m *sessionDataMutator) SetMultipleModificationsOfTable(val bool) { + m.data.MultipleModificationsOfTable = val +} + // Utility functions related to scrubbing sensitive information on SQL Stats. // quantizeCounts ensures that the Count field in the diff --git a/pkg/sql/logictest/testdata/logic_test/with b/pkg/sql/logictest/testdata/logic_test/with index 61ff01c7992b..53aeeabf9576 100644 --- a/pkg/sql/logictest/testdata/logic_test/with +++ b/pkg/sql/logictest/testdata/logic_test/with @@ -997,6 +997,31 @@ EXPERIMENTAL SCRUB TABLE t WITH OPTIONS INDEX ALL statement ok RESET CLUSTER SETTING sql.multiple_modifications_of_table.enabled +# Multiple mutations can also be explicitly allowed with a session setting. +statement ok +SET enable_multiple_modifications_of_table = true + +# Multiple updates of different rows in the same table. +query II rowsort +WITH + u1 AS (UPDATE t SET j = j - 40 WHERE i < 20 RETURNING *), + u2 AS (UPDATE t SET j = j + 40 WHERE i >= 20 RETURNING *) +TABLE u1 UNION ALL TABLE u2 +---- +2 60 +4 60 +6 60 +8 60 +20 440 + +# Check for corruption. +query TTTTTTTT +EXPERIMENTAL SCRUB TABLE t WITH OPTIONS INDEX ALL +---- + +statement ok +RESET enable_multiple_modifications_of_table + # Tests with multiple mutations on the same table, modifying the same # rows. These testcases vary the type of mutation and the form. All should fail # with an error. When issue 70731 is fixed, some might no longer fail. diff --git a/pkg/sql/opt/optbuilder/util.go b/pkg/sql/opt/optbuilder/util.go index db6a3da42faa..08f27aca140b 100644 --- a/pkg/sql/opt/optbuilder/util.go +++ b/pkg/sql/opt/optbuilder/util.go @@ -27,6 +27,7 @@ import ( "github.com/cockroachdb/errors" ) +// TODO(michae2): Remove this when #70731 is fixed. var multipleModificationsOfTableEnabled = settings.RegisterBoolSetting( settings.TenantWritable, "sql.multiple_modifications_of_table.enabled", @@ -493,7 +494,9 @@ func (b *Builder) checkMultipleMutations(tab cat.Table, simpleInsert bool) { } allSimpleInserts = allSimpleInserts && simpleInsert b.areAllTableMutationsSimpleInserts[tab.ID()] = allSimpleInserts - if !allSimpleInserts && !multipleModificationsOfTableEnabled.Get(&b.evalCtx.Settings.SV) { + if !allSimpleInserts && + !multipleModificationsOfTableEnabled.Get(&b.evalCtx.Settings.SV) && + !b.evalCtx.SessionData().MultipleModificationsOfTable { panic(pgerror.Newf( pgcode.FeatureNotSupported, "multiple modification subqueries of the same table %q are not supported unless "+ diff --git a/pkg/sql/sessiondatapb/local_only_session_data.proto b/pkg/sql/sessiondatapb/local_only_session_data.proto index a107c7ec71d5..156d6c357aa9 100644 --- a/pkg/sql/sessiondatapb/local_only_session_data.proto +++ b/pkg/sql/sessiondatapb/local_only_session_data.proto @@ -246,6 +246,10 @@ message LocalOnlySessionData { // (with no column name specifiers) to expect and ignore not visible column // fields. bool expect_and_ignore_not_visible_columns_in_copy = 67; + // MultipleModificationsOfTable allows statements containing multiple INSERT + // ON CONFLICT, UPSERT, UPDATE, or DELETE subqueries modifying the same table, + // at the risk of data corruption if the same row is modified multiple times. + bool multiple_modifications_of_table = 68; /////////////////////////////////////////////////////////////////////////// // WARNING: consider whether a session parameter you're adding needs to // diff --git a/pkg/sql/vars.go b/pkg/sql/vars.go index 242dabf510c9..04eadb9127fa 100644 --- a/pkg/sql/vars.go +++ b/pkg/sql/vars.go @@ -2049,6 +2049,24 @@ var varGen = map[string]sessionVar{ }, GlobalDefault: globalFalse, }, + + // TODO(michae2): Remove this when #70731 is fixed. + // CockroachDB extension. + `enable_multiple_modifications_of_table`: { + GetStringVal: makePostgresBoolGetStringValFn(`enable_multiple_modifications_of_table`), + Set: func(_ context.Context, m sessionDataMutator, s string) error { + b, err := paramparse.ParseBoolVar("enable_multiple_modifications_of_table", s) + if err != nil { + return err + } + m.SetMultipleModificationsOfTable(b) + return nil + }, + Get: func(evalCtx *extendedEvalContext) (string, error) { + return formatBoolAsPostgresSetting(evalCtx.SessionData().MultipleModificationsOfTable), nil + }, + GlobalDefault: globalFalse, + }, } const compatErrMsg = "this parameter is currently recognized only for compatibility and has no effect in CockroachDB."