From b187052561691c5108a1d24602a6a4420aa97881 Mon Sep 17 00:00:00 2001 From: Oliver Tan Date: Mon, 18 Jan 2021 09:46:54 +1100 Subject: [PATCH] sql: implement REGIONAL BY ROW logic on CREATE TABLE Release note (sql change): Implemented REGIONAL BY ROW logic on create table. This is gated behind the experimental session variable `experimental_enable_implicit_column_partitioning`. --- .../testdata/logic_test/regional_by_row | 217 ++++++++++++++++++ pkg/sql/catalog/descriptor.go | 2 + pkg/sql/create_table.go | 143 ++++++++++-- .../logictest/testdata/logic_test/multiregion | 101 -------- pkg/sql/region_util.go | 103 +++++++-- pkg/sql/region_util_test.go | 115 +++++++++- pkg/sql/sem/tree/name_resolution.go | 3 - pkg/sql/sem/tree/region.go | 11 + pkg/sql/show_create_clauses.go | 7 + 9 files changed, 552 insertions(+), 150 deletions(-) create mode 100644 pkg/ccl/logictestccl/testdata/logic_test/regional_by_row diff --git a/pkg/ccl/logictestccl/testdata/logic_test/regional_by_row b/pkg/ccl/logictestccl/testdata/logic_test/regional_by_row new file mode 100644 index 000000000000..a0f6494eed02 --- /dev/null +++ b/pkg/ccl/logictestccl/testdata/logic_test/regional_by_row @@ -0,0 +1,217 @@ +# LogicTest: multiregion-9node-3region-3azs + +statement ok +CREATE DATABASE multi_region_test_db PRIMARY REGION "ca-central-1" REGIONS "ap-southeast-2", "us-east-1" SURVIVE REGION FAILURE + +statement error REGIONAL BY ROW is currently experimental +CREATE TABLE regional_by_row_table (pk int) LOCALITY REGIONAL BY ROW + +statement ok +SET experimental_enable_implicit_column_partitioning = true + +statement error cannot set LOCALITY on a database that is not multi-region enabled +CREATE TABLE regional_by_row_table (pk int) LOCALITY REGIONAL BY ROW + +statement ok +USE multi_region_test_db + +statement error REGIONAL BY ROW on a TABLE containing PARTITION BY is not supported +CREATE TABLE regional_by_row_table ( + pk int +) +PARTITION BY LIST (pk) (PARTITION one VALUES IN ((1))) +LOCALITY REGIONAL BY ROW + +statement error REGIONAL BY ROW AS is not yet supported +CREATE TABLE regional_by_row_table_on_col (a int) LOCALITY REGIONAL BY ROW AS a + +statement error REGIONAL BY ROW on a table with an INDEX containing PARTITION BY is not supported +CREATE TABLE regional_by_row_table ( + pk int, + a int, + INDEX (a) PARTITION BY LIST (a) (PARTITION one VALUES IN ((1))) +) +LOCALITY REGIONAL BY ROW + +statement error REGIONAL BY ROW on a table with an UNIQUE constraint containing PARTITION BY is not supported +CREATE TABLE regional_by_row_table ( + pk int, + a int, + UNIQUE (a) PARTITION BY LIST (a) (PARTITION one VALUES IN ((1))) +) +LOCALITY REGIONAL BY ROW + +statement ok +CREATE TABLE regional_by_row_table ( + pk int PRIMARY KEY, + a int, + b int, + INDEX (a), + UNIQUE (b), + FAMILY (pk, a, b) +) LOCALITY REGIONAL BY ROW + +# TODO(#multiregion): determine how we should display the presence of a crdb_region column +# to the user. +# TODO(#multiregion): ensure this CREATE TABLE statement round trips. +query T +SELECT create_statement FROM [SHOW CREATE TABLE regional_by_row_table] +---- +CREATE TABLE public.regional_by_row_table ( + pk INT8 NOT NULL, + a INT8 NULL, + b INT8 NULL, + crdb_region public.crdb_internal_region NOT NULL DEFAULT gateway_region()::public.crdb_internal_region, + CONSTRAINT "primary" PRIMARY KEY (pk ASC), + INDEX regional_by_row_table_a_idx (a ASC), + UNIQUE INDEX regional_by_row_table_b_key (b ASC), + FAMILY fam_0_pk_a_b_crdb_region (pk, a, b, crdb_region) +) LOCALITY REGIONAL BY ROW; +ALTER PARTITION "ap-southeast-2" OF INDEX multi_region_test_db.public.regional_by_row_table@regional_by_row_table_a_idx CONFIGURE ZONE USING + num_replicas = 3, + constraints = '{+region=ap-southeast-2: 1}', + lease_preferences = '[[+region=ap-southeast-2]]'; +ALTER PARTITION "ap-southeast-2" OF INDEX multi_region_test_db.public.regional_by_row_table@regional_by_row_table_b_key CONFIGURE ZONE USING + num_replicas = 3, + constraints = '{+region=ap-southeast-2: 1}', + lease_preferences = '[[+region=ap-southeast-2]]'; +ALTER PARTITION "ap-southeast-2" OF INDEX multi_region_test_db.public.regional_by_row_table@primary CONFIGURE ZONE USING + num_replicas = 3, + constraints = '{+region=ap-southeast-2: 1}', + lease_preferences = '[[+region=ap-southeast-2]]'; +ALTER PARTITION "ca-central-1" OF INDEX multi_region_test_db.public.regional_by_row_table@regional_by_row_table_a_idx CONFIGURE ZONE USING + num_replicas = 3, + constraints = '{+region=ca-central-1: 1}', + lease_preferences = '[[+region=ca-central-1]]'; +ALTER PARTITION "ca-central-1" OF INDEX multi_region_test_db.public.regional_by_row_table@regional_by_row_table_b_key CONFIGURE ZONE USING + num_replicas = 3, + constraints = '{+region=ca-central-1: 1}', + lease_preferences = '[[+region=ca-central-1]]'; +ALTER PARTITION "ca-central-1" OF INDEX multi_region_test_db.public.regional_by_row_table@primary CONFIGURE ZONE USING + num_replicas = 3, + constraints = '{+region=ca-central-1: 1}', + lease_preferences = '[[+region=ca-central-1]]'; +ALTER PARTITION "us-east-1" OF INDEX multi_region_test_db.public.regional_by_row_table@regional_by_row_table_a_idx CONFIGURE ZONE USING + num_replicas = 3, + constraints = '{+region=us-east-1: 1}', + lease_preferences = '[[+region=us-east-1]]'; +ALTER PARTITION "us-east-1" OF INDEX multi_region_test_db.public.regional_by_row_table@regional_by_row_table_b_key CONFIGURE ZONE USING + num_replicas = 3, + constraints = '{+region=us-east-1: 1}', + lease_preferences = '[[+region=us-east-1]]'; +ALTER PARTITION "us-east-1" OF INDEX multi_region_test_db.public.regional_by_row_table@primary CONFIGURE ZONE USING + num_replicas = 3, + constraints = '{+region=us-east-1: 1}', + lease_preferences = '[[+region=us-east-1]]' + +query TTB colnames +SELECT index_name, column_name, implicit FROM crdb_internal.index_columns +WHERE descriptor_name = 'regional_by_row_table' AND column_type = 'key' +ORDER BY 1, 2 +---- +index_name column_name implicit +primary crdb_region true +primary pk false +regional_by_row_table_a_idx a false +regional_by_row_table_a_idx crdb_region true +regional_by_row_table_b_key b false +regional_by_row_table_b_key crdb_region true + +query TTTTIT colnames +SHOW TABLES +---- +schema_name table_name type owner estimated_row_count locality +public regional_by_row_table table root 0 REGIONAL BY ROW + +query TI +INSERT INTO regional_by_row_table (pk, a, b) VALUES +(1, 2, 3), (4, 5, 6) +RETURNING crdb_region, pk +---- +ap-southeast-2 1 +ap-southeast-2 4 + +query TI +INSERT INTO regional_by_row_table (crdb_region, pk, a, b) VALUES +('ca-central-1', 7, 8, 9) +RETURNING crdb_region, pk +---- +ca-central-1 7 + +query TI nodeidx=3 +INSERT INTO multi_region_test_db.regional_by_row_table (pk, a, b) VALUES +(10, 11, 12) +RETURNING crdb_region, pk +---- +ca-central-1 10 + +query TI nodeidx=6 +INSERT INTO multi_region_test_db.regional_by_row_table (pk, a, b) VALUES +(20, 21, 22) +RETURNING crdb_region, pk +---- +us-east-1 20 + +query TI +INSERT INTO regional_by_row_table (crdb_region, pk, a, b) VALUES +(gateway_region()::crdb_internal_region, 23, 24, 25) +RETURNING crdb_region, pk +---- +ap-southeast-2 23 + +query TIII +SELECT crdb_region, pk, a, b FROM regional_by_row_table +ORDER BY pk +---- +ap-southeast-2 1 2 3 +ap-southeast-2 4 5 6 +ca-central-1 7 8 9 +ca-central-1 10 11 12 +us-east-1 20 21 22 +ap-southeast-2 23 24 25 + +# Create some tables to validate that their zone configurations are adjusted appropriately. +statement ok +CREATE DATABASE alter_survive_db PRIMARY REGION "us-east-1" REGIONS "ca-central-1", "ap-southeast-2" SURVIVE REGION FAILURE + +statement ok +USE alter_survive_db + +statement ok +CREATE TABLE t_regional_by_row () LOCALITY REGIONAL BY ROW + +query TT +SHOW ZONE CONFIGURATION FOR TABLE t_regional_by_row PARTITION "us-east-1" +---- +PARTITION "us-east-1" OF TABLE t_regional_by_row ALTER PARTITION "us-east-1" OF TABLE t_regional_by_row CONFIGURE ZONE USING + range_min_bytes = 134217728, + range_max_bytes = 536870912, + gc.ttlseconds = 90000, + num_replicas = 3, + constraints = '{+region=us-east-1: 1}', + lease_preferences = '[[+region=us-east-1]]' + +query TT +SHOW ZONE CONFIGURATION FOR TABLE t_regional_by_row +---- +DATABASE alter_survive_db ALTER DATABASE alter_survive_db CONFIGURE ZONE USING + range_min_bytes = 134217728, + range_max_bytes = 536870912, + gc.ttlseconds = 90000, + num_replicas = 3, + constraints = '{+region=ap-southeast-2: 1, +region=ca-central-1: 1, +region=us-east-1: 1}', + lease_preferences = '[[+region=us-east-1]]' + +statement ok +ALTER DATABASE alter_survive_db SURVIVE ZONE FAILURE + +query TT +SHOW ZONE CONFIGURATION FOR TABLE t_regional_by_row PARTITION "us-east-1" +---- +PARTITION "us-east-1" OF TABLE t_regional_by_row ALTER PARTITION "us-east-1" OF TABLE t_regional_by_row CONFIGURE ZONE USING + range_min_bytes = 134217728, + range_max_bytes = 536870912, + gc.ttlseconds = 90000, + num_replicas = 3, + constraints = '{+region=us-east-1: 3}', + lease_preferences = '[[+region=us-east-1]]' diff --git a/pkg/sql/catalog/descriptor.go b/pkg/sql/catalog/descriptor.go index d38ad2e5c401..7595941c806d 100644 --- a/pkg/sql/catalog/descriptor.go +++ b/pkg/sql/catalog/descriptor.go @@ -246,6 +246,8 @@ type TableDescriptor interface { ForeachInboundFK(f func(fk *descpb.ForeignKeyConstraint) error) error FindActiveColumnByName(s string) (*descpb.ColumnDescriptor, error) WritableColumns() []descpb.ColumnDescriptor + + GetLocalityConfig() *descpb.TableDescriptor_LocalityConfig } // Index is an interface around the index descriptor types. diff --git a/pkg/sql/create_table.go b/pkg/sql/create_table.go index aa116ce888d0..094d8904a9ee 100644 --- a/pkg/sql/create_table.go +++ b/pkg/sql/create_table.go @@ -34,6 +34,7 @@ import ( "github.com/cockroachdb/cockroach/pkg/sql/catalog/resolver" "github.com/cockroachdb/cockroach/pkg/sql/catalog/schemaexpr" "github.com/cockroachdb/cockroach/pkg/sql/catalog/tabledesc" + "github.com/cockroachdb/cockroach/pkg/sql/catalog/typedesc" "github.com/cockroachdb/cockroach/pkg/sql/paramparse" "github.com/cockroachdb/cockroach/pkg/sql/parser" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" @@ -378,7 +379,7 @@ func (n *createTableNode) startExec(params runParams) error { if err := params.p.applyZoneConfigFromTableLocalityConfig( params.ctx, n.n.Table, - *desc.LocalityConfig, + desc.TableDesc(), *dbDesc.RegionConfig, ); err != nil { return err @@ -1349,6 +1350,114 @@ func NewTableDesc( } } + // Add implied columns under REGIONAL BY ROW. + locality := n.Locality + var partitionByAll *tree.PartitionBy + if locality != nil && locality.LocalityLevel == tree.LocalityLevelRow { + // TODO(#multiregion): consider decoupling implicit column partitioning from the + // REGIONAL BY ROW experimental flag. + if !evalCtx.SessionData.ImplicitColumnPartitioningEnabled { + return nil, errors.WithHint( + pgerror.New( + pgcode.FeatureNotSupported, + "REGIONAL BY ROW is currently experimental", + ), + "to enable, use SET experimental_enable_implicit_column_partitioning = true", + ) + } + + // Check no PARTITION BY is set on any field. + if n.PartitionByTable.ContainsPartitions() { + return nil, pgerror.New( + pgcode.FeatureNotSupported, + "REGIONAL BY ROW on a TABLE containing PARTITION BY is not supported", + ) + } + for _, def := range n.Defs { + switch d := def.(type) { + case *tree.IndexTableDef: + if d.PartitionByIndex.ContainsPartitions() { + return nil, pgerror.New( + pgcode.FeatureNotSupported, + "REGIONAL BY ROW on a table with an INDEX containing PARTITION BY is not supported", + ) + } + case *tree.UniqueConstraintTableDef: + if d.PartitionByIndex.ContainsPartitions() { + return nil, pgerror.New( + pgcode.FeatureNotSupported, + "REGIONAL BY ROW on a table with an UNIQUE constraint containing PARTITION BY is not supported", + ) + } + } + } + if n.Locality.RegionalByRowColumn != "" { + return nil, unimplemented.New( + "REGIONAL BY ROW AS", + "REGIONAL BY ROW AS is not yet supported", + ) + } + + // Check the table is multi-region enabled. + dbDesc, err := catalogkv.MustGetDatabaseDescByID(ctx, txn, evalCtx.Codec, parentID) + if err != nil { + return nil, err + } + if !dbDesc.IsMultiRegion() { + return nil, pgerror.Newf( + pgcode.InvalidTableDefinition, + "cannot set LOCALITY on a database that is not multi-region enabled", + ) + } + + // Add implicit crdb_region column. + oid := typedesc.TypeIDToOID(dbDesc.RegionConfig.RegionEnumID) + // TODO(#multiregion): set the column visibility to be hidden. + c := &tree.ColumnTableDef{ + Name: tree.RegionalByRowRegionDefaultColName, + Type: &tree.OIDTypeReference{OID: oid}, + } + c.Nullable.Nullability = tree.NotNull + c.DefaultExpr.Expr = &tree.CastExpr{ + Expr: &tree.FuncExpr{ + Func: tree.WrapFunction("gateway_region"), + }, + Type: &tree.OIDTypeReference{OID: oid}, + SyntaxMode: tree.CastShort, + } + n.Defs = append(n.Defs, c) + columnDefaultExprs = append(columnDefaultExprs, nil) + + // Construct the partitioning for the PARTITION ALL BY. + listPartition := make([]tree.ListPartition, len(dbDesc.RegionConfig.Regions)) + for i, region := range dbDesc.RegionConfig.Regions { + listPartition[i] = tree.ListPartition{ + Name: tree.UnrestrictedName(region.Name), + Exprs: tree.Exprs{tree.NewStrVal(string(region.Name))}, + } + } + + desc.PartitionAllBy = true + partitionByAll = &tree.PartitionBy{ + Fields: tree.NameList{tree.RegionalByRowRegionDefaultColName}, + List: listPartition, + } + } + + if n.PartitionByTable != nil && n.PartitionByTable.All { + if !evalCtx.SessionData.ImplicitColumnPartitioningEnabled { + return nil, errors.WithHint( + pgerror.New( + pgcode.FeatureNotSupported, + "PARTITION ALL BY LIST/RANGE is currently experimental", + ), + "to enable, use SET experimental_enable_implicit_column_partitioning = true", + ) + } + desc.PartitionAllBy = true + partitionByAll = n.PartitionByTable.PartitionBy + } + for i, def := range n.Defs { if d, ok := def.(*tree.ColumnTableDef); ok { // NewTableDesc is called sometimes with a nil SemaCtx (for example @@ -1512,21 +1621,6 @@ func NewTableDesc( return nil } - var partitionByAll *tree.PartitionBy - if n.PartitionByTable != nil && n.PartitionByTable.All { - if !evalCtx.SessionData.ImplicitColumnPartitioningEnabled { - return nil, errors.WithHint( - pgerror.New( - pgcode.FeatureNotSupported, - "PARTITION ALL BY LIST/RANGE is currently experimental", - ), - "to enable, use SET experimental_enable_implicit_column_partitioning = true", - ) - } - desc.PartitionAllBy = true - partitionByAll = n.PartitionByTable.PartitionBy - } - idxValidator := schemaexpr.MakeIndexPredicateValidator(ctx, n.Table, &desc, semaCtx) for _, def := range n.Defs { switch d := def.(type) { @@ -1794,12 +1888,17 @@ func NewTableDesc( } } - if n.PartitionByTable.ContainsPartitions() { + if n.PartitionByTable.ContainsPartitions() || partitionByAll != nil { + partitionBy := partitionByAll + if partitionBy == nil { + partitionBy = n.PartitionByTable.PartitionBy + } + newPrimaryIndex, numImplicitColumns, err := detectImplicitPartitionColumns( evalCtx, &desc, *desc.GetPrimaryIndex().IndexDesc(), - n.PartitionByTable.PartitionBy, + partitionBy, ) if err != nil { return nil, err @@ -1811,7 +1910,7 @@ func NewTableDesc( &desc, &newPrimaryIndex, numImplicitColumns, - n.PartitionByTable.PartitionBy, + partitionBy, ) if err != nil { return nil, err @@ -2027,12 +2126,6 @@ func NewTableDesc( desc.LocalityConfig.Locality = &descpb.TableDescriptor_LocalityConfig_RegionalByRow_{ RegionalByRow: &descpb.TableDescriptor_LocalityConfig_RegionalByRow{}, } - if n.Locality.RegionalByRowColumn != "" { - return nil, unimplemented.New( - "REGIONAL BY ROW AS", - "REGIONAL BY ROW AS is not yet supported", - ) - } default: return nil, errors.Newf("unknown locality level: %v", n.Locality.LocalityLevel) } diff --git a/pkg/sql/logictest/testdata/logic_test/multiregion b/pkg/sql/logictest/testdata/logic_test/multiregion index 254d598c1e2d..a6530ed77b49 100644 --- a/pkg/sql/logictest/testdata/logic_test/multiregion +++ b/pkg/sql/logictest/testdata/logic_test/multiregion @@ -289,31 +289,6 @@ TABLE "regional_us-east-1_table" ALTER TABLE "regional_us-east-1_table" CONFIGU statement error region "test4" has not been added to database "multi_region_test_db"\nHINT: available regions: ap-southeast-2, ca-central-1, us-east-1 CREATE TABLE regional_test4_table (a int) LOCALITY REGIONAL BY TABLE IN "test4" -statement error REGIONAL BY ROW AS is not yet supported -CREATE TABLE regional_by_row_table_on_col (a int) LOCALITY REGIONAL BY ROW AS a - -statement ok -CREATE TABLE regional_by_row_table (a int) LOCALITY REGIONAL BY ROW - -query T -SELECT create_statement FROM [SHOW CREATE TABLE regional_by_row_table] ----- -CREATE TABLE public.regional_by_row_table ( - a INT8 NULL, - FAMILY "primary" (a, rowid) -) LOCALITY REGIONAL BY ROW - -query TT -SHOW ZONE CONFIGURATION FOR TABLE regional_by_row_table ----- -TABLE regional_by_row_table ALTER TABLE regional_by_row_table CONFIGURE ZONE USING - range_min_bytes = 134217728, - range_max_bytes = 536870912, - gc.ttlseconds = 90000, - num_replicas = 3, - constraints = '[]', - lease_preferences = '[]' - statement ok CREATE TABLE global_table (a int) LOCALITY GLOBAL @@ -341,7 +316,6 @@ SHOW TABLES ---- schema_name table_name type owner estimated_row_count locality public global_table table root 0 GLOBAL -public regional_by_row_table table root 0 REGIONAL BY ROW public regional_implicit_primary_region_table table root 0 REGIONAL BY TABLE IN PRIMARY REGION public regional_primary_region_table table root 0 REGIONAL BY TABLE IN PRIMARY REGION public regional_us-east-1_table table root 0 REGIONAL BY TABLE IN "us-east-1" @@ -437,9 +411,6 @@ ALTER DATABASE new_db ADD REGION "us-west-1" statement error implementation pending ALTER DATABASE new_db DROP REGION "us-west-1" -statement error implementation pending -ALTER TABLE a SET LOCALITY REGIONAL BY ROW - statement ok CREATE DATABASE alter_primary_region_db @@ -576,9 +547,6 @@ statement ok alter database alter_survive_db add region "us-east-1" # Create some tables to validate that their zone configurations are adjusted appropriately. -statement ok -create table t_regional_by_table_in_us_east_1 (i int) locality regional by table in "us-east-1" - query TT SHOW ZONE CONFIGURATION FOR DATABASE alter_survive_db ---- @@ -590,17 +558,6 @@ DATABASE alter_survive_db ALTER DATABASE alter_survive_db CONFIGURE ZONE USING constraints = '{+region=ap-southeast-2: 1, +region=ca-central-1: 1, +region=us-east-1: 1}', lease_preferences = '[[+region=ca-central-1]]' -query TT -SHOW ZONE CONFIGURATION FOR TABLE t_regional_by_table_in_us_east_1 ----- -TABLE t_regional_by_table_in_us_east_1 ALTER TABLE t_regional_by_table_in_us_east_1 CONFIGURE ZONE USING - range_min_bytes = 134217728, - range_max_bytes = 536870912, - gc.ttlseconds = 90000, - num_replicas = 3, - constraints = '{+region=us-east-1: 3}', - lease_preferences = '[[+region=us-east-1]]' - statement ok create table t_no_locality (i int) @@ -629,20 +586,6 @@ TABLE t_global ALTER TABLE t_global CONFIGURE ZONE USING constraints = '{+region=ap-southeast-2: 1, +region=ca-central-1: 1, +region=us-east-1: 1}', lease_preferences = '[[+region=ca-central-1]]' -statement ok -create table t_regional_by_row (i int) locality regional by row - -query TT -SHOW ZONE CONFIGURATION FOR TABLE t_regional_by_row ----- -TABLE t_regional_by_row ALTER TABLE t_regional_by_row CONFIGURE ZONE USING - range_min_bytes = 134217728, - range_max_bytes = 536870912, - gc.ttlseconds = 90000, - num_replicas = 3, - constraints = '[]', - lease_preferences = '[]' - query TTTTT colnames SHOW DATABASES ---- @@ -702,17 +645,6 @@ DATABASE alter_survive_db ALTER DATABASE alter_survive_db CONFIGURE ZONE USING constraints = '{+region=ap-southeast-2: 1, +region=ca-central-1: 1, +region=us-east-1: 1}', lease_preferences = '[[+region=ca-central-1]]' -query TT -SHOW ZONE CONFIGURATION FOR TABLE t_regional_by_table_in_us_east_1 ----- -TABLE t_regional_by_table_in_us_east_1 ALTER TABLE t_regional_by_table_in_us_east_1 CONFIGURE ZONE USING - range_min_bytes = 134217728, - range_max_bytes = 536870912, - gc.ttlseconds = 90000, - num_replicas = 3, - constraints = '{+region=us-east-1: 1}', - lease_preferences = '[[+region=us-east-1]]' - query TT SHOW ZONE CONFIGURATION FOR TABLE t_no_locality ---- @@ -735,17 +667,6 @@ TABLE t_global ALTER TABLE t_global CONFIGURE ZONE USING constraints = '{+region=ap-southeast-2: 1, +region=ca-central-1: 1, +region=us-east-1: 1}', lease_preferences = '[[+region=ca-central-1]]' -query TT -SHOW ZONE CONFIGURATION FOR TABLE t_regional_by_row ----- -TABLE t_regional_by_row ALTER TABLE t_regional_by_row CONFIGURE ZONE USING - range_min_bytes = 134217728, - range_max_bytes = 536870912, - gc.ttlseconds = 90000, - num_replicas = 3, - constraints = '[]', - lease_preferences = '[]' - statement ok alter database alter_survive_db survive zone failure @@ -777,17 +698,6 @@ DATABASE alter_survive_db ALTER DATABASE alter_survive_db CONFIGURE ZONE USING constraints = '{+region=ap-southeast-2: 1, +region=ca-central-1: 1, +region=us-east-1: 1}', lease_preferences = '[[+region=ca-central-1]]' -query TT -SHOW ZONE CONFIGURATION FOR TABLE t_regional_by_table_in_us_east_1 ----- -TABLE t_regional_by_table_in_us_east_1 ALTER TABLE t_regional_by_table_in_us_east_1 CONFIGURE ZONE USING - range_min_bytes = 134217728, - range_max_bytes = 536870912, - gc.ttlseconds = 90000, - num_replicas = 3, - constraints = '{+region=us-east-1: 3}', - lease_preferences = '[[+region=us-east-1]]' - query TT SHOW ZONE CONFIGURATION FOR TABLE t_no_locality ---- @@ -809,14 +719,3 @@ TABLE t_global ALTER TABLE t_global CONFIGURE ZONE USING num_replicas = 3, constraints = '{+region=ap-southeast-2: 1, +region=ca-central-1: 1, +region=us-east-1: 1}', lease_preferences = '[[+region=ca-central-1]]' - -query TT -SHOW ZONE CONFIGURATION FOR TABLE t_regional_by_row ----- -TABLE t_regional_by_row ALTER TABLE t_regional_by_row CONFIGURE ZONE USING - range_min_bytes = 134217728, - range_max_bytes = 536870912, - gc.ttlseconds = 90000, - num_replicas = 3, - constraints = '[]', - lease_preferences = '[]' diff --git a/pkg/sql/region_util.go b/pkg/sql/region_util.go index c18e8bc17499..118104eab779 100644 --- a/pkg/sql/region_util.go +++ b/pkg/sql/region_util.go @@ -118,6 +118,30 @@ func zoneConfigFromRegionConfigForDatabase( } } +// zoneConfigFromRegionConfigForPartition generates a desired ZoneConfig based +// on the region config for the given partition. +// TODO(#multiregion,aayushshah15): properly configure this for region survivability and leaseholder +// preferences when new zone configuration parameters merge. +func zoneConfigFromRegionConfigForPartition( + partitionRegion descpb.DatabaseDescriptor_RegionConfig_Region, + regionConfig descpb.DatabaseDescriptor_RegionConfig, +) (*zonepb.ZoneConfig, error) { + zc := &zonepb.ZoneConfig{ + NumReplicas: proto.Int32(zoneConfigNumReplicasFromRegionConfig(regionConfig)), + LeasePreferences: []zonepb.LeasePreference{ + {Constraints: []zonepb.Constraint{makeRequiredZoneConstraintForRegion(partitionRegion.Name)}}, + }, + } + var err error + if zc.Constraints, err = constraintsConjunctionForRegionalLocality( + partitionRegion.Name, + regionConfig, + ); err != nil { + return nil, err + } + return zc, err +} + // zoneConfigNumReplicasFromRegionConfig generates the number of replicas needed given a region config. func zoneConfigNumReplicasFromRegionConfig( regionConfig descpb.DatabaseDescriptor_RegionConfig, @@ -190,10 +214,9 @@ func zoneConfigFromTableLocalityConfig( {Constraints: []zonepb.Constraint{makeRequiredZoneConstraintForRegion(preferredRegion)}}, } case *descpb.TableDescriptor_LocalityConfig_RegionalByRow_: - // TODO(#multiregion): figure out correct partition/subzone fields to make this work. - // We need to fill the table with PARTITION BY to get the ball rolling on this. - // For now, return a dummy field for now so that other tests relying on this path of code to - // exercise other functionality (i.e. SHOW) pass. + // We purposely do not set anything here at table level - this should be done at + // partition level instead. + return nil, nil } return &ret, nil } @@ -225,17 +248,11 @@ func (p *planner) applyZoneConfigForMultiRegion( func (p *planner) applyZoneConfigFromTableLocalityConfig( ctx context.Context, tblName tree.TableName, - localityConfig descpb.TableDescriptor_LocalityConfig, + desc *descpb.TableDescriptor, regionConfig descpb.DatabaseDescriptor_RegionConfig, ) error { - localityZoneConfig, err := zoneConfigFromTableLocalityConfig(localityConfig, regionConfig) - if err != nil { - return err - } - // If we do not have to configure anything, exit early. - if localityZoneConfig == nil { - return nil - } + localityConfig := *desc.LocalityConfig + // Construct an explicit name so that CONFIGURE ZONE has the fully qualified name. // Without this, the table may fail to resolve. explicitTblName := tree.MakeTableNameWithSchema( @@ -243,6 +260,64 @@ func (p *planner) applyZoneConfigFromTableLocalityConfig( tblName.SchemaName, tblName.ObjectName, ) + + // Apply zone configs for each index partition. + // TODO(otan): depending on what we decide for cascading zone configs, some of this + // code will have to change. + switch localityConfig.Locality.(type) { + case *descpb.TableDescriptor_LocalityConfig_RegionalByRow_: + for _, region := range regionConfig.Regions { + zc, err := zoneConfigFromRegionConfigForPartition(region, regionConfig) + if err != nil { + return err + } + + for _, idx := range desc.Indexes { + if err := p.applyZoneConfigForMultiRegion( + ctx, + tree.ZoneSpecifier{ + TableOrIndex: tree.TableIndexName{ + Table: explicitTblName, + Index: tree.UnrestrictedName(idx.Name), + }, + Partition: tree.Name(region.Name), + }, + zc, + "index-multiregion-set-zone-config", + ); err != nil { + return err + } + } + + if err := p.applyZoneConfigForMultiRegion( + ctx, + tree.ZoneSpecifier{ + TableOrIndex: tree.TableIndexName{ + Table: explicitTblName, + Index: tree.UnrestrictedName(desc.PrimaryIndex.Name), + }, + Partition: tree.Name(region.Name), + }, + zc, + "primary-index-multiregion-set-zone-config", + ); err != nil { + return err + } + } + } + + localityZoneConfig, err := zoneConfigFromTableLocalityConfig( + localityConfig, + regionConfig, + ) + if err != nil { + return err + } + // If we do not have to configure anything, exit early. + if localityZoneConfig == nil { + return nil + } + return p.applyZoneConfigForMultiRegion( ctx, tree.ZoneSpecifier{TableOrIndex: tree.TableIndexName{Table: explicitTblName}}, @@ -317,7 +392,7 @@ func (p *planner) updateZoneConfigsForAllTables(ctx context.Context, desc *dbdes if err := p.applyZoneConfigFromTableLocalityConfig( ctx, tbNames[i], - *tbDesc.LocalityConfig, + tbDesc.TableDesc(), *desc.RegionConfig, ); err != nil { return err diff --git a/pkg/sql/region_util_test.go b/pkg/sql/region_util_test.go index d76cbd6e3b8d..30911c07ca00 100644 --- a/pkg/sql/region_util_test.go +++ b/pkg/sql/region_util_test.go @@ -225,7 +225,7 @@ func TestZoneConfigFromTableLocalityConfig(t *testing.T) { {Name: "region_d"}, }, PrimaryRegion: "region_b", - SurvivalGoal: descpb.SurvivalGoal_ZONE_FAILURE, + SurvivalGoal: descpb.SurvivalGoal_REGION_FAILURE, }, expected: &zonepb.ZoneConfig{ NumReplicas: proto.Int32(4), @@ -250,9 +250,7 @@ func TestZoneConfigFromTableLocalityConfig(t *testing.T) { PrimaryRegion: "region_b", SurvivalGoal: descpb.SurvivalGoal_ZONE_FAILURE, }, - expected: &zonepb.ZoneConfig{ - NumReplicas: proto.Int32(4), - }, + expected: nil, }, { desc: "4-region regional by row table with region survival", @@ -271,9 +269,7 @@ func TestZoneConfigFromTableLocalityConfig(t *testing.T) { PrimaryRegion: "region_b", SurvivalGoal: descpb.SurvivalGoal_ZONE_FAILURE, }, - expected: &zonepb.ZoneConfig{ - NumReplicas: proto.Int32(4), - }, + expected: nil, }, { desc: "4-region regional by table with zone survival on primary region", @@ -384,3 +380,108 @@ func TestZoneConfigFromTableLocalityConfig(t *testing.T) { }) } } + +func TestZoneConfigFromRegionConfigForPartition(t *testing.T) { + defer leaktest.AfterTest(t)() + + testCases := []struct { + desc string + region descpb.DatabaseDescriptor_RegionConfig_Region + regionConfig descpb.DatabaseDescriptor_RegionConfig + expected *zonepb.ZoneConfig + }{ + { + desc: "4-region table with zone survivability", + region: descpb.DatabaseDescriptor_RegionConfig_Region{ + Name: "region_a", + }, + regionConfig: descpb.DatabaseDescriptor_RegionConfig{ + Regions: []descpb.DatabaseDescriptor_RegionConfig_Region{ + {Name: "region_b"}, + {Name: "region_c"}, + {Name: "region_a"}, + {Name: "region_d"}, + }, + PrimaryRegion: "region_b", + SurvivalGoal: descpb.SurvivalGoal_ZONE_FAILURE, + }, + expected: &zonepb.ZoneConfig{ + NumReplicas: proto.Int32(4), + Constraints: []zonepb.ConstraintsConjunction{ + { + NumReplicas: 4, + Constraints: []zonepb.Constraint{ + { + Type: zonepb.Constraint_REQUIRED, + Key: "region", + Value: "region_a", + }, + }, + }, + }, + InheritedConstraints: false, + LeasePreferences: []zonepb.LeasePreference{ + { + Constraints: []zonepb.Constraint{ + { + Type: zonepb.Constraint_REQUIRED, + Key: "region", + Value: "region_a", + }, + }, + }, + }, + }, + }, + { + desc: "4-region global table with region survivaability", + region: descpb.DatabaseDescriptor_RegionConfig_Region{ + Name: "region_a", + }, + regionConfig: descpb.DatabaseDescriptor_RegionConfig{ + Regions: []descpb.DatabaseDescriptor_RegionConfig_Region{ + {Name: "region_b"}, + {Name: "region_c"}, + {Name: "region_a"}, + {Name: "region_d"}, + }, + PrimaryRegion: "region_b", + SurvivalGoal: descpb.SurvivalGoal_REGION_FAILURE, + }, + expected: &zonepb.ZoneConfig{ + NumReplicas: proto.Int32(4), + Constraints: []zonepb.ConstraintsConjunction{ + { + NumReplicas: 1, + Constraints: []zonepb.Constraint{ + { + Type: zonepb.Constraint_REQUIRED, + Key: "region", + Value: "region_a", + }, + }, + }, + }, + InheritedConstraints: false, + LeasePreferences: []zonepb.LeasePreference{ + { + Constraints: []zonepb.Constraint{ + { + Type: zonepb.Constraint_REQUIRED, + Key: "region", + Value: "region_a", + }, + }, + }, + }, + }, + }, + } + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + zc, err := zoneConfigFromRegionConfigForPartition(tc.region, tc.regionConfig) + require.NoError(t, err) + require.Equal(t, tc.expected, zc) + }) + } +} diff --git a/pkg/sql/sem/tree/name_resolution.go b/pkg/sql/sem/tree/name_resolution.go index 81e53568f74e..73c7c5f2412c 100644 --- a/pkg/sql/sem/tree/name_resolution.go +++ b/pkg/sql/sem/tree/name_resolution.go @@ -116,9 +116,6 @@ const ( PublicSchema string = sessiondata.PublicSchemaName // PublicSchemaName is the same, typed as Name. PublicSchemaName Name = Name(PublicSchema) - // RegionEnum is the name of the per-database region enum required for - // multi-region. - RegionEnum string = "crdb_internal_region" ) // NumResolutionResults represents the number of results in the lookup diff --git a/pkg/sql/sem/tree/region.go b/pkg/sql/sem/tree/region.go index 67becaf51eb6..bae4271850b7 100644 --- a/pkg/sql/sem/tree/region.go +++ b/pkg/sql/sem/tree/region.go @@ -27,6 +27,17 @@ const ( LocalityLevelRow ) +const ( + // RegionEnum is the name of the per-database region enum required for + // multi-region. + RegionEnum string = "crdb_internal_region" + // RegionalByRowRegionDefaultCol is the default name of the REGIONAL BY ROW + // column name if the AS field is not populated. + RegionalByRowRegionDefaultCol string = "crdb_region" + // RegionalByRowRegionDefaultColName is the same, typed as Name. + RegionalByRowRegionDefaultColName Name = Name(RegionalByRowRegionDefaultCol) +) + // Locality defines the locality for a given table. type Locality struct { LocalityLevel LocalityLevel diff --git a/pkg/sql/show_create_clauses.go b/pkg/sql/show_create_clauses.go index c04a71845136..fe4bb9794865 100644 --- a/pkg/sql/show_create_clauses.go +++ b/pkg/sql/show_create_clauses.go @@ -336,6 +336,13 @@ func ShowCreatePartitioning( if tableDesc.IsPartitionAllBy() && tableDesc.GetPrimaryIndexID() != idxDesc.ID { return nil } + // Do not print PARTITION ALL BY if we are a REGIONAL BY ROW table. + if c := tableDesc.GetLocalityConfig(); c != nil { + switch c.Locality.(type) { + case *descpb.TableDescriptor_LocalityConfig_RegionalByRow_: + return nil + } + } // We don't need real prefixes in the DecodePartitionTuple calls because we // only use the tree.Datums part of the output.