From b2b237a2c219ddd1a78f77d2bfa5dc0b638bd7f5 Mon Sep 17 00:00:00 2001 From: Raphael 'kena' Poss Date: Fri, 20 Jan 2023 16:11:10 +0100 Subject: [PATCH 1/2] sql,ccl: new tenant info field ServiceMode Summary of changes: - the new TenantInfo.ServiceMode field indicates how to run servers. - new syntax: `ALTER TENANT ... START SERVICE EXTERNAL/SHARED`, `ALTER TENANT ... STOP SERVICE`. - tenants created via `create_tenant()` (via CC serverless control plane) start in service mode EXTERNAL. - other tenants start in service mode NONE. - need ALTER TENANT STOP SERVICE before dropping a tenant. - except in the case of `crdb_internal.destroy_tenant` for compat with CC serverless control plane. This commit does not contain the logic to start/stop servers yet. Release note: None --- .../settings/settings-for-tenants.txt | 2 +- docs/generated/settings/settings.html | 2 +- docs/generated/sql/bnf/stmt_block.bnf | 3 + pkg/ccl/backupccl/backup_test.go | 36 +++---- pkg/ccl/backupccl/restore_job.go | 13 +-- .../testdata/backup-restore/restore-tenants | 22 ++-- .../testdata/logic_test/tenant_usage | 1 + .../tenant/directory_cache_test.go | 21 ++-- .../replication_stream_e2e_test.go | 4 +- .../streamingest/stream_ingestion_planning.go | 3 +- .../streamingccl/streamingest/testdata/simple | 8 +- pkg/ccl/testccl/sqlccl/tenant_gc_test.go | 4 +- pkg/clusterversion/cockroach_versions.go | 7 ++ pkg/sql/BUILD.bazel | 1 + pkg/sql/catalog/bootstrap/metadata.go | 8 +- pkg/sql/catalog/bootstrap/testdata/testdata | 7 +- pkg/sql/catalog/colinfo/result_columns.go | 3 +- pkg/sql/catalog/descpb/tenant.proto | 19 ++++ pkg/sql/catalog/systemschema/system.go | 30 ++++-- pkg/sql/drop_tenant.go | 2 +- pkg/sql/faketreeeval/evalctx.go | 2 +- pkg/sql/gcjob_test/gc_job_test.go | 2 +- .../testdata/logic_test/crdb_internal_catalog | 2 +- .../testdata/logic_test/information_schema | 1 + .../logictest/testdata/logic_test/pg_catalog | 2 + pkg/sql/logictest/testdata/logic_test/tenant | 102 +++++++++--------- .../testdata/logic_test/tenant_builtins | 29 ++--- pkg/sql/opaque.go | 3 + pkg/sql/parser/help_test.go | 3 + pkg/sql/parser/sql.y | 46 +++++++- pkg/sql/parser/testdata/alter_tenant | 24 +++++ pkg/sql/rename_tenant.go | 2 +- pkg/sql/sem/builtins/builtins.go | 17 ++- pkg/sql/sem/eval/deps.go | 2 +- pkg/sql/sem/tree/alter_tenant.go | 34 ++++++ pkg/sql/sem/tree/stmt.go | 10 ++ pkg/sql/sem/tree/walk.go | 20 ++++ pkg/sql/show_tenant.go | 1 + pkg/sql/tenant_creation.go | 18 +++- pkg/sql/tenant_deletion.go | 14 ++- pkg/sql/tenant_service.go | 68 ++++++++++++ pkg/sql/tenant_test.go | 1 + pkg/sql/tenant_update.go | 81 ++++++++++---- pkg/sql/tests/system_table_test.go | 2 +- pkg/sql/tests/testdata/initial_keys | 3 +- pkg/sql/walk.go | 2 + .../upgrades/tenant_table_migration.go | 79 +++++++++++--- pkg/upgrade/upgrades/upgrades.go | 5 + 48 files changed, 588 insertions(+), 183 deletions(-) create mode 100644 pkg/sql/tenant_service.go diff --git a/docs/generated/settings/settings-for-tenants.txt b/docs/generated/settings/settings-for-tenants.txt index 1d256d3e947e..529b3cafc7d2 100644 --- a/docs/generated/settings/settings-for-tenants.txt +++ b/docs/generated/settings/settings-for-tenants.txt @@ -297,4 +297,4 @@ trace.jaeger.agent string the address of a Jaeger agent to receive traces using trace.opentelemetry.collector string address of an OpenTelemetry trace collector to receive traces using the otel gRPC protocol, as :. If no port is specified, 4317 will be used. trace.span_registry.enabled boolean true if set, ongoing traces can be seen at https:///#/debug/tracez trace.zipkin.collector string the address of a Zipkin instance to receive traces, as :. If no port is specified, 9411 will be used. -version version 1000022.2-30 set the active cluster version in the format '.' +version version 1000022.2-32 set the active cluster version in the format '.' diff --git a/docs/generated/settings/settings.html b/docs/generated/settings/settings.html index bb9b50924c4f..0ef19edc2bac 100644 --- a/docs/generated/settings/settings.html +++ b/docs/generated/settings/settings.html @@ -235,6 +235,6 @@
trace.opentelemetry.collector
stringaddress of an OpenTelemetry trace collector to receive traces using the otel gRPC protocol, as <host>:<port>. If no port is specified, 4317 will be used.
trace.span_registry.enabled
booleantrueif set, ongoing traces can be seen at https://<ui>/#/debug/tracez
trace.zipkin.collector
stringthe address of a Zipkin instance to receive traces, as <host>:<port>. If no port is specified, 9411 will be used. -
version
version1000022.2-30set the active cluster version in the format '<major>.<minor>' +
version
version1000022.2-32set the active cluster version in the format '<major>.<minor>' diff --git a/docs/generated/sql/bnf/stmt_block.bnf b/docs/generated/sql/bnf/stmt_block.bnf index 62735d5dc9be..ab02f9588e1c 100644 --- a/docs/generated/sql/bnf/stmt_block.bnf +++ b/docs/generated/sql/bnf/stmt_block.bnf @@ -1338,11 +1338,13 @@ unreserved_keyword ::= | 'SEQUENCE' | 'SEQUENCES' | 'SERVER' + | 'SERVICE' | 'SESSION' | 'SESSIONS' | 'SET' | 'SETS' | 'SHARE' + | 'SHARED' | 'SHOW' | 'SIMPLE' | 'SKIP' @@ -1361,6 +1363,7 @@ unreserved_keyword ::= | 'STATEMENTS' | 'STATISTICS' | 'STDIN' + | 'STOP' | 'STORAGE' | 'STORE' | 'STORED' diff --git a/pkg/ccl/backupccl/backup_test.go b/pkg/ccl/backupccl/backup_test.go index 40e4f6abcfe4..bd34e5a0a825 100644 --- a/pkg/ccl/backupccl/backup_test.go +++ b/pkg/ccl/backupccl/backup_test.go @@ -6885,7 +6885,7 @@ func TestBackupRestoreTenant(t *testing.T) { {`1`, `true`, `system`, - `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "tenantReplicationJobId": "0"}`, + `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "serviceMode": "SHARED", "tenantReplicationJobId": "0"}`, }, }) restoreDB.Exec(t, `RESTORE TENANT 10 FROM 'nodelocal://1/t10'`) @@ -6896,13 +6896,13 @@ func TestBackupRestoreTenant(t *testing.T) { `1`, `true`, `system`, - `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "tenantReplicationJobId": "0"}`, + `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "serviceMode": "SHARED", "tenantReplicationJobId": "0"}`, }, { `10`, `true`, `tenant-10`, - `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "10", "name": "tenant-10", "tenantReplicationJobId": "0"}`, + `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "10", "name": "tenant-10", "serviceMode": "NONE", "tenantReplicationJobId": "0"}`, }, }, ) @@ -6936,13 +6936,13 @@ func TestBackupRestoreTenant(t *testing.T) { `1`, `true`, `system`, - `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "tenantReplicationJobId": "0"}`, + `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "serviceMode": "SHARED", "tenantReplicationJobId": "0"}`, }, { `10`, `false`, `NULL`, - `{"capabilities": {"canAdminSplit": false}, "dataState": "DROP", "droppedName": "tenant-10", "id": "10", "name": "", "tenantReplicationJobId": "0"}`, + `{"capabilities": {"canAdminSplit": false}, "dataState": "DROP", "droppedName": "tenant-10", "id": "10", "name": "", "serviceMode": "NONE", "tenantReplicationJobId": "0"}`, }, }, ) @@ -6971,13 +6971,13 @@ func TestBackupRestoreTenant(t *testing.T) { `1`, `true`, `system`, - `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "tenantReplicationJobId": "0"}`, + `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "serviceMode": "SHARED", "tenantReplicationJobId": "0"}`, }, { `10`, `true`, `tenant-10`, - `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "10", "name": "tenant-10", "tenantReplicationJobId": "0"}`, + `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "10", "name": "tenant-10", "serviceMode": "NONE", "tenantReplicationJobId": "0"}`, }, }, ) @@ -7006,7 +7006,7 @@ func TestBackupRestoreTenant(t *testing.T) { `1`, `true`, `system`, - `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "tenantReplicationJobId": "0"}`, + `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "serviceMode": "SHARED", "tenantReplicationJobId": "0"}`, }, }) restoreDB.Exec(t, `RESTORE TENANT 10 FROM 'nodelocal://1/t10'`) @@ -7017,13 +7017,13 @@ func TestBackupRestoreTenant(t *testing.T) { `1`, `true`, `system`, - `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "tenantReplicationJobId": "0"}`, + `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "serviceMode": "SHARED", "tenantReplicationJobId": "0"}`, }, { `10`, `true`, `tenant-10`, - `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "10", "name": "tenant-10", "tenantReplicationJobId": "0"}`, + `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "10", "name": "tenant-10", "serviceMode": "NONE", "tenantReplicationJobId": "0"}`, }, }, ) @@ -7050,7 +7050,7 @@ func TestBackupRestoreTenant(t *testing.T) { `1`, `true`, `system`, - `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "tenantReplicationJobId": "0"}`, + `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "serviceMode": "SHARED", "tenantReplicationJobId": "0"}`, }, }) restoreDB.Exec(t, `RESTORE TENANT 10 FROM 'nodelocal://1/clusterwide'`) @@ -7061,13 +7061,13 @@ func TestBackupRestoreTenant(t *testing.T) { `1`, `true`, `system`, - `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "tenantReplicationJobId": "0"}`, + `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "serviceMode": "SHARED", "tenantReplicationJobId": "0"}`, }, { `10`, `true`, `tenant-10`, - `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "10", "name": "tenant-10", "tenantReplicationJobId": "0"}`, + `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "10", "name": "tenant-10", "serviceMode": "NONE", "tenantReplicationJobId": "0"}`, }, }, ) @@ -7105,7 +7105,7 @@ func TestBackupRestoreTenant(t *testing.T) { `1`, `true`, `system`, - `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "tenantReplicationJobId": "0"}`, + `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "serviceMode": "SHARED", "tenantReplicationJobId": "0"}`, }, }) restoreDB.Exec(t, `RESTORE FROM 'nodelocal://1/clusterwide'`) @@ -7115,25 +7115,25 @@ func TestBackupRestoreTenant(t *testing.T) { { `1`, `true`, `system`, - `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "tenantReplicationJobId": "0"}`, + `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "serviceMode": "SHARED", "tenantReplicationJobId": "0"}`, }, { `10`, `true`, `tenant-10`, - `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "10", "name": "tenant-10", "tenantReplicationJobId": "0"}`, + `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "10", "name": "tenant-10", "serviceMode": "NONE", "tenantReplicationJobId": "0"}`, }, { `11`, `true`, `tenant-11`, - `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "11", "name": "tenant-11", "tenantReplicationJobId": "0"}`, + `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "11", "name": "tenant-11", "serviceMode": "NONE", "tenantReplicationJobId": "0"}`, }, { `20`, `true`, `tenant-20`, - `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "20", "name": "tenant-20", "tenantReplicationJobId": "0"}`, + `{"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "20", "name": "tenant-20", "serviceMode": "NONE", "tenantReplicationJobId": "0"}`, }, }, ) diff --git a/pkg/ccl/backupccl/restore_job.go b/pkg/ccl/backupccl/restore_job.go index 669b337667b9..8d9a292deb7b 100644 --- a/pkg/ccl/backupccl/restore_job.go +++ b/pkg/ccl/backupccl/restore_job.go @@ -1198,18 +1198,19 @@ func createImportingDescriptors( if err != nil { return err } - for _, tenant := range details.Tenants { - switch tenant.DataState { + for _, tenantInfoCopy := range details.Tenants { + switch tenantInfoCopy.DataState { case descpb.TenantInfo_READY: // If the tenant was backed up in the `READY` state then we create // the restored record in an `ADD` state and mark it `READY` at // the end of the restore. - tenant.DataState = descpb.TenantInfo_ADD + tenantInfoCopy.ServiceMode = descpb.TenantInfo_NONE + tenantInfoCopy.DataState = descpb.TenantInfo_ADD case descpb.TenantInfo_DROP, descpb.TenantInfo_ADD: // If the tenant was backed up in a `DROP` or `ADD` state then we must // create the restored tenant record in that state as well. default: - return errors.AssertionFailedf("unknown tenant data state %v", tenant) + return errors.AssertionFailedf("unknown tenant data state %v", tenantInfoCopy) } spanConfigs := p.ExecCfg().SpanConfigKVAccessor.WithTxn(ctx, txn.KV()) if _, err := sql.CreateTenantRecord( @@ -1218,7 +1219,7 @@ func createImportingDescriptors( p.ExecCfg().Settings, txn, spanConfigs, - &tenant, + &tenantInfoCopy, initialTenantZoneConfig, ); err != nil { return err @@ -2197,7 +2198,7 @@ func (r *restoreResumer) publishDescriptors( // the tenant as the final step of the restore. The tenant has already // been created at an earlier stage in the restore in an `ADD` state. if err := sql.ActivateTenant( - ctx, r.execCfg.Settings, r.execCfg.Codec, txn, tenant.ID, + ctx, r.execCfg.Settings, r.execCfg.Codec, txn, tenant.ID, tenant.ServiceMode, ); err != nil { return err } diff --git a/pkg/ccl/backupccl/testdata/backup-restore/restore-tenants b/pkg/ccl/backupccl/testdata/backup-restore/restore-tenants index 4d492962e230..6c6ef56ab9f5 100644 --- a/pkg/ccl/backupccl/testdata/backup-restore/restore-tenants +++ b/pkg/ccl/backupccl/testdata/backup-restore/restore-tenants @@ -15,15 +15,15 @@ SELECT crdb_internal.create_tenant(6); # Drop one of them. exec-sql -DROP TENANT [5] +ALTER TENANT [5] STOP SERVICE; DROP TENANT [5] ---- query-sql SELECT id,active,crdb_internal.pb_to_json('cockroach.sql.sqlbase.TenantInfo', info, true) FROM system.tenants; ---- -1 true {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "tenantReplicationJobId": "0"} -5 false {"capabilities": {"canAdminSplit": false}, "dataState": "DROP", "droppedName": "tenant-5", "id": "5", "name": "", "tenantReplicationJobId": "0"} -6 true {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "6", "name": "tenant-6", "tenantReplicationJobId": "0"} +1 true {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "serviceMode": "SHARED", "tenantReplicationJobId": "0"} +5 false {"capabilities": {"canAdminSplit": false}, "dataState": "DROP", "droppedName": "tenant-5", "id": "5", "name": "", "serviceMode": "NONE", "tenantReplicationJobId": "0"} +6 true {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "6", "name": "tenant-6", "serviceMode": "EXTERNAL", "tenantReplicationJobId": "0"} exec-sql BACKUP INTO 'nodelocal://1/cluster' @@ -49,9 +49,9 @@ RESTORE FROM LATEST IN 'nodelocal://1/cluster' query-sql SELECT id,active,crdb_internal.pb_to_json('cockroach.sql.sqlbase.TenantInfo', info, true) FROM system.tenants; ---- -1 true {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "tenantReplicationJobId": "0"} -5 false {"capabilities": {"canAdminSplit": false}, "dataState": "DROP", "droppedName": "tenant-5", "id": "5", "name": "tenant-5", "tenantReplicationJobId": "0"} -6 true {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "6", "name": "tenant-6", "tenantReplicationJobId": "0"} +1 true {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "serviceMode": "SHARED", "tenantReplicationJobId": "0"} +5 false {"capabilities": {"canAdminSplit": false}, "dataState": "DROP", "droppedName": "tenant-5", "id": "5", "name": "tenant-5", "serviceMode": "NONE", "tenantReplicationJobId": "0"} +6 true {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "6", "name": "tenant-6", "serviceMode": "EXTERNAL", "tenantReplicationJobId": "0"} exec-sql expect-error-regex=(tenant 6 already exists) RESTORE TENANT 6 FROM LATEST IN 'nodelocal://1/tenant6'; @@ -75,7 +75,7 @@ RESTORE TENANT 6 FROM LATEST IN 'nodelocal://1/tenant6' WITH tenant_name = 'newn query-sql SELECT id,active,crdb_internal.pb_to_json('cockroach.sql.sqlbase.TenantInfo', info, true) FROM system.tenants; ---- -1 true {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "tenantReplicationJobId": "0"} -2 true {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "2", "name": "newname", "tenantReplicationJobId": "0"} -5 false {"capabilities": {"canAdminSplit": false}, "dataState": "DROP", "droppedName": "tenant-5", "id": "5", "name": "tenant-5", "tenantReplicationJobId": "0"} -6 true {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "6", "name": "tenant-6", "tenantReplicationJobId": "0"} +1 true {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "serviceMode": "SHARED", "tenantReplicationJobId": "0"} +2 true {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "2", "name": "newname", "serviceMode": "EXTERNAL", "tenantReplicationJobId": "0"} +5 false {"capabilities": {"canAdminSplit": false}, "dataState": "DROP", "droppedName": "tenant-5", "id": "5", "name": "tenant-5", "serviceMode": "NONE", "tenantReplicationJobId": "0"} +6 true {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "6", "name": "tenant-6", "serviceMode": "EXTERNAL", "tenantReplicationJobId": "0"} diff --git a/pkg/ccl/logictestccl/testdata/logic_test/tenant_usage b/pkg/ccl/logictestccl/testdata/logic_test/tenant_usage index de87c68c971f..ba4b826bb912 100644 --- a/pkg/ccl/logictestccl/testdata/logic_test/tenant_usage +++ b/pkg/ccl/logictestccl/testdata/logic_test/tenant_usage @@ -20,6 +20,7 @@ SELECT crdb_internal.update_tenant_resource_limits(5, 1000, 100, 0, now(), 0) # Note this marks the tenant as dropped. The GC will not delete the tenant # until after the ttl expires. statement ok +ALTER TENANT [5] STOP SERVICE; DROP TENANT [5] query error tenant "5" is not active diff --git a/pkg/ccl/sqlproxyccl/tenant/directory_cache_test.go b/pkg/ccl/sqlproxyccl/tenant/directory_cache_test.go index 312dd33e9ba7..0d34b9943cfd 100644 --- a/pkg/ccl/sqlproxyccl/tenant/directory_cache_test.go +++ b/pkg/ccl/sqlproxyccl/tenant/directory_cache_test.go @@ -405,14 +405,19 @@ func createTenant(tc serverutils.TestClusterInterface, id roachpb.TenantID) erro func destroyTenant(tc serverutils.TestClusterInterface, id roachpb.TenantID) error { srv := tc.Server(0) conn := srv.InternalExecutor().(*sql.InternalExecutor) - if _, err := conn.Exec( - context.Background(), - "testserver-destroy-tenant", - nil, /* txn */ - "DROP TENANT [$1] IMMEDIATE", - id.ToUint64(), - ); err != nil { - return err + for _, stmt := range []string{ + `ALTER TENANT [$1] STOP SERVICE`, + `DROP TENANT [$1] IMMEDIATE`, + } { + if _, err := conn.Exec( + context.Background(), + "testserver-destroy-tenant", + nil, /* txn */ + stmt, + id.ToUint64(), + ); err != nil { + return err + } } return nil } diff --git a/pkg/ccl/streamingccl/streamingest/replication_stream_e2e_test.go b/pkg/ccl/streamingccl/streamingest/replication_stream_e2e_test.go index 2bd2cc885ad8..afe332ee9c82 100644 --- a/pkg/ccl/streamingccl/streamingest/replication_stream_e2e_test.go +++ b/pkg/ccl/streamingccl/streamingest/replication_stream_e2e_test.go @@ -925,6 +925,7 @@ func TestTenantStreamingShowTenant(t *testing.T) { id int dest string status string + serviceMode string source string sourceUri string jobId int @@ -933,10 +934,11 @@ func TestTenantStreamingShowTenant(t *testing.T) { cutoverTime []byte // should be nil ) row := c.DestSysSQL.QueryRow(t, fmt.Sprintf("SHOW TENANT %s WITH REPLICATION STATUS", args.DestTenantName)) - row.Scan(&id, &dest, &status, &source, &sourceUri, &jobId, &maxReplTime, &protectedTime, &cutoverTime) + row.Scan(&id, &dest, &status, &serviceMode, &source, &sourceUri, &jobId, &maxReplTime, &protectedTime, &cutoverTime) require.Equal(t, 2, id) require.Equal(t, "destination", dest) require.Equal(t, "REPLICATING", status) + require.Equal(t, "NONE", serviceMode) require.Equal(t, "source", source) require.Equal(t, c.SrcURL.String(), sourceUri) require.Equal(t, ingestionJobID, jobId) diff --git a/pkg/ccl/streamingccl/streamingest/stream_ingestion_planning.go b/pkg/ccl/streamingccl/streamingest/stream_ingestion_planning.go index 31225fd2d02b..c14f7f6e5bdf 100644 --- a/pkg/ccl/streamingccl/streamingest/stream_ingestion_planning.go +++ b/pkg/ccl/streamingccl/streamingest/stream_ingestion_planning.go @@ -149,13 +149,14 @@ func ingestionPlanHook( sourceTenant, dstTenantName, dstTenantID) } - // Create a new tenant for the replication stream + // Create a new tenant for the replication stream. jobID := p.ExecCfg().JobRegistry.MakeJobID() tenantInfo := &descpb.TenantInfoWithUsage{ TenantInfo: descpb.TenantInfo{ // dstTenantID may be zero which will cause auto-allocation. ID: dstTenantID, DataState: descpb.TenantInfo_ADD, + ServiceMode: descpb.TenantInfo_NONE, Name: roachpb.TenantName(dstTenantName), TenantReplicationJobID: jobID, }, diff --git a/pkg/ccl/streamingccl/streamingest/testdata/simple b/pkg/ccl/streamingccl/streamingest/testdata/simple index 85878eae177d..40b460769103 100644 --- a/pkg/ccl/streamingccl/streamingest/testdata/simple +++ b/pkg/ccl/streamingccl/streamingest/testdata/simple @@ -28,14 +28,14 @@ IMPORT INTO d.x CSV DATA ('userfile:///dx/export*-n*.0.csv'); query-sql as=source-system SHOW TENANTS ---- -1 system READY -10 source READY +1 system READY SHARED +10 source READY NONE query-sql as=destination-system SHOW TENANTS ---- -1 system READY -2 destination REPLICATING +1 system READY SHARED +2 destination REPLICATING NONE let $ts as=source-system SELECT clock_timestamp()::timestamp::string diff --git a/pkg/ccl/testccl/sqlccl/tenant_gc_test.go b/pkg/ccl/testccl/sqlccl/tenant_gc_test.go index 8a86c8fc5413..0dc3ae258683 100644 --- a/pkg/ccl/testccl/sqlccl/tenant_gc_test.go +++ b/pkg/ccl/testccl/sqlccl/tenant_gc_test.go @@ -121,7 +121,9 @@ func TestGCTenantRemovesSpanConfigs(t *testing.T) { ) error { return sql.TestingUpdateTenantRecord( ctx, ts.ClusterSettings(), txn, - &descpb.TenantInfo{ID: tenantID.ToUint64(), DataState: descpb.TenantInfo_DROP}, + &descpb.TenantInfo{ID: tenantID.ToUint64(), + ServiceMode: descpb.TenantInfo_NONE, + DataState: descpb.TenantInfo_DROP}, ) })) diff --git a/pkg/clusterversion/cockroach_versions.go b/pkg/clusterversion/cockroach_versions.go index 92bb5cc6d747..1a5c1fa945ff 100644 --- a/pkg/clusterversion/cockroach_versions.go +++ b/pkg/clusterversion/cockroach_versions.go @@ -401,6 +401,9 @@ const ( // chagnefeeds created prior to this version. V23_1_ChangefeedExpressionProductionReady + // V23_1TenantServiceMode marks the introduction of tenant service modes. + V23_1TenantServiceMode + // ************************************************* // Step (1): Add new versions here. // Do not add new versions to a patch release. @@ -687,6 +690,10 @@ var rawVersionsSingleton = keyedVersions{ Key: V23_1_ChangefeedExpressionProductionReady, Version: roachpb.Version{Major: 22, Minor: 2, Internal: 30}, }, + { + Key: V23_1TenantServiceMode, + Version: roachpb.Version{Major: 22, Minor: 2, Internal: 32}, + }, // ************************************************* // Step (2): Add new versions here. diff --git a/pkg/sql/BUILD.bazel b/pkg/sql/BUILD.bazel index 092775939373..1c2f7eb5eff7 100644 --- a/pkg/sql/BUILD.bazel +++ b/pkg/sql/BUILD.bazel @@ -244,6 +244,7 @@ go_library( "tenant_creation.go", "tenant_deletion.go", "tenant_gc.go", + "tenant_service.go", "tenant_settings.go", "tenant_spec.go", "tenant_update.go", diff --git a/pkg/sql/catalog/bootstrap/metadata.go b/pkg/sql/catalog/bootstrap/metadata.go index 101e3b9d60cf..89cea15be93c 100644 --- a/pkg/sql/catalog/bootstrap/metadata.go +++ b/pkg/sql/catalog/bootstrap/metadata.go @@ -561,9 +561,10 @@ func addSystemDatabaseToSchema( // system tenant entry. func addSystemTenantEntry(target *MetadataSchema) { info := descpb.TenantInfo{ - ID: roachpb.SystemTenantID.ToUint64(), - Name: catconstants.SystemTenantName, - DataState: descpb.TenantInfo_READY, + ID: roachpb.SystemTenantID.ToUint64(), + Name: catconstants.SystemTenantName, + DataState: descpb.TenantInfo_READY, + ServiceMode: descpb.TenantInfo_SHARED, } infoBytes, err := protoutil.Marshal(&info) if err != nil { @@ -583,6 +584,7 @@ func addSystemTenantEntry(target *MetadataSchema) { tree.MakeDBool(true), tree.NewDBytes(tree.DBytes(infoBytes)), tree.NewDString(string(info.Name)), + tree.NewDString(strings.ToLower(info.ServiceMode.String())), ) if err != nil { panic(err) diff --git a/pkg/sql/catalog/bootstrap/testdata/testdata b/pkg/sql/catalog/bootstrap/testdata/testdata index 310380cd1e3c..36082e5abec2 100644 --- a/pkg/sql/catalog/bootstrap/testdata/testdata +++ b/pkg/sql/catalog/bootstrap/testdata/testdata @@ -1,4 +1,4 @@ -system hash=0520333938dc6f1d408132c88fff8e23fadbb41a96c6cf10e6143790b53a80db +system hash=489be910036d0ef40a9633b52cae7f852b5f9f7f7d95bfea6a50f0ff5521c430 ---- [{"key":"04646573632d696467656e","value":"01c801"} ,{"key":"8b"} @@ -8,7 +8,7 @@ system hash=0520333938dc6f1d408132c88fff8e23fadbb41a96c6cf10e6143790b53a80db ,{"key":"8b898d8a89","value":"030aea020a057a6f6e65731805200128013a0042270a02696410011a0c08011040180030005014600020003000680070007800800100880100980100422b0a06636f6e66696710021a0c08081000180030005011600020013000680070007800800100880100980100480352630a077072696d61727910011801220269642a06636f6e666967300140004a10080010001a00200028003000380040005a0070027a0408002000800100880100900104980101a20106080012001800a80100b20100ba0100c00100c80100d00101e0010060026a250a0d0a0561646d696e10e00318e0030a0c0a04726f6f7410e00318e00312046e6f64651802800101880103980100b201130a077072696d61727910001a02696420012800b2011c0a0c66616d5f325f636f6e66696710021a06636f6e66696720022802b80103c20100e80100f2010408001200f801008002009202009a0200b20200b80200c0021dc80200e00200800300880302a80300b00300"} ,{"key":"8b898e8a89","value":"030aa9040a0873657474696e67731806200128013a0042290a046e616d6510011a0c08071000180030005019600020003000680070007800800100880100980100422a0a0576616c756510021a0c0807100018003000501960002000300068007000780080010088010098010042440a0b6c6173745570646174656410031a0d080510001800300050da08600020002a116e6f7728293a3a3a54494d455354414d503000680070007800800100880100980100422e0a0976616c75655479706510041a0c0807100018003000501960002001300068007000780080010088010098010048055280010a077072696d6172791001180122046e616d652a0576616c75652a0b6c617374557064617465642a0976616c756554797065300140004a10080010001a00200028003000380040005a007002700370047a0408002000800100880100900104980101a20106080012001800a80100b20100ba0100c00100c80100d00101e0010060026a250a0d0a0561646d696e10e00318e0030a0c0a04726f6f7410e00318e00312046e6f64651802800101880103980100b201590a2666616d5f305f6e616d655f76616c75655f6c617374557064617465645f76616c75655479706510001a046e616d651a0576616c75651a0b6c617374557064617465641a0976616c75655479706520012002200320042800b80101c20100e80100f2010408001200f801008002009202009a0200b20200b80200c0021dc80200e00200800300880302a80300b00300"} ,{"key":"8b898f8a89","value":"030ac4020a1164657363726970746f725f69645f7365711807200128013a00422a0a0576616c756510011a0c080110401800300050146000200030006800700078008001008801009801004800525c0a077072696d61727910011800220576616c7565300140004a10080010001a00200028003000380040005a007a0408002000800100880100900104980101a20106080012001800a80100b20100ba0100c00100c80100d00100e0010060006a210a0b0a0561646d696e102018200a0a0a04726f6f741020182012046e6f64651802800100880103980100b201160a077072696d61727910001a0576616c756520012801b80100c20100e2011c0801100118ffffffffffffffff7f2001280032040800100038014200e80100f2010408001200f801008002009202009a0200b20200b80200c0021dc80200e00200800300880300a80300b00300"} -,{"key":"8b89908a89","value":"030a87050a0774656e616e74731808200128013a0042270a02696410011a0c0801104018003000501460002000300068007000780080010088010098010042310a0661637469766510021a0c08001000180030005010600020002a0474727565300068007000780080010088010098010042290a04696e666f10031a0c080810001800300050116000200130006800700078008001008801009801004288010a046e616d6510041a0c080710001800300050196000200130005a5d637264625f696e7465726e616c2e70625f746f5f6a736f6e2827636f636b726f6163682e73716c2e73716c626173652e54656e616e74496e666f273a3a3a535452494e472c20696e666f292d3e3e276e616d65273a3a3a535452494e476800700078008001018801009801004805526b0a077072696d61727910011801220269642a066163746976652a04696e666f300140004a10080010001a00200028003000380040005a00700270037a0408002000800100880100900104980101a20106080012001800a80100b20100ba0100c00100c80100d00102e001005a660a1074656e616e74735f6e616d655f6964781002180122046e616d653004380140004a10080010001a00200028003000380040005a007a0408002000800100880100900103980100a20106080012001800a80100b20100ba0100c00100c80100d00101e0010060036a210a0b0a0561646d696e102018200a0a0a04726f6f741020182012046e6f64651802800101880103980100b201250a077072696d61727910001a0269641a066163746976651a04696e666f2001200220032800b80101c20100e80100f2010408001200f801008002009202009a0200b20200b80200c0021dc80200e00200800300880303a80300b00300"} +,{"key":"8b89908a89","value":"030aa0070a0774656e616e74731808200128013a0042270a02696410011a0c0801104018003000501460002000300068007000780080010088010098010042310a0661637469766510021a0c08001000180030005010600020002a0474727565300068007000780080010088010098010042290a04696e666f10031a0c080810001800300050116000200130006800700078008001008801009801004288010a046e616d6510041a0c080710001800300050196000200130005a5d637264625f696e7465726e616c2e70625f746f5f6a736f6e2827636f636b726f6163682e73716c2e73716c626173652e54656e616e74496e666f273a3a3a535452494e472c20696e666f292d3e3e276e616d65273a3a3a535452494e47680070007800800101880100980100429e010a0c736572766963655f6d6f646510051a0c080710001800300050196000200130005a6b6c6f77657228637264625f696e7465726e616c2e70625f746f5f6a736f6e2827636f636b726f6163682e73716c2e73716c626173652e54656e616e74496e666f273a3a3a535452494e472c20696e666f292d3e3e27736572766963654d6f6465273a3a3a535452494e47296800700078008001018801009801004806526b0a077072696d61727910011801220269642a066163746976652a04696e666f300140004a10080010001a00200028003000380040005a00700270037a0408002000800100880100900104980101a20106080012001800a80100b20100ba0100c00100c80100d00102e001005a660a1074656e616e74735f6e616d655f6964781002180122046e616d653004380140004a10080010001a00200028003000380040005a007a0408002000800100880100900103980100a20106080012001800a80100b20100ba0100c00100c80100d00101e001005a760a1874656e616e74735f736572766963655f6d6f64655f69647810031800220c736572766963655f6d6f64653005380140004a10080010001a00200028003000380040005a007a0408002000800100880100900103980100a20106080012001800a80100b20100ba0100c00100c80100d00100e0010060046a210a0b0a0561646d696e102018200a0a0a04726f6f741020182012046e6f64651802800101880103980100b201250a077072696d61727910001a0269641a066163746976651a04696e666f2001200220032800b80101c20100e80100f2010408001200f801008002009202009a0200b20200b80200c0021dc80200e00200800300880303a80300b00300"} ,{"key":"8b89938a89","value":"030afa030a056c65617365180b200128013a00422b0a0664657363494410011a0c08011040180030005014600020003000680070007800800100880100980100422c0a0776657273696f6e10021a0c08011040180030005014600020003000680070007800800100880100980100422b0a066e6f6465494410031a0c0801104018003000501460002000300068007000780080010088010098010042300a0a65787069726174696f6e10041a0d080510001800300050da0860002000300068007000780080010088010098010048055286010a077072696d617279100118012206646573634944220776657273696f6e220a65787069726174696f6e22066e6f64654944300130023004300340004000400040004a10080010001a00200028003000380040005a007a0408002000800100880100900104980101a20106080012001800a80100b20100ba0100c00100c80100d00101e0010060026a250a0d0a0561646d696e10e00318e0030a0c0a04726f6f7410e00318e00312046e6f64651802800101880103980100b2013a0a077072696d61727910001a066465736349441a0776657273696f6e1a066e6f646549441a0a65787069726174696f6e20012002200320042800b80101c20100e80100f2010408001200f801008002009202009a0200b20200b80200c0021dc80200e00200800300880302a80300b00300"} ,{"key":"8b89948a89","value":"030afa050a086576656e746c6f67180c200128013a00422f0a0974696d657374616d7010011a0d080510001800300050da08600020003000680070007800800100880100980100422e0a096576656e745479706510021a0c08071000180030005019600020003000680070007800800100880100980100422d0a08746172676574494410031a0c0801104018003000501460002000300068007000780080010088010098010042300a0b7265706f7274696e67494410041a0c0801104018003000501460002000300068007000780080010088010098010042290a04696e666f10051a0c0807100018003000501960002001300068007000780080010088010098010042380a08756e69717565494410061a0c08081000180030005011600020002a09757569645f7634282930006800700078008001008801009801004807529e010a077072696d61727910011801220974696d657374616d702208756e6971756549442a096576656e74547970652a0874617267657449442a0b7265706f7274696e6749442a04696e666f30013006400040004a10080010001a00200028003000380040005a0070027003700470057a0408002000800100880100900104980101a20106080012001800a80100b20100ba0100c00100c80100d00101e0010060026a250a0d0a0561646d696e10e00318e0030a0c0a04726f6f7410e00318e00312046e6f64651802800101880103980100b201260a077072696d61727910001a0974696d657374616d701a08756e697175654944200120062800b201220a0f66616d5f325f6576656e745479706510021a096576656e745479706520022802b201200a0e66616d5f335f746172676574494410031a08746172676574494420032803b201260a1166616d5f345f7265706f7274696e67494410041a0b7265706f7274696e67494420042804b201180a0a66616d5f355f696e666f10051a04696e666f20052805b80106c20100e80100f2010408001200f801008002009202009a0200b20200b80200c0021dc80200e00200800300880302a80300b00300"} ,{"key":"8b89958a89","value":"030ad9060a0872616e67656c6f67180d200128013a00422f0a0974696d657374616d7010011a0d080510001800300050da08600020003000680070007800800100880100980100422c0a0772616e6765494410021a0c08011040180030005014600020003000680070007800800100880100980100422c0a0773746f7265494410031a0c08011040180030005014600020003000680070007800800100880100980100422e0a096576656e745479706510041a0c0807100018003000501960002000300068007000780080010088010098010042310a0c6f7468657252616e6765494410051a0c0801104018003000501460002001300068007000780080010088010098010042290a04696e666f10061a0c08071000180030005019600020013000680070007800800100880100980100423d0a08756e69717565494410071a0c08011040180030005014600020002a0e756e697175655f726f77696428293000680070007800800100880100980100480852a9010a077072696d61727910011801220974696d657374616d702208756e6971756549442a0772616e676549442a0773746f726549442a096576656e74547970652a0c6f7468657252616e676549442a04696e666f30013007400040004a10080010001a00200028003000380040005a00700270037004700570067a0408002000800100880100900104980101a20106080012001800a80100b20100ba0100c00100c80100d00101e0010060026a250a0d0a0561646d696e10e00318e0030a0c0a04726f6f7410e00318e00312046e6f64651802800101880103980100b201260a077072696d61727910001a0974696d657374616d701a08756e697175654944200120072800b2011e0a0d66616d5f325f72616e6765494410021a0772616e6765494420022802b2011e0a0d66616d5f335f73746f7265494410031a0773746f7265494420032803b201220a0f66616d5f345f6576656e745479706510041a096576656e745479706520042804b201280a1266616d5f355f6f7468657252616e6765494410051a0c6f7468657252616e6765494420052805b201180a0a66616d5f365f696e666f10061a04696e666f20062806b80107c20100e80100f2010408001200f801008002009202009a0200b20200b80200c0021dc80200e00200800300880302a80300b00300"} @@ -59,8 +59,9 @@ system hash=0520333938dc6f1d408132c88fff8e23fadbb41a96c6cf10e6143790b53a80db ,{"key":"8f"} ,{"key":"8f898888","value":"01c801"} ,{"key":"90"} -,{"key":"90898988","value":"0a2a1612080110001a0673797374656d220028003200"} +,{"key":"90898988","value":"0a2a1614080110001a0673797374656d2200280032003802"} ,{"key":"908a1273797374656d000188","value":"0389"} +,{"key":"908b1273686172656400018988","value":"03"} ,{"key":"93"} ,{"key":"94"} ,{"key":"95"} diff --git a/pkg/sql/catalog/colinfo/result_columns.go b/pkg/sql/catalog/colinfo/result_columns.go index 3f75744c281f..a1c70b37bed8 100644 --- a/pkg/sql/catalog/colinfo/result_columns.go +++ b/pkg/sql/catalog/colinfo/result_columns.go @@ -279,7 +279,8 @@ var ExportColumns = ResultColumns{ var TenantColumns = ResultColumns{ {Name: "id", Typ: types.Int}, {Name: "name", Typ: types.String}, - {Name: "status", Typ: types.String}, + {Name: "data_status", Typ: types.String}, + {Name: "service_mode", Typ: types.String}, } // TenantColumnsWithReplication is appended to TenantColumns for diff --git a/pkg/sql/catalog/descpb/tenant.proto b/pkg/sql/catalog/descpb/tenant.proto index 98c0ddfb3ace..c1c897c4bf92 100644 --- a/pkg/sql/catalog/descpb/tenant.proto +++ b/pkg/sql/catalog/descpb/tenant.proto @@ -32,6 +32,20 @@ message TenantInfo { DROP = 2; } + // The way the tenant can be served to clients. + enum ServiceMode { + // Service allowed using separate processes. + // This is the default value for backward-compatibility with + // records created for CockroachCloud Serverless pre-v23.1. + EXTERNAL = 0; + // No service allowed. + NONE = 1; + // Service allowed using shared-process multitenancy. + // This mode causes KV nodes to spontaneously start the SQL service + // for the tenant. + SHARED = 2; + } + // ID is the internal numeric identifier of the tenant. // It remains mostly invisible to clients. optional uint64 id = 1 [(gogoproto.nullable) = false, (gogoproto.customname) = "ID"]; @@ -65,6 +79,11 @@ message TenantInfo { optional cockroach.multitenant.tenantcapabilitiespb.TenantCapabilities capabilities = 6 [ (gogoproto.nullable) = false ]; + + // ServiceMode determines how the tenant should be served. + // Beware that the column name is "fixed" + // because it is referred to in a virtual column definition in system.tenants. + optional ServiceMode service_mode = 7 [(gogoproto.nullable) = false]; } // TenantInfoAndUsage contains the information for a tenant in a multi-tenant diff --git a/pkg/sql/catalog/systemschema/system.go b/pkg/sql/catalog/systemschema/system.go index 1bb8e14fbcbe..0c7f433e6acf 100644 --- a/pkg/sql/catalog/systemschema/system.go +++ b/pkg/sql/catalog/systemschema/system.go @@ -100,16 +100,19 @@ CREATE TABLE system.settings ( DescIDSequenceSchema = ` CREATE SEQUENCE system.descriptor_id_seq;` - tenantNameComputeExpr = `crdb_internal.pb_to_json('cockroach.sql.sqlbase.TenantInfo':::STRING, info)->>'name':::STRING` - TenantsTableSchema = ` + tenantNameComputeExpr = `crdb_internal.pb_to_json('cockroach.sql.sqlbase.TenantInfo':::STRING, info)->>'name':::STRING` + tenantServiceModeComputeExpr = `lower(crdb_internal.pb_to_json('cockroach.sql.sqlbase.TenantInfo':::STRING, info)->>'serviceMode':::STRING)` + TenantsTableSchema = ` CREATE TABLE system.tenants ( - id INT8 NOT NULL, - active BOOL NOT NULL DEFAULT true, - info BYTES, - name STRING GENERATED ALWAYS AS (` + tenantNameComputeExpr + `) VIRTUAL, + id INT8 NOT NULL, + active BOOL NOT NULL DEFAULT true, + info BYTES, + name STRING GENERATED ALWAYS AS (` + tenantNameComputeExpr + `) VIRTUAL, + service_mode STRING GENERATED ALWAYS AS (` + tenantServiceModeComputeExpr + `) VIRTUAL, CONSTRAINT "primary" PRIMARY KEY (id), FAMILY "primary" (id, active, info), - UNIQUE INDEX tenants_name_idx (name ASC) + UNIQUE INDEX tenants_name_idx (name ASC), + INDEX tenants_service_mode_idx (service_mode ASC) );` // RoleIDSequenceSchema starts at 100 so we have reserved IDs for special @@ -121,6 +124,7 @@ CREATE SEQUENCE system.role_id_seq START 100 MINVALUE 100 MAXVALUE 2147483647;` ) var tenantNameComputeExprStr = tenantNameComputeExpr +var tenantServiceModeComputeExprStr = tenantServiceModeComputeExpr var indexUsageComputeExprStr = indexUsageComputeExpr // These system tables are not part of the system config. @@ -1214,6 +1218,9 @@ var ( {Name: "name", ID: 4, Type: types.String, Nullable: true, Virtual: true, ComputeExpr: &tenantNameComputeExprStr}, + {Name: "service_mode", ID: 5, Type: types.String, Nullable: true, + Virtual: true, + ComputeExpr: &tenantServiceModeComputeExprStr}, }, []descpb.ColumnFamilyDescriptor{{ Name: "primary", @@ -1232,6 +1239,15 @@ var ( KeySuffixColumnIDs: []descpb.ColumnID{1}, Version: descpb.StrictIndexColumnIDGuaranteesVersion, }, + descpb.IndexDescriptor{ + Name: "tenants_service_mode_idx", + ID: 3, + KeyColumnNames: []string{"service_mode"}, + KeyColumnDirections: []catenumpb.IndexColumn_Direction{catenumpb.IndexColumn_ASC}, + KeyColumnIDs: []descpb.ColumnID{5}, + KeySuffixColumnIDs: []descpb.ColumnID{1}, + Version: descpb.StrictIndexColumnIDGuaranteesVersion, + }, )) ) diff --git a/pkg/sql/drop_tenant.go b/pkg/sql/drop_tenant.go index cc220bc6760a..93b20db14f4a 100644 --- a/pkg/sql/drop_tenant.go +++ b/pkg/sql/drop_tenant.go @@ -51,7 +51,7 @@ func (n *dropTenantNode) startExec(params runParams) error { } return err } - return params.p.DropTenantByID(params.ctx, tenInfo.ID, n.immediate) + return params.p.DropTenantByID(params.ctx, tenInfo.ID, n.immediate, false /* ignoreServiceMode */) } func (n *dropTenantNode) Next(_ runParams) (bool, error) { return false, nil } diff --git a/pkg/sql/faketreeeval/evalctx.go b/pkg/sql/faketreeeval/evalctx.go index 29c1ca3a830a..7a8b78060e5d 100644 --- a/pkg/sql/faketreeeval/evalctx.go +++ b/pkg/sql/faketreeeval/evalctx.go @@ -547,7 +547,7 @@ func (c *DummyTenantOperator) LookupTenantID( // DropTenantByID is part of the tree.TenantOperator interface. func (c *DummyTenantOperator) DropTenantByID( - ctx context.Context, tenantID uint64, synchronous bool, + ctx context.Context, tenantID uint64, synchronous, ignoreServiceMode bool, ) error { return errors.WithStack(errEvalTenant) } diff --git a/pkg/sql/gcjob_test/gc_job_test.go b/pkg/sql/gcjob_test/gc_job_test.go index 4b95b05b6c94..82977a7bcf00 100644 --- a/pkg/sql/gcjob_test/gc_job_test.go +++ b/pkg/sql/gcjob_test/gc_job_test.go @@ -493,7 +493,7 @@ func TestGCTenant(t *testing.T) { require.EqualError( t, gcClosure(dropTenID, progress), - `GC state for tenant id:11 data_state:DROP name:"tenant-11" dropped_name:"" tenant_replication_job_id:0 capabilities:<> is DELETED yet the tenant row still exists`, + `GC state for tenant id:11 data_state:DROP name:"tenant-11" dropped_name:"" tenant_replication_job_id:0 capabilities:<> service_mode:EXTERNAL is DELETED yet the tenant row still exists`, ) }) diff --git a/pkg/sql/logictest/testdata/logic_test/crdb_internal_catalog b/pkg/sql/logictest/testdata/logic_test/crdb_internal_catalog index 1c48df380a62..98240d20b1fc 100644 --- a/pkg/sql/logictest/testdata/logic_test/crdb_internal_catalog +++ b/pkg/sql/logictest/testdata/logic_test/crdb_internal_catalog @@ -109,7 +109,7 @@ SELECT id, strip_volatile(descriptor) FROM crdb_internal.kv_catalog_descriptor 5 {"table": {"columns": [{"id": 1, "name": "id", "type": {"family": "IntFamily", "oid": 20, "width": 64}}, {"id": 2, "name": "config", "nullable": true, "type": {"family": "BytesFamily", "oid": 17}}], "formatVersion": 3, "id": 5, "name": "zones", "nextColumnId": 3, "nextConstraintId": 2, "nextIndexId": 2, "nextMutationId": 1, "parentId": 1, "primaryIndex": {"constraintId": 1, "encodingType": 1, "foreignKey": {}, "geoConfig": {}, "id": 1, "interleave": {}, "keyColumnDirections": ["ASC"], "keyColumnIds": [1], "keyColumnNames": ["id"], "name": "primary", "partitioning": {}, "sharded": {}, "storeColumnIds": [2], "storeColumnNames": ["config"], "unique": true, "version": 4}, "privileges": {"ownerProto": "node", "users": [{"privileges": "480", "userProto": "admin", "withGrantOption": "480"}, {"privileges": "480", "userProto": "root", "withGrantOption": "480"}], "version": 2}, "replacementOf": {"time": {}}, "unexposedParentSchemaId": 29, "version": "1"}} 6 {"table": {"columns": [{"id": 1, "name": "name", "type": {"family": "StringFamily", "oid": 25}}, {"id": 2, "name": "value", "type": {"family": "StringFamily", "oid": 25}}, {"defaultExpr": "now():::TIMESTAMP", "id": 3, "name": "lastUpdated", "type": {"family": "TimestampFamily", "oid": 1114}}, {"id": 4, "name": "valueType", "nullable": true, "type": {"family": "StringFamily", "oid": 25}}], "formatVersion": 3, "id": 6, "name": "settings", "nextColumnId": 5, "nextConstraintId": 2, "nextIndexId": 2, "nextMutationId": 1, "parentId": 1, "primaryIndex": {"constraintId": 1, "encodingType": 1, "foreignKey": {}, "geoConfig": {}, "id": 1, "interleave": {}, "keyColumnDirections": ["ASC"], "keyColumnIds": [1], "keyColumnNames": ["name"], "name": "primary", "partitioning": {}, "sharded": {}, "storeColumnIds": [2, 3, 4], "storeColumnNames": ["value", "lastUpdated", "valueType"], "unique": true, "version": 4}, "privileges": {"ownerProto": "node", "users": [{"privileges": "480", "userProto": "admin", "withGrantOption": "480"}, {"privileges": "480", "userProto": "root", "withGrantOption": "480"}], "version": 2}, "replacementOf": {"time": {}}, "unexposedParentSchemaId": 29, "version": "1"}} 7 {"table": {"columns": [{"id": 1, "name": "value", "type": {"family": "IntFamily", "oid": 20, "width": 64}}], "formatVersion": 3, "id": 7, "name": "descriptor_id_seq", "parentId": 1, "primaryIndex": {"encodingType": 1, "foreignKey": {}, "geoConfig": {}, "id": 1, "interleave": {}, "keyColumnDirections": ["ASC"], "keyColumnIds": [1], "keyColumnNames": ["value"], "name": "primary", "partitioning": {}, "sharded": {}, "version": 4}, "privileges": {"ownerProto": "node", "users": [{"privileges": "32", "userProto": "admin", "withGrantOption": "32"}, {"privileges": "32", "userProto": "root", "withGrantOption": "32"}], "version": 2}, "replacementOf": {"time": {}}, "sequenceOpts": {"cacheSize": "1", "increment": "1", "maxValue": "9223372036854775807", "minValue": "1", "sequenceOwner": {}, "start": "1"}, "unexposedParentSchemaId": 29, "version": "1"}} -8 {"table": {"columns": [{"id": 1, "name": "id", "type": {"family": "IntFamily", "oid": 20, "width": 64}}, {"defaultExpr": "true", "id": 2, "name": "active", "type": {"oid": 16}}, {"id": 3, "name": "info", "nullable": true, "type": {"family": "BytesFamily", "oid": 17}}, {"computeExpr": "crdb_internal.pb_to_json('cockroach.sql.sqlbase.TenantInfo':::STRING, info)->>'name':::STRING", "id": 4, "name": "name", "nullable": true, "type": {"family": "StringFamily", "oid": 25}, "virtual": true}], "formatVersion": 3, "id": 8, "indexes": [{"constraintId": 1, "foreignKey": {}, "geoConfig": {}, "id": 2, "interleave": {}, "keyColumnDirections": ["ASC"], "keyColumnIds": [4], "keyColumnNames": ["name"], "keySuffixColumnIds": [1], "name": "tenants_name_idx", "partitioning": {}, "sharded": {}, "unique": true, "version": 3}], "name": "tenants", "nextColumnId": 5, "nextConstraintId": 3, "nextIndexId": 3, "nextMutationId": 1, "parentId": 1, "primaryIndex": {"constraintId": 2, "encodingType": 1, "foreignKey": {}, "geoConfig": {}, "id": 1, "interleave": {}, "keyColumnDirections": ["ASC"], "keyColumnIds": [1], "keyColumnNames": ["id"], "name": "primary", "partitioning": {}, "sharded": {}, "storeColumnIds": [2, 3], "storeColumnNames": ["active", "info"], "unique": true, "version": 4}, "privileges": {"ownerProto": "node", "users": [{"privileges": "32", "userProto": "admin", "withGrantOption": "32"}, {"privileges": "32", "userProto": "root", "withGrantOption": "32"}], "version": 2}, "replacementOf": {"time": {}}, "unexposedParentSchemaId": 29, "version": "1"}} +8 {"table": {"columns": [{"id": 1, "name": "id", "type": {"family": "IntFamily", "oid": 20, "width": 64}}, {"defaultExpr": "true", "id": 2, "name": "active", "type": {"oid": 16}}, {"id": 3, "name": "info", "nullable": true, "type": {"family": "BytesFamily", "oid": 17}}, {"computeExpr": "crdb_internal.pb_to_json('cockroach.sql.sqlbase.TenantInfo':::STRING, info)->>'name':::STRING", "id": 4, "name": "name", "nullable": true, "type": {"family": "StringFamily", "oid": 25}, "virtual": true}, {"computeExpr": "lower(crdb_internal.pb_to_json('cockroach.sql.sqlbase.TenantInfo':::STRING, info)->>'serviceMode'):::STRING", "id": 5, "name": "service_mode", "nullable": true, "type": {"family": "StringFamily", "oid": 25}, "virtual": true}], "formatVersion": 3, "id": 8, "indexes": [{"constraintId": 1, "foreignKey": {}, "geoConfig": {}, "id": 2, "interleave": {}, "keyColumnDirections": ["ASC"], "keyColumnIds": [4], "keyColumnNames": ["name"], "keySuffixColumnIds": [1], "name": "tenants_name_idx", "partitioning": {}, "sharded": {}, "unique": true, "version": 3}, {"foreignKey": {}, "geoConfig": {}, "id": 3, "interleave": {}, "keyColumnDirections": ["ASC"], "keyColumnIds": [5], "keyColumnNames": ["service_mode"], "keySuffixColumnIds": [1], "name": "tenants_service_mode_idx", "partitioning": {}, "sharded": {}, "version": 3}], "name": "tenants", "nextColumnId": 6, "nextConstraintId": 3, "nextIndexId": 4, "nextMutationId": 1, "parentId": 1, "primaryIndex": {"constraintId": 2, "encodingType": 1, "foreignKey": {}, "geoConfig": {}, "id": 1, "interleave": {}, "keyColumnDirections": ["ASC"], "keyColumnIds": [1], "keyColumnNames": ["id"], "name": "primary", "partitioning": {}, "sharded": {}, "storeColumnIds": [2, 3], "storeColumnNames": ["active", "info"], "unique": true, "version": 4}, "privileges": {"ownerProto": "node", "users": [{"privileges": "32", "userProto": "admin", "withGrantOption": "32"}, {"privileges": "32", "userProto": "root", "withGrantOption": "32"}], "version": 2}, "replacementOf": {"time": {}}, "unexposedParentSchemaId": 29, "version": "1"}} 11 {"table": {"columns": [{"id": 1, "name": "descID", "type": {"family": "IntFamily", "oid": 20, "width": 64}}, {"id": 2, "name": "version", "type": {"family": "IntFamily", "oid": 20, "width": 64}}, {"id": 3, "name": "nodeID", "type": {"family": "IntFamily", "oid": 20, "width": 64}}, {"id": 4, "name": "expiration", "type": {"family": "TimestampFamily", "oid": 1114}}], "formatVersion": 3, "id": 11, "name": "lease", "nextColumnId": 5, "nextConstraintId": 2, "nextIndexId": 2, "nextMutationId": 1, "parentId": 1, "primaryIndex": {"constraintId": 1, "encodingType": 1, "foreignKey": {}, "geoConfig": {}, "id": 1, "interleave": {}, "keyColumnDirections": ["ASC", "ASC", "ASC", "ASC"], "keyColumnIds": [1, 2, 4, 3], "keyColumnNames": ["descID", "version", "expiration", "nodeID"], "name": "primary", "partitioning": {}, "sharded": {}, "unique": true, "version": 4}, "privileges": {"ownerProto": "node", "users": [{"privileges": "480", "userProto": "admin", "withGrantOption": "480"}, {"privileges": "480", "userProto": "root", "withGrantOption": "480"}], "version": 2}, "replacementOf": {"time": {}}, "unexposedParentSchemaId": 29, "version": "1"}} 12 {"table": {"columns": [{"id": 1, "name": "timestamp", "type": {"family": "TimestampFamily", "oid": 1114}}, {"id": 2, "name": "eventType", "type": {"family": "StringFamily", "oid": 25}}, {"id": 3, "name": "targetID", "type": {"family": "IntFamily", "oid": 20, "width": 64}}, {"id": 4, "name": "reportingID", "type": {"family": "IntFamily", "oid": 20, "width": 64}}, {"id": 5, "name": "info", "nullable": true, "type": {"family": "StringFamily", "oid": 25}}, {"defaultExpr": "uuid_v4()", "id": 6, "name": "uniqueID", "type": {"family": "BytesFamily", "oid": 17}}], "formatVersion": 3, "id": 12, "name": "eventlog", "nextColumnId": 7, "nextConstraintId": 2, "nextIndexId": 2, "nextMutationId": 1, "parentId": 1, "primaryIndex": {"constraintId": 1, "encodingType": 1, "foreignKey": {}, "geoConfig": {}, "id": 1, "interleave": {}, "keyColumnDirections": ["ASC", "ASC"], "keyColumnIds": [1, 6], "keyColumnNames": ["timestamp", "uniqueID"], "name": "primary", "partitioning": {}, "sharded": {}, "storeColumnIds": [2, 3, 4, 5], "storeColumnNames": ["eventType", "targetID", "reportingID", "info"], "unique": true, "version": 4}, "privileges": {"ownerProto": "node", "users": [{"privileges": "480", "userProto": "admin", "withGrantOption": "480"}, {"privileges": "480", "userProto": "root", "withGrantOption": "480"}], "version": 2}, "replacementOf": {"time": {}}, "unexposedParentSchemaId": 29, "version": "1"}} 13 {"table": {"columns": [{"id": 1, "name": "timestamp", "type": {"family": "TimestampFamily", "oid": 1114}}, {"id": 2, "name": "rangeID", "type": {"family": "IntFamily", "oid": 20, "width": 64}}, {"id": 3, "name": "storeID", "type": {"family": "IntFamily", "oid": 20, "width": 64}}, {"id": 4, "name": "eventType", "type": {"family": "StringFamily", "oid": 25}}, {"id": 5, "name": "otherRangeID", "nullable": true, "type": {"family": "IntFamily", "oid": 20, "width": 64}}, {"id": 6, "name": "info", "nullable": true, "type": {"family": "StringFamily", "oid": 25}}, {"defaultExpr": "unique_rowid()", "id": 7, "name": "uniqueID", "type": {"family": "IntFamily", "oid": 20, "width": 64}}], "formatVersion": 3, "id": 13, "name": "rangelog", "nextColumnId": 8, "nextConstraintId": 2, "nextIndexId": 2, "nextMutationId": 1, "parentId": 1, "primaryIndex": {"constraintId": 1, "encodingType": 1, "foreignKey": {}, "geoConfig": {}, "id": 1, "interleave": {}, "keyColumnDirections": ["ASC", "ASC"], "keyColumnIds": [1, 7], "keyColumnNames": ["timestamp", "uniqueID"], "name": "primary", "partitioning": {}, "sharded": {}, "storeColumnIds": [2, 3, 4, 5, 6], "storeColumnNames": ["rangeID", "storeID", "eventType", "otherRangeID", "info"], "unique": true, "version": 4}, "privileges": {"ownerProto": "node", "users": [{"privileges": "480", "userProto": "admin", "withGrantOption": "480"}, {"privileges": "480", "userProto": "root", "withGrantOption": "480"}], "version": 2}, "replacementOf": {"time": {}}, "unexposedParentSchemaId": 29, "version": "1"}} diff --git a/pkg/sql/logictest/testdata/logic_test/information_schema b/pkg/sql/logictest/testdata/logic_test/information_schema index b71ba9af19f8..ab90b2ec3f29 100644 --- a/pkg/sql/logictest/testdata/logic_test/information_schema +++ b/pkg/sql/logictest/testdata/logic_test/information_schema @@ -2313,6 +2313,7 @@ system public tenants active system public tenants id 1 system public tenants info 3 system public tenants name 4 +system public tenants service_mode 5 system public transaction_statistics agg_interval 5 system public transaction_statistics aggregated_ts 1 system public transaction_statistics app_name 3 diff --git a/pkg/sql/logictest/testdata/logic_test/pg_catalog b/pkg/sql/logictest/testdata/logic_test/pg_catalog index 8d0e7c822ede..58f91d0534f7 100644 --- a/pkg/sql/logictest/testdata/logic_test/pg_catalog +++ b/pkg/sql/logictest/testdata/logic_test/pg_catalog @@ -1147,6 +1147,7 @@ indexrelid indrelid indnatts indisunique indnullsnotdistinct indisprimary 2148104569 21 2 true false true false true false true false false true false 1 2 3403232968 3403232968 0 0 2 2 NULL NULL 2 2268653844 40 4 true false true false true false true false false true false 1 2 3 4 0 0 0 0 0 0 0 0 2 2 2 2 NULL NULL 4 2361445172 8 1 true false true false true false true false false true false 1 0 0 2 NULL NULL 1 +2361445174 8 1 false false false false false false true false false true false 5 3403232968 0 2 NULL NULL 1 2361445175 8 1 true false false false true false true false false true false 4 3403232968 0 2 NULL NULL 1 2407840836 24 3 true false true false true false true false false true false 1 2 3 0 0 0 0 0 0 2 2 2 NULL NULL 3 2528390115 47 1 true false true false true false true false false true false 1 0 0 2 NULL NULL 1 @@ -1244,6 +1245,7 @@ indexrelid operator_argument_type_oid operator_argument_position 2268653844 0 3 2268653844 0 4 2361445172 0 1 +2361445174 0 1 2361445175 0 1 2407840836 0 1 2407840836 0 2 diff --git a/pkg/sql/logictest/testdata/logic_test/tenant b/pkg/sql/logictest/testdata/logic_test/tenant index 03988c096db2..e432630e101b 100644 --- a/pkg/sql/logictest/testdata/logic_test/tenant +++ b/pkg/sql/logictest/testdata/logic_test/tenant @@ -3,7 +3,7 @@ query IBIT colnames SELECT id, active, length(info), name FROM system.tenants ORDER BY id ---- id active length name -1 true 18 system +1 true 20 system # Create a few tenants. @@ -38,49 +38,49 @@ FROM system.tenants ORDER BY id ---- id active name crdb_internal.pb_to_json -1 true system {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "tenantReplicationJobId": "0"} -2 true tenant-one {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "2", "name": "tenant-one", "tenantReplicationJobId": "0"} -3 true two {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "3", "name": "two", "tenantReplicationJobId": "0"} -4 true three {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "4", "name": "three", "tenantReplicationJobId": "0"} +1 true system {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "serviceMode": "SHARED", "tenantReplicationJobId": "0"} +2 true tenant-one {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "2", "name": "tenant-one", "serviceMode": "NONE", "tenantReplicationJobId": "0"} +3 true two {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "3", "name": "two", "serviceMode": "NONE", "tenantReplicationJobId": "0"} +4 true three {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "4", "name": "three", "serviceMode": "NONE", "tenantReplicationJobId": "0"} -query ITT colnames +query ITTT colnames SHOW TENANT system ---- -id name status -1 system READY +id name data_status service_mode +1 system READY SHARED -query ITT colnames +query ITTT colnames SHOW TENANT "tenant-one" ---- -id name status -2 tenant-one READY +id name data_status service_mode +2 tenant-one READY NONE -query ITT colnames +query ITTT colnames SHOW TENANT "two" ---- -id name status -3 two READY +id name data_status service_mode +3 two READY NONE -query ITT colnames +query ITTT colnames SHOW TENANT two ---- -id name status -3 two READY +id name data_status service_mode +3 two READY NONE -query ITT colnames +query ITTT colnames SHOW TENANT three ---- -id name status -4 three READY +id name data_status service_mode +4 three READY NONE -query ITT colnames +query ITTT colnames SHOW TENANTS ---- -id name status -1 system READY -2 tenant-one READY -3 two READY -4 three READY +id name data_status service_mode +1 system READY SHARED +2 tenant-one READY NONE +3 two READY NONE +4 three READY NONE statement error tenant name cannot be empty ALTER TENANT [4] RENAME TO "" @@ -97,20 +97,20 @@ ALTER TENANT [4] RENAME TO blux statement ok ALTER TENANT blux RENAME TO 'blix' -query ITT colnames +query ITTT colnames SELECT * FROM [SHOW TENANTS] WHERE id = 4 ---- -id name status -4 blix READY +id name data_status service_mode +4 blix READY NONE statement ok ALTER TENANT blix RENAME TO three -query ITT colnames +query ITTT colnames SELECT * FROM [SHOW TENANTS] WHERE id = 4 ---- -id name status -4 three READY +id name data_status service_mode +4 three READY NONE statement error tenant "seven" does not exist SHOW TENANT seven @@ -158,7 +158,7 @@ FROM system.tenants WHERE name = 'four' ORDER BY id ---- id active name crdb_internal.pb_to_json -5 true four {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "5", "name": "four", "tenantReplicationJobId": "0"} +5 true four {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "5", "name": "four", "serviceMode": "NONE", "tenantReplicationJobId": "0"} statement ok DROP TENANT four @@ -232,14 +232,14 @@ FROM system.tenants ORDER BY id ---- id active name crdb_internal.pb_to_json -1 true system {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "tenantReplicationJobId": "0"} -2 true tenant-one {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "2", "name": "tenant-one", "tenantReplicationJobId": "0"} -3 true two {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "3", "name": "two", "tenantReplicationJobId": "0"} -4 true three {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "4", "name": "three", "tenantReplicationJobId": "0"} -5 false NULL {"capabilities": {"canAdminSplit": false}, "dataState": "DROP", "droppedName": "four", "id": "5", "name": "", "tenantReplicationJobId": "0"} -6 false NULL {"capabilities": {"canAdminSplit": false}, "dataState": "DROP", "droppedName": "five-requiring-quotes", "id": "6", "name": "", "tenantReplicationJobId": "0"} -7 false NULL {"capabilities": {"canAdminSplit": false}, "dataState": "DROP", "droppedName": "to-be-reclaimed", "id": "7", "name": "", "tenantReplicationJobId": "0"} -8 true to-be-reclaimed {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "8", "name": "to-be-reclaimed", "tenantReplicationJobId": "0"} +1 true system {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "serviceMode": "SHARED", "tenantReplicationJobId": "0"} +2 true tenant-one {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "2", "name": "tenant-one", "serviceMode": "NONE", "tenantReplicationJobId": "0"} +3 true two {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "3", "name": "two", "serviceMode": "NONE", "tenantReplicationJobId": "0"} +4 true three {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "4", "name": "three", "serviceMode": "NONE", "tenantReplicationJobId": "0"} +5 false NULL {"capabilities": {"canAdminSplit": false}, "dataState": "DROP", "droppedName": "four", "id": "5", "name": "", "serviceMode": "NONE", "tenantReplicationJobId": "0"} +6 false NULL {"capabilities": {"canAdminSplit": false}, "dataState": "DROP", "droppedName": "five-requiring-quotes", "id": "6", "name": "", "serviceMode": "NONE", "tenantReplicationJobId": "0"} +7 false NULL {"capabilities": {"canAdminSplit": false}, "dataState": "DROP", "droppedName": "to-be-reclaimed", "id": "7", "name": "", "serviceMode": "NONE", "tenantReplicationJobId": "0"} +8 true to-be-reclaimed {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "8", "name": "to-be-reclaimed", "serviceMode": "NONE", "tenantReplicationJobId": "0"} # More valid tenant names. statement ok @@ -247,15 +247,15 @@ CREATE TENANT "1"; CREATE TENANT "a-b"; CREATE TENANT "hello-100" -query ITT colnames +query ITTT colnames SHOW TENANTS ---- -id name status -1 system READY -2 tenant-one READY -3 two READY -4 three READY -8 to-be-reclaimed READY -9 1 READY -10 a-b READY -11 hello-100 READY +id name data_status service_mode +1 system READY SHARED +2 tenant-one READY NONE +3 two READY NONE +4 three READY NONE +8 to-be-reclaimed READY NONE +9 1 READY NONE +10 a-b READY NONE +11 hello-100 READY NONE diff --git a/pkg/sql/logictest/testdata/logic_test/tenant_builtins b/pkg/sql/logictest/testdata/logic_test/tenant_builtins index ad4355432286..0b8d64e4c738 100644 --- a/pkg/sql/logictest/testdata/logic_test/tenant_builtins +++ b/pkg/sql/logictest/testdata/logic_test/tenant_builtins @@ -3,7 +3,7 @@ query IBIT colnames SELECT id, active, length(info), name FROM system.tenants ORDER BY id ---- id active length name -1 true 18 system +1 true 20 system # Create three tenants. @@ -12,6 +12,11 @@ SELECT crdb_internal.create_tenant(5) ---- 5 +# create_tenant auto-sets the service for backward-compatibility. +# Reset it here so the tests below don't get confused. +statement ok +ALTER TENANT [5] STOP SERVICE + query error invalid tenant name SELECT crdb_internal.create_tenant(10, 'ABC') @@ -37,10 +42,10 @@ FROM system.tenants ORDER BY id ---- id active name crdb_internal.pb_to_json -1 true system {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "tenantReplicationJobId": "0"} -2 true tenant-number-eleven {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "2", "name": "tenant-number-eleven", "tenantReplicationJobId": "0"} -5 true tenant-5 {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "5", "name": "tenant-5", "tenantReplicationJobId": "0"} -10 true tenant-number-ten {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "10", "name": "tenant-number-ten", "tenantReplicationJobId": "0"} +1 true system {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "serviceMode": "SHARED", "tenantReplicationJobId": "0"} +2 true tenant-number-eleven {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "2", "name": "tenant-number-eleven", "serviceMode": "NONE", "tenantReplicationJobId": "0"} +5 true tenant-5 {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "5", "name": "tenant-5", "serviceMode": "NONE", "tenantReplicationJobId": "0"} +10 true tenant-number-ten {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "10", "name": "tenant-number-ten", "serviceMode": "NONE", "tenantReplicationJobId": "0"} # Check we can add a name where none existed before. statement ok @@ -107,10 +112,10 @@ FROM system.tenants ORDER BY id ---- id active name crdb_internal.pb_to_json -1 true system {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "tenantReplicationJobId": "0"} -2 true tenant-number-eleven {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "2", "name": "tenant-number-eleven", "tenantReplicationJobId": "0"} -5 false NULL {"capabilities": {"canAdminSplit": false}, "dataState": "DROP", "droppedName": "my-new-tenant-name", "id": "5", "name": "", "tenantReplicationJobId": "0"} -10 true tenant-number-ten {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "10", "name": "tenant-number-ten", "tenantReplicationJobId": "0"} +1 true system {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "serviceMode": "SHARED", "tenantReplicationJobId": "0"} +2 true tenant-number-eleven {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "2", "name": "tenant-number-eleven", "serviceMode": "NONE", "tenantReplicationJobId": "0"} +5 false NULL {"capabilities": {"canAdminSplit": false}, "dataState": "DROP", "droppedName": "my-new-tenant-name", "id": "5", "name": "", "serviceMode": "NONE", "tenantReplicationJobId": "0"} +10 true tenant-number-ten {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "10", "name": "tenant-number-ten", "serviceMode": "NONE", "tenantReplicationJobId": "0"} # Try to recreate an existing tenant. @@ -217,9 +222,9 @@ FROM system.tenants ORDER BY id ---- id active crdb_internal.pb_to_json -1 true {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "tenantReplicationJobId": "0"} -2 true {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "2", "name": "tenant-number-eleven", "tenantReplicationJobId": "0"} -10 true {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "10", "name": "tenant-number-ten", "tenantReplicationJobId": "0"} +1 true {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "1", "name": "system", "serviceMode": "SHARED", "tenantReplicationJobId": "0"} +2 true {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "2", "name": "tenant-number-eleven", "serviceMode": "NONE", "tenantReplicationJobId": "0"} +10 true {"capabilities": {"canAdminSplit": false}, "dataState": "READY", "droppedName": "", "id": "10", "name": "tenant-number-ten", "serviceMode": "NONE", "tenantReplicationJobId": "0"} query error tenant resource limits require a CCL binary SELECT crdb_internal.update_tenant_resource_limits(10, 1000, 100, 0, now(), 0) diff --git a/pkg/sql/opaque.go b/pkg/sql/opaque.go index 62077f4644dd..1d66a09f32f4 100644 --- a/pkg/sql/opaque.go +++ b/pkg/sql/opaque.go @@ -132,6 +132,8 @@ func planOpaque(ctx context.Context, p *planner, stmt tree.Statement) (planNode, return p.AlterTenantSetClusterSetting(ctx, n) case *tree.AlterTenantRename: return p.alterRenameTenant(ctx, n) + case *tree.AlterTenantService: + return p.alterTenantService(ctx, n) case *tree.AlterType: return p.AlterType(ctx, n) case *tree.AlterRole: @@ -314,6 +316,7 @@ func init() { &tree.AlterTableSetSchema{}, &tree.AlterTenantRename{}, &tree.AlterTenantSetClusterSetting{}, + &tree.AlterTenantService{}, &tree.AlterType{}, &tree.AlterSequence{}, &tree.AlterRole{}, diff --git a/pkg/sql/parser/help_test.go b/pkg/sql/parser/help_test.go index 4f5531492265..0ff8fec6a189 100644 --- a/pkg/sql/parser/help_test.go +++ b/pkg/sql/parser/help_test.go @@ -63,6 +63,9 @@ func TestContextualHelp(t *testing.T) { {`ALTER TENANT foo RENAME TO bar ??`, `ALTER TENANT RENAME`}, + {`ALTER TENANT foo START SERVICE ??`, `ALTER TENANT SERVICE`}, + {`ALTER TENANT foo STOP ??`, `ALTER TENANT SERVICE`}, + {`ALTER TYPE ??`, `ALTER TYPE`}, {`ALTER TYPE t ??`, `ALTER TYPE`}, {`ALTER TYPE t ADD VALUE ??`, `ALTER TYPE`}, diff --git a/pkg/sql/parser/sql.y b/pkg/sql/parser/sql.y index 334c9d79f92c..cfe644a88526 100644 --- a/pkg/sql/parser/sql.y +++ b/pkg/sql/parser/sql.y @@ -949,12 +949,12 @@ func (u *sqlSymUnion) cteMaterializeClause() tree.CTEMaterializeClause { %token SAVEPOINT SCANS SCATTER SCHEDULE SCHEDULES SCROLL SCHEMA SCHEMA_ONLY SCHEMAS SCRUB %token SEARCH SECOND SECONDARY SECURITY SELECT SEQUENCE SEQUENCES -%token SERIALIZABLE SERVER SESSION SESSIONS SESSION_USER SET SETOF SETS SETTING SETTINGS -%token SHARE SHOW SIMILAR SIMPLE SKIP SKIP_LOCALITIES_CHECK SKIP_MISSING_FOREIGN_KEYS +%token SERIALIZABLE SERVER SERVICE SESSION SESSIONS SESSION_USER SET SETOF SETS SETTING SETTINGS +%token SHARE SHARED SHOW SIMILAR SIMPLE SKIP SKIP_LOCALITIES_CHECK SKIP_MISSING_FOREIGN_KEYS %token SKIP_MISSING_SEQUENCES SKIP_MISSING_SEQUENCE_OWNERS SKIP_MISSING_VIEWS SMALLINT SMALLSERIAL SNAPSHOT SOME SPLIT SQL %token SQLLOGIN -%token STABLE START STATE STATISTICS STATUS STDIN STREAM STRICT STRING STORAGE STORE STORED STORING SUBSTRING SUPER +%token STABLE START STATE STATISTICS STATUS STDIN STREAM STRICT STRING STOP STORAGE STORE STORED STORING SUBSTRING SUPER %token SUPPORT SURVIVE SURVIVAL SYMMETRIC SYNTAX SYSTEM SQRT SUBSCRIPTION STATEMENTS %token TABLE TABLES TABLESPACE TEMP TEMPLATE TEMPORARY TENANT TENANT_NAME TENANTS TESTING_RELOCATE TEXT THEN @@ -1049,6 +1049,7 @@ func (u *sqlSymUnion) cteMaterializeClause() tree.CTEMaterializeClause { // Other ALTER TENANT statements. %type alter_tenant_replication_stmt %type alter_tenant_rename_stmt +%type alter_tenant_service_stmt // ALTER PARTITION %type alter_zone_partition_stmt @@ -6179,6 +6180,7 @@ alter_tenant_stmt: alter_tenant_replication_stmt // EXTEND WITH HELP: ALTER TENANT REPLICATION | alter_tenant_csetting_stmt // EXTEND WITH HELP: ALTER TENANT CLUSTER SETTING | alter_tenant_rename_stmt // EXTEND WITH HELP: ALTER TENANT RENAME +| alter_tenant_service_stmt // EXTEND WITH HELP: ALTER TENANT SERVICE | ALTER TENANT error // SHOW HELP: ALTER TENANT tenant_spec: @@ -6201,6 +6203,41 @@ alter_tenant_rename_stmt: } } +// %Help: ALTER TENANT SERVICE - alter tenant service mode +// %Category: Experimental +// %Text: +// ALTER TENANT START SERVICE EXTERNAL +// ALTER TENANT START SERVICE SHARED +// ALTER TENANT STOP SERVICE +alter_tenant_service_stmt: + ALTER TENANT tenant_spec START SERVICE EXTERNAL + { + /* SKIP DOC */ + $$.val = &tree.AlterTenantService{ + TenantSpec: $3.tenantSpec(), + Command: tree.TenantStartServiceExternal, + } + } +| ALTER TENANT tenant_spec START SERVICE SHARED + { + /* SKIP DOC */ + $$.val = &tree.AlterTenantService{ + TenantSpec: $3.tenantSpec(), + Command: tree.TenantStartServiceShared, + } + } +| ALTER TENANT tenant_spec STOP SERVICE + { + /* SKIP DOC */ + $$.val = &tree.AlterTenantService{ + TenantSpec: $3.tenantSpec(), + Command: tree.TenantStopService, + } + } +| ALTER TENANT tenant_spec START error // SHOW HELP: ALTER TENANT SERVICE +| ALTER TENANT tenant_spec STOP error // SHOW HELP: ALTER TENANT SERVICE + + // %Help: ALTER TENANT REPLICATION - alter tenant replication stream // %Category: Experimental // %Text: @@ -16054,11 +16091,13 @@ unreserved_keyword: | SEQUENCE | SEQUENCES | SERVER +| SERVICE | SESSION | SESSIONS | SET | SETS | SHARE +| SHARED | SHOW | SIMPLE | SKIP @@ -16077,6 +16116,7 @@ unreserved_keyword: | STATEMENTS | STATISTICS | STDIN +| STOP | STORAGE | STORE | STORED diff --git a/pkg/sql/parser/testdata/alter_tenant b/pkg/sql/parser/testdata/alter_tenant index ba86731fcc6f..0619bc954968 100644 --- a/pkg/sql/parser/testdata/alter_tenant +++ b/pkg/sql/parser/testdata/alter_tenant @@ -190,3 +190,27 @@ ALTER TENANT 'foo' RENAME TO bar ALTER TENANT ('foo') RENAME TO (bar) -- fully parenthesized ALTER TENANT '_' RENAME TO bar -- literals removed ALTER TENANT 'foo' RENAME TO _ -- identifiers removed + +parse +ALTER TENANT 'foo' START SERVICE EXTERNAL +---- +ALTER TENANT 'foo' START SERVICE EXTERNAL +ALTER TENANT ('foo') START SERVICE EXTERNAL -- fully parenthesized +ALTER TENANT '_' START SERVICE EXTERNAL -- literals removed +ALTER TENANT 'foo' START SERVICE EXTERNAL -- identifiers removed + +parse +ALTER TENANT 'foo' START SERVICE SHARED +---- +ALTER TENANT 'foo' START SERVICE SHARED +ALTER TENANT ('foo') START SERVICE SHARED -- fully parenthesized +ALTER TENANT '_' START SERVICE SHARED -- literals removed +ALTER TENANT 'foo' START SERVICE SHARED -- identifiers removed + +parse +ALTER TENANT 'foo' STOP SERVICE +---- +ALTER TENANT 'foo' STOP SERVICE +ALTER TENANT ('foo') STOP SERVICE -- fully parenthesized +ALTER TENANT '_' STOP SERVICE -- literals removed +ALTER TENANT 'foo' STOP SERVICE -- identifiers removed diff --git a/pkg/sql/rename_tenant.go b/pkg/sql/rename_tenant.go index 2b74e0469005..f45b033558fb 100644 --- a/pkg/sql/rename_tenant.go +++ b/pkg/sql/rename_tenant.go @@ -69,7 +69,7 @@ func (n *renameTenantNode) startExec(params runParams) error { if err != nil { return err } - return params.p.renameTenant(params.ctx, rec.ID, newName) + return params.p.renameTenant(params.ctx, rec, newName) } func (n *renameTenantNode) Next(_ runParams) (bool, error) { return false, nil } diff --git a/pkg/sql/sem/builtins/builtins.go b/pkg/sql/sem/builtins/builtins.go index acb682d1e16c..ee9436a0c3ce 100644 --- a/pkg/sql/sem/builtins/builtins.go +++ b/pkg/sql/sem/builtins/builtins.go @@ -4931,16 +4931,20 @@ value if you rely on the HLC for accuracy.`, `Must be run by the system tenant.`, Volatility: volatility.Volatile, }, + // This overload is provided for compatibility with CC Serverless + // v22.2 and previous versions. tree.Overload{ Types: tree.ParamTypes{ {Name: "id", Typ: types.Int}, }, ReturnType: tree.FixedReturnType(types.Int), IsUDF: true, - Body: `SELECT crdb_internal.create_tenant(json_build_object('id', $1))`, - Info: `create_tenant(id) is an alias for create_tenant('{"id": id}'::jsonb)`, + Body: `SELECT crdb_internal.create_tenant(json_build_object('id', $1, 'service_mode', + 'external'))`, + Info: `create_tenant(id) is an alias for create_tenant('{"id": id, "service_mode": "external"}'::jsonb)`, Volatility: volatility.Volatile, }, + // This overload is provided for use in tests. tree.Overload{ Types: tree.ParamTypes{ {Name: "id", Typ: types.Int}, @@ -4952,6 +4956,7 @@ value if you rely on the HLC for accuracy.`, Info: `create_tenant(id, name) is an alias for create_tenant('{"id": id, "name": name}'::jsonb)`, Volatility: volatility.Volatile, }, + // This overload is deprecated. Use CREATE TENANT instead. tree.Overload{ Types: tree.ParamTypes{ {Name: "name", Typ: types.String}, @@ -4959,7 +4964,8 @@ value if you rely on the HLC for accuracy.`, ReturnType: tree.FixedReturnType(types.Int), IsUDF: true, Body: `SELECT crdb_internal.create_tenant(json_build_object('name', $1))`, - Info: `create_tenant(name) is an alias for create_tenant('{"name": name}'::jsonb)`, + Info: `create_tenant(name) is an alias for create_tenant('{"name": name}'::jsonb). +DO NOT USE -- USE 'CREATE TENANT' INSTEAD`, Volatility: volatility.Volatile, }, ), @@ -5026,8 +5032,11 @@ value if you rely on the HLC for accuracy.`, return nil, err } synchronous := tree.MustBeDBool(args[1]) + + // Note: we pass true to ignoreServiceMode for compatibility + // with CC Serverless pre-v23.1. if err := evalCtx.Tenant.DropTenantByID( - ctx, uint64(sTenID), bool(synchronous), + ctx, uint64(sTenID), bool(synchronous), true, /* ignoreServiceMode */ ); err != nil { return nil, err } diff --git a/pkg/sql/sem/eval/deps.go b/pkg/sql/sem/eval/deps.go index 1ec74f89b33c..70f77717198e 100644 --- a/pkg/sql/sem/eval/deps.go +++ b/pkg/sql/sem/eval/deps.go @@ -545,7 +545,7 @@ type TenantOperator interface { // DropTenantByID attempts to uninstall an existing tenant from the system. // It returns an error if the tenant does not exist. If synchronous is true // the gc job will not wait for a GC ttl. - DropTenantByID(ctx context.Context, tenantID uint64, synchronous bool) error + DropTenantByID(ctx context.Context, tenantID uint64, synchronous, ignoreServiceMode bool) error // GCTenant attempts to garbage collect a DROP tenant from the system. Upon // success it also removes the tenant record. diff --git a/pkg/sql/sem/tree/alter_tenant.go b/pkg/sql/sem/tree/alter_tenant.go index c0329f241a6c..6dd56a603dec 100644 --- a/pkg/sql/sem/tree/alter_tenant.go +++ b/pkg/sql/sem/tree/alter_tenant.go @@ -83,3 +83,37 @@ func (n *AlterTenantRename) Format(ctx *FmtCtx) { ctx.WriteString(" RENAME TO ") ctx.FormatNode(n.NewName) } + +// AlterTenantService represents an ALTER TENANT START/STOP SERVICE statement. +type AlterTenantService struct { + TenantSpec *TenantSpec + Command TenantServiceCmd +} + +// TenantServiceCmd represents a parameter to ALTER TENANT. +type TenantServiceCmd int8 + +const ( + // TenantStartServiceExternal encodes START SERVICE EXTERNAL. + TenantStartServiceExternal TenantServiceCmd = 0 + // TenantStartServiceExternal encodes START SERVICE SHARED. + TenantStartServiceShared TenantServiceCmd = 1 + // TenantStartServiceExternal encodes STOP SERVICE. + TenantStopService TenantServiceCmd = 2 +) + +var _ Statement = &AlterTenantService{} + +// Format implements the NodeFormatter interface. +func (n *AlterTenantService) Format(ctx *FmtCtx) { + ctx.WriteString("ALTER TENANT ") + ctx.FormatNode(n.TenantSpec) + switch n.Command { + case TenantStartServiceExternal: + ctx.WriteString(" START SERVICE EXTERNAL") + case TenantStartServiceShared: + ctx.WriteString(" START SERVICE SHARED") + case TenantStopService: + ctx.WriteString(" STOP SERVICE") + } +} diff --git a/pkg/sql/sem/tree/stmt.go b/pkg/sql/sem/tree/stmt.go index 6c6c35f718b5..e86e6cfeca50 100644 --- a/pkg/sql/sem/tree/stmt.go +++ b/pkg/sql/sem/tree/stmt.go @@ -469,6 +469,15 @@ func (*AlterTenantRename) StatementType() StatementType { return TypeDCL } // StatementTag returns a short string identifying the type of statement. func (*AlterTenantRename) StatementTag() string { return "ALTER TENANT RENAME" } +// StatementReturnType implements the Statement interface. +func (*AlterTenantService) StatementReturnType() StatementReturnType { return Ack } + +// StatementType implements the Statement interface. +func (*AlterTenantService) StatementType() StatementType { return TypeDCL } + +// StatementTag returns a short string identifying the type of statement. +func (*AlterTenantService) StatementTag() string { return "ALTER TENANT SERVICE" } + // StatementReturnType implements the Statement interface. func (*AlterType) StatementReturnType() StatementReturnType { return DDL } @@ -2114,6 +2123,7 @@ func (n *AlterTableSetSchema) String() string { return AsString( func (n *AlterTenantSetClusterSetting) String() string { return AsString(n) } func (n *AlterTenantRename) String() string { return AsString(n) } func (n *AlterTenantReplication) String() string { return AsString(n) } +func (n *AlterTenantService) String() string { return AsString(n) } func (n *AlterType) String() string { return AsString(n) } func (n *AlterRole) String() string { return AsString(n) } func (n *AlterRoleSet) String() string { return AsString(n) } diff --git a/pkg/sql/sem/tree/walk.go b/pkg/sql/sem/tree/walk.go index 1887a5de2ad2..126fe807293c 100644 --- a/pkg/sql/sem/tree/walk.go +++ b/pkg/sql/sem/tree/walk.go @@ -1088,6 +1088,25 @@ func (n *AlterTenantRename) walkStmt(v Visitor) Statement { return ret } +// copyNode makes a copy of this Statement without recursing in any child Statements. +func (n *AlterTenantService) copyNode() *AlterTenantService { + stmtCopy := *n + return &stmtCopy +} + +// walkStmt is part of the walkableStmt interface. +func (n *AlterTenantService) walkStmt(v Visitor) Statement { + ret := n + ts, changed := walkTenantSpec(v, n.TenantSpec) + if changed { + if ret == n { + ret = n.copyNode() + } + ret.TenantSpec = ts + } + return ret +} + // copyNode makes a copy of this Statement without recursing in any child Statements. func (n *DropTenant) copyNode() *DropTenant { stmtCopy := *n @@ -1814,6 +1833,7 @@ func (stmt *BeginTransaction) walkStmt(v Visitor) Statement { var _ walkableStmt = &AlterTenantRename{} var _ walkableStmt = &AlterTenantReplication{} +var _ walkableStmt = &AlterTenantService{} var _ walkableStmt = &AlterTenantSetClusterSetting{} var _ walkableStmt = &Backup{} var _ walkableStmt = &BeginTransaction{} diff --git a/pkg/sql/show_tenant.go b/pkg/sql/show_tenant.go index 76a4ad635e38..6df8d2a423ae 100644 --- a/pkg/sql/show_tenant.go +++ b/pkg/sql/show_tenant.go @@ -239,6 +239,7 @@ func (n *showTenantNode) Values() tree.Datums { tree.NewDInt(tree.DInt(tenantInfo.ID)), tree.NewDString(string(tenantInfo.Name)), tree.NewDString(string(v.tenantStatus)), + tree.NewDString(tenantInfo.ServiceMode.String()), } if n.withReplication { diff --git a/pkg/sql/tenant_creation.go b/pkg/sql/tenant_creation.go index 09bd75dee58a..bd8bb6043987 100644 --- a/pkg/sql/tenant_creation.go +++ b/pkg/sql/tenant_creation.go @@ -58,8 +58,9 @@ func (p *planner) CreateTenant( } type createTenantConfig struct { - ID *uint64 `json:"id,omitempty"` - Name *string `json:"name,omitempty"` + ID *uint64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + ServiceMode *string `json:"service_mode,omitempty"` } func (p *planner) createTenantInternal( @@ -73,6 +74,14 @@ func (p *planner) createTenantInternal( if ctcfg.Name != nil { name = roachpb.TenantName(*ctcfg.Name) } + serviceMode := descpb.TenantInfo_NONE + if ctcfg.ServiceMode != nil { + v, ok := descpb.TenantInfo_ServiceMode_value[strings.ToUpper(*ctcfg.ServiceMode)] + if !ok { + return tid, pgerror.Newf(pgcode.Syntax, "unknown service mode: %q", *ctcfg.ServiceMode) + } + serviceMode = descpb.TenantInfo_ServiceMode(v) + } // tenantID uint64, name roachpb.TenantName, if p.EvalContext().TxnReadOnly { @@ -91,8 +100,9 @@ func (p *planner) createTenantInternal( ID: tenantID, // We synchronously initialize the tenant's keyspace below, so // we can skip the ADD state and go straight to the READY state. - DataState: descpb.TenantInfo_READY, - Name: name, + DataState: descpb.TenantInfo_READY, + Name: name, + ServiceMode: serviceMode, }, } diff --git a/pkg/sql/tenant_deletion.go b/pkg/sql/tenant_deletion.go index 23c4c1821751..b1a19688dd76 100644 --- a/pkg/sql/tenant_deletion.go +++ b/pkg/sql/tenant_deletion.go @@ -21,13 +21,15 @@ import ( "github.com/cockroachdb/cockroach/pkg/settings/cluster" "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" "github.com/cockroachdb/cockroach/pkg/sql/isql" + "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" + "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" "github.com/cockroachdb/cockroach/pkg/util/timeutil" "github.com/cockroachdb/errors" ) // DropTenantByID implements the tree.TenantOperator interface. func (p *planner) DropTenantByID( - ctx context.Context, tenID uint64, synchronousImmediateDrop bool, + ctx context.Context, tenID uint64, synchronousImmediateDrop, ignoreServiceMode bool, ) error { if err := p.validateDropTenant(ctx); err != nil { return err @@ -46,6 +48,7 @@ func (p *planner) DropTenantByID( p.User(), info, synchronousImmediateDrop, + ignoreServiceMode, ) } @@ -70,6 +73,7 @@ func dropTenantInternal( user username.SQLUsername, info *descpb.TenantInfo, synchronousImmediateDrop bool, + ignoreServiceMode bool, ) error { const op = "destroy" tenID := info.ID @@ -77,8 +81,14 @@ func dropTenantInternal( return err } + if !ignoreServiceMode && info.ServiceMode != descpb.TenantInfo_NONE { + return errors.WithHint(pgerror.Newf(pgcode.ObjectNotInPrerequisiteState, + "cannot drop tenant %q (%d) in service mode %v", info.Name, tenID, info.ServiceMode), + "Use ALTER TENANT STOP SERVICE before DROP TENANT.") + } + if info.DataState == descpb.TenantInfo_DROP { - return errors.Errorf("tenant %d is already in data state DROP", tenID) + return errors.Errorf("tenant %q (%d) is already in data state DROP", info.Name, tenID) } // Mark the tenant as dropping. diff --git a/pkg/sql/tenant_service.go b/pkg/sql/tenant_service.go new file mode 100644 index 000000000000..a4433a61d2a2 --- /dev/null +++ b/pkg/sql/tenant_service.go @@ -0,0 +1,68 @@ +// Copyright 2023 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 ( + "context" + + "github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" + "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" + "github.com/cockroachdb/errors" +) + +type alterTenantServiceNode struct { + tenantSpec tenantSpec + newMode descpb.TenantInfo_ServiceMode +} + +func (p *planner) alterTenantService( + ctx context.Context, n *tree.AlterTenantService, +) (planNode, error) { + // Even though the call to Update in startExec also + // performs this check, we need to do this early because otherwise + // the lookup of the ID from the name will fail. + if err := rejectIfCantCoordinateMultiTenancy(p.execCfg.Codec, "set tenant service"); err != nil { + return nil, err + } + + var newMode descpb.TenantInfo_ServiceMode + switch n.Command { + case tree.TenantStopService: + newMode = descpb.TenantInfo_NONE + case tree.TenantStartServiceExternal: + newMode = descpb.TenantInfo_EXTERNAL + case tree.TenantStartServiceShared: + newMode = descpb.TenantInfo_SHARED + default: + return nil, errors.AssertionFailedf("unhandled case: %+v", n) + } + + tspec, err := p.planTenantSpec(ctx, n.TenantSpec, "ALTER TENANT SERVICE") + if err != nil { + return nil, err + } + return &alterTenantServiceNode{ + tenantSpec: tspec, + newMode: newMode, + }, nil +} + +func (n *alterTenantServiceNode) startExec(params runParams) error { + rec, err := n.tenantSpec.getTenantInfo(params.ctx, params.p) + if err != nil { + return err + } + return params.p.setTenantService(params.ctx, rec, n.newMode) +} + +func (n *alterTenantServiceNode) Next(_ runParams) (bool, error) { return false, nil } +func (n *alterTenantServiceNode) Values() tree.Datums { return tree.Datums{} } +func (n *alterTenantServiceNode) Close(_ context.Context) {} diff --git a/pkg/sql/tenant_test.go b/pkg/sql/tenant_test.go index 3da8947352e1..659a75cb879f 100644 --- a/pkg/sql/tenant_test.go +++ b/pkg/sql/tenant_test.go @@ -88,6 +88,7 @@ SELECT id, active FROM system.tenants WHERE id = 10 checkKVsExistForTenant(t, true /* shouldExist */) // Destroy the tenant, make sure it does not have data and state. + tdb.Exec(t, "ALTER TENANT [10] STOP SERVICE") tdb.Exec(t, "DROP TENANT [10] IMMEDIATE") tdb.CheckQueryResults(t, tenantStateQuery, [][]string{}) checkKVsExistForTenant(t, false /* shouldExist */) diff --git a/pkg/sql/tenant_update.go b/pkg/sql/tenant_update.go index 1efbacf7c921..9f5922507460 100644 --- a/pkg/sql/tenant_update.go +++ b/pkg/sql/tenant_update.go @@ -50,7 +50,10 @@ func UpdateTenantRecord( `UPDATE system.tenants SET active = $2, info = $3 WHERE id = $1`, tenID, active, infoBytes, ); err != nil { - return errors.Wrap(err, "activating tenant") + if pgerror.GetPGCode(err) == pgcode.UniqueViolation { + return pgerror.Newf(pgcode.DuplicateObject, "name %q is already taken", info.Name) + } + return err } else if num != 1 { logcrash.ReportOrPanic(ctx, &settings.SV, "unexpected number of rows affected: %d", num) } @@ -64,6 +67,10 @@ func validateTenantInfo(info *descpb.TenantInfo) error { if info.DroppedName != "" && info.DataState != descpb.TenantInfo_DROP { return errors.Newf("tenant in data state %v with dropped name %q", info.DataState, info.DroppedName) } + if info.ServiceMode != descpb.TenantInfo_NONE && info.DataState != descpb.TenantInfo_READY { + return errors.Newf("cannot use tenant service mode %v with data state %v", + info.ServiceMode, info.DataState) + } return nil } @@ -108,7 +115,12 @@ func (p *planner) UpdateTenantResourceLimits( // The caller is responsible for checking that the user is authorized // to take this action. func ActivateTenant( - ctx context.Context, settings *cluster.Settings, codec keys.SQLCodec, txn isql.Txn, tenID uint64, + ctx context.Context, + settings *cluster.Settings, + codec keys.SQLCodec, + txn isql.Txn, + tenID uint64, + serviceMode descpb.TenantInfo_ServiceMode, ) error { const op = "activate" if err := rejectIfCantCoordinateMultiTenancy(codec, op); err != nil { @@ -126,6 +138,7 @@ func ActivateTenant( // Mark the tenant as active. info.DataState = descpb.TenantInfo_READY + info.ServiceMode = serviceMode if err := UpdateTenantRecord(ctx, settings, txn, info); err != nil { return errors.Wrap(err, "activating tenant") } @@ -133,8 +146,41 @@ func ActivateTenant( return nil } +func (p *planner) setTenantService( + ctx context.Context, info *descpb.TenantInfo, newMode descpb.TenantInfo_ServiceMode, +) error { + if p.EvalContext().TxnReadOnly { + return readOnlyError("ALTER TENANT SERVICE") + } + + if err := p.RequireAdminRole(ctx, "set tenant service"); err != nil { + return err + } + if err := rejectIfCantCoordinateMultiTenancy(p.ExecCfg().Codec, "set tenant service"); err != nil { + return err + } + if err := rejectIfSystemTenant(info.ID, "set tenant service"); err != nil { + return err + } + + if newMode == info.ServiceMode { + // No-op. Do nothing. + return nil + } + + if newMode != descpb.TenantInfo_NONE && info.ServiceMode != descpb.TenantInfo_NONE { + return errors.WithHint(pgerror.Newf(pgcode.ObjectNotInPrerequisiteState, + "cannot change service mode %v to %v directly", + info.ServiceMode, newMode), + "Use ALTER TENANT STOP SERVICE first.") + } + + info.ServiceMode = newMode + return UpdateTenantRecord(ctx, p.ExecCfg().Settings, p.InternalSQLTxn(), info) +} + func (p *planner) renameTenant( - ctx context.Context, tenantID uint64, tenantName roachpb.TenantName, + ctx context.Context, info *descpb.TenantInfo, newName roachpb.TenantName, ) error { if p.EvalContext().TxnReadOnly { return readOnlyError("ALTER TENANT RENAME TO") @@ -146,12 +192,12 @@ func (p *planner) renameTenant( if err := rejectIfCantCoordinateMultiTenancy(p.ExecCfg().Codec, "rename tenant"); err != nil { return err } - if err := rejectIfSystemTenant(tenantID, "rename"); err != nil { + if err := rejectIfSystemTenant(info.ID, "rename"); err != nil { return err } - if tenantName != "" { - if err := tenantName.IsValid(); err != nil { + if newName != "" { + if err := newName.IsValid(); err != nil { return pgerror.WithCandidateCode(err, pgcode.Syntax) } @@ -160,21 +206,14 @@ func (p *planner) renameTenant( } } - if num, err := p.InternalSQLTxn().ExecEx( - ctx, "rename-tenant", p.txn, sessiondata.NodeUserSessionDataOverride, - `UPDATE system.public.tenants -SET info = -crdb_internal.json_to_pb('cockroach.sql.sqlbase.TenantInfo', - crdb_internal.pb_to_json('cockroach.sql.sqlbase.TenantInfo', info) || - json_build_object('name', $2)) -WHERE id = $1`, tenantID, tenantName); err != nil { - if pgerror.GetPGCode(err) == pgcode.UniqueViolation { - return pgerror.Newf(pgcode.DuplicateObject, "name %q is already taken", tenantName) - } - return errors.Wrap(err, "renaming tenant") - } else if num != 1 { - return pgerror.Newf(pgcode.UndefinedObject, "tenant %d not found", tenantID) + if info.ServiceMode != descpb.TenantInfo_NONE { + return errors.WithHint(pgerror.Newf(pgcode.ObjectNotInPrerequisiteState, + "cannot rename tenant in service mode %v", info.ServiceMode), + "Use ALTER TENANT STOP SERVICE before renaming a tenant.") } - return nil + info.Name = newName + return errors.Wrap( + UpdateTenantRecord(ctx, p.ExecCfg().Settings, p.InternalSQLTxn(), info), + "renaming tenant") } diff --git a/pkg/sql/tests/system_table_test.go b/pkg/sql/tests/system_table_test.go index 82fb2463f918..665c8f9117f6 100644 --- a/pkg/sql/tests/system_table_test.go +++ b/pkg/sql/tests/system_table_test.go @@ -52,7 +52,7 @@ func TestInitialKeys(t *testing.T) { var nonDescKeys int if systemTenant { codec = keys.SystemSQLCodec - nonDescKeys = 14 + nonDescKeys = 15 } else { codec = keys.MakeSQLCodec(roachpb.MustMakeTenantID(5)) nonDescKeys = 4 diff --git a/pkg/sql/tests/testdata/initial_keys b/pkg/sql/tests/testdata/initial_keys index 2533b85b3b00..4c9c419d3204 100644 --- a/pkg/sql/tests/testdata/initial_keys +++ b/pkg/sql/tests/testdata/initial_keys @@ -1,6 +1,6 @@ initial-keys tenant=system ---- -100 keys: +101 keys: /System/"desc-idgen" /Table/3/1/1/2/1 /Table/3/1/3/2/1 @@ -56,6 +56,7 @@ initial-keys tenant=system /Table/7/1/0/0 /Table/8/1/1/0 /Table/8/2/"system"/0 + /Table/8/3/"shared"/1/0 /NamespaceTable/30/1/0/0/"system"/4/1 /NamespaceTable/30/1/1/0/"public"/4/1 /NamespaceTable/30/1/1/29/"comments"/4/1 diff --git a/pkg/sql/walk.go b/pkg/sql/walk.go index acf57492ce24..5aa166399ac5 100644 --- a/pkg/sql/walk.go +++ b/pkg/sql/walk.go @@ -216,6 +216,7 @@ func (v *planVisitor) visitInternal(plan planNode, name string) { } case *alterTenantSetClusterSettingNode: + case *alterTenantServiceNode: case *createViewNode: case *setVarNode: case *setClusterSettingNode: @@ -361,6 +362,7 @@ var planNodeNames = map[reflect.Type]string{ reflect.TypeOf(&alterTableSetLocalityNode{}): "alter table set locality", reflect.TypeOf(&alterTableSetSchemaNode{}): "alter table set schema", reflect.TypeOf(&alterTenantSetClusterSettingNode{}): "alter tenant set cluster setting", + reflect.TypeOf(&alterTenantServiceNode{}): "alter tenant service", reflect.TypeOf(&alterTypeNode{}): "alter type", reflect.TypeOf(&alterRoleNode{}): "alter role", reflect.TypeOf(&alterRoleSetNode{}): "alter role set var", diff --git a/pkg/upgrade/upgrades/tenant_table_migration.go b/pkg/upgrade/upgrades/tenant_table_migration.go index 64762797d4db..9b931573c29a 100644 --- a/pkg/upgrade/upgrades/tenant_table_migration.go +++ b/pkg/upgrade/upgrades/tenant_table_migration.go @@ -38,6 +38,36 @@ VALUES (1, true, crdb_internal.json_to_pb('cockroach.sql.sqlbase.TenantInfo', '{ func addTenantNameColumnAndSystemTenantEntry( ctx context.Context, cs clusterversion.ClusterVersion, d upgrade.TenantDeps, ) error { + tenantsTableID, err := getTenantsTableID(ctx, d) + if err != nil || tenantsTableID == 0 { + return err + } + + for _, op := range []operation{ + { + name: "add-tenant-name-column", + schemaList: []string{"name"}, + query: addTenantNameColumn, + schemaExistsFn: hasColumn, + }, + { + name: "make-tenant-name-unique", + schemaList: []string{"tenants_name_idx"}, + query: addTenantNameIndex, + schemaExistsFn: hasIndex, + }, + } { + if err := migrateTable(ctx, cs, d, op, tenantsTableID, systemschema.TenantsTable); err != nil { + return err + } + } + + _, err = d.InternalExecutor.ExecEx(ctx, "add-system-entry", nil, + sessiondata.NodeUserSessionDataOverride, addSystemTenantEntry) + return err +} + +func getTenantsTableID(ctx context.Context, d upgrade.TenantDeps) (descpb.ID, error) { rows, err := d.InternalExecutor.QueryRowEx(ctx, "get-tenant-table-id", nil, sessiondata.NodeUserSessionDataOverride, ` SELECT n2.id @@ -50,27 +80,52 @@ SELECT n2.id catconstants.TenantsTableName, ) if err != nil { - return err + return 0, err } if rows == nil { // No system.tenants table. Nothing to do. - return nil + return 0, nil } - - // Retrieve the tenant table ID from the query above. tenantsTableID := descpb.ID(int64(*rows[0].(*tree.DInt))) + return tenantsTableID, nil +} + +const addTenantServiceModeColumn = ` +ALTER TABLE system.public.tenants ADD COLUMN service_mode STRING +AS (lower(crdb_internal.pb_to_json('cockroach.sql.sqlbase.TenantInfo', info)->>'serviceMode')) VIRTUAL` + +const addTenantServiceModeIndex = ` +CREATE INDEX tenants_service_mode_idx ON system.public.tenants (service_mode ASC) +` + +const updateSystemTenantEntry = ` +UPDATE system.public.tenants SET info = +crdb_internal.json_to_pb('cockroach.sql.sqlbase.TenantInfo', +crdb_internal.pb_to_json('cockroach.sql.sqlbase.TenantInfo', info) || +json_build_object('serviceMode', 'SHARED')) +WHERE id = 1 +` + +func addTenantServiceModeColumnAndUpdateSystemTenantEntry( + ctx context.Context, cs clusterversion.ClusterVersion, d upgrade.TenantDeps, +) error { + tenantsTableID, err := getTenantsTableID(ctx, d) + if err != nil || tenantsTableID == 0 { + return err + } + for _, op := range []operation{ { - name: "add-tenant-name-column", - schemaList: []string{"name"}, - query: addTenantNameColumn, + name: "add-tenant-service-mode-column", + schemaList: []string{"service_mode"}, + query: addTenantServiceModeColumn, schemaExistsFn: hasColumn, }, { - name: "make-tenant-name-unique", - schemaList: []string{"tenants_name_idx"}, - query: addTenantNameIndex, + name: "make-tenant-service-mode-idx", + schemaList: []string{"tenants_service_mode_idx"}, + query: addTenantServiceModeIndex, schemaExistsFn: hasIndex, }, } { @@ -79,7 +134,7 @@ SELECT n2.id } } - _, err = d.InternalExecutor.ExecEx(ctx, "add-system-entry", nil, - sessiondata.NodeUserSessionDataOverride, addSystemTenantEntry) + _, err = d.InternalExecutor.ExecEx(ctx, "update-system-entry", nil, + sessiondata.NodeUserSessionDataOverride, updateSystemTenantEntry) return err } diff --git a/pkg/upgrade/upgrades/upgrades.go b/pkg/upgrade/upgrades/upgrades.go index 5b41e4c26b7a..7f897100fd3b 100644 --- a/pkg/upgrade/upgrades/upgrades.go +++ b/pkg/upgrade/upgrades/upgrades.go @@ -253,6 +253,11 @@ var upgrades = []upgradebase.Upgrade{ upgrade.NoPrecondition, alterSystemSQLInstancesAddSqlAddr, ), + upgrade.NewTenantUpgrade("add a service_mode column to system.tenants and update the system tenant entry", + toCV(clusterversion.V23_1TenantServiceMode), + upgrade.NoPrecondition, + addTenantServiceModeColumnAndUpdateSystemTenantEntry, + ), } func init() { From e1b76bdaabe4560f8bae5c2d1391989b0424bc0d Mon Sep 17 00:00:00 2001 From: Raphael 'kena' Poss Date: Sat, 21 Jan 2023 20:59:49 +0100 Subject: [PATCH 2/2] sql: make the output columns of SHOW TENANT lowercase All the SHOW statements report status-like data in lowercase. SHOW TENANT(s) should not be different. Release note: None --- .../replication_stream_e2e_test.go | 6 +-- .../streamingccl/streamingest/testdata/simple | 8 ++-- pkg/sql/logictest/testdata/logic_test/tenant | 39 ++++++++++--------- pkg/sql/show_tenant.go | 21 +++++----- 4 files changed, 39 insertions(+), 35 deletions(-) diff --git a/pkg/ccl/streamingccl/streamingest/replication_stream_e2e_test.go b/pkg/ccl/streamingccl/streamingest/replication_stream_e2e_test.go index afe332ee9c82..94b744aa1244 100644 --- a/pkg/ccl/streamingccl/streamingest/replication_stream_e2e_test.go +++ b/pkg/ccl/streamingccl/streamingest/replication_stream_e2e_test.go @@ -909,7 +909,7 @@ func TestTenantStreamingShowTenant(t *testing.T) { require.Equal(t, "destination", rowStr[0][1]) if rowStr[0][3] == "NULL" { // There is no source yet, therefore the replication is not fully initialized. - require.Equal(t, "INITIALIZING REPLICATION", rowStr[0][2]) + require.Equal(t, "initializing replication", rowStr[0][2]) } jobutils.WaitForJobToRun(c.T, c.SrcSysSQL, jobspb.JobID(producerJobID)) @@ -937,8 +937,8 @@ func TestTenantStreamingShowTenant(t *testing.T) { row.Scan(&id, &dest, &status, &serviceMode, &source, &sourceUri, &jobId, &maxReplTime, &protectedTime, &cutoverTime) require.Equal(t, 2, id) require.Equal(t, "destination", dest) - require.Equal(t, "REPLICATING", status) - require.Equal(t, "NONE", serviceMode) + require.Equal(t, "replicating", status) + require.Equal(t, "none", serviceMode) require.Equal(t, "source", source) require.Equal(t, c.SrcURL.String(), sourceUri) require.Equal(t, ingestionJobID, jobId) diff --git a/pkg/ccl/streamingccl/streamingest/testdata/simple b/pkg/ccl/streamingccl/streamingest/testdata/simple index 40b460769103..8fe1762d4d30 100644 --- a/pkg/ccl/streamingccl/streamingest/testdata/simple +++ b/pkg/ccl/streamingccl/streamingest/testdata/simple @@ -28,14 +28,14 @@ IMPORT INTO d.x CSV DATA ('userfile:///dx/export*-n*.0.csv'); query-sql as=source-system SHOW TENANTS ---- -1 system READY SHARED -10 source READY NONE +1 system ready shared +10 source ready none query-sql as=destination-system SHOW TENANTS ---- -1 system READY SHARED -2 destination REPLICATING NONE +1 system ready shared +2 destination replicating none let $ts as=source-system SELECT clock_timestamp()::timestamp::string diff --git a/pkg/sql/logictest/testdata/logic_test/tenant b/pkg/sql/logictest/testdata/logic_test/tenant index e432630e101b..52f1edba8986 100644 --- a/pkg/sql/logictest/testdata/logic_test/tenant +++ b/pkg/sql/logictest/testdata/logic_test/tenant @@ -47,40 +47,40 @@ query ITTT colnames SHOW TENANT system ---- id name data_status service_mode -1 system READY SHARED +1 system ready shared query ITTT colnames SHOW TENANT "tenant-one" ---- id name data_status service_mode -2 tenant-one READY NONE +2 tenant-one ready none query ITTT colnames SHOW TENANT "two" ---- id name data_status service_mode -3 two READY NONE +3 two ready none query ITTT colnames SHOW TENANT two ---- id name data_status service_mode -3 two READY NONE +3 two ready none query ITTT colnames SHOW TENANT three ---- id name data_status service_mode -4 three READY NONE +4 three ready none query ITTT colnames SHOW TENANTS ---- id name data_status service_mode -1 system READY SHARED -2 tenant-one READY NONE -3 two READY NONE -4 three READY NONE +1 system ready shared +2 tenant-one ready none +3 two ready none +4 three ready none statement error tenant name cannot be empty ALTER TENANT [4] RENAME TO "" @@ -101,7 +101,7 @@ query ITTT colnames SELECT * FROM [SHOW TENANTS] WHERE id = 4 ---- id name data_status service_mode -4 blix READY NONE +4 blix ready none statement ok ALTER TENANT blix RENAME TO three @@ -110,7 +110,7 @@ query ITTT colnames SELECT * FROM [SHOW TENANTS] WHERE id = 4 ---- id name data_status service_mode -4 three READY NONE +4 three ready none statement error tenant "seven" does not exist SHOW TENANT seven @@ -251,11 +251,12 @@ query ITTT colnames SHOW TENANTS ---- id name data_status service_mode -1 system READY SHARED -2 tenant-one READY NONE -3 two READY NONE -4 three READY NONE -8 to-be-reclaimed READY NONE -9 1 READY NONE -10 a-b READY NONE -11 hello-100 READY NONE +1 system ready shared +2 tenant-one ready none +3 two ready none +4 three ready none +8 to-be-reclaimed ready none +9 1 ready none +10 a-b ready none +11 hello-100 ready none + diff --git a/pkg/sql/show_tenant.go b/pkg/sql/show_tenant.go index 6df8d2a423ae..c36be19a172a 100644 --- a/pkg/sql/show_tenant.go +++ b/pkg/sql/show_tenant.go @@ -13,6 +13,7 @@ package sql import ( "context" "fmt" + "strings" "time" "github.com/cockroachdb/cockroach/pkg/jobs" @@ -31,12 +32,12 @@ import ( type tenantStatus string const ( - initReplication tenantStatus = "INITIALIZING REPLICATION" - replicating tenantStatus = "REPLICATING" - replicationPaused tenantStatus = "REPLICATION PAUSED" - cuttingOver tenantStatus = "REPLICATION CUTTING OVER" + initReplication tenantStatus = "initializing replication" + replicating tenantStatus = "replicating" + replicationPaused tenantStatus = "replication paused" + cuttingOver tenantStatus = "replication cutting over" // Users should not see this status normally. - replicationUnknownFormat tenantStatus = "REPLICATION UNKNOWN (%s)" + replicationUnknownFormat tenantStatus = "replication unknown (%s)" ) type tenantValues struct { @@ -176,7 +177,8 @@ func (n *showTenantNode) getTenantValues( if n.withReplication { return nil, errors.Newf("tenant %q does not have an active replication job", tenantInfo.Name) } - values.tenantStatus = tenantStatus(values.tenantInfo.DataState.String()) + dataState := strings.ToLower(values.tenantInfo.DataState.String()) + values.tenantStatus = tenantStatus(dataState) return &values, nil } @@ -206,9 +208,10 @@ func (n *showTenantNode) getTenantValues( values.tenantStatus = getTenantStatus(job.Status(), values.replicationInfo) case descpb.TenantInfo_READY, descpb.TenantInfo_DROP: - values.tenantStatus = tenantStatus(values.tenantInfo.DataState.String()) + dataState := strings.ToLower(values.tenantInfo.DataState.String()) + values.tenantStatus = tenantStatus(dataState) default: - return nil, errors.Newf("tenant %q state is unknown: %s", tenantInfo.Name, values.tenantInfo.DataState.String()) + return nil, errors.Newf("tenant %q state is unknown: %s", tenantInfo.Name, values.tenantInfo.DataState) } return &values, nil } @@ -239,7 +242,7 @@ func (n *showTenantNode) Values() tree.Datums { tree.NewDInt(tree.DInt(tenantInfo.ID)), tree.NewDString(string(tenantInfo.Name)), tree.NewDString(string(v.tenantStatus)), - tree.NewDString(tenantInfo.ServiceMode.String()), + tree.NewDString(strings.ToLower(tenantInfo.ServiceMode.String())), } if n.withReplication {