Skip to content

Commit 67e16cb

Browse files
craig[bot]fqazi
andcommitted
107815: sql/schemachanger: disable CREATE SEQUENCE / CREATE SCHEMA by default in declarative schema changer r=fqazi a=fqazi For 23.2 and keeping things more conservative, we will keep CREATE SEQUENCE / CREATE SCHEMA running in the older declarative schema changer. This patch will do the following: 1. CREATE SEQUENCE / CREATE SCHEMA are disabled by default 2. A new cluster setting sql.schema.force_declarative_statements can now be used to enable/disable declarative features in a more granular matter. Fixes: cockroachdb#107734 Co-authored-by: Faizan Qazi <[email protected]>
2 parents d9d421e + ab1a740 commit 67e16cb

File tree

16 files changed

+319
-86
lines changed

16 files changed

+319
-86
lines changed

pkg/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,7 @@ ALL_TESTS = [
521521
"//pkg/sql/schemachanger/corpus:corpus_test",
522522
"//pkg/sql/schemachanger/rel:rel_test",
523523
"//pkg/sql/schemachanger/scbackup:scbackup_test",
524+
"//pkg/sql/schemachanger/scbuild/internal/scbuildstmt:scbuildstmt_test",
524525
"//pkg/sql/schemachanger/scbuild:scbuild_test",
525526
"//pkg/sql/schemachanger/scdecomp:scdecomp_test",
526527
"//pkg/sql/schemachanger/scexec/backfiller:backfiller_test",
@@ -1974,6 +1975,7 @@ GO_TARGETS = [
19741975
"//pkg/sql/schemachanger/scbackup:scbackup",
19751976
"//pkg/sql/schemachanger/scbackup:scbackup_test",
19761977
"//pkg/sql/schemachanger/scbuild/internal/scbuildstmt:scbuildstmt",
1978+
"//pkg/sql/schemachanger/scbuild/internal/scbuildstmt:scbuildstmt_test",
19771979
"//pkg/sql/schemachanger/scbuild:scbuild",
19781980
"//pkg/sql/schemachanger/scbuild:scbuild_test",
19791981
"//pkg/sql/schemachanger/scdecomp:scdecomp",

pkg/sql/logictest/testdata/logic_test/event_log

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -708,8 +708,8 @@ FROM system.eventlog
708708
WHERE "eventType" = 'create_schema'
709709
ORDER BY "timestamp", info
710710
----
711-
1 {"EventType": "create_schema", "Owner": "root", "SchemaName": "test.sc", "Statement": "CREATE SCHEMA test.sc", "Tag": "CREATE SCHEMA", "User": "root"}
712-
1 {"EventType": "create_schema", "Owner": "root", "SchemaName": "test.s", "Statement": "CREATE SCHEMA test.s", "Tag": "CREATE SCHEMA", "User": "root"}
711+
1 {"EventType": "create_schema", "Owner": "root", "SchemaName": "test.sc", "Statement": "CREATE SCHEMA \"\".sc", "Tag": "CREATE SCHEMA", "User": "root"}
712+
1 {"EventType": "create_schema", "Owner": "root", "SchemaName": "test.s", "Statement": "CREATE SCHEMA \"\".s", "Tag": "CREATE SCHEMA", "User": "root"}
713713
1 {"EventType": "create_schema", "Owner": "u", "SchemaName": "test.u", "Statement": "CREATE SCHEMA AUTHORIZATION u", "Tag": "CREATE SCHEMA", "User": "root"}
714714

715715
statement ok

pkg/sql/logictest/testdata/logic_test/new_schema_changer

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1501,3 +1501,33 @@ public admin
15011501

15021502
statement ok
15031503
COMMIT
1504+
1505+
1506+
subtest statement-control
1507+
1508+
# Disable ALTER table completely and validate it works
1509+
statement ok
1510+
SET use_declarative_schema_changer = 'on'
1511+
1512+
statement ok
1513+
SET CLUSTER SETTING sql.schema.force_declarative_statements='!ALTER TABLE'
1514+
1515+
statement ok
1516+
CREATE TABLE stmt_ctrl(n int primary key)
1517+
1518+
statement error pgcode 0A000 cannot explain a statement which is not supported by the declarative schema changer
1519+
EXPLAIN (DDL) ALTER TABLE stmt_ctrl ADD COLUMN j BOOL
1520+
1521+
statement ok
1522+
ALTER TABLE stmt_ctrl ADD COLUMN fallback_works BOOL
1523+
1524+
# Validate that CREATE SEQUENCE is disabled and can be re-enabled with the same flag.
1525+
statement error pgcode 0A000 cannot explain a statement which is not supported by the declarative schema changer
1526+
EXPLAIN (DDL) CREATE SEQUENCE sq2
1527+
1528+
statement ok
1529+
SET CLUSTER SETTING sql.schema.force_declarative_statements='+CREATE SEQUENCE'
1530+
1531+
skipif config local-mixed-22.2-23.1
1532+
statement ok
1533+
EXPLAIN (DDL) CREATE SEQUENCE sq2

pkg/sql/schemachanger/scbuild/internal/scbuildstmt/BUILD.bazel

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
load("@io_bazel_rules_go//go:def.bzl", "go_library")
1+
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
22

33
go_library(
44
name = "scbuildstmt",
@@ -28,6 +28,7 @@ go_library(
2828
"drop_view.go",
2929
"helpers.go",
3030
"process.go",
31+
"statement_control.go",
3132
],
3233
importpath = "github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scbuild/internal/scbuildstmt",
3334
visibility = ["//pkg/sql/schemachanger/scbuild:__subpackages__"],
@@ -38,6 +39,7 @@ go_library(
3839
"//pkg/keys",
3940
"//pkg/security/username",
4041
"//pkg/server/telemetry",
42+
"//pkg/settings",
4143
"//pkg/settings/cluster",
4244
"//pkg/sql/catalog",
4345
"//pkg/sql/catalog/catenumpb",
@@ -78,3 +80,14 @@ go_library(
7880
"@com_github_lib_pq//oid",
7981
],
8082
)
83+
84+
go_test(
85+
name = "scbuildstmt_test",
86+
srcs = ["process_test.go"],
87+
args = ["-test.timeout=295s"],
88+
embed = [":scbuildstmt"],
89+
deps = [
90+
"//pkg/settings",
91+
"@com_github_stretchr_testify//require",
92+
],
93+
)

pkg/sql/schemachanger/scbuild/internal/scbuildstmt/process.go

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import (
2525
type supportedStatement struct {
2626
// fn is a function to perform a schema change.
2727
fn interface{}
28+
// statementTag tag for this statement.
29+
statementTag string
2830
// checks contains a coarse-grained function to filter out most
2931
// unsupported statements.
3032
// It's possible for certain unsupported statements to pass it but will
@@ -44,28 +46,32 @@ var supportedStatements = map[reflect.Type]supportedStatement{
4446
// Alter table will have commands individually whitelisted via the
4547
// supportedAlterTableStatements list, so wwe will consider it fully supported
4648
// here.
47-
reflect.TypeOf((*tree.AlterTable)(nil)): {fn: AlterTable, on: true, checks: alterTableChecks},
48-
reflect.TypeOf((*tree.CreateIndex)(nil)): {fn: CreateIndex, on: true, checks: isV231Active},
49-
reflect.TypeOf((*tree.DropDatabase)(nil)): {fn: DropDatabase, on: true, checks: isV221Active},
50-
reflect.TypeOf((*tree.DropOwnedBy)(nil)): {fn: DropOwnedBy, on: true, checks: isV222Active},
51-
reflect.TypeOf((*tree.DropSchema)(nil)): {fn: DropSchema, on: true, checks: isV221Active},
52-
reflect.TypeOf((*tree.DropSequence)(nil)): {fn: DropSequence, on: true, checks: isV221Active},
53-
reflect.TypeOf((*tree.DropTable)(nil)): {fn: DropTable, on: true, checks: isV221Active},
54-
reflect.TypeOf((*tree.DropType)(nil)): {fn: DropType, on: true, checks: isV221Active},
55-
reflect.TypeOf((*tree.DropView)(nil)): {fn: DropView, on: true, checks: isV221Active},
56-
reflect.TypeOf((*tree.CommentOnDatabase)(nil)): {fn: CommentOnDatabase, on: true, checks: isV222Active},
57-
reflect.TypeOf((*tree.CommentOnSchema)(nil)): {fn: CommentOnSchema, on: true, checks: isV222Active},
58-
reflect.TypeOf((*tree.CommentOnTable)(nil)): {fn: CommentOnTable, on: true, checks: isV222Active},
59-
reflect.TypeOf((*tree.CommentOnColumn)(nil)): {fn: CommentOnColumn, on: true, checks: isV222Active},
60-
reflect.TypeOf((*tree.CommentOnIndex)(nil)): {fn: CommentOnIndex, on: true, checks: isV222Active},
61-
reflect.TypeOf((*tree.CommentOnConstraint)(nil)): {fn: CommentOnConstraint, on: true, checks: isV222Active},
62-
reflect.TypeOf((*tree.DropIndex)(nil)): {fn: DropIndex, on: true, checks: isV231Active},
63-
reflect.TypeOf((*tree.DropFunction)(nil)): {fn: DropFunction, on: true, checks: isV231Active},
64-
reflect.TypeOf((*tree.CreateRoutine)(nil)): {fn: CreateFunction, on: true, checks: isV231Active},
65-
reflect.TypeOf((*tree.CreateSchema)(nil)): {fn: CreateSchema, on: true, checks: isV232Active},
66-
reflect.TypeOf((*tree.CreateSequence)(nil)): {fn: CreateSequence, on: true, checks: isV232Active},
49+
reflect.TypeOf((*tree.AlterTable)(nil)): {fn: AlterTable, statementTag: tree.AlterTableTag, on: true, checks: alterTableChecks},
50+
reflect.TypeOf((*tree.CreateIndex)(nil)): {fn: CreateIndex, statementTag: tree.CreateIndexTag, on: true, checks: isV231Active},
51+
reflect.TypeOf((*tree.DropDatabase)(nil)): {fn: DropDatabase, statementTag: tree.DropDatabaseTag, on: true, checks: isV221Active},
52+
reflect.TypeOf((*tree.DropOwnedBy)(nil)): {fn: DropOwnedBy, statementTag: tree.DropOwnedByTag, on: true, checks: isV222Active},
53+
reflect.TypeOf((*tree.DropSchema)(nil)): {fn: DropSchema, statementTag: tree.DropSchemaTag, on: true, checks: isV221Active},
54+
reflect.TypeOf((*tree.DropSequence)(nil)): {fn: DropSequence, statementTag: tree.DropSequenceTag, on: true, checks: isV221Active},
55+
reflect.TypeOf((*tree.DropTable)(nil)): {fn: DropTable, statementTag: tree.DropTableTag, on: true, checks: isV221Active},
56+
reflect.TypeOf((*tree.DropType)(nil)): {fn: DropType, statementTag: tree.DropTypeTag, on: true, checks: isV221Active},
57+
reflect.TypeOf((*tree.DropView)(nil)): {fn: DropView, statementTag: tree.DropViewTag, on: true, checks: isV221Active},
58+
reflect.TypeOf((*tree.CommentOnConstraint)(nil)): {fn: CommentOnConstraint, statementTag: tree.CommentOnConstraintTag, on: true, checks: isV222Active},
59+
reflect.TypeOf((*tree.CommentOnDatabase)(nil)): {fn: CommentOnDatabase, statementTag: tree.CommentOnDatabaseTag, on: true, checks: isV222Active},
60+
reflect.TypeOf((*tree.CommentOnSchema)(nil)): {fn: CommentOnSchema, statementTag: tree.CommentOnSchemaTag, on: true, checks: isV222Active},
61+
reflect.TypeOf((*tree.CommentOnTable)(nil)): {fn: CommentOnTable, statementTag: tree.CommentOnTableTag, on: true, checks: isV222Active},
62+
reflect.TypeOf((*tree.CommentOnColumn)(nil)): {fn: CommentOnColumn, statementTag: tree.CommentOnColumnTag, on: true, checks: isV222Active},
63+
reflect.TypeOf((*tree.CommentOnIndex)(nil)): {fn: CommentOnIndex, statementTag: tree.CommentOnIndexTag, on: true, checks: isV222Active},
64+
reflect.TypeOf((*tree.DropIndex)(nil)): {fn: DropIndex, statementTag: tree.DropIndexTag, on: true, checks: isV231Active},
65+
reflect.TypeOf((*tree.DropFunction)(nil)): {fn: DropFunction, statementTag: tree.DropFunctionTag, on: true, checks: isV231Active},
66+
reflect.TypeOf((*tree.CreateRoutine)(nil)): {fn: CreateFunction, statementTag: tree.CreateRoutineTag, on: true, checks: isV231Active},
67+
reflect.TypeOf((*tree.CreateSchema)(nil)): {fn: CreateSchema, statementTag: tree.CreateSchemaTag, on: false, checks: isV232Active},
68+
reflect.TypeOf((*tree.CreateSequence)(nil)): {fn: CreateSequence, statementTag: tree.CreateSequenceTag, on: false, checks: isV232Active},
6769
}
6870

71+
// supportedStatementTags tracks statement tags which are implemented
72+
// by the declarative schema changer.
73+
var supportedStatementTags = map[string]struct{}{}
74+
6975
func init() {
7076
boolType := reflect.TypeOf((*bool)(nil)).Elem()
7177
// Check function signatures inside the supportedStatements map.
@@ -99,6 +105,9 @@ func init() {
99105
statementType, checks))
100106
}
101107
}
108+
// Fetch the statement tag using the statement tag method on the type,
109+
// we can use this as a blacklist of blocked schema changes.
110+
supportedStatementTags[statementEntry.statementTag] = struct{}{}
102111
}
103112
}
104113

@@ -157,9 +166,16 @@ func isFullySupportedWithFalsePositiveInternal(
157166
// Process dispatches on the statement type to populate the BuilderState
158167
// embedded in the BuildCtx. Any error will be panicked.
159168
func Process(b BuildCtx, n tree.Statement) {
169+
newSchemaChangerMode := b.EvalCtx().SessionData().NewSchemaChangerMode
170+
// Check if the feature is either forced disabled or enabled,
171+
// using a cluster setting.
172+
disabledStatements := getSchemaChangerStatementControl(&b.ClusterSettings().SV)
173+
if forcedEnabled := disabledStatements.CheckStatementControl(n); forcedEnabled {
174+
newSchemaChangerMode = sessiondatapb.UseNewSchemaChangerUnsafe
175+
}
160176
// Run a few "quick checks" to see if the statement is not supported.
161177
if !IsFullySupportedWithFalsePositive(n, b.EvalCtx().Settings.Version.ActiveVersion(b),
162-
b.EvalCtx().SessionData().NewSchemaChangerMode) {
178+
newSchemaChangerMode) {
163179
panic(scerrors.NotImplementedError(n))
164180
}
165181

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright 2023 The Cockroach Authors.
2+
//
3+
// Use of this software is governed by the Business Source License
4+
// included in the file licenses/BSL.txt.
5+
//
6+
// As of the Change Date specified in that file, in accordance with
7+
// the Business Source License, use of this software will be governed
8+
// by the Apache License, Version 2.0, included in the file
9+
// licenses/APL.txt.
10+
11+
package scbuildstmt
12+
13+
import (
14+
"reflect"
15+
"strings"
16+
"testing"
17+
18+
"github.com/cockroachdb/cockroach/pkg/settings"
19+
"github.com/stretchr/testify/require"
20+
)
21+
22+
func TestSupportedStatements(t *testing.T) {
23+
sv := &settings.Values{}
24+
// Non-existent tags should error out.
25+
require.Error(t, schemaChangerDisabledStatements.Validate(sv, "FAKE STATEMENT"))
26+
// Generate the full set of statements
27+
allTags := strings.Builder{}
28+
noTags := strings.Builder{}
29+
first := true
30+
for typ, stmt := range supportedStatements {
31+
require.Greaterf(t, len(stmt.statementTag), 0, "statement tag is missing %v %v", typ, stmt)
32+
// Validate tags matches the statement tag
33+
typTag, found := typ.MethodByName("StatementTag")
34+
require.True(t, found, "unable to find stmt: %v %v", typ, stmt)
35+
ret := typTag.Func.Call([]reflect.Value{reflect.New(typ.Elem())})
36+
require.Equal(t, ret[0].String(), stmt.statementTag, "statement tag is different in AST")
37+
// Validate all tags are supported.
38+
require.NoError(t, schemaChangerDisabledStatements.Validate(sv, "+"+stmt.statementTag))
39+
require.NoError(t, schemaChangerDisabledStatements.Validate(sv, "!"+stmt.statementTag))
40+
// Validate all of them can be specified at once.
41+
if !first {
42+
allTags.WriteString(",")
43+
noTags.WriteString(",")
44+
}
45+
first = false
46+
allTags.WriteString("+")
47+
allTags.WriteString(stmt.statementTag)
48+
noTags.WriteString("!")
49+
noTags.WriteString(stmt.statementTag)
50+
}
51+
require.NoError(t, schemaChangerDisabledStatements.Validate(sv, allTags.String()))
52+
require.NoError(t, schemaChangerDisabledStatements.Validate(sv, noTags.String()))
53+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// Copyright 2023 The Cockroach Authors.
2+
//
3+
// Use of this software is governed by the Business Source License
4+
// included in the file licenses/BSL.txt.
5+
//
6+
// As of the Change Date specified in that file, in accordance with
7+
// the Business Source License, use of this software will be governed
8+
// by the Apache License, Version 2.0, included in the file
9+
// licenses/APL.txt.
10+
11+
package scbuildstmt
12+
13+
import (
14+
"strings"
15+
16+
"github.com/cockroachdb/cockroach/pkg/settings"
17+
"github.com/cockroachdb/cockroach/pkg/sql/schemachanger/scerrors"
18+
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
19+
"github.com/cockroachdb/errors"
20+
)
21+
22+
// schemaStatementControl track if a statement tag is enabled or disabled
23+
// forcefully by the user.
24+
type schemaStatementControl map[string]bool
25+
26+
// schemaChangerDisabledStatements statements which are disabled
27+
// for the declarative schema changer. Users can specify statement
28+
// tags for each statement and a "!" symbol in front can have the opposite
29+
// effect to force enable fully unimplemented features.
30+
var schemaChangerDisabledStatements = func() *settings.StringSetting {
31+
return settings.RegisterValidatedStringSetting(
32+
settings.TenantWritable,
33+
"sql.schema.force_declarative_statements",
34+
"allows force enabling / disabling declarative schema changer for specific statements",
35+
"",
36+
func(values *settings.Values, s string) error {
37+
if s == "" {
38+
return nil
39+
}
40+
// First split the string into individual tags.
41+
tags := strings.Split(s, ",")
42+
for _, tag := range tags {
43+
tag = strings.ToUpper(strings.TrimSpace(tag))
44+
if len(tag) > 0 && (tag[0] == '+' || tag[0] == '!') {
45+
tag = tag[1:]
46+
} else {
47+
return errors.Errorf("tag is not properly formatted, must start with '+' or '!' (%s)", tag)
48+
}
49+
if _, ok := supportedStatementTags[tag]; !ok {
50+
return errors.Errorf("statement tag %q is not controlled by the declarative schema changer", tag)
51+
}
52+
}
53+
return nil
54+
})
55+
}()
56+
57+
// CheckStatementControl if a statement is forced to disabled or enabled. If a
58+
// statement is disabled then an not implemented error will be panicked. Otherwise,
59+
// a flag is returned indicating if this statement has been *forced* to be enabled.
60+
func (c schemaStatementControl) CheckStatementControl(n tree.Statement) (forceEnabled bool) {
61+
// This map is only created *if* any force flags are set.
62+
if c == nil {
63+
return false
64+
}
65+
enabledOrDisabled, found := c[n.StatementTag()]
66+
if !found {
67+
return false
68+
}
69+
if !enabledOrDisabled {
70+
panic(scerrors.NotImplementedErrorf(n,
71+
"statement has been disabled via cluster setting"))
72+
}
73+
return enabledOrDisabled
74+
}
75+
76+
// GetSchemaChangerStatementControl returns a map of statements that
77+
// are explicitly disabled by administrators for the declarative schema
78+
// changer.
79+
func getSchemaChangerStatementControl(sv *settings.Values) schemaStatementControl {
80+
statements := schemaChangerDisabledStatements.Get(sv)
81+
var statementMap schemaStatementControl
82+
for _, tag := range strings.Split(statements, ",") {
83+
tag = strings.ToUpper(strings.TrimSpace(tag))
84+
if len(tag) == 0 {
85+
continue
86+
}
87+
enabledOrDisabled := true
88+
tagStart := tag[0]
89+
tag = tag[1:]
90+
// If the tag starts with a ! its disabled.
91+
if tagStart == '!' {
92+
enabledOrDisabled = false
93+
}
94+
if statementMap == nil {
95+
statementMap = make(schemaStatementControl)
96+
}
97+
statementMap[tag] = enabledOrDisabled
98+
}
99+
return statementMap
100+
}

pkg/sql/schemachanger/schemachanger_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,7 @@ func TestSchemaChangeWaitsForOtherSchemaChanges(t *testing.T) {
493493
defer s.Stopper().Stop(ctx)
494494

495495
tdb := sqlutils.MakeSQLRunner(sqlDB)
496+
tdb.Exec(t, "SET CLUSTER SETTING sql.schema.force_declarative_statements='+CREATE SCHEMA'")
496497
tdb.Exec(t, `CREATE DATABASE db`)
497498
tdb.Exec(t, `CREATE SCHEMA db.s1`)
498499
tdb.Exec(t, `CREATE SCHEMA db.s2`)

pkg/sql/schemachanger/testdata/end_to_end/add_column_default_seq/add_column_default_seq.side_effects

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,8 @@ upsert descriptor #107
134134
+ columnIds:
135135
+ - 2
136136
+ id: 106
137-
formatVersion: 3
138-
id: 107
137+
families:
138+
- columnIds:
139139
...
140140
start: "1"
141141
unexposedParentSchemaId: 105
@@ -289,8 +289,8 @@ upsert descriptor #107
289289
+ columnIds:
290290
+ - 2
291291
+ id: 106
292-
formatVersion: 3
293-
id: 107
292+
families:
293+
- columnIds:
294294
...
295295
start: "1"
296296
unexposedParentSchemaId: 105

pkg/sql/schemachanger/testdata/end_to_end/alter_table_add_check_with_seq_and_udt/alter_table_add_check_with_seq_and_udt.side_effects

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ upsert descriptor #104
3434
+ dependedOnBy:
3535
+ - byId: true
3636
+ id: 107
37-
formatVersion: 3
38-
id: 104
37+
families:
38+
- columnIds:
3939
...
4040
start: "1"
4141
unexposedParentSchemaId: 101
@@ -120,8 +120,8 @@ upsert descriptor #104
120120
+ dependedOnBy:
121121
+ - byId: true
122122
+ id: 107
123-
formatVersion: 3
124-
id: 104
123+
families:
124+
- columnIds:
125125
...
126126
start: "1"
127127
unexposedParentSchemaId: 101

0 commit comments

Comments
 (0)