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.enabledbooleantrueset to true to enable exports, false to disable; default is true feature.import.enabledbooleantrueset to true to enable imports, false to disable; default is true feature.restore.enabledbooleantrueset to true to enable restore, false to disable; default is true +feature.schema_change.enabledbooleantrueset to true to enable schema changes, false to disable; default is true feature.stats.enabledbooleantrueset to true to enable CREATE STATISTICS/ANALYZE, false to disable; default is true jobs.retention_timeduration336h0m0sthe amount of time to retain records for completed jobs before kv.allocator.load_based_lease_rebalancing.enabledbooleantrueset 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 }