diff --git a/docs/generated/settings/settings.html b/docs/generated/settings/settings.html
index 8573fb3198c1..e1bab54b9c5a 100644
--- a/docs/generated/settings/settings.html
+++ b/docs/generated/settings/settings.html
@@ -18,6 +18,7 @@
feature.export.enabled | boolean | true | set to true to enable exports, false to disable; default is true |
feature.import.enabled | boolean | true | set to true to enable imports, false to disable; default is true |
feature.restore.enabled | boolean | true | set to true to enable restore, false to disable; default is true |
+feature.schema_change.enabled | boolean | true | set to true to enable schema changes, false to disable; default is true |
feature.stats.enabled | boolean | true | set to true to enable CREATE STATISTICS/ANALYZE, false to disable; default is true |
jobs.retention_time | duration | 336h0m0s | the amount of time to retain records for completed jobs before |
kv.allocator.load_based_lease_rebalancing.enabled | boolean | true | set to enable rebalancing of range leases based on load and latency |
diff --git a/pkg/ccl/backupccl/testdata/backup-restore/feature-flags b/pkg/ccl/backupccl/testdata/backup-restore/feature-flags
index 6ec458b989d4..115e24946e94 100644
--- a/pkg/ccl/backupccl/testdata/backup-restore/feature-flags
+++ b/pkg/ccl/backupccl/testdata/backup-restore/feature-flags
@@ -12,7 +12,7 @@ exec-sql
SET CLUSTER SETTING feature.backup.enabled = FALSE;
BACKUP TO 'nodelocal://0/test-root/';
----
-pq: BACKUP feature was disabled by the database administrator
+pq: feature BACKUP was disabled by the database administrator
# Test running backup when feature flag is enabled.
exec-sql
@@ -29,7 +29,7 @@ exec-sql
SET CLUSTER SETTING feature.restore.enabled = FALSE;
RESTORE TABLE d.t FROM 'nodelocal://0/test-root/';
----
-pq: RESTORE feature was disabled by the database administrator
+pq: feature RESTORE was disabled by the database administrator
# Test running restore when feature flag is enabled.
exec-sql
diff --git a/pkg/ccl/changefeedccl/changefeed_test.go b/pkg/ccl/changefeedccl/changefeed_test.go
index 7010879875d4..95ef42567ab3 100644
--- a/pkg/ccl/changefeedccl/changefeed_test.go
+++ b/pkg/ccl/changefeedccl/changefeed_test.go
@@ -1885,9 +1885,9 @@ func TestChangefeedErrors(t *testing.T) {
// Feature flag for changefeeds is off — test that CREATE CHANGEFEED and
// EXPERIMENTAL CHANGEFEED FOR surface error.
sqlDB.Exec(t, `SET CLUSTER SETTING feature.changefeed.enabled = false`)
- sqlDB.ExpectErr(t, `CHANGEFEED feature was disabled by the database administrator`,
+ sqlDB.ExpectErr(t, `feature CHANGEFEED was disabled by the database administrator`,
`CREATE CHANGEFEED FOR foo`)
- sqlDB.ExpectErr(t, `CHANGEFEED feature was disabled by the database administrator`,
+ sqlDB.ExpectErr(t, `feature CHANGEFEED was disabled by the database administrator`,
`EXPERIMENTAL CHANGEFEED FOR foo`)
sqlDB.Exec(t, `SET CLUSTER SETTING feature.changefeed.enabled = true`)
diff --git a/pkg/ccl/importccl/exportcsv_test.go b/pkg/ccl/importccl/exportcsv_test.go
index 36f5eb0de52e..77f690830aa0 100644
--- a/pkg/ccl/importccl/exportcsv_test.go
+++ b/pkg/ccl/importccl/exportcsv_test.go
@@ -424,11 +424,11 @@ func TestExportFeatureFlag(t *testing.T) {
// Feature flag is off — test that EXPORT surfaces error.
sqlDB.Exec(t, `SET CLUSTER SETTING feature.export.enabled = FALSE`)
- sqlDB.Exec(t, `CREATE TABLE feature_flag (a INT PRIMARY KEY)`)
- sqlDB.ExpectErr(t, `EXPORT feature was disabled by the database administrator`,
- `EXPORT INTO CSV 'nodelocal://0/%s/' FROM TABLE feature_flag`)
+ sqlDB.Exec(t, `CREATE TABLE feature_flags (a INT PRIMARY KEY)`)
+ sqlDB.ExpectErr(t, `feature EXPORT was disabled by the database administrator`,
+ `EXPORT INTO CSV 'nodelocal://0/%s/' FROM TABLE feature_flags`)
// Feature flag is on — test that EXPORT does not error.
sqlDB.Exec(t, `SET CLUSTER SETTING feature.export.enabled = TRUE`)
- sqlDB.Exec(t, `EXPORT INTO CSV 'nodelocal://0/%s/' FROM TABLE feature_flag`)
+ sqlDB.Exec(t, `EXPORT INTO CSV 'nodelocal://0/%s/' FROM TABLE feature_flags`)
}
diff --git a/pkg/ccl/importccl/import_stmt_test.go b/pkg/ccl/importccl/import_stmt_test.go
index b0a2f8da5199..4dfb8b1a91e2 100644
--- a/pkg/ccl/importccl/import_stmt_test.go
+++ b/pkg/ccl/importccl/import_stmt_test.go
@@ -2162,17 +2162,17 @@ func TestImportFeatureFlag(t *testing.T) {
// Feature flag is off — test that IMPORT and IMPORT INTO surface error.
sqlDB.Exec(t, `SET CLUSTER SETTING feature.import.enabled = FALSE`)
- sqlDB.ExpectErr(t, `IMPORT feature was disabled by the database administrator`,
+ sqlDB.ExpectErr(t, `feature IMPORT was disabled by the database administrator`,
fmt.Sprintf(`IMPORT TABLE t (a INT8 PRIMARY KEY, b STRING) CSV DATA (%s)`, testFiles.files[0]))
- sqlDB.Exec(t, `CREATE TABLE feature_flag (a INT8 PRIMARY KEY, b STRING)`)
- sqlDB.ExpectErr(t, `IMPORT feature was disabled by the database administrator`,
- fmt.Sprintf(`IMPORT INTO feature_flag (a, b) CSV DATA (%s)`, testFiles.files[0]))
+ sqlDB.Exec(t, `CREATE TABLE feature_flags (a INT8 PRIMARY KEY, b STRING)`)
+ sqlDB.ExpectErr(t, `feature IMPORT was disabled by the database administrator`,
+ fmt.Sprintf(`IMPORT INTO feature_flags (a, b) CSV DATA (%s)`, testFiles.files[0]))
// Feature flag is on — test that IMPORT and IMPORT INTO do not error.
sqlDB.Exec(t, `SET CLUSTER SETTING feature.import.enabled = TRUE`)
sqlDB.Exec(t, fmt.Sprintf(`IMPORT TABLE t (a INT8 PRIMARY KEY, b STRING) CSV DATA (%s)`,
testFiles.files[0]))
- sqlDB.Exec(t, fmt.Sprintf(`IMPORT INTO feature_flag (a, b) CSV DATA (%s)`, testFiles.files[0]))
+ sqlDB.Exec(t, fmt.Sprintf(`IMPORT INTO feature_flags (a, b) CSV DATA (%s)`, testFiles.files[0]))
}
func TestImportObjectLevelRBAC(t *testing.T) {
diff --git a/pkg/featureflag/feature_flags.go b/pkg/featureflag/feature_flags.go
index 5984e5da510e..144a4bc2ad04 100644
--- a/pkg/featureflag/feature_flags.go
+++ b/pkg/featureflag/feature_flags.go
@@ -25,7 +25,7 @@ const FeatureFlagEnabledDefault = true
func CheckEnabled(s *settings.BoolSetting, sv *settings.Values, featureName string) error {
if enabled := s.Get(sv); !enabled {
return pgerror.Newf(pgcode.OperatorIntervention,
- "%s feature was disabled by the database administrator",
+ "feature %s was disabled by the database administrator",
featureName)
}
return nil
diff --git a/pkg/sql/BUILD.bazel b/pkg/sql/BUILD.bazel
index 029eea4d0e50..6f8447e9f864 100644
--- a/pkg/sql/BUILD.bazel
+++ b/pkg/sql/BUILD.bazel
@@ -153,6 +153,7 @@ go_library(
"scan.go",
"scatter.go",
"schema.go",
+ "schema_change_cluster_setting.go",
"schema_changer.go",
"schema_changer_metrics.go",
"scrub.go",
diff --git a/pkg/sql/alter_database.go b/pkg/sql/alter_database.go
index ab144775514d..d71206442214 100644
--- a/pkg/sql/alter_database.go
+++ b/pkg/sql/alter_database.go
@@ -30,6 +30,13 @@ type alterDatabaseOwnerNode struct {
func (p *planner) AlterDatabaseOwner(
ctx context.Context, n *tree.AlterDatabaseOwner,
) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "ALTER DATABASE",
+ ); err != nil {
+ return nil, err
+ }
+
dbDesc, err := p.ResolveMutableDatabaseDescriptor(ctx, n.Name.String(), true /* required */)
if err != nil {
return nil, err
@@ -102,6 +109,12 @@ func (n *alterDatabaseOwnerNode) Close(context.Context) {}
func (p *planner) AlterDatabaseAddRegion(
ctx context.Context, n *tree.AlterDatabaseAddRegion,
) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "ALTER DATABASE",
+ ); err != nil {
+ return nil, err
+ }
return nil, unimplemented.New("alter database add region", "implementation pending")
}
@@ -109,6 +122,12 @@ func (p *planner) AlterDatabaseAddRegion(
func (p *planner) AlterDatabaseDropRegion(
ctx context.Context, n *tree.AlterDatabaseDropRegion,
) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "ALTER DATABASE",
+ ); err != nil {
+ return nil, err
+ }
return nil, unimplemented.New("alter database drop region", "implementation pending")
}
@@ -123,5 +142,11 @@ func (p *planner) AlterDatabasePrimaryRegion(
func (p *planner) AlterDatabaseSurvivalGoal(
ctx context.Context, n *tree.AlterDatabaseSurvivalGoal,
) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "ALTER DATABASE",
+ ); err != nil {
+ return nil, err
+ }
return nil, unimplemented.New("alter database survive", "implementation pending")
}
diff --git a/pkg/sql/alter_index.go b/pkg/sql/alter_index.go
index 1ddbff703e4f..121680611797 100644
--- a/pkg/sql/alter_index.go
+++ b/pkg/sql/alter_index.go
@@ -31,6 +31,13 @@ type alterIndexNode struct {
// AlterIndex applies a schema change on an index.
// Privileges: CREATE on table.
func (p *planner) AlterIndex(ctx context.Context, n *tree.AlterIndex) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "ALTER INDEX",
+ ); err != nil {
+ return nil, err
+ }
+
tableDesc, indexDesc, err := p.getTableAndIndex(ctx, &n.Index, privilege.CREATE)
if err != nil {
return nil, err
diff --git a/pkg/sql/alter_schema.go b/pkg/sql/alter_schema.go
index bf2815836ca3..17dc5bb10d94 100644
--- a/pkg/sql/alter_schema.go
+++ b/pkg/sql/alter_schema.go
@@ -39,6 +39,13 @@ type alterSchemaNode struct {
var _ planNode = &alterSchemaNode{n: nil}
func (p *planner) AlterSchema(ctx context.Context, n *tree.AlterSchema) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "ALTER SCHEMA",
+ ); err != nil {
+ return nil, err
+ }
+
dbName := p.CurrentDatabase()
if n.Schema.ExplicitCatalog {
dbName = n.Schema.Catalog()
diff --git a/pkg/sql/alter_sequence.go b/pkg/sql/alter_sequence.go
index efd4ea2dfa08..2cb76b5d5d30 100644
--- a/pkg/sql/alter_sequence.go
+++ b/pkg/sql/alter_sequence.go
@@ -28,6 +28,13 @@ type alterSequenceNode struct {
// AlterSequence transforms a tree.AlterSequence into a plan node.
func (p *planner) AlterSequence(ctx context.Context, n *tree.AlterSequence) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "ALTER SEQUENCE",
+ ); err != nil {
+ return nil, err
+ }
+
seqDesc, err := p.ResolveMutableTableDescriptorEx(
ctx, n.Name, !n.IfExists, tree.ResolveRequireSequenceDesc,
)
diff --git a/pkg/sql/alter_table.go b/pkg/sql/alter_table.go
index ac31283ed0d6..920d5f1557aa 100644
--- a/pkg/sql/alter_table.go
+++ b/pkg/sql/alter_table.go
@@ -53,6 +53,13 @@ type alterTableNode struct {
// notes: postgres requires CREATE on the table.
// mysql requires ALTER, CREATE, INSERT on the table.
func (p *planner) AlterTable(ctx context.Context, n *tree.AlterTable) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "ALTER TABLE",
+ ); err != nil {
+ return nil, err
+ }
+
tableDesc, err := p.ResolveMutableTableDescriptorEx(
ctx, n.Table, !n.IfExists, tree.ResolveRequireTableDesc,
)
diff --git a/pkg/sql/alter_table_regional_affinity.go b/pkg/sql/alter_table_regional_affinity.go
index d11971eea47f..9aacf060ffa3 100644
--- a/pkg/sql/alter_table_regional_affinity.go
+++ b/pkg/sql/alter_table_regional_affinity.go
@@ -21,5 +21,11 @@ import (
func (p *planner) AlterTableRegionalAffinity(
ctx context.Context, n *tree.AlterTableRegionalAffinity,
) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "ALTER TABLE",
+ ); err != nil {
+ return nil, err
+ }
return nil, unimplemented.New("alter table locality", "implementation pending")
}
diff --git a/pkg/sql/alter_table_set_schema.go b/pkg/sql/alter_table_set_schema.go
index 64a77fdf2b10..bdade2a9cd30 100644
--- a/pkg/sql/alter_table_set_schema.go
+++ b/pkg/sql/alter_table_set_schema.go
@@ -33,6 +33,13 @@ type alterTableSetSchemaNode struct {
func (p *planner) AlterTableSetSchema(
ctx context.Context, n *tree.AlterTableSetSchema,
) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "ALTER TABLE/VIEW/SEQUENCE SET SCHEMA",
+ ); err != nil {
+ return nil, err
+ }
+
tn := n.Name.ToTableName()
requiredTableKind := tree.ResolveAnyTableKind
if n.IsView {
diff --git a/pkg/sql/alter_type.go b/pkg/sql/alter_type.go
index 8870906cd98b..0cf6206d1b66 100644
--- a/pkg/sql/alter_type.go
+++ b/pkg/sql/alter_type.go
@@ -36,6 +36,13 @@ type alterTypeNode struct {
var _ planNode = &alterTypeNode{n: nil}
func (p *planner) AlterType(ctx context.Context, n *tree.AlterType) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "ALTER TYPE",
+ ); err != nil {
+ return nil, err
+ }
+
// Resolve the type.
desc, err := p.ResolveMutableTypeDescriptor(ctx, n.Type, true /* required */)
if err != nil {
diff --git a/pkg/sql/comment_on_column.go b/pkg/sql/comment_on_column.go
index cc03655deac0..3c18e1637128 100644
--- a/pkg/sql/comment_on_column.go
+++ b/pkg/sql/comment_on_column.go
@@ -29,6 +29,13 @@ type commentOnColumnNode struct {
// CommentOnColumn add comment on a column.
// Privileges: CREATE on table.
func (p *planner) CommentOnColumn(ctx context.Context, n *tree.CommentOnColumn) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "COMMENT ON COLUMN",
+ ); err != nil {
+ return nil, err
+ }
+
var tableName tree.TableName
if n.ColumnItem.TableName != nil {
tableName = n.ColumnItem.TableName.ToTableName()
diff --git a/pkg/sql/comment_on_database.go b/pkg/sql/comment_on_database.go
index 2cb45bbe7541..14f75ade2fe6 100644
--- a/pkg/sql/comment_on_database.go
+++ b/pkg/sql/comment_on_database.go
@@ -32,6 +32,13 @@ type commentOnDatabaseNode struct {
func (p *planner) CommentOnDatabase(
ctx context.Context, n *tree.CommentOnDatabase,
) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "COMMENT ON DATABASE",
+ ); err != nil {
+ return nil, err
+ }
+
dbDesc, err := p.ResolveUncachedDatabaseByName(ctx, string(n.Name), true)
if err != nil {
return nil, err
diff --git a/pkg/sql/comment_on_index.go b/pkg/sql/comment_on_index.go
index 2e5b91c89d9c..f4f4a5f2578c 100644
--- a/pkg/sql/comment_on_index.go
+++ b/pkg/sql/comment_on_index.go
@@ -31,6 +31,13 @@ type commentOnIndexNode struct {
// CommentOnIndex adds a comment on an index.
// Privileges: CREATE on table.
func (p *planner) CommentOnIndex(ctx context.Context, n *tree.CommentOnIndex) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "COMMENT ON INDEX",
+ ); err != nil {
+ return nil, err
+ }
+
tableDesc, indexDesc, err := p.getTableAndIndex(ctx, &n.Index, privilege.CREATE)
if err != nil {
return nil, err
diff --git a/pkg/sql/comment_on_table.go b/pkg/sql/comment_on_table.go
index cc7bfe63a627..5e62f1ed178e 100644
--- a/pkg/sql/comment_on_table.go
+++ b/pkg/sql/comment_on_table.go
@@ -31,6 +31,13 @@ type commentOnTableNode struct {
// notes: postgres requires CREATE on the table.
// mysql requires ALTER, CREATE, INSERT on the table.
func (p *planner) CommentOnTable(ctx context.Context, n *tree.CommentOnTable) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "COMMENT ON TABLE",
+ ); err != nil {
+ return nil, err
+ }
+
tableDesc, err := p.ResolveUncachedTableDescriptorEx(ctx, n.Table, true, tree.ResolveRequireTableDesc)
if err != nil {
return nil, err
diff --git a/pkg/sql/create_database.go b/pkg/sql/create_database.go
index 794c39ca5465..8c6537af55f5 100644
--- a/pkg/sql/create_database.go
+++ b/pkg/sql/create_database.go
@@ -30,6 +30,13 @@ type createDatabaseNode struct {
// CreateDatabase creates a database.
// Privileges: superuser or CREATEDB
func (p *planner) CreateDatabase(ctx context.Context, n *tree.CreateDatabase) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "CREATE DATABASE",
+ ); err != nil {
+ return nil, err
+ }
+
if n.Name == "" {
return nil, errEmptyDatabaseName
}
diff --git a/pkg/sql/create_index.go b/pkg/sql/create_index.go
index 8d5d1a91dd4d..1e8c64fb55a0 100644
--- a/pkg/sql/create_index.go
+++ b/pkg/sql/create_index.go
@@ -44,6 +44,12 @@ type createIndexNode struct {
// notes: postgres requires CREATE on the table.
// mysql requires INDEX on the table.
func (p *planner) CreateIndex(ctx context.Context, n *tree.CreateIndex) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "CREATE INDEX",
+ ); err != nil {
+ return nil, err
+ }
tableDesc, err := p.ResolveMutableTableDescriptor(
ctx, &n.Table, true /*required*/, tree.ResolveRequireTableOrViewDesc,
)
diff --git a/pkg/sql/create_schema.go b/pkg/sql/create_schema.go
index 081b05611eff..9c8b779b4534 100644
--- a/pkg/sql/create_schema.go
+++ b/pkg/sql/create_schema.go
@@ -37,6 +37,13 @@ func (n *createSchemaNode) startExec(params runParams) error {
}
func (p *planner) createUserDefinedSchema(params runParams, n *tree.CreateSchema) error {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "CREATE SCHEMA",
+ ); err != nil {
+ return err
+ }
+
// Users can't create a schema without being connected to a DB.
if p.CurrentDatabase() == "" {
return pgerror.New(pgcode.UndefinedDatabase,
@@ -177,6 +184,13 @@ func (n *createSchemaNode) Close(ctx context.Context) {}
// CreateSchema creates a schema.
func (p *planner) CreateSchema(ctx context.Context, n *tree.CreateSchema) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "CREATE SCHEMA",
+ ); err != nil {
+ return nil, err
+ }
+
return &createSchemaNode{
n: n,
}, nil
diff --git a/pkg/sql/create_sequence.go b/pkg/sql/create_sequence.go
index 3e9fd3157b69..83b09550e72b 100644
--- a/pkg/sql/create_sequence.go
+++ b/pkg/sql/create_sequence.go
@@ -34,6 +34,13 @@ type createSequenceNode struct {
}
func (p *planner) CreateSequence(ctx context.Context, n *tree.CreateSequence) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "CREATE SEQUENCE",
+ ); err != nil {
+ return nil, err
+ }
+
un := n.Name.ToUnresolvedObjectName()
dbDesc, _, prefix, err := p.ResolveTargetObject(ctx, un)
if err != nil {
diff --git a/pkg/sql/create_type.go b/pkg/sql/create_type.go
index cb1575cad3e4..fc204ae2a671 100644
--- a/pkg/sql/create_type.go
+++ b/pkg/sql/create_type.go
@@ -43,6 +43,13 @@ type createTypeNode struct {
var _ planNode = &createTypeNode{n: nil}
func (p *planner) CreateType(ctx context.Context, n *tree.CreateType) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "CREATE TYPE",
+ ); err != nil {
+ return nil, err
+ }
+
return &createTypeNode{n: n}, nil
}
diff --git a/pkg/sql/drop_database.go b/pkg/sql/drop_database.go
index 0586c5cba244..67896c4f5d01 100644
--- a/pkg/sql/drop_database.go
+++ b/pkg/sql/drop_database.go
@@ -44,6 +44,13 @@ type dropDatabaseNode struct {
// Notes: postgres allows only the database owner to DROP a database.
// mysql requires the DROP privileges on the database.
func (p *planner) DropDatabase(ctx context.Context, n *tree.DropDatabase) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "DROP DATABASE",
+ ); err != nil {
+ return nil, err
+ }
+
if n.Name == "" {
return nil, errEmptyDatabaseName
}
diff --git a/pkg/sql/drop_index.go b/pkg/sql/drop_index.go
index a5f11e4465b6..8f513f42897d 100644
--- a/pkg/sql/drop_index.go
+++ b/pkg/sql/drop_index.go
@@ -42,6 +42,13 @@ type dropIndexNode struct {
// Notes: postgres allows only the index owner to DROP an index.
// mysql requires the INDEX privilege on the table.
func (p *planner) DropIndex(ctx context.Context, n *tree.DropIndex) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "DROP INDEX",
+ ); err != nil {
+ return nil, err
+ }
+
// Keep a track of the indexes that exist to check. When the IF EXISTS
// options are provided, we will simply not include any indexes that
// don't exist and continue execution.
diff --git a/pkg/sql/drop_owned_by.go b/pkg/sql/drop_owned_by.go
index ba9bfafa0e0b..afb969eed20c 100644
--- a/pkg/sql/drop_owned_by.go
+++ b/pkg/sql/drop_owned_by.go
@@ -26,6 +26,12 @@ type dropOwnedByNode struct {
}
func (p *planner) DropOwnedBy(ctx context.Context) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "DROP OWNED BY",
+ ); err != nil {
+ return nil, err
+ }
telemetry.Inc(sqltelemetry.CreateDropOwnedByCounter())
// TODO(angelaw): Implementation.
return nil, unimplemented.NewWithIssue(55381, "drop owned by is not yet implemented")
diff --git a/pkg/sql/drop_schema.go b/pkg/sql/drop_schema.go
index 5fbcf1cbfa5c..8b934e4f2046 100644
--- a/pkg/sql/drop_schema.go
+++ b/pkg/sql/drop_schema.go
@@ -39,6 +39,13 @@ type dropSchemaNode struct {
var _ planNode = &dropSchemaNode{n: nil}
func (p *planner) DropSchema(ctx context.Context, n *tree.DropSchema) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "DROP SCHEMA",
+ ); err != nil {
+ return nil, err
+ }
+
isAdmin, err := p.HasAdminRole(ctx)
if err != nil {
return nil, err
diff --git a/pkg/sql/drop_sequence.go b/pkg/sql/drop_sequence.go
index 4d1a76a75426..4c73df4f0fb8 100644
--- a/pkg/sql/drop_sequence.go
+++ b/pkg/sql/drop_sequence.go
@@ -32,6 +32,13 @@ type dropSequenceNode struct {
}
func (p *planner) DropSequence(ctx context.Context, n *tree.DropSequence) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "DROP SEQUENCE",
+ ); err != nil {
+ return nil, err
+ }
+
td := make([]toDelete, 0, len(n.Names))
for i := range n.Names {
tn := &n.Names[i]
diff --git a/pkg/sql/drop_table.go b/pkg/sql/drop_table.go
index bcc9a8ebbe13..4306cdcfdddc 100644
--- a/pkg/sql/drop_table.go
+++ b/pkg/sql/drop_table.go
@@ -47,6 +47,13 @@ type toDelete struct {
// Notes: postgres allows only the table owner to DROP a table.
// mysql requires the DROP privilege on the table.
func (p *planner) DropTable(ctx context.Context, n *tree.DropTable) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "DROP TABLE",
+ ); err != nil {
+ return nil, err
+ }
+
td := make(map[descpb.ID]toDelete, len(n.Names))
for i := range n.Names {
tn := &n.Names[i]
diff --git a/pkg/sql/drop_type.go b/pkg/sql/drop_type.go
index 0a44e45b57bc..e782a2b64f80 100644
--- a/pkg/sql/drop_type.go
+++ b/pkg/sql/drop_type.go
@@ -36,6 +36,13 @@ type dropTypeNode struct {
var _ planNode = &dropTypeNode{n: nil}
func (p *planner) DropType(ctx context.Context, n *tree.DropType) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "DROP TYPE",
+ ); err != nil {
+ return nil, err
+ }
+
node := &dropTypeNode{
n: n,
td: make(map[descpb.ID]*typedesc.Mutable),
diff --git a/pkg/sql/drop_view.go b/pkg/sql/drop_view.go
index ca732de2c3d9..9fcba9fe1f69 100644
--- a/pkg/sql/drop_view.go
+++ b/pkg/sql/drop_view.go
@@ -35,6 +35,13 @@ type dropViewNode struct {
// Notes: postgres allows only the view owner to DROP a view.
// mysql requires the DROP privilege on the view.
func (p *planner) DropView(ctx context.Context, n *tree.DropView) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "DROP VIEW",
+ ); err != nil {
+ return nil, err
+ }
+
td := make([]toDelete, 0, len(n.Names))
for i := range n.Names {
tn := &n.Names[i]
diff --git a/pkg/sql/export.go b/pkg/sql/export.go
index 59f2443f2bd9..5ab4055309ec 100644
--- a/pkg/sql/export.go
+++ b/pkg/sql/export.go
@@ -92,8 +92,10 @@ func (ef *execFactory) ConstructExport(
input exec.Node, fileName tree.TypedExpr, fileFormat string, options []exec.KVOption,
) (exec.Node, error) {
if !featureExportEnabled.Get(&ef.planner.ExecCfg().Settings.SV) {
- return nil, pgerror.Newf(pgcode.OperatorIntervention,
- "EXPORT feature was disabled by the database administrator")
+ return nil, pgerror.Newf(
+ pgcode.OperatorIntervention,
+ "feature EXPORT was disabled by the database administrator",
+ )
}
if err := featureflag.CheckEnabled(featureExportEnabled,
diff --git a/pkg/sql/logictest/testdata/logic_test/distsql_stats b/pkg/sql/logictest/testdata/logic_test/distsql_stats
index ca2d82d60817..44c1c80c3801 100644
--- a/pkg/sql/logictest/testdata/logic_test/distsql_stats
+++ b/pkg/sql/logictest/testdata/logic_test/distsql_stats
@@ -47,10 +47,10 @@ NULL /1 {1} 1
statement ok
SET CLUSTER SETTING feature.stats.enabled = FALSE
-statement error pq: ANALYZE/CREATE STATISTICS feature was disabled by the database administrator
+statement error pq: feature ANALYZE/CREATE STATISTICS was disabled by the database administrator
CREATE STATISTICS s1 ON a FROM data
-statement error pq: ANALYZE/CREATE STATISTICS feature was disabled by the database administrator
+statement error pq: feature ANALYZE/CREATE STATISTICS was disabled by the database administrator
ANALYZE data
statement ok
diff --git a/pkg/sql/logictest/testdata/logic_test/schema_change_feature_flags b/pkg/sql/logictest/testdata/logic_test/schema_change_feature_flags
new file mode 100644
index 000000000000..94f5b2a7fc42
--- /dev/null
+++ b/pkg/sql/logictest/testdata/logic_test/schema_change_feature_flags
@@ -0,0 +1,256 @@
+# Test all feature flags when set to off in SQL package.
+statement ok
+SET CLUSTER SETTING feature.schema_change.enabled = FALSE;
+
+# Test CREATE DATABASE.
+statement error pq: feature CREATE DATABASE is part of the schema change category, which was disabled by the database administrator
+CREATE DATABASE d;
+
+# Test CREATE TABLE.
+statement error pq: feature CREATE TABLE is part of the schema change category, which was disabled by the database administrator
+CREATE TABLE t();
+
+# Test CREATE SCHEMA.
+statement error pq: feature CREATE SCHEMA is part of the schema change category, which was disabled by the database administrator
+CREATE SCHEMA s;
+
+# Test CREATE TYPE.
+statement ok
+SET CLUSTER SETTING feature.schema_change.enabled = TRUE;
+CREATE SCHEMA s;
+SET CLUSTER SETTING feature.schema_change.enabled = FALSE
+
+statement error pq: feature CREATE TYPE is part of the schema change category, which was disabled by the database administrator
+CREATE TYPE s.typ AS ENUM ()
+
+# Test CREATE VIEW.
+statement error pq: feature CREATE VIEW is part of the schema change category, which was disabled by the database administrator
+CREATE VIEW public.bar (x) AS SELECT 1 AS x
+
+# Test CREATE SEQUENCE.
+statement error pq: feature CREATE SEQUENCE is part of the schema change category, which was disabled by the database administrator
+CREATE SEQUENCE seq
+
+# Test CREATE INDEX.
+statement ok
+SET CLUSTER SETTING feature.schema_change.enabled = TRUE;
+CREATE TABLE t1(a INT8, b INT8);
+SET CLUSTER SETTING feature.schema_change.enabled = FALSE
+
+statement error pq: feature CREATE INDEX is part of the schema change category, which was disabled by the database administrator
+CREATE INDEX on t1 (a, b)
+
+# Test ALTER DATABASE OWNER.
+statement ok
+SET CLUSTER SETTING feature.schema_change.enabled = TRUE;
+CREATE DATABASE d;
+SET CLUSTER SETTING feature.schema_change.enabled = FALSE
+
+statement error pq: feature ALTER DATABASE is part of the schema change category, which was disabled by the database administrator
+ALTER DATABASE d OWNER TO testuser
+
+# Test ALTER DATABASE ADD REGION.
+statement error pq: feature ALTER DATABASE is part of the schema change category, which was disabled by the database administrator
+ALTER DATABASE d ADD REGION "us-west-1"
+
+# Test ALTER DATABASE DROP REGION.
+statement error pq: feature ALTER DATABASE is part of the schema change category, which was disabled by the database administrator
+ALTER DATABASE d DROP REGION "us-west-1"
+
+# Test ALTER DATABASE SURVIVE.
+statement error pq: feature ALTER DATABASE is part of the schema change category, which was disabled by the database administrator
+ALTER DATABASE d SURVIVE REGION FAILURE
+
+# Test RENAME DATABASE.
+statement error pq: feature ALTER DATABASE is part of the schema change category, which was disabled by the database administrator
+ALTER DATABASE d RENAME TO r
+
+# Test REPARENT DATABASE
+statement error pq: feature REPARENT DATABASE is part of the schema change category, which was disabled by the database administrator
+ALTER DATABASE d CONVERT TO SCHEMA WITH PARENT test
+
+statement ok
+SET CLUSTER SETTING feature.schema_change.enabled = TRUE;
+CREATE TABLE t();
+SET CLUSTER SETTING feature.schema_change.enabled = FALSE
+
+# Test ALTER TABLE PARTITION BY.
+statement error pq: feature ALTER TABLE is part of the schema change category, which was disabled by the database administrator
+ALTER TABLE t1 PARTITION BY NOTHING
+
+# Test ALTER TABLE ADD CONSTRAINT.
+statement error pq: feature ALTER TABLE is part of the schema change category, which was disabled by the database administrator
+ALTER TABLE t1 ADD CONSTRAINT a_unique UNIQUE (a)
+
+# Test ALTER TABLE RENAME CONSTRAINT.
+statement error pq: feature ALTER TABLE is part of the schema change category, which was disabled by the database administrator
+ALTER TABLE t1 RENAME CONSTRAINT a_unique to r
+
+# Test ALTER TABLE ADD COLUMN.
+statement error pq: feature ALTER TABLE is part of the schema change category, which was disabled by the database administrator
+ALTER TABLE t1 ADD COLUMN a STRING
+
+# Test ALTER TABLE DROP COLUMN.
+statement error pq: feature ALTER TABLE is part of the schema change category, which was disabled by the database administrator
+ALTER TABLE t1 DROP COLUMN IF EXISTS a
+
+# Test ALTER TABLE DROP CONSTRAINT.
+statement error pq: feature ALTER TABLE is part of the schema change category, which was disabled by the database administrator
+ALTER TABLE t1 DROP CONSTRAINT IF EXISTS a_unique
+
+# Test ALTER TABLE ALTER COLUMN SET.
+statement error pq: feature ALTER TABLE is part of the schema change category, which was disabled by the database administrator
+ALTER TABLE t1 ALTER COLUMN a SET NOT NULL
+
+# Test ALTER TABLE ALTER COLUMN DROP NOT NULL.
+statement error pq: feature ALTER TABLE is part of the schema change category, which was disabled by the database administrator
+ALTER TABLE t1 ALTER COLUMN a DROP NOT NULL
+
+# Test ALTER TABLE ALTER COLUMN DROP STORED.
+statement error pq: feature ALTER TABLE is part of the schema change category, which was disabled by the database administrator
+ALTER TABLE t1 ALTER COLUMN a DROP STORED
+
+# Test ALTER TABLE CONFIGURE ZONE.
+statement error pq: feature CONFIGURE ZONE is part of the schema change category, which was disabled by the database administrator
+ALTER TABLE t1 CONFIGURE ZONE USING num_replicas=5
+
+# Test RENAME TABLE.
+statement error pq: feature RENAME TABLE/VIEW/SEQUENCE is part of the schema change category, which was disabled by the database administrator
+ALTER TABLE t RENAME TO r
+
+# Test ALTER TABLE SET SCHEMA.
+statement error pq: feature ALTER TABLE/VIEW/SEQUENCE SET SCHEMA is part of the schema change category, which was disabled by the database administrator
+ALTER TABLE t SET SCHEMA s
+
+# Test ALTER TABLE SET REGIONAL AFFINITY.
+statement error pq: feature ALTER TABLE is part of the schema change category, which was disabled by the database administrator
+ALTER TABLE t SET REGIONAL AFFINITY TO NONE
+
+statement ok
+SET CLUSTER SETTING feature.schema_change.enabled = TRUE;
+CREATE INDEX i on t1 (a, b);
+SET CLUSTER SETTING feature.schema_change.enabled = FALSE;
+
+# Test RENAME COLUMN, throws ALTER TABLE error.
+statement error pq: feature ALTER TABLE is part of the schema change category, which was disabled by the database administrator
+ALTER TABLE t1 RENAME COLUMN a to c
+
+# Test ALTER INDEX CONFIGURE ZONE.
+statement error pq: feature CONFIGURE ZONE is part of the schema change category, which was disabled by the database administrator
+ALTER INDEX t1@i CONFIGURE ZONE DISCARD
+
+# Test RENAME INDEX
+statement error pq: feature RENAME INDEX is part of the schema change category, which was disabled by the database administrator
+ALTER INDEX t1@i RENAME TO r
+
+# Test RENAME SCHEMA, throws ALTER SCHEMA error.
+statement error pq: feature ALTER SCHEMA is part of the schema change category, which was disabled by the database administrator
+ALTER SCHEMA s RENAME TO r
+
+# Test ALTER SCHEMA.
+statement error pq: feature ALTER SCHEMA is part of the schema change category, which was disabled by the database administrator
+ALTER SCHEMA s OWNER TO testuser
+
+# Test ALTER TYPE.
+statement ok
+SET CLUSTER SETTING feature.schema_change.enabled = TRUE;
+CREATE TYPE s.typ AS ENUM ();
+SET CLUSTER SETTING feature.schema_change.enabled = FALSE
+
+# Test ALTER TYPE ADD VALUE.
+statement error pq: feature ALTER TYPE is part of the schema change category, which was disabled by the database administrator
+ALTER TYPE s.typ ADD VALUE 'hi'
+
+# Test ALTER TYPE RENAME VALUE TO.
+statement error pq: feature ALTER TYPE is part of the schema change category, which was disabled by the database administrator
+ALTER TYPE s.typ RENAME VALUE 'hi' TO 'no'
+
+# Test ALTER TYPE RENAME TO.
+statement error pq: feature ALTER TYPE is part of the schema change category, which was disabled by the database administrator
+ALTER TYPE s.typ RENAME TO no
+
+# Test ALTER TYPE SET SCHEMA.
+statement error pq: feature ALTER TYPE is part of the schema change category, which was disabled by the database administrator
+ALTER TYPE s.typ SET SCHEMA s
+
+# Test ALTER SEQUENCE.
+statement ok
+SET CLUSTER SETTING feature.schema_change.enabled = TRUE;
+CREATE SEQUENCE seq;
+SET CLUSTER SETTING feature.schema_change.enabled = FALSE
+
+# Test RENAME SEQUENCE.
+statement error pq: feature RENAME TABLE/VIEW/SEQUENCE is part of the schema change category, which was disabled by the database administrator
+ALTER SEQUENCE seq RENAME TO something
+
+# Test ALTER SEQUENCE SET SCHEMA
+statement error pq: feature ALTER TABLE/VIEW/SEQUENCE SET SCHEMA is part of the schema change category, which was disabled by the database administrator
+ALTER SEQUENCE seq SET SCHEMA s
+
+statement error pq: feature ALTER SEQUENCE is part of the schema change category, which was disabled by the database administrator
+ALTER SEQUENCE seq NO CYCLE
+
+# Test REASSIGN OWNED BY.
+statement error pq: feature REASSIGN OWNED BY is part of the schema change category, which was disabled by the database administrator
+REASSIGN OWNED BY root TO testuser
+
+# Test DROP OWNED BY.
+statement error pq: feature DROP OWNED BY is part of the schema change category, which was disabled by the database administrator
+DROP OWNED BY testuser
+
+# Test DROP DATABASE.
+statement error pq: feature DROP DATABASE is part of the schema change category, which was disabled by the database administrator
+DROP DATABASE d
+
+# Test DROP SCHEMA.
+statement error pq: feature DROP SCHEMA is part of the schema change category, which was disabled by the database administrator
+DROP SCHEMA s
+
+# Test DROP TYPE.
+statement error pq: feature DROP TYPE is part of the schema change category, which was disabled by the database administrator
+DROP TYPE s.typ
+
+# Test DROP TABLE.
+statement error pq: feature DROP TABLE is part of the schema change category, which was disabled by the database administrator
+DROP TABLE t
+
+# Test DROP SEQUENCE.
+statement error pq: feature DROP SEQUENCE is part of the schema change category, which was disabled by the database administrator
+DROP SEQUENCE seq
+
+# Test DROP VIEW.
+statement ok
+SET CLUSTER SETTING feature.schema_change.enabled = TRUE;
+CREATE VIEW public.bar (x) AS SELECT 1 AS x;
+SET CLUSTER SETTING feature.schema_change.enabled = FALSE
+
+# Test ALTER VIEW SET SCHEMA
+statement error pq: feature ALTER TABLE/VIEW/SEQUENCE SET SCHEMA is part of the schema change category, which was disabled by the database administrator
+ALTER VIEW public.bar SET SCHEMA s
+
+statement error pq: feature DROP VIEW is part of the schema change category, which was disabled by the database administrator
+DROP VIEW public.bar
+
+# Test DROP INDEX.
+statement error pq: feature DROP INDEX is part of the schema change category, which was disabled by the database administrator
+DROP INDEX t1@i
+
+# Test COMMENT ON COLUMN.
+statement error pq: feature COMMENT ON COLUMN is part of the schema change category, which was disabled by the database administrator
+COMMENT ON COLUMN t.a IS 'comment'
+
+# Test COMMENT ON DATABASE.
+statement error pq: feature COMMENT ON DATABASE is part of the schema change category, which was disabled by the database administrator
+COMMENT ON DATABASE d IS 'comment'
+
+# Test COMMENT ON INDEX.
+statement error pq: feature COMMENT ON INDEX is part of the schema change category, which was disabled by the database administrator
+COMMENT ON INDEX t1@i IS 'comment'
+
+# Test COMMENT ON TABLE.
+statement error pq: feature COMMENT ON TABLE is part of the schema change category, which was disabled by the database administrator
+COMMENT ON TABLE t IS 'comment'
+
+# Reset feature flag to true so that test objects can be dropped.
+statement ok
+SET CLUSTER SETTING feature.schema_change.enabled = TRUE
diff --git a/pkg/sql/opt_exec_factory.go b/pkg/sql/opt_exec_factory.go
index 38565b85e79e..74b825c200ea 100644
--- a/pkg/sql/opt_exec_factory.go
+++ b/pkg/sql/opt_exec_factory.go
@@ -1607,6 +1607,12 @@ func (ef *execFactory) ConstructDeleteRange(
func (ef *execFactory) ConstructCreateTable(
schema cat.Schema, ct *tree.CreateTable,
) (exec.Node, error) {
+ if err := checkSchemaChangeEnabled(
+ &ef.planner.ExecCfg().Settings.SV,
+ "CREATE TABLE",
+ ); err != nil {
+ return nil, err
+ }
return &createTableNode{
n: ct,
dbDesc: schema.(*optSchema).database,
@@ -1617,6 +1623,13 @@ func (ef *execFactory) ConstructCreateTable(
func (ef *execFactory) ConstructCreateTableAs(
input exec.Node, schema cat.Schema, ct *tree.CreateTable,
) (exec.Node, error) {
+ if err := checkSchemaChangeEnabled(
+ &ef.planner.ExecCfg().Settings.SV,
+ "CREATE TABLE",
+ ); err != nil {
+ return nil, err
+ }
+
return &createTableNode{
n: ct,
dbDesc: schema.(*optSchema).database,
@@ -1637,6 +1650,13 @@ func (ef *execFactory) ConstructCreateView(
deps opt.ViewDeps,
) (exec.Node, error) {
+ if err := checkSchemaChangeEnabled(
+ &ef.planner.ExecCfg().Settings.SV,
+ "CREATE VIEW",
+ ); err != nil {
+ return nil, err
+ }
+
planDeps := make(planDependencies, len(deps))
for _, d := range deps {
desc, err := getDescForDataSource(d.DataSource)
diff --git a/pkg/sql/pgwire/testdata/pgtest/notice b/pkg/sql/pgwire/testdata/pgtest/notice
index c670417ea373..648ad7f6a688 100644
--- a/pkg/sql/pgwire/testdata/pgtest/notice
+++ b/pkg/sql/pgwire/testdata/pgtest/notice
@@ -55,7 +55,7 @@ Query {"String": "DROP INDEX t_x_idx"}
until crdb_only
CommandComplete
----
-{"Severity":"NOTICE","Code":"00000","Message":"the data for dropped indexes is reclaimed asynchronously","Detail":"","Hint":"The reclamation delay can be customized in the zone configuration for the table.","Position":0,"InternalPosition":0,"InternalQuery":"","Where":"","SchemaName":"","TableName":"","ColumnName":"","DataTypeName":"","ConstraintName":"","File":"drop_index.go","Line":521,"Routine":"dropIndexByName","UnknownFields":null}
+{"Severity":"NOTICE","Code":"00000","Message":"the data for dropped indexes is reclaimed asynchronously","Detail":"","Hint":"The reclamation delay can be customized in the zone configuration for the table.","Position":0,"InternalPosition":0,"InternalQuery":"","Where":"","SchemaName":"","TableName":"","ColumnName":"","DataTypeName":"","ConstraintName":"","File":"drop_index.go","Line":528,"Routine":"dropIndexByName","UnknownFields":null}
{"Type":"CommandComplete","CommandTag":"DROP INDEX"}
until noncrdb_only
diff --git a/pkg/sql/reassign_owned_by.go b/pkg/sql/reassign_owned_by.go
index 13f0ed1fbbb7..afbe6b8d26c1 100644
--- a/pkg/sql/reassign_owned_by.go
+++ b/pkg/sql/reassign_owned_by.go
@@ -31,6 +31,13 @@ type reassignOwnedByNode struct {
}
func (p *planner) ReassignOwnedBy(ctx context.Context, n *tree.ReassignOwnedBy) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "REASSIGN OWNED BY",
+ ); err != nil {
+ return nil, err
+ }
+
// Check all roles in old roles exist. Checks in authorization.go will confirm that current user
// is a member of old roles and new roles and has CREATE privilege.
for _, oldRole := range n.OldRoles {
diff --git a/pkg/sql/rename_column.go b/pkg/sql/rename_column.go
index 2c1612d54f2f..573fc55b4450 100644
--- a/pkg/sql/rename_column.go
+++ b/pkg/sql/rename_column.go
@@ -37,6 +37,13 @@ type renameColumnNode struct {
// notes: postgres requires CREATE on the table.
// mysql requires ALTER, CREATE, INSERT on the table.
func (p *planner) RenameColumn(ctx context.Context, n *tree.RenameColumn) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "RENAME COLUMN",
+ ); err != nil {
+ return nil, err
+ }
+
// Check if table exists.
tableDesc, err := p.ResolveMutableTableDescriptor(ctx, &n.Table, !n.IfExists, tree.ResolveRequireTableDesc)
if err != nil {
diff --git a/pkg/sql/rename_database.go b/pkg/sql/rename_database.go
index bc1ac374644b..81e70b1b4c2e 100644
--- a/pkg/sql/rename_database.go
+++ b/pkg/sql/rename_database.go
@@ -43,6 +43,13 @@ type renameDatabaseNode struct {
// Privileges: superuser + DROP or ownership + CREATEDB privileges
// Notes: mysql >= 5.1.23 does not allow database renames.
func (p *planner) RenameDatabase(ctx context.Context, n *tree.RenameDatabase) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "ALTER DATABASE",
+ ); err != nil {
+ return nil, err
+ }
+
if n.Name == "" || n.NewName == "" {
return nil, errEmptyDatabaseName
}
diff --git a/pkg/sql/rename_index.go b/pkg/sql/rename_index.go
index bbd14e4a3c03..fca6a5baf3a4 100644
--- a/pkg/sql/rename_index.go
+++ b/pkg/sql/rename_index.go
@@ -35,6 +35,13 @@ type renameIndexNode struct {
// notes: postgres requires CREATE on the table.
// mysql requires ALTER, CREATE, INSERT on the table.
func (p *planner) RenameIndex(ctx context.Context, n *tree.RenameIndex) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "RENAME INDEX",
+ ); err != nil {
+ return nil, err
+ }
+
_, tableDesc, err := expandMutableIndexName(ctx, p, n.Index, !n.IfExists /* requireTable */)
if err != nil {
return nil, err
diff --git a/pkg/sql/rename_table.go b/pkg/sql/rename_table.go
index 8b85348161c2..a921e57af3c6 100644
--- a/pkg/sql/rename_table.go
+++ b/pkg/sql/rename_table.go
@@ -41,6 +41,13 @@ type renameTableNode struct {
// mysql requires ALTER, DROP on the original table, and CREATE, INSERT
// on the new table (and does not copy privileges over).
func (p *planner) RenameTable(ctx context.Context, n *tree.RenameTable) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "RENAME TABLE/VIEW/SEQUENCE",
+ ); err != nil {
+ return nil, err
+ }
+
oldTn := n.Name.ToTableName()
newTn := n.NewName.ToTableName()
toRequire := tree.ResolveRequireTableOrViewDesc
diff --git a/pkg/sql/reparent_database.go b/pkg/sql/reparent_database.go
index 219439063691..6fcac10a72f5 100644
--- a/pkg/sql/reparent_database.go
+++ b/pkg/sql/reparent_database.go
@@ -41,6 +41,13 @@ type reparentDatabaseNode struct {
func (p *planner) ReparentDatabase(
ctx context.Context, n *tree.ReparentDatabase,
) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "REPARENT DATABASE",
+ ); err != nil {
+ return nil, err
+ }
+
// We'll only allow the admin to perform this reparenting action.
if err := p.RequireAdminRole(ctx, "ALTER DATABASE ... CONVERT TO SCHEMA"); err != nil {
return nil, err
diff --git a/pkg/sql/schema_change_cluster_setting.go b/pkg/sql/schema_change_cluster_setting.go
new file mode 100644
index 000000000000..5af088058422
--- /dev/null
+++ b/pkg/sql/schema_change_cluster_setting.go
@@ -0,0 +1,39 @@
+// Copyright 2020 The Cockroach Authors.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+package sql
+
+import (
+ "fmt"
+
+ "github.com/cockroachdb/cockroach/pkg/featureflag"
+ "github.com/cockroachdb/cockroach/pkg/settings"
+)
+
+// featureSchemaChangeEnabled is the cluste rsetting used to enable and disable
+// any features that require schema changes. Documentation for which features
+// are covered TBD.
+var featureSchemaChangeEnabled = settings.RegisterPublicBoolSetting(
+ "feature.schema_change.enabled",
+ "set to true to enable schema changes, false to disable; default is true",
+ featureflag.FeatureFlagEnabledDefault)
+
+// checkSchemaChangeEnabled is a method that wraps the featureflag.CheckEnabled
+// method specifically for all features that are categorized as schema changes.
+func checkSchemaChangeEnabled(sv *settings.Values, schemaFeatureName string) error {
+ if err := featureflag.CheckEnabled(
+ featureSchemaChangeEnabled,
+ sv,
+ fmt.Sprintf("%s is part of the schema change category, which", schemaFeatureName),
+ ); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/pkg/sql/set_zone_config.go b/pkg/sql/set_zone_config.go
index 9c13456e8b10..fff6617bc389 100644
--- a/pkg/sql/set_zone_config.go
+++ b/pkg/sql/set_zone_config.go
@@ -103,6 +103,13 @@ func loadYAML(dst interface{}, yamlString string) {
}
func (p *planner) SetZoneConfig(ctx context.Context, n *tree.SetZoneConfig) (planNode, error) {
+ if err := checkSchemaChangeEnabled(
+ &p.ExecCfg().Settings.SV,
+ "CONFIGURE ZONE",
+ ); err != nil {
+ return nil, err
+ }
+
if err := checkPrivilegeForSetZoneConfig(ctx, p, n.ZoneSpecifier); err != nil {
return nil, err
}