mirror of https://github.com/citusdata/citus.git
Add citus_schema_distribute/undistribute udfs to convert a schema into a tenant schema / back to a regular schema (#6933)
* Currently we do not allow any Citus tables other than Citus local tables inside a regular schema before executing `citus_schema_distribute`. * `citus_schema_undistribute` expects only single shard distributed tables inside a tenant schema. DESCRIPTION: Adds the udf `citus_schema_distribute` to convert a regular schema into a tenant schema. DESCRIPTION: Adds the udf `citus_schema_undistribute` to convert a tenant schema back to a regular schema. --------- Co-authored-by: Onur Tirtir <onurcantirtir@gmail.com>pull/6997/head^2
parent
e37ee16d59
commit
213d363bc3
|
@ -361,6 +361,37 @@ worker_change_sequence_dependency(PG_FUNCTION_ARGS)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* UndistributeTables undistributes given relations. It first collects all foreign keys
|
||||
* to recreate them after the undistribution. Then, drops the foreign keys and
|
||||
* undistributes the relations. Finally, it recreates foreign keys.
|
||||
*/
|
||||
void
|
||||
UndistributeTables(List *relationIdList)
|
||||
{
|
||||
/*
|
||||
* Collect foreign keys for recreation and then drop fkeys and undistribute
|
||||
* tables.
|
||||
*/
|
||||
List *originalForeignKeyRecreationCommands = NIL;
|
||||
Oid relationId = InvalidOid;
|
||||
foreach_oid(relationId, relationIdList)
|
||||
{
|
||||
List *fkeyCommandsForRelation =
|
||||
GetFKeyCreationCommandsRelationInvolvedWithTableType(relationId,
|
||||
INCLUDE_ALL_TABLE_TYPES);
|
||||
originalForeignKeyRecreationCommands = list_concat(
|
||||
originalForeignKeyRecreationCommands, fkeyCommandsForRelation);
|
||||
DropFKeysAndUndistributeTable(relationId);
|
||||
}
|
||||
|
||||
/* We can skip foreign key validations as we are sure about them at start */
|
||||
bool skip_validation = true;
|
||||
ExecuteForeignKeyCreateCommandList(originalForeignKeyRecreationCommands,
|
||||
skip_validation);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* UndistributeTable undistributes the given table. It uses ConvertTable function to
|
||||
* create a new local table and move everything to that table.
|
||||
|
|
|
@ -159,10 +159,6 @@ static void EnsureCitusTableCanBeCreated(Oid relationOid);
|
|||
static void PropagatePrerequisiteObjectsForDistributedTable(Oid relationId);
|
||||
static void EnsureDistributedSequencesHaveOneType(Oid relationId,
|
||||
List *seqInfoList);
|
||||
static List * GetFKeyCreationCommandsRelationInvolvedWithTableType(Oid relationId,
|
||||
int tableTypeFlag);
|
||||
static Oid DropFKeysAndUndistributeTable(Oid relationId);
|
||||
static void DropFKeysRelationInvolvedWithTableType(Oid relationId, int tableTypeFlag);
|
||||
static void CopyLocalDataIntoShards(Oid relationId);
|
||||
static List * TupleDescColumnNameList(TupleDesc tupleDescriptor);
|
||||
|
||||
|
@ -1580,7 +1576,7 @@ EnsureDistributedSequencesHaveOneType(Oid relationId, List *seqInfoList)
|
|||
* commands to recreate the foreign keys that relation with relationId is involved
|
||||
* with given table type.
|
||||
*/
|
||||
static List *
|
||||
List *
|
||||
GetFKeyCreationCommandsRelationInvolvedWithTableType(Oid relationId, int tableTypeFlag)
|
||||
{
|
||||
int referencingFKeysFlag = INCLUDE_REFERENCING_CONSTRAINTS |
|
||||
|
@ -1606,7 +1602,7 @@ GetFKeyCreationCommandsRelationInvolvedWithTableType(Oid relationId, int tableTy
|
|||
* Also note that callers are responsible for storing & recreating foreign
|
||||
* keys to be dropped if needed.
|
||||
*/
|
||||
static Oid
|
||||
Oid
|
||||
DropFKeysAndUndistributeTable(Oid relationId)
|
||||
{
|
||||
DropFKeysRelationInvolvedWithTableType(relationId, INCLUDE_ALL_TABLE_TYPES);
|
||||
|
@ -1639,7 +1635,7 @@ DropFKeysAndUndistributeTable(Oid relationId)
|
|||
* DropFKeysRelationInvolvedWithTableType drops foreign keys that relation
|
||||
* with relationId is involved with given table type.
|
||||
*/
|
||||
static void
|
||||
void
|
||||
DropFKeysRelationInvolvedWithTableType(Oid relationId, int tableTypeFlag)
|
||||
{
|
||||
int referencingFKeysFlag = INCLUDE_REFERENCING_CONSTRAINTS |
|
||||
|
|
|
@ -8,28 +8,43 @@
|
|||
|
||||
#include "postgres.h"
|
||||
#include "miscadmin.h"
|
||||
|
||||
#include "access/genam.h"
|
||||
#include "catalog/catalog.h"
|
||||
#include "catalog/pg_namespace_d.h"
|
||||
#include "commands/extension.h"
|
||||
#include "distributed/argutils.h"
|
||||
#include "distributed/backend_data.h"
|
||||
#include "distributed/colocation_utils.h"
|
||||
#include "distributed/commands.h"
|
||||
#include "distributed/listutils.h"
|
||||
#include "distributed/metadata_sync.h"
|
||||
#include "distributed/metadata/distobject.h"
|
||||
#include "distributed/multi_partitioning_utils.h"
|
||||
#include "distributed/tenant_schema_metadata.h"
|
||||
#include "distributed/metadata/distobject.h"
|
||||
#include "distributed/worker_shard_visibility.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/fmgroids.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/syscache.h"
|
||||
|
||||
|
||||
PG_FUNCTION_INFO_V1(citus_internal_unregister_tenant_schema_globally);
|
||||
|
||||
static void UnregisterTenantSchemaGlobally(Oid schemaId, char *schemaName);
|
||||
static List * SchemaGetNonShardTableIdList(Oid schemaId);
|
||||
static void EnsureSchemaCanBeDistributed(Oid schemaId, List *schemaTableIdList);
|
||||
static void EnsureTenantSchemaNameAllowed(Oid schemaId);
|
||||
static void EnsureTableKindSupportedForTenantSchema(Oid relationId);
|
||||
static void EnsureFKeysForTenantTable(Oid relationId);
|
||||
static void EnsureSchemaExist(Oid schemaId);
|
||||
|
||||
/* controlled via citus.enable_schema_based_sharding GUC */
|
||||
bool EnableSchemaBasedSharding = false;
|
||||
|
||||
|
||||
PG_FUNCTION_INFO_V1(citus_internal_unregister_tenant_schema_globally);
|
||||
PG_FUNCTION_INFO_V1(citus_schema_distribute);
|
||||
PG_FUNCTION_INFO_V1(citus_schema_undistribute);
|
||||
|
||||
/*
|
||||
* ShouldUseSchemaBasedSharding returns true if schema given name should be
|
||||
* used as a tenant schema.
|
||||
|
@ -108,6 +123,112 @@ ShouldCreateTenantSchemaTable(Oid relationId)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* EnsureTableKindSupportedForTenantSchema ensures that given table's kind is
|
||||
* supported by a tenant schema.
|
||||
*/
|
||||
static void
|
||||
EnsureTableKindSupportedForTenantSchema(Oid relationId)
|
||||
{
|
||||
if (IsForeignTable(relationId))
|
||||
{
|
||||
ereport(ERROR, (errmsg("cannot create a foreign table in a distributed "
|
||||
"schema")));
|
||||
}
|
||||
|
||||
if (PartitionTable(relationId))
|
||||
{
|
||||
ErrorIfIllegalPartitioningInTenantSchema(PartitionParentOid(relationId),
|
||||
relationId);
|
||||
}
|
||||
|
||||
if (PartitionedTable(relationId))
|
||||
{
|
||||
List *partitionList = PartitionList(relationId);
|
||||
|
||||
Oid partitionRelationId = InvalidOid;
|
||||
foreach_oid(partitionRelationId, partitionList)
|
||||
{
|
||||
ErrorIfIllegalPartitioningInTenantSchema(relationId, partitionRelationId);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsChildTable(relationId) || IsParentTable(relationId))
|
||||
{
|
||||
ereport(ERROR, (errmsg("tables in a distributed schema cannot inherit or "
|
||||
"be inherited")));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* EnsureFKeysForTenantTable ensures that all referencing and referenced foreign
|
||||
* keys are allowed for given table.
|
||||
*/
|
||||
static void
|
||||
EnsureFKeysForTenantTable(Oid relationId)
|
||||
{
|
||||
Oid tenantSchemaId = get_rel_namespace(relationId);
|
||||
int fKeyReferencingFlags = INCLUDE_REFERENCING_CONSTRAINTS | INCLUDE_ALL_TABLE_TYPES;
|
||||
List *referencingForeignKeys = GetForeignKeyOids(relationId, fKeyReferencingFlags);
|
||||
Oid foreignKeyId = InvalidOid;
|
||||
foreach_oid(foreignKeyId, referencingForeignKeys)
|
||||
{
|
||||
Oid referencingTableId = GetReferencingTableId(foreignKeyId);
|
||||
Oid referencedTableId = GetReferencedTableId(foreignKeyId);
|
||||
Oid referencedTableSchemaId = get_rel_namespace(referencedTableId);
|
||||
|
||||
/* We allow foreign keys to a table in the same schema */
|
||||
if (tenantSchemaId == referencedTableSchemaId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow foreign keys to the other schema only if the referenced table is
|
||||
* a reference table.
|
||||
*/
|
||||
if (!IsCitusTable(referencedTableId) ||
|
||||
!IsCitusTableType(referencedTableId, REFERENCE_TABLE))
|
||||
{
|
||||
ereport(ERROR, (errmsg("foreign keys from distributed schemas can only "
|
||||
"point to the same distributed schema or reference "
|
||||
"tables in regular schemas"),
|
||||
errdetail("\"%s\" references \"%s\" via foreign key "
|
||||
"constraint \"%s\"",
|
||||
generate_qualified_relation_name(
|
||||
referencingTableId),
|
||||
generate_qualified_relation_name(referencedTableId),
|
||||
get_constraint_name(foreignKeyId))));
|
||||
}
|
||||
}
|
||||
|
||||
int fKeyReferencedFlags = INCLUDE_REFERENCED_CONSTRAINTS | INCLUDE_ALL_TABLE_TYPES;
|
||||
List *referencedForeignKeys = GetForeignKeyOids(relationId, fKeyReferencedFlags);
|
||||
foreach_oid(foreignKeyId, referencedForeignKeys)
|
||||
{
|
||||
Oid referencingTableId = GetReferencingTableId(foreignKeyId);
|
||||
Oid referencedTableId = GetReferencedTableId(foreignKeyId);
|
||||
Oid referencingTableSchemaId = get_rel_namespace(referencingTableId);
|
||||
|
||||
/* We allow foreign keys from a table in the same schema */
|
||||
if (tenantSchemaId == referencingTableSchemaId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Not allow any foreign keys from the other schema */
|
||||
ereport(ERROR, (errmsg("cannot create foreign keys to tables in a distributed "
|
||||
"schema from another schema"),
|
||||
errdetail("\"%s\" references \"%s\" via foreign key "
|
||||
"constraint \"%s\"",
|
||||
generate_qualified_relation_name(referencingTableId),
|
||||
generate_qualified_relation_name(referencedTableId),
|
||||
get_constraint_name(foreignKeyId))));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* CreateTenantSchemaTable creates a tenant table with given relationId.
|
||||
*
|
||||
|
@ -130,20 +251,12 @@ CreateTenantSchemaTable(Oid relationId)
|
|||
* prefer to throw an error with a more meaningful message, rather
|
||||
* than saying "operation is not allowed on this node".
|
||||
*/
|
||||
ereport(ERROR, (errmsg("cannot create a tenant table from a worker node"),
|
||||
ereport(ERROR, (errmsg("cannot create tables in a distributed schema from "
|
||||
"a worker node"),
|
||||
errhint("Connect to the coordinator node and try again.")));
|
||||
}
|
||||
|
||||
if (IsForeignTable(relationId))
|
||||
{
|
||||
/* throw an error that is nicer than the one CreateSingleShardTable() would throw */
|
||||
ereport(ERROR, (errmsg("cannot create a tenant table from a foreign table")));
|
||||
}
|
||||
else if (PartitionTable(relationId))
|
||||
{
|
||||
ErrorIfIllegalPartitioningInTenantSchema(PartitionParentOid(relationId),
|
||||
relationId);
|
||||
}
|
||||
EnsureTableKindSupportedForTenantSchema(relationId);
|
||||
|
||||
/*
|
||||
* We don't expect this to happen because ShouldCreateTenantSchemaTable()
|
||||
|
@ -153,7 +266,7 @@ CreateTenantSchemaTable(Oid relationId)
|
|||
uint32 colocationId = SchemaIdGetTenantColocationId(schemaId);
|
||||
if (colocationId == INVALID_COLOCATION_ID)
|
||||
{
|
||||
ereport(ERROR, (errmsg("schema \"%s\" is not a tenant schema",
|
||||
ereport(ERROR, (errmsg("schema \"%s\" is not distributed",
|
||||
get_namespace_name(schemaId))));
|
||||
}
|
||||
|
||||
|
@ -169,29 +282,16 @@ CreateTenantSchemaTable(Oid relationId)
|
|||
* ErrorIfIllegalPartitioningInTenantSchema throws an error if the
|
||||
* partitioning relationship between the parent and the child is illegal
|
||||
* because they are in different schemas while one of them is a tenant table.
|
||||
*
|
||||
* This function assumes that either the parent or the child are in a tenant
|
||||
* schema.
|
||||
*/
|
||||
void
|
||||
ErrorIfIllegalPartitioningInTenantSchema(Oid parentRelationId, Oid partitionRelationId)
|
||||
{
|
||||
Oid partitionSchemaId = get_rel_namespace(partitionRelationId);
|
||||
Oid parentSchemaId = get_rel_namespace(parentRelationId);
|
||||
|
||||
bool partitionIsTenantTable = IsTenantSchema(partitionSchemaId);
|
||||
bool parentIsTenantTable = IsTenantSchema(parentSchemaId);
|
||||
|
||||
bool illegalPartitioning = false;
|
||||
if (partitionIsTenantTable != parentIsTenantTable)
|
||||
if (get_rel_namespace(partitionRelationId) != get_rel_namespace(parentRelationId))
|
||||
{
|
||||
illegalPartitioning = true;
|
||||
}
|
||||
else if (partitionIsTenantTable && parentIsTenantTable)
|
||||
{
|
||||
illegalPartitioning = (parentSchemaId != partitionSchemaId);
|
||||
}
|
||||
|
||||
if (illegalPartitioning)
|
||||
{
|
||||
ereport(ERROR, (errmsg("partitioning with tenant tables is not "
|
||||
ereport(ERROR, (errmsg("partitioning within a distributed schema is not "
|
||||
"supported when the parent and the child "
|
||||
"are in different schemas")));
|
||||
}
|
||||
|
@ -199,9 +299,236 @@ ErrorIfIllegalPartitioningInTenantSchema(Oid parentRelationId, Oid partitionRela
|
|||
|
||||
|
||||
/*
|
||||
* citus_internal_unregister_tenant_schema_globally removes given schema from
|
||||
* the tenant schema metadata table, deletes the colocation group of the schema
|
||||
* and sends the command to do the same on the workers.
|
||||
* CreateTenantSchemaColocationId returns new colocation id for a tenant schema.
|
||||
*/
|
||||
uint32
|
||||
CreateTenantSchemaColocationId(void)
|
||||
{
|
||||
int shardCount = 1;
|
||||
int replicationFactor = 1;
|
||||
Oid distributionColumnType = InvalidOid;
|
||||
Oid distributionColumnCollation = InvalidOid;
|
||||
uint32 schemaColocationId = CreateColocationGroup(
|
||||
shardCount, replicationFactor, distributionColumnType,
|
||||
distributionColumnCollation);
|
||||
return schemaColocationId;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* SchemaGetNonShardTableIdList returns all nonshard relation ids
|
||||
* inside given schema.
|
||||
*/
|
||||
static List *
|
||||
SchemaGetNonShardTableIdList(Oid schemaId)
|
||||
{
|
||||
List *relationIdList = NIL;
|
||||
|
||||
/* scan all relations in pg_class and return all tables inside given schema */
|
||||
Relation relationRelation = relation_open(RelationRelationId, AccessShareLock);
|
||||
|
||||
ScanKeyData scanKey[1] = { 0 };
|
||||
ScanKeyInit(&scanKey[0], Anum_pg_class_relnamespace, BTEqualStrategyNumber,
|
||||
F_OIDEQ, ObjectIdGetDatum(schemaId));
|
||||
SysScanDesc scanDescriptor = systable_beginscan(relationRelation, ClassNameNspIndexId,
|
||||
true, NULL, 1, scanKey);
|
||||
|
||||
HeapTuple heapTuple = NULL;
|
||||
while (HeapTupleIsValid(heapTuple = systable_getnext(scanDescriptor)))
|
||||
{
|
||||
Form_pg_class relationForm = (Form_pg_class) GETSTRUCT(heapTuple);
|
||||
char *relationName = NameStr(relationForm->relname);
|
||||
Oid relationId = get_relname_relid(relationName, schemaId);
|
||||
|
||||
if (!OidIsValid(relationId))
|
||||
{
|
||||
ereport(ERROR, errmsg("table %s is dropped by a concurrent operation",
|
||||
relationName));
|
||||
}
|
||||
|
||||
/* skip shards */
|
||||
if (RelationIsAKnownShard(relationId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (RegularTable(relationId) || PartitionTable(relationId) ||
|
||||
IsForeignTable(relationId))
|
||||
{
|
||||
relationIdList = lappend_oid(relationIdList, relationId);
|
||||
}
|
||||
}
|
||||
|
||||
systable_endscan(scanDescriptor);
|
||||
relation_close(relationRelation, AccessShareLock);
|
||||
|
||||
return relationIdList;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* EnsureSchemaCanBeDistributed ensures the schema can be distributed.
|
||||
* Caller should take required the lock on relations and the schema.
|
||||
*
|
||||
* It checks:
|
||||
* - Schema name is in the allowed-list,
|
||||
* - Schema does not depend on an extension (created by extension),
|
||||
* - No extension depends on the schema (CREATE EXTENSION <ext> SCHEMA <schema>),
|
||||
* - Current user should be the owner of tables under the schema,
|
||||
* - Table kinds are supported,
|
||||
* - Referencing and referenced foreign keys for the tables under the schema are
|
||||
* supported,
|
||||
* - Tables under the schema are not owned by an extension,
|
||||
* - Only Citus local and Postgres local tables exist under the schema.
|
||||
*/
|
||||
static void
|
||||
EnsureSchemaCanBeDistributed(Oid schemaId, List *schemaTableIdList)
|
||||
{
|
||||
/* Ensure schema name is allowed */
|
||||
EnsureTenantSchemaNameAllowed(schemaId);
|
||||
|
||||
/* Any schema owned by extension is not allowed */
|
||||
char *schemaName = get_namespace_name(schemaId);
|
||||
ObjectAddress *schemaAddress = palloc0(sizeof(ObjectAddress));
|
||||
ObjectAddressSet(*schemaAddress, NamespaceRelationId, schemaId);
|
||||
if (IsAnyObjectAddressOwnedByExtension(list_make1(schemaAddress), NULL))
|
||||
{
|
||||
ereport(ERROR, (errmsg("schema %s, which is owned by an extension, cannot "
|
||||
"be distributed", schemaName)));
|
||||
}
|
||||
|
||||
/* Extension schemas are not allowed */
|
||||
ObjectAddress *extensionAddress = FirstExtensionWithSchema(schemaId);
|
||||
if (extensionAddress)
|
||||
{
|
||||
char *extensionName = get_extension_name(extensionAddress->objectId);
|
||||
ereport(ERROR, (errmsg("schema %s cannot be distributed since it is the schema "
|
||||
"of extension %s", schemaName, extensionName)));
|
||||
}
|
||||
|
||||
Oid relationId = InvalidOid;
|
||||
foreach_oid(relationId, schemaTableIdList)
|
||||
{
|
||||
/* Ensure table owner */
|
||||
EnsureTableOwner(relationId);
|
||||
|
||||
/* Check relation kind */
|
||||
EnsureTableKindSupportedForTenantSchema(relationId);
|
||||
|
||||
/* Check foreign keys */
|
||||
EnsureFKeysForTenantTable(relationId);
|
||||
|
||||
/* Check table not owned by an extension */
|
||||
ObjectAddress *tableAddress = palloc0(sizeof(ObjectAddress));
|
||||
ObjectAddressSet(*tableAddress, RelationRelationId, relationId);
|
||||
if (IsAnyObjectAddressOwnedByExtension(list_make1(tableAddress), NULL))
|
||||
{
|
||||
char *tableName = get_namespace_name(schemaId);
|
||||
ereport(ERROR, (errmsg("schema cannot be distributed since it has "
|
||||
"table %s which is owned by an extension",
|
||||
tableName)));
|
||||
}
|
||||
|
||||
/* Postgres local tables are allowed */
|
||||
if (!IsCitusTable(relationId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Only Citus local tables, amongst Citus table types, are allowed */
|
||||
if (!IsCitusTableType(relationId, CITUS_LOCAL_TABLE))
|
||||
{
|
||||
ereport(ERROR, (errmsg("schema already has distributed tables"),
|
||||
errhint("Undistribute distributed tables under "
|
||||
"the schema before distributing the schema.")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* EnsureTenantSchemaNameAllowed ensures if given schema is applicable for registering
|
||||
* as a tenant schema.
|
||||
*/
|
||||
static void
|
||||
EnsureTenantSchemaNameAllowed(Oid schemaId)
|
||||
{
|
||||
char *schemaName = get_namespace_name(schemaId);
|
||||
|
||||
/* public schema is not allowed */
|
||||
if (strcmp(schemaName, "public") == 0)
|
||||
{
|
||||
ereport(ERROR, (errmsg("public schema cannot be distributed")));
|
||||
}
|
||||
|
||||
/* information_schema schema is not allowed */
|
||||
if (strcmp(schemaName, "information_schema") == 0)
|
||||
{
|
||||
ereport(ERROR, (errmsg("information_schema schema cannot be distributed")));
|
||||
}
|
||||
|
||||
/* pg_temp_xx and pg_toast_temp_xx schemas are not allowed */
|
||||
if (isAnyTempNamespace(schemaId))
|
||||
{
|
||||
ereport(ERROR, (errmsg("temporary schema cannot be distributed")));
|
||||
}
|
||||
|
||||
/* pg_catalog schema is not allowed */
|
||||
if (IsCatalogNamespace(schemaId))
|
||||
{
|
||||
ereport(ERROR, (errmsg("pg_catalog schema cannot be distributed")));
|
||||
}
|
||||
|
||||
/* pg_toast schema is not allowed */
|
||||
if (IsToastNamespace(schemaId))
|
||||
{
|
||||
ereport(ERROR, (errmsg("pg_toast schema cannot be distributed")));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* EnsureSchemaExist ensures that schema exists. Caller is responsible to take
|
||||
* the required lock on the schema.
|
||||
*/
|
||||
static void
|
||||
EnsureSchemaExist(Oid schemaId)
|
||||
{
|
||||
if (!SearchSysCacheExists1(NAMESPACEOID, ObjectIdGetDatum(schemaId)))
|
||||
{
|
||||
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_SCHEMA),
|
||||
errmsg("schema with OID %u does not exist", schemaId)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* UnregisterTenantSchemaGlobally removes given schema from the tenant schema
|
||||
* metadata table, deletes the colocation group of the schema and sends the
|
||||
* command to do the same on the workers.
|
||||
*/
|
||||
static void
|
||||
UnregisterTenantSchemaGlobally(Oid schemaId, char *schemaName)
|
||||
{
|
||||
uint32 tenantSchemaColocationId = SchemaIdGetTenantColocationId(schemaId);
|
||||
|
||||
DeleteTenantSchemaLocally(schemaId);
|
||||
if (EnableMetadataSync)
|
||||
{
|
||||
SendCommandToWorkersWithMetadata(TenantSchemaDeleteCommand(schemaName));
|
||||
}
|
||||
|
||||
DeleteColocationGroup(tenantSchemaColocationId);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* citus_internal_unregister_tenant_schema_globally, called by Citus drop hook,
|
||||
* unregisters the schema when a tenant schema is dropped.
|
||||
*
|
||||
* NOTE: We need to pass schema_name as an argument. We cannot use schema id
|
||||
* to obtain schema name since the schema would have already been dropped when this
|
||||
* udf is called by the drop hook.
|
||||
*/
|
||||
Datum
|
||||
citus_internal_unregister_tenant_schema_globally(PG_FUNCTION_ARGS)
|
||||
|
@ -227,18 +554,185 @@ citus_internal_unregister_tenant_schema_globally(PG_FUNCTION_ARGS)
|
|||
if (HeapTupleIsValid(namespaceTuple))
|
||||
{
|
||||
ReleaseSysCache(namespaceTuple);
|
||||
|
||||
ereport(ERROR, (errmsg("schema is expected to be already dropped "
|
||||
"because this function is only expected to "
|
||||
"be called from Citus drop hook")));
|
||||
}
|
||||
UnregisterTenantSchemaGlobally(schemaId, schemaNameStr);
|
||||
PG_RETURN_VOID();
|
||||
}
|
||||
|
||||
uint32 tenantSchemaColocationId = SchemaIdGetTenantColocationId(schemaId);
|
||||
|
||||
DeleteTenantSchemaLocally(schemaId);
|
||||
SendCommandToWorkersWithMetadata(TenantSchemaDeleteCommand(schemaNameStr));
|
||||
/*
|
||||
* citus_schema_distribute gets a regular schema name, then converts it to a tenant
|
||||
* schema.
|
||||
*/
|
||||
Datum
|
||||
citus_schema_distribute(PG_FUNCTION_ARGS)
|
||||
{
|
||||
CheckCitusVersion(ERROR);
|
||||
EnsureCoordinator();
|
||||
|
||||
DeleteColocationGroup(tenantSchemaColocationId);
|
||||
Oid schemaId = PG_GETARG_OID(0);
|
||||
EnsureSchemaExist(schemaId);
|
||||
EnsureSchemaOwner(schemaId);
|
||||
|
||||
/* Prevent concurrent table creation under the schema */
|
||||
LockDatabaseObject(NamespaceRelationId, schemaId, 0, AccessExclusiveLock);
|
||||
|
||||
/*
|
||||
* We should ensure the existence of the schema after taking the lock since
|
||||
* the schema could have been dropped before we acquired the lock.
|
||||
*/
|
||||
EnsureSchemaExist(schemaId);
|
||||
EnsureSchemaOwner(schemaId);
|
||||
|
||||
/* Return if the schema is already a tenant schema */
|
||||
char *schemaName = get_namespace_name(schemaId);
|
||||
if (IsTenantSchema(schemaId))
|
||||
{
|
||||
ereport(NOTICE, (errmsg("schema %s is already distributed", schemaName)));
|
||||
PG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/* Take lock on the relations and filter out partition tables */
|
||||
List *tableIdListInSchema = SchemaGetNonShardTableIdList(schemaId);
|
||||
List *tableIdListToConvert = NIL;
|
||||
Oid relationId = InvalidOid;
|
||||
foreach_oid(relationId, tableIdListInSchema)
|
||||
{
|
||||
/* prevent concurrent drop of the relation */
|
||||
LockRelationOid(relationId, AccessShareLock);
|
||||
EnsureRelationExists(relationId);
|
||||
|
||||
/*
|
||||
* Skip partitions as they would be distributed by the parent table.
|
||||
*
|
||||
* We should filter out partitions here before distributing the schema.
|
||||
* Otherwise, converted partitioned table would change oid of partitions and its
|
||||
* partition tables would fail with oid not exist.
|
||||
*/
|
||||
if (PartitionTable(relationId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
tableIdListToConvert = lappend_oid(tableIdListToConvert, relationId);
|
||||
}
|
||||
|
||||
/* Makes sure the schema can be distributed. */
|
||||
EnsureSchemaCanBeDistributed(schemaId, tableIdListInSchema);
|
||||
|
||||
ereport(NOTICE, (errmsg("distributing the schema %s", schemaName)));
|
||||
|
||||
/* Create colocation id and then single shard tables with the colocation id */
|
||||
uint32 colocationId = CreateTenantSchemaColocationId();
|
||||
ColocationParam colocationParam = {
|
||||
.colocationParamType = COLOCATE_WITH_COLOCATION_ID,
|
||||
.colocationId = colocationId,
|
||||
};
|
||||
|
||||
/*
|
||||
* Collect foreign keys for recreation and then drop fkeys and create single shard
|
||||
* tables.
|
||||
*/
|
||||
List *originalForeignKeyRecreationCommands = NIL;
|
||||
foreach_oid(relationId, tableIdListToConvert)
|
||||
{
|
||||
List *fkeyCommandsForRelation =
|
||||
GetFKeyCreationCommandsRelationInvolvedWithTableType(relationId,
|
||||
INCLUDE_ALL_TABLE_TYPES);
|
||||
originalForeignKeyRecreationCommands = list_concat(
|
||||
originalForeignKeyRecreationCommands, fkeyCommandsForRelation);
|
||||
|
||||
DropFKeysRelationInvolvedWithTableType(relationId, INCLUDE_ALL_TABLE_TYPES);
|
||||
CreateSingleShardTable(relationId, colocationParam);
|
||||
}
|
||||
|
||||
/* We can skip foreign key validations as we are sure about them at start */
|
||||
bool skip_validation = true;
|
||||
ExecuteForeignKeyCreateCommandList(originalForeignKeyRecreationCommands,
|
||||
skip_validation);
|
||||
|
||||
/* Register the schema locally and sync it to workers */
|
||||
InsertTenantSchemaLocally(schemaId, colocationId);
|
||||
char *registerSchemaCommand = TenantSchemaInsertCommand(schemaId, colocationId);
|
||||
if (EnableMetadataSync)
|
||||
{
|
||||
SendCommandToWorkersWithMetadata(registerSchemaCommand);
|
||||
}
|
||||
|
||||
PG_RETURN_VOID();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* citus_schema_undistribute gets a tenant schema name, then converts it to a regular
|
||||
* schema by undistributing all tables under it.
|
||||
*/
|
||||
Datum
|
||||
citus_schema_undistribute(PG_FUNCTION_ARGS)
|
||||
{
|
||||
CheckCitusVersion(ERROR);
|
||||
EnsureCoordinator();
|
||||
|
||||
Oid schemaId = PG_GETARG_OID(0);
|
||||
EnsureSchemaExist(schemaId);
|
||||
EnsureSchemaOwner(schemaId);
|
||||
|
||||
/* Prevent concurrent table creation under the schema */
|
||||
LockDatabaseObject(NamespaceRelationId, schemaId, 0, AccessExclusiveLock);
|
||||
|
||||
/*
|
||||
* We should ensure the existence of the schema after taking the lock since
|
||||
* the schema could have been dropped before we acquired the lock.
|
||||
*/
|
||||
EnsureSchemaExist(schemaId);
|
||||
EnsureSchemaOwner(schemaId);
|
||||
|
||||
/* The schema should be a tenant schema */
|
||||
char *schemaName = get_namespace_name(schemaId);
|
||||
if (!IsTenantSchema(schemaId))
|
||||
{
|
||||
ereport(ERROR, (errmsg("schema %s is not distributed", schemaName)));
|
||||
}
|
||||
|
||||
ereport(NOTICE, (errmsg("undistributing schema %s", schemaName)));
|
||||
|
||||
/* Take lock on the relations and filter out partition tables */
|
||||
List *tableIdListInSchema = SchemaGetNonShardTableIdList(schemaId);
|
||||
List *tableIdListToConvert = NIL;
|
||||
Oid relationId = InvalidOid;
|
||||
foreach_oid(relationId, tableIdListInSchema)
|
||||
{
|
||||
/* prevent concurrent drop of the relation */
|
||||
LockRelationOid(relationId, AccessShareLock);
|
||||
EnsureRelationExists(relationId);
|
||||
|
||||
/*
|
||||
* Skip partitions as they would be undistributed by the parent table.
|
||||
*
|
||||
* We should filter out partitions here before undistributing the schema.
|
||||
* Otherwise, converted partitioned table would change oid of partitions and its
|
||||
* partition tables would fail with oid not exist.
|
||||
*/
|
||||
if (PartitionTable(relationId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
tableIdListToConvert = lappend_oid(tableIdListToConvert, relationId);
|
||||
|
||||
/* Only single shard tables are expected during the undistribution of the schema */
|
||||
Assert(IsCitusTableType(relationId, SINGLE_SHARD_DISTRIBUTED));
|
||||
}
|
||||
|
||||
/*
|
||||
* First, we need to delete schema metadata and sync it to workers. Otherwise,
|
||||
* we would get error from `ErrorIfTenantTable` while undistributing the tables.
|
||||
*/
|
||||
UnregisterTenantSchemaGlobally(schemaId, schemaName);
|
||||
UndistributeTables(tableIdListToConvert);
|
||||
|
||||
PG_RETURN_VOID();
|
||||
}
|
||||
|
@ -253,7 +747,8 @@ ErrorIfTenantTable(Oid relationId, char *operationName)
|
|||
{
|
||||
if (IsTenantSchema(get_rel_namespace(relationId)))
|
||||
{
|
||||
ereport(ERROR, (errmsg("%s is not allowed for %s because it is a tenant table",
|
||||
ereport(ERROR, (errmsg("%s is not allowed for %s because it belongs to "
|
||||
"a distributed schema",
|
||||
generate_qualified_relation_name(relationId),
|
||||
operationName)));
|
||||
}
|
||||
|
|
|
@ -414,7 +414,11 @@ PostprocessCreateTableStmtPartitionOf(CreateStmt *createStatement, const
|
|||
}
|
||||
}
|
||||
|
||||
ErrorIfIllegalPartitioningInTenantSchema(PartitionParentOid(relationId), relationId);
|
||||
if (IsTenantSchema(get_rel_namespace(parentRelationId)) ||
|
||||
IsTenantSchema(get_rel_namespace(relationId)))
|
||||
{
|
||||
ErrorIfIllegalPartitioningInTenantSchema(parentRelationId, relationId);
|
||||
}
|
||||
|
||||
/*
|
||||
* If a partition is being created and if its parent is a distributed
|
||||
|
@ -496,8 +500,12 @@ PreprocessAlterTableStmtAttachPartition(AlterTableStmt *alterTableStatement,
|
|||
return NIL;
|
||||
}
|
||||
|
||||
ErrorIfIllegalPartitioningInTenantSchema(parentRelationId,
|
||||
partitionRelationId);
|
||||
if (IsTenantSchema(get_rel_namespace(parentRelationId)) ||
|
||||
IsTenantSchema(get_rel_namespace(partitionRelationId)))
|
||||
{
|
||||
ErrorIfIllegalPartitioningInTenantSchema(parentRelationId,
|
||||
partitionRelationId);
|
||||
}
|
||||
|
||||
if (!IsCitusTable(parentRelationId))
|
||||
{
|
||||
|
|
|
@ -1192,6 +1192,47 @@ IsAnyObjectAddressOwnedByExtension(const List *targets,
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* FirstExtensionWithSchema returns the first extension address whose schema is the same
|
||||
* as given schema. If no extension depends on the schema, it returns NULL.
|
||||
* i.e. decide if given schema is an extension schema as in
|
||||
* `CREATE EXTENSION <ext> [WITH] SCHEMA <schema>;`
|
||||
*/
|
||||
ObjectAddress *
|
||||
FirstExtensionWithSchema(Oid schemaId)
|
||||
{
|
||||
ObjectAddress *extensionAddress = NULL;
|
||||
|
||||
Relation relation = table_open(ExtensionRelationId, AccessShareLock);
|
||||
|
||||
ScanKeyData entry[1];
|
||||
ScanKeyInit(&entry[0], Anum_pg_extension_extnamespace, BTEqualStrategyNumber,
|
||||
F_INT4EQ, schemaId);
|
||||
|
||||
SysScanDesc scan = systable_beginscan(relation, InvalidOid, false, NULL, 1, entry);
|
||||
HeapTuple extensionTuple = systable_getnext(scan);
|
||||
if (HeapTupleIsValid(extensionTuple))
|
||||
{
|
||||
int extensionIdIndex = Anum_pg_extension_oid;
|
||||
TupleDesc tupleDescriptor = RelationGetDescr(relation);
|
||||
bool isNull = false;
|
||||
Datum extensionIdDatum = heap_getattr(extensionTuple, extensionIdIndex,
|
||||
tupleDescriptor, &isNull);
|
||||
Oid extensionId = DatumGetObjectId(extensionIdDatum);
|
||||
|
||||
extensionAddress = palloc0(sizeof(ObjectAddress));
|
||||
extensionAddress->objectId = extensionId;
|
||||
extensionAddress->classId = ExtensionRelationId;
|
||||
extensionAddress->objectSubId = 0;
|
||||
}
|
||||
|
||||
systable_endscan(scan);
|
||||
table_close(relation, AccessShareLock);
|
||||
|
||||
return extensionAddress;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* IsObjectAddressOwnedByCitus returns true if the given object address
|
||||
* is owned by the citus or citus_columnar extensions.
|
||||
|
|
|
@ -2256,6 +2256,21 @@ EnsureTableOwner(Oid relationId)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* Check that the current user has owner rights to schemaId, error out if
|
||||
* not. Superusers are regarded as owners.
|
||||
*/
|
||||
void
|
||||
EnsureSchemaOwner(Oid schemaId)
|
||||
{
|
||||
if (!pg_namespace_ownercheck(schemaId, GetUserId()))
|
||||
{
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SCHEMA,
|
||||
get_namespace_name(schemaId));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Check that the current user has owner rights to functionId, error out if
|
||||
* not. Superusers are regarded as owners. Functions and procedures are
|
||||
|
|
|
@ -26,3 +26,7 @@ GRANT SELECT ON pg_catalog.pg_dist_tenant_schema TO public;
|
|||
|
||||
#include "udfs/citus_tables/12.0-1.sql"
|
||||
#include "udfs/citus_shards/12.0-1.sql"
|
||||
|
||||
-- udfs to convert a regular/tenant schema to a tenant/regular schema
|
||||
#include "udfs/citus_schema_distribute/12.0-1.sql"
|
||||
#include "udfs/citus_schema_undistribute/12.0-1.sql"
|
||||
|
|
|
@ -30,6 +30,9 @@ BEGIN
|
|||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
DROP FUNCTION pg_catalog.citus_schema_distribute(regnamespace);
|
||||
DROP FUNCTION pg_catalog.citus_schema_undistribute(regnamespace);
|
||||
|
||||
DROP FUNCTION pg_catalog.citus_internal_add_tenant_schema(Oid, int);
|
||||
|
||||
#include "../udfs/citus_prepare_pg_upgrade/11.2-1.sql"
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
CREATE OR REPLACE FUNCTION pg_catalog.citus_schema_distribute(schemaname regnamespace)
|
||||
RETURNS void
|
||||
LANGUAGE C STRICT
|
||||
AS 'MODULE_PATHNAME', $$citus_schema_distribute$$;
|
||||
COMMENT ON FUNCTION pg_catalog.citus_schema_distribute(schemaname regnamespace)
|
||||
IS 'distributes a schema, allowing it to move between nodes';
|
|
@ -0,0 +1,6 @@
|
|||
CREATE OR REPLACE FUNCTION pg_catalog.citus_schema_distribute(schemaname regnamespace)
|
||||
RETURNS void
|
||||
LANGUAGE C STRICT
|
||||
AS 'MODULE_PATHNAME', $$citus_schema_distribute$$;
|
||||
COMMENT ON FUNCTION pg_catalog.citus_schema_distribute(schemaname regnamespace)
|
||||
IS 'distributes a schema, allowing it to move between nodes';
|
|
@ -0,0 +1,6 @@
|
|||
CREATE OR REPLACE FUNCTION pg_catalog.citus_schema_undistribute(schemaname regnamespace)
|
||||
RETURNS void
|
||||
LANGUAGE C STRICT
|
||||
AS 'MODULE_PATHNAME', $$citus_schema_undistribute$$;
|
||||
COMMENT ON FUNCTION pg_catalog.citus_schema_undistribute(schemaname regnamespace)
|
||||
IS 'reverts schema distribution, moving it back to the coordinator';
|
|
@ -0,0 +1,6 @@
|
|||
CREATE OR REPLACE FUNCTION pg_catalog.citus_schema_undistribute(schemaname regnamespace)
|
||||
RETURNS void
|
||||
LANGUAGE C STRICT
|
||||
AS 'MODULE_PATHNAME', $$citus_schema_undistribute$$;
|
||||
COMMENT ON FUNCTION pg_catalog.citus_schema_undistribute(schemaname regnamespace)
|
||||
IS 'reverts schema distribution, moving it back to the coordinator';
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "distributed/metadata_utility.h"
|
||||
#include "utils/rel.h"
|
||||
#include "nodes/parsenodes.h"
|
||||
#include "tcop/dest.h"
|
||||
|
@ -273,6 +274,10 @@ extern List * GetForeignConstraintToReferenceTablesCommands(Oid relationId);
|
|||
extern List * GetForeignConstraintToDistributedTablesCommands(Oid relationId);
|
||||
extern List * GetForeignConstraintFromDistributedTablesCommands(Oid relationId);
|
||||
extern List * GetForeignConstraintCommandsInternal(Oid relationId, int flags);
|
||||
extern Oid DropFKeysAndUndistributeTable(Oid relationId);
|
||||
extern void DropFKeysRelationInvolvedWithTableType(Oid relationId, int tableTypeFlag);
|
||||
extern List * GetFKeyCreationCommandsRelationInvolvedWithTableType(Oid relationId,
|
||||
int tableTypeFlag);
|
||||
extern bool AnyForeignKeyDependsOnIndex(Oid indexId);
|
||||
extern bool HasForeignKeyWithLocalTable(Oid relationId);
|
||||
extern bool HasForeignKeyToReferenceTable(Oid relationOid);
|
||||
|
@ -793,5 +798,6 @@ extern void ErrorIfIllegalPartitioningInTenantSchema(Oid parentRelationId,
|
|||
extern void CreateTenantSchemaTable(Oid relationId);
|
||||
extern void ErrorIfTenantTable(Oid relationId, char *operationName);
|
||||
extern void ErrorIfTenantSchema(Oid nspOid, char *operationName);
|
||||
extern uint32 CreateTenantSchemaColocationId(void);
|
||||
|
||||
#endif /*CITUS_COMMANDS_H */
|
||||
|
|
|
@ -30,6 +30,7 @@ extern bool IsTableOwnedByExtension(Oid relationId);
|
|||
extern bool ObjectAddressDependsOnExtension(const ObjectAddress *target);
|
||||
extern bool IsAnyObjectAddressOwnedByExtension(const List *targets,
|
||||
ObjectAddress *extensionAddress);
|
||||
extern ObjectAddress * FirstExtensionWithSchema(Oid schemaId);
|
||||
extern bool IsObjectAddressOwnedByCitus(const ObjectAddress *objectAddress);
|
||||
extern ObjectAddress PgGetObjectAddress(char *ttype, ArrayType *namearr,
|
||||
ArrayType *argsarr);
|
||||
|
|
|
@ -364,6 +364,7 @@ extern void CreateDistributedTable(Oid relationId, char *distributionColumnName,
|
|||
extern void CreateReferenceTable(Oid relationId);
|
||||
extern void CreateTruncateTrigger(Oid relationId);
|
||||
extern TableConversionReturn * UndistributeTable(TableConversionParameters *params);
|
||||
extern void UndistributeTables(List *relationIdList);
|
||||
|
||||
extern void EnsureAllObjectDependenciesExistOnAllNodes(const List *targets);
|
||||
extern DeferredErrorMessage * DeferErrorIfCircularDependencyExists(const
|
||||
|
@ -383,6 +384,7 @@ extern void EnsureTableOwner(Oid relationId);
|
|||
extern void EnsureHashDistributedTable(Oid relationId);
|
||||
extern void EnsureHashOrSingleShardDistributedTable(Oid relationId);
|
||||
extern void EnsureFunctionOwner(Oid functionId);
|
||||
extern void EnsureSchemaOwner(Oid schemaId);
|
||||
extern void EnsureSuperUser(void);
|
||||
extern void ErrorIfTableIsACatalogTable(Relation relation);
|
||||
extern void EnsureTableNotDistributed(Oid relationId);
|
||||
|
|
|
@ -0,0 +1,923 @@
|
|||
SET citus.next_shard_id TO 1730000;
|
||||
SET citus.shard_replication_factor TO 1;
|
||||
SET client_min_messages TO WARNING;
|
||||
SET citus.enable_schema_based_sharding TO off;
|
||||
SELECT 1 FROM citus_add_node('localhost', :master_port, groupid => 0);
|
||||
?column?
|
||||
---------------------------------------------------------------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
CREATE USER tenantuser superuser;
|
||||
SET role tenantuser;
|
||||
-- check invalid input
|
||||
SELECT citus_schema_distribute(1);
|
||||
ERROR: schema with OID 1 does not exist
|
||||
SELECT citus_schema_undistribute(1);
|
||||
ERROR: schema with OID 1 does not exist
|
||||
-- noop
|
||||
SELECT citus_schema_distribute(null);
|
||||
citus_schema_distribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT citus_schema_undistribute(null);
|
||||
citus_schema_undistribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- public and some others cannot be distributed as a tenant schema, but check what happens
|
||||
-- if we try to call citus_schema_undistribute() for such a schema.
|
||||
SELECT citus_schema_undistribute('public');
|
||||
ERROR: schema public is not distributed
|
||||
-- create non-tenant schema
|
||||
CREATE SCHEMA citus_schema_distribute_undistribute;
|
||||
-- create tenant schema
|
||||
CREATE SCHEMA tenant1;
|
||||
CREATE TABLE tenant1.table1(id int PRIMARY KEY, name text);
|
||||
INSERT INTO tenant1.table1 SELECT i, 'asd'::text FROM generate_series(1,20) i;
|
||||
CREATE TABLE tenant1.table2(id int REFERENCES tenant1.table1(id), num bigint UNIQUE);
|
||||
INSERT INTO tenant1.table2 SELECT i, i FROM generate_series(1,20) i;
|
||||
CREATE TABLE citus_schema_distribute_undistribute.ref(id int PRIMARY KEY);
|
||||
SELECT create_reference_table('citus_schema_distribute_undistribute.ref');
|
||||
create_reference_table
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
INSERT INTO citus_schema_distribute_undistribute.ref SELECT i FROM generate_series(1,100) i;
|
||||
-- autoconverted to Citus local table due to foreign key to reference table
|
||||
CREATE TABLE tenant1.table3(id int REFERENCES citus_schema_distribute_undistribute.ref(id));
|
||||
INSERT INTO tenant1.table3 SELECT i FROM generate_series(1,100) i;
|
||||
-- Citus local table with autoconverted=false
|
||||
CREATE TABLE tenant1.table3x(id int PRIMARY KEY REFERENCES citus_schema_distribute_undistribute.ref(id));
|
||||
SELECT citus_add_local_table_to_metadata('tenant1.table3x');
|
||||
citus_add_local_table_to_metadata
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
INSERT INTO tenant1.table3x SELECT i FROM generate_series(1,100) i;
|
||||
-- foreign key to another local table in the same schema
|
||||
CREATE TABLE tenant1.table3y(id int PRIMARY KEY REFERENCES tenant1.table3x(id));
|
||||
SELECT citus_add_local_table_to_metadata('tenant1.table3y');
|
||||
citus_add_local_table_to_metadata
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
INSERT INTO tenant1.table3y SELECT i FROM generate_series(1,100) i;
|
||||
-- table with composite type
|
||||
CREATE TYPE tenant1.catname AS ENUM ('baby', 'teen', 'mid');
|
||||
CREATE TYPE tenant1.agecat AS (below_age int, name tenant1.catname);
|
||||
CREATE TABLE tenant1.table4(id int, age tenant1.agecat);
|
||||
-- create autoconverted partitioned table
|
||||
CREATE TABLE tenant1.partitioned_table(id int REFERENCES citus_schema_distribute_undistribute.ref(id)) PARTITION BY RANGE(id);
|
||||
CREATE TABLE tenant1.partition1 PARTITION OF tenant1.partitioned_table FOR VALUES FROM (1) TO (11);
|
||||
CREATE TABLE tenant1.partition2 PARTITION OF tenant1.partitioned_table FOR VALUES FROM (11) TO (21);
|
||||
INSERT INTO tenant1.partitioned_table SELECT i FROM generate_series(1,20) i;
|
||||
-- create view
|
||||
CREATE VIEW tenant1.view1 AS SELECT * FROM tenant1.table1 JOIN tenant1.table2 USING(id);
|
||||
WARNING: "view tenant1.view1" has dependency to "table tenant1.table2" that is not in Citus' metadata
|
||||
DETAIL: "view tenant1.view1" will be created only locally
|
||||
HINT: Distribute "table tenant1.table2" first to distribute "view tenant1.view1"
|
||||
-- create view in regular schema
|
||||
CREATE VIEW citus_schema_distribute_undistribute.view2 AS SELECT * FROM tenant1.view1;
|
||||
WARNING: "view citus_schema_distribute_undistribute.view2" has dependency to "table tenant1.table2" that is not in Citus' metadata
|
||||
DETAIL: "view citus_schema_distribute_undistribute.view2" will be created only locally
|
||||
HINT: Distribute "table tenant1.table2" first to distribute "view citus_schema_distribute_undistribute.view2"
|
||||
-- create materialized view
|
||||
CREATE MATERIALIZED VIEW tenant1.view2 AS SELECT * FROM tenant1.table1;
|
||||
-- create collation
|
||||
CREATE COLLATION citus_schema_distribute_undistribute.german_phonebook (provider = icu, locale = 'de-u-co-phonebk');
|
||||
-- create type
|
||||
CREATE TYPE citus_schema_distribute_undistribute.pair_type AS (a int, b int);
|
||||
-- Create function
|
||||
CREATE FUNCTION citus_schema_distribute_undistribute.one_as_result() RETURNS INT LANGUAGE SQL AS
|
||||
$$
|
||||
SELECT 1;
|
||||
$$;
|
||||
-- create text search dictionary
|
||||
CREATE TEXT SEARCH DICTIONARY citus_schema_distribute_undistribute.my_german_dict (
|
||||
template = snowball,
|
||||
language = german,
|
||||
stopwords = german
|
||||
);
|
||||
-- create text search config
|
||||
CREATE TEXT SEARCH CONFIGURATION citus_schema_distribute_undistribute.my_ts_config ( parser = default );
|
||||
ALTER TEXT SEARCH CONFIGURATION citus_schema_distribute_undistribute.my_ts_config ALTER MAPPING FOR asciiword WITH citus_schema_distribute_undistribute.my_german_dict;
|
||||
-- create sequence
|
||||
CREATE SEQUENCE citus_schema_distribute_undistribute.seq;
|
||||
-- create complex table
|
||||
CREATE TABLE tenant1.complextable (id int PRIMARY KEY default nextval('citus_schema_distribute_undistribute.seq'), col int default (citus_schema_distribute_undistribute.one_as_result()), myserial serial, phone text COLLATE citus_schema_distribute_undistribute.german_phonebook, initials citus_schema_distribute_undistribute.pair_type);
|
||||
CREATE SEQUENCE tenant1.seq_owned OWNED BY tenant1.complextable.id;
|
||||
-- not allowed from workers
|
||||
SELECT run_command_on_workers($$SELECT citus_schema_distribute('tenant1');$$);
|
||||
run_command_on_workers
|
||||
---------------------------------------------------------------------
|
||||
(localhost,57637,f,"ERROR: operation is not allowed on this node")
|
||||
(localhost,57638,f,"ERROR: operation is not allowed on this node")
|
||||
(2 rows)
|
||||
|
||||
SELECT run_command_on_workers($$SELECT citus_schema_undistribute('tenant1');$$);
|
||||
run_command_on_workers
|
||||
---------------------------------------------------------------------
|
||||
(localhost,57637,f,"ERROR: operation is not allowed on this node")
|
||||
(localhost,57638,f,"ERROR: operation is not allowed on this node")
|
||||
(2 rows)
|
||||
|
||||
-- inherited table not allowed
|
||||
CREATE TABLE citus_schema_distribute_undistribute.cities (
|
||||
name text,
|
||||
population real,
|
||||
elevation int
|
||||
);
|
||||
CREATE TABLE citus_schema_distribute_undistribute.capitals (
|
||||
state char(2) UNIQUE NOT NULL
|
||||
) INHERITS (citus_schema_distribute_undistribute.cities);
|
||||
-- temporarily move "cities" into tenant1 (not a tenant schema yet) to test citus_schema_distribute() with a table that is inherited
|
||||
ALTER TABLE citus_schema_distribute_undistribute.cities SET SCHEMA tenant1;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
ERROR: tables in a distributed schema cannot inherit or be inherited
|
||||
ALTER TABLE tenant1.cities SET SCHEMA citus_schema_distribute_undistribute;
|
||||
-- temporarily move "capitals" into tenant1 (not a tenant schema yet) to test citus_schema_distribute() with a table that inherits
|
||||
ALTER TABLE citus_schema_distribute_undistribute.capitals SET SCHEMA tenant1;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
ERROR: tables in a distributed schema cannot inherit or be inherited
|
||||
ALTER TABLE tenant1.capitals SET SCHEMA citus_schema_distribute_undistribute;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
citus_schema_distribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
citus_schema_undistribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
CREATE TABLE citus_schema_distribute_undistribute.illegal_partitioned_table(id int) PARTITION BY RANGE(id);
|
||||
CREATE TABLE citus_schema_distribute_undistribute.illegal_partition1 PARTITION OF citus_schema_distribute_undistribute.illegal_partitioned_table FOR VALUES FROM (1) TO (11);
|
||||
-- temporarily move "illegal_partitioned_table" into tenant1 (not a tenant schema yet) to test citus_schema_distribute() with a partition table whose parent is created in another schema
|
||||
ALTER TABLE citus_schema_distribute_undistribute.illegal_partitioned_table SET SCHEMA tenant1;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas
|
||||
ALTER TABLE tenant1.illegal_partitioned_table SET SCHEMA citus_schema_distribute_undistribute;
|
||||
-- temporarily move "illegal_partition1" into tenant1 (not a tenant schema yet) to test citus_schema_distribute() with a parent table whose partition is created in another schema
|
||||
ALTER TABLE citus_schema_distribute_undistribute.illegal_partition1 SET SCHEMA tenant1;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas
|
||||
ALTER TABLE tenant1.illegal_partition1 SET SCHEMA citus_schema_distribute_undistribute;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
citus_schema_distribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
citus_schema_undistribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- foreign key to a local table in another schema is not allowed
|
||||
CREATE TABLE citus_schema_distribute_undistribute.tbl1(id int PRIMARY KEY);
|
||||
CREATE TABLE tenant1.table3z(id int PRIMARY KEY REFERENCES citus_schema_distribute_undistribute.tbl1(id));
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
ERROR: foreign keys from distributed schemas can only point to the same distributed schema or reference tables in regular schemas
|
||||
DETAIL: "tenant1.table3z" references "citus_schema_distribute_undistribute.tbl1" via foreign key constraint "table3z_id_fkey"
|
||||
SELECT create_reference_table('citus_schema_distribute_undistribute.tbl1');
|
||||
create_reference_table
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
citus_schema_distribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
citus_schema_undistribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- foreign key to a distributed table in another schema is not allowed
|
||||
CREATE TABLE citus_schema_distribute_undistribute.tbl2(id int PRIMARY KEY);
|
||||
SELECT create_distributed_table('citus_schema_distribute_undistribute.tbl2','id');
|
||||
create_distributed_table
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
CREATE TABLE tenant1.table3w(id int PRIMARY KEY REFERENCES citus_schema_distribute_undistribute.tbl2(id));
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
ERROR: foreign keys from distributed schemas can only point to the same distributed schema or reference tables in regular schemas
|
||||
DETAIL: "tenant1.table3w" references "citus_schema_distribute_undistribute.tbl2" via foreign key constraint "table3w_id_fkey"
|
||||
DROP TABLE citus_schema_distribute_undistribute.tbl2 CASCADE;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
citus_schema_distribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
citus_schema_undistribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- foreign key from a local table in another schema is not allowed
|
||||
CREATE TABLE tenant1.table3q(id int PRIMARY KEY);
|
||||
CREATE TABLE citus_schema_distribute_undistribute.tbl3(id int PRIMARY KEY REFERENCES tenant1.table3q(id));
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
ERROR: cannot create foreign keys to tables in a distributed schema from another schema
|
||||
DETAIL: "citus_schema_distribute_undistribute.tbl3" references "tenant1.table3q" via foreign key constraint "tbl3_id_fkey"
|
||||
DROP TABLE citus_schema_distribute_undistribute.tbl3 CASCADE;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
citus_schema_distribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
citus_schema_undistribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- foreign key from a reference table in another schema is not allowed
|
||||
CREATE TABLE tenant1.table3t(id int PRIMARY KEY);
|
||||
CREATE TABLE citus_schema_distribute_undistribute.tbl4(id int PRIMARY KEY REFERENCES tenant1.table3t(id));
|
||||
SELECT create_reference_table('citus_schema_distribute_undistribute.tbl4');
|
||||
create_reference_table
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
ERROR: cannot create foreign keys to tables in a distributed schema from another schema
|
||||
DETAIL: "citus_schema_distribute_undistribute.tbl4" references "tenant1.table3t" via foreign key constraint "tbl4_id_fkey"
|
||||
DROP TABLE citus_schema_distribute_undistribute.tbl4 CASCADE;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
citus_schema_distribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
citus_schema_undistribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- only allowed for schema owner or superuser
|
||||
CREATE USER dummyregular;
|
||||
SET role dummyregular;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
ERROR: must be owner of schema tenant1
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
ERROR: must be owner of schema tenant1
|
||||
-- assign all tables to dummyregular except table5
|
||||
SET role tenantuser;
|
||||
SELECT result FROM run_command_on_all_nodes($$ REASSIGN OWNED BY tenantuser TO dummyregular; $$);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
REASSIGN OWNED
|
||||
REASSIGN OWNED
|
||||
REASSIGN OWNED
|
||||
(3 rows)
|
||||
|
||||
CREATE TABLE tenant1.table5(id int);
|
||||
-- table owner check fails the distribution
|
||||
SET role dummyregular;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
ERROR: must be owner of table table5
|
||||
-- alter table owner, then redistribute
|
||||
SET role tenantuser;
|
||||
ALTER TABLE tenant1.table5 OWNER TO dummyregular;
|
||||
SET role dummyregular;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
citus_schema_distribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- show the schema is a tenant schema now
|
||||
SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset
|
||||
-- below query verifies the same colocationid in pg_dist_tenant_schema, pg_dist_colocation and all entries in pg_dist_partition at the same time
|
||||
SELECT '$$' ||
|
||||
' SELECT colocationid = ' || :tenant1_colocid ||
|
||||
' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' ||
|
||||
' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''tenant1%'')' ||
|
||||
'$$'
|
||||
AS verify_tenant_query \gset
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*) AS total FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1%' $$);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
15
|
||||
15
|
||||
15
|
||||
(3 rows)
|
||||
|
||||
SELECT result FROM run_command_on_all_nodes(:verify_tenant_query);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
t
|
||||
t
|
||||
t
|
||||
(3 rows)
|
||||
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
citus_schema_undistribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- show the schema is a regular schema
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
(3 rows)
|
||||
|
||||
-- below query verifies the tenant colocationid is removed from both pg_dist_colocation and all entries in pg_dist_partition at the same time
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
t
|
||||
t
|
||||
t
|
||||
(3 rows)
|
||||
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
(3 rows)
|
||||
|
||||
RESET role;
|
||||
SELECT result FROM run_command_on_all_nodes($$ REASSIGN OWNED BY dummyregular TO tenantuser; $$);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
REASSIGN OWNED
|
||||
REASSIGN OWNED
|
||||
REASSIGN OWNED
|
||||
(3 rows)
|
||||
|
||||
DROP USER dummyregular;
|
||||
CREATE USER dummysuper superuser;
|
||||
SET role dummysuper;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
citus_schema_distribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- show the schema is a tenant schema now
|
||||
SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset
|
||||
SELECT '$$' ||
|
||||
' SELECT colocationid = ' || :tenant1_colocid ||
|
||||
' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' ||
|
||||
' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''tenant1%'')' ||
|
||||
'$$'
|
||||
AS verify_tenant_query \gset
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*) AS total FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1%' $$);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
15
|
||||
15
|
||||
15
|
||||
(3 rows)
|
||||
|
||||
SELECT result FROM run_command_on_all_nodes(:verify_tenant_query);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
t
|
||||
t
|
||||
t
|
||||
(3 rows)
|
||||
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
citus_schema_undistribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- show the schema is a regular schema
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
(3 rows)
|
||||
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
t
|
||||
t
|
||||
t
|
||||
(3 rows)
|
||||
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
(3 rows)
|
||||
|
||||
RESET role;
|
||||
DROP USER dummysuper;
|
||||
-- foreign table
|
||||
CREATE TABLE tenant1.foreign_table_test (id integer NOT NULL, data text, a bigserial);
|
||||
INSERT INTO tenant1.foreign_table_test SELECT i FROM generate_series(1,100) i;
|
||||
CREATE EXTENSION postgres_fdw;
|
||||
CREATE SERVER foreign_server
|
||||
FOREIGN DATA WRAPPER postgres_fdw
|
||||
OPTIONS (host 'localhost', port :'master_port', dbname 'regression');
|
||||
CREATE USER MAPPING FOR CURRENT_USER
|
||||
SERVER foreign_server
|
||||
OPTIONS (user 'postgres');
|
||||
CREATE FOREIGN TABLE tenant1.foreign_table (
|
||||
id integer NOT NULL,
|
||||
data text,
|
||||
a bigserial
|
||||
)
|
||||
SERVER foreign_server
|
||||
OPTIONS (schema_name 'tenant1', table_name 'foreign_table_test');
|
||||
-- foreign table not allowed
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
ERROR: cannot create a foreign table in a distributed schema
|
||||
ALTER FOREIGN TABLE tenant1.foreign_table SET SCHEMA citus_schema_distribute_undistribute;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
citus_schema_distribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
citus_schema_undistribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- already have distributed table error
|
||||
CREATE TABLE tenant1.dist(id int);
|
||||
SELECT create_distributed_table('tenant1.dist', 'id');
|
||||
create_distributed_table
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
ERROR: schema already has distributed tables
|
||||
HINT: Undistribute distributed tables under the schema before distributing the schema.
|
||||
SELECT undistribute_table('tenant1.dist');
|
||||
undistribute_table
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
citus_schema_distribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
citus_schema_undistribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
CREATE TABLE tenant1.ref2(id int);
|
||||
SELECT create_reference_table('tenant1.ref2');
|
||||
create_reference_table
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
ERROR: schema already has distributed tables
|
||||
HINT: Undistribute distributed tables under the schema before distributing the schema.
|
||||
SELECT undistribute_table('tenant1.ref2');
|
||||
undistribute_table
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
citus_schema_distribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
citus_schema_undistribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
BEGIN;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
citus_schema_distribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
ROLLBACK;
|
||||
-- show the schema is a regular schema
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
(3 rows)
|
||||
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
t
|
||||
t
|
||||
t
|
||||
(3 rows)
|
||||
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
(3 rows)
|
||||
|
||||
-- errors not a tenant schema
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
ERROR: schema tenant1 is not distributed
|
||||
-- make it a tenant schema
|
||||
BEGIN;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
citus_schema_distribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
COMMIT;
|
||||
-- show the schema is a tenant schema now
|
||||
SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset
|
||||
SELECT '$$' ||
|
||||
' SELECT colocationid = ' || :tenant1_colocid ||
|
||||
' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' ||
|
||||
' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''tenant1%'')' ||
|
||||
'$$'
|
||||
AS verify_tenant_query \gset
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*) AS total FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1%' $$);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
18
|
||||
18
|
||||
18
|
||||
(3 rows)
|
||||
|
||||
SELECT result FROM run_command_on_all_nodes(:verify_tenant_query);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
t
|
||||
t
|
||||
t
|
||||
(3 rows)
|
||||
|
||||
-- already a tenant schema notice
|
||||
SET client_min_messages TO NOTICE;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
NOTICE: schema tenant1 is already distributed
|
||||
citus_schema_distribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SET client_min_messages TO WARNING;
|
||||
-- convert back to a regular schema
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
citus_schema_undistribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- show the schema is a regular schema now
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
(3 rows)
|
||||
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
t
|
||||
t
|
||||
t
|
||||
(3 rows)
|
||||
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
(3 rows)
|
||||
|
||||
-- tables still have valid data
|
||||
SELECT COUNT(*) FROM tenant1.partitioned_table;
|
||||
count
|
||||
---------------------------------------------------------------------
|
||||
20
|
||||
(1 row)
|
||||
|
||||
SELECT COUNT(*) FROM citus_schema_distribute_undistribute.foreign_table;
|
||||
count
|
||||
---------------------------------------------------------------------
|
||||
100
|
||||
(1 row)
|
||||
|
||||
SELECT COUNT(*) FROM tenant1.table3;
|
||||
count
|
||||
---------------------------------------------------------------------
|
||||
100
|
||||
(1 row)
|
||||
|
||||
TRUNCATE citus_schema_distribute_undistribute.ref CASCADE;
|
||||
SELECT COUNT(*) FROM tenant1.table3;
|
||||
count
|
||||
---------------------------------------------------------------------
|
||||
0
|
||||
(1 row)
|
||||
|
||||
-- disallowed namespaces
|
||||
SELECT citus_schema_distribute('public');
|
||||
ERROR: public schema cannot be distributed
|
||||
SELECT citus_schema_distribute('pg_catalog');
|
||||
ERROR: pg_catalog schema cannot be distributed
|
||||
SELECT citus_schema_distribute('pg_toast');
|
||||
ERROR: pg_toast schema cannot be distributed
|
||||
CREATE TEMP TABLE xx(id int); -- create a temp table in case we do not have any pg_temp_xx schema yet
|
||||
SELECT nspname AS temp_schema_name FROM pg_namespace WHERE nspname LIKE 'pg_temp%' LIMIT 1 \gset
|
||||
SELECT nspname AS temp_toast_schema_name FROM pg_namespace WHERE nspname LIKE 'pg_toast_temp%' LIMIT 1 \gset
|
||||
SELECT citus_schema_distribute(:'temp_schema_name');
|
||||
ERROR: temporary schema cannot be distributed
|
||||
SELECT citus_schema_distribute(:'temp_toast_schema_name');
|
||||
ERROR: temporary schema cannot be distributed
|
||||
SELECT citus_schema_distribute('citus');
|
||||
ERROR: schema citus, which is owned by an extension, cannot be distributed
|
||||
CREATE SCHEMA extensionschema;
|
||||
CREATE EXTENSION citext SCHEMA extensionschema;
|
||||
SELECT citus_schema_distribute('extensionschema');
|
||||
ERROR: schema extensionschema cannot be distributed since it is the schema of extension citext
|
||||
DROP SCHEMA extensionschema CASCADE;
|
||||
ALTER EXTENSION citus ADD TABLE tenant1.table1;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
ERROR: schema cannot be distributed since it has table tenant1 which is owned by an extension
|
||||
ALTER EXTENSION citus DROP TABLE tenant1.table1;
|
||||
-- weird schema and table names
|
||||
CREATE SCHEMA "CiTuS.TeeN";
|
||||
CREATE TABLE "CiTuS.TeeN"."TeeNTabLE.1!?!"(id int PRIMARY KEY, name text);
|
||||
INSERT INTO "CiTuS.TeeN"."TeeNTabLE.1!?!" SELECT i, 'asd'::text FROM generate_series(1,20) i;
|
||||
SELECT citus_schema_distribute('"CiTuS.TeeN"');
|
||||
citus_schema_distribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- show the schema is a tenant schema now
|
||||
SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset
|
||||
SELECT '$$' ||
|
||||
' SELECT colocationid = ' || :tenant1_colocid ||
|
||||
' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' ||
|
||||
' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''"CiTuS.TeeN"%'')' ||
|
||||
'$$'
|
||||
AS verify_tenant_query \gset
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*) AS total FROM pg_dist_partition WHERE logicalrelid::text LIKE '"CiTuS.TeeN"%' $$);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
1
|
||||
1
|
||||
1
|
||||
(3 rows)
|
||||
|
||||
SELECT result FROM run_command_on_all_nodes(:verify_tenant_query);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
t
|
||||
t
|
||||
t
|
||||
(3 rows)
|
||||
|
||||
SELECT citus_schema_undistribute('"CiTuS.TeeN"');
|
||||
citus_schema_undistribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- show the schema is a regular schema
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
(3 rows)
|
||||
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
t
|
||||
t
|
||||
t
|
||||
(3 rows)
|
||||
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
(3 rows)
|
||||
|
||||
-- try setting the schema again after adding a distributed table into the schema. It should complain about distributed table.
|
||||
CREATE TABLE tenant1.new_dist(id int);
|
||||
SELECT create_distributed_table('tenant1.new_dist', 'id');
|
||||
create_distributed_table
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
ERROR: schema already has distributed tables
|
||||
HINT: Undistribute distributed tables under the schema before distributing the schema.
|
||||
SELECT undistribute_table('tenant1.new_dist');
|
||||
undistribute_table
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
citus_schema_distribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
citus_schema_undistribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- try setting the schema again after adding a single shard table into the schema. It should complain about distributed table.
|
||||
CREATE TABLE tenant1.single_shard_t(id int);
|
||||
SELECT create_distributed_table('tenant1.single_shard_t', NULL);
|
||||
create_distributed_table
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
ERROR: schema already has distributed tables
|
||||
HINT: Undistribute distributed tables under the schema before distributing the schema.
|
||||
SELECT undistribute_table('tenant1.single_shard_t');
|
||||
undistribute_table
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- try setting the schema again. It should succeed now.
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
citus_schema_distribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- show the schema is a tenant schema now
|
||||
SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset
|
||||
SELECT '$$' ||
|
||||
' SELECT colocationid = ' || :tenant1_colocid ||
|
||||
' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' ||
|
||||
' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''tenant1%'')' ||
|
||||
'$$'
|
||||
AS verify_tenant_query \gset
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*) AS total FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1%' $$);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
20
|
||||
20
|
||||
20
|
||||
(3 rows)
|
||||
|
||||
SELECT result FROM run_command_on_all_nodes(:verify_tenant_query);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
t
|
||||
t
|
||||
t
|
||||
(3 rows)
|
||||
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
citus_schema_undistribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- show the schema is a regular schema now
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
(3 rows)
|
||||
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
t
|
||||
t
|
||||
t
|
||||
(3 rows)
|
||||
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
(3 rows)
|
||||
|
||||
-- create an empty tenant schema to verify colocation id is removed successfully after we undistribute it
|
||||
CREATE SCHEMA empty_tenant;
|
||||
SELECT citus_schema_distribute('empty_tenant');
|
||||
citus_schema_distribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- show the schema is a tenant schema now
|
||||
SELECT colocationid AS empty_tenant_colocid FROM pg_dist_tenant_schema schemaid \gset
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace FROM pg_dist_colocation JOIN pg_dist_tenant_schema USING(colocationid) $$);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
empty_tenant
|
||||
empty_tenant
|
||||
empty_tenant
|
||||
(3 rows)
|
||||
|
||||
SELECT citus_schema_undistribute('empty_tenant');
|
||||
citus_schema_undistribute
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- show the schema is a regular schema now
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
(3 rows)
|
||||
|
||||
SELECT '$$' || 'SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = ' || :empty_tenant_colocid || '$$'
|
||||
AS verify_empty_tenant_query \gset
|
||||
SELECT result FROM run_command_on_all_nodes(:verify_empty_tenant_query);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
t
|
||||
t
|
||||
t
|
||||
(3 rows)
|
||||
|
||||
-- cleanup
|
||||
DROP SCHEMA "CiTuS.TeeN" CASCADE;
|
||||
DROP SCHEMA tenant1 CASCADE;
|
||||
DROP SCHEMA empty_tenant CASCADE;
|
||||
DROP EXTENSION postgres_fdw CASCADE;
|
||||
DROP SCHEMA citus_schema_distribute_undistribute CASCADE;
|
||||
DROP USER tenantuser;
|
||||
SELECT citus_remove_node('localhost', :master_port);
|
||||
citus_remove_node
|
||||
---------------------------------------------------------------------
|
||||
|
||||
(1 row)
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -1362,8 +1362,10 @@ SELECT * FROM multi_extension.print_extension_changes();
|
|||
| function citus_internal_add_tenant_schema(oid,integer) void
|
||||
| function citus_internal_delete_tenant_schema(oid) void
|
||||
| function citus_internal_unregister_tenant_schema_globally(oid,text) void
|
||||
| function citus_schema_distribute(regnamespace) void
|
||||
| function citus_schema_undistribute(regnamespace) void
|
||||
| table pg_dist_tenant_schema
|
||||
(4 rows)
|
||||
(6 rows)
|
||||
|
||||
DROP TABLE multi_extension.prev_objects, multi_extension.extension_diff;
|
||||
-- show running version
|
||||
|
|
|
@ -76,28 +76,28 @@ SELECT citus_add_local_table_to_metadata('tenant_2.test_table');
|
|||
ERROR: table "test_table" is already distributed
|
||||
-- verify we don't allow update_distributed_table_colocation for tenant tables
|
||||
SELECT update_distributed_table_colocation('tenant_2.test_table', colocate_with => 'none');
|
||||
ERROR: tenant_2.test_table is not allowed for update_distributed_table_colocation because it is a tenant table
|
||||
ERROR: tenant_2.test_table is not allowed for update_distributed_table_colocation because it belongs to a distributed schema
|
||||
-- verify we also don't allow colocate_with a tenant table
|
||||
SELECT update_distributed_table_colocation('regular_schema.test_table', colocate_with => 'tenant_2.test_table');
|
||||
ERROR: tenant_2.test_table is not allowed for colocate_with because it is a tenant table
|
||||
ERROR: tenant_2.test_table is not allowed for colocate_with because it belongs to a distributed schema
|
||||
-- verify we don't allow undistribute_table for tenant tables
|
||||
SELECT undistribute_table('tenant_2.test_table');
|
||||
ERROR: tenant_2.test_table is not allowed for undistribute_table because it is a tenant table
|
||||
ERROR: tenant_2.test_table is not allowed for undistribute_table because it belongs to a distributed schema
|
||||
-- verify we don't allow alter_distributed_table for tenant tables
|
||||
SELECT alter_distributed_table('tenant_2.test_table', colocate_with => 'none');
|
||||
ERROR: tenant_2.test_table is not allowed for alter_distributed_table because it is a tenant table
|
||||
ERROR: tenant_2.test_table is not allowed for alter_distributed_table because it belongs to a distributed schema
|
||||
-- verify we also don't allow colocate_with a tenant table
|
||||
SELECT alter_distributed_table('regular_schema.test_table', colocate_with => 'tenant_2.test_table');
|
||||
ERROR: tenant_2.test_table is not allowed for colocate_with because it is a tenant table
|
||||
ERROR: tenant_2.test_table is not allowed for colocate_with because it belongs to a distributed schema
|
||||
-- verify we don't allow ALTER TABLE SET SCHEMA for tenant tables
|
||||
ALTER TABLE tenant_2.test_table SET SCHEMA regular_schema;
|
||||
ERROR: tenant_2.test_table is not allowed for ALTER TABLE SET SCHEMA because it is a tenant table
|
||||
ERROR: tenant_2.test_table is not allowed for ALTER TABLE SET SCHEMA because it belongs to a distributed schema
|
||||
-- verify we don't allow ALTER TABLE SET SCHEMA for tenant schemas
|
||||
ALTER TABLE regular_schema.test_table SET SCHEMA tenant_2;
|
||||
ERROR: tenant_2 is not allowed for ALTER TABLE SET SCHEMA because it is a distributed schema
|
||||
-- the same, from tenant schema to tenant schema
|
||||
ALTER TABLE tenant_2.test_table SET SCHEMA tenant_3;
|
||||
ERROR: tenant_2.test_table is not allowed for ALTER TABLE SET SCHEMA because it is a tenant table
|
||||
ERROR: tenant_2.test_table is not allowed for ALTER TABLE SET SCHEMA because it belongs to a distributed schema
|
||||
-- (on coordinator) verify that colocation id is set for empty tenants too
|
||||
SELECT colocationid > 0 FROM pg_dist_tenant_schema
|
||||
WHERE schemaid::regnamespace::text IN ('tenant_1', 'tenant_3');
|
||||
|
@ -200,7 +200,7 @@ CREATE FOREIGN TABLE tenant_4.foreign_table (
|
|||
id bigint not null,
|
||||
full_name text not null default ''
|
||||
) SERVER fake_fdw_server OPTIONS (encoding 'utf-8', compression 'true', table_name 'foreign_table');
|
||||
ERROR: cannot create a tenant table from a foreign table
|
||||
ERROR: cannot create a foreign table in a distributed schema
|
||||
-- verify that we don't allow creating a foreign table in a tenant schema
|
||||
CREATE TEMPORARY TABLE tenant_4.temp_table (a int, b text);
|
||||
ERROR: cannot create temporary relation in non-temporary schema
|
||||
|
@ -275,13 +275,13 @@ CREATE TABLE tenant_5.tbl_1(a int, b text);
|
|||
CREATE TABLE tenant_5.partitioned_table(a int, b text) PARTITION BY RANGE (a);
|
||||
-- verify that we don't allow creating a partition table that is child of a partitioned table in a different tenant schema
|
||||
CREATE TABLE tenant_4.partitioned_table_child_2 PARTITION OF tenant_5.partitioned_table FOR VALUES FROM (1) TO (2);
|
||||
ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas
|
||||
ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas
|
||||
-- verify that we don't allow creating a local partition table that is child of a tenant partitioned table
|
||||
CREATE TABLE regular_schema.local_child_table PARTITION OF tenant_5.partitioned_table FOR VALUES FROM (1) TO (2);
|
||||
ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas
|
||||
ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas
|
||||
SET citus.use_citus_managed_tables TO ON;
|
||||
CREATE TABLE regular_schema.local_child_table PARTITION OF tenant_5.partitioned_table FOR VALUES FROM (1) TO (2);
|
||||
ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas
|
||||
ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas
|
||||
RESET citus.use_citus_managed_tables;
|
||||
CREATE TABLE regular_schema.local_partitioned_table(a int, b text) PARTITION BY RANGE (a);
|
||||
CREATE TABLE regular_schema.citus_local_partitioned_table(a int, b text) PARTITION BY RANGE (a);
|
||||
|
@ -300,11 +300,11 @@ SELECT create_distributed_table('regular_schema.dist_partitioned_table', 'a');
|
|||
|
||||
-- verify that we don't allow creating a partition table that is child of a non-tenant partitioned table
|
||||
CREATE TABLE tenant_4.partitioned_table_child_2 PARTITION OF regular_schema.local_partitioned_table FOR VALUES FROM (1) TO (2);
|
||||
ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas
|
||||
ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas
|
||||
CREATE TABLE tenant_4.partitioned_table_child_2 PARTITION OF regular_schema.citus_local_partitioned_table FOR VALUES FROM (1) TO (2);
|
||||
ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas
|
||||
ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas
|
||||
CREATE TABLE tenant_4.partitioned_table_child_2 PARTITION OF regular_schema.dist_partitioned_table FOR VALUES FROM (1) TO (2);
|
||||
ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas
|
||||
ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas
|
||||
CREATE TABLE tenant_4.parent_attach_test(a int, b text) PARTITION BY RANGE (a);
|
||||
CREATE TABLE tenant_4.child_attach_test(a int, b text);
|
||||
CREATE TABLE tenant_5.parent_attach_test(a int, b text) PARTITION BY RANGE (a);
|
||||
|
@ -341,21 +341,21 @@ SELECT create_distributed_table('regular_schema.child_attach_test_dist', 'a');
|
|||
|
||||
-- verify that we don't allow attaching a tenant table into a tenant partitioned table, if they are not in the same schema
|
||||
ALTER TABLE tenant_4.parent_attach_test ATTACH PARTITION tenant_5.child_attach_test FOR VALUES FROM (1) TO (2);
|
||||
ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas
|
||||
ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas
|
||||
-- verify that we don't allow attaching a non-tenant table into a tenant partitioned table
|
||||
ALTER TABLE tenant_4.parent_attach_test ATTACH PARTITION regular_schema.child_attach_test_local FOR VALUES FROM (1) TO (2);
|
||||
ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas
|
||||
ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas
|
||||
ALTER TABLE tenant_4.parent_attach_test ATTACH PARTITION regular_schema.child_attach_test_citus_local FOR VALUES FROM (1) TO (2);
|
||||
ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas
|
||||
ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas
|
||||
ALTER TABLE tenant_4.parent_attach_test ATTACH PARTITION regular_schema.child_attach_test_dist FOR VALUES FROM (1) TO (2);
|
||||
ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas
|
||||
ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas
|
||||
-- verify that we don't allow attaching a tenant table into a non-tenant partitioned table
|
||||
ALTER TABLE regular_schema.parent_attach_test_local ATTACH PARTITION tenant_4.child_attach_test FOR VALUES FROM (1) TO (2);
|
||||
ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas
|
||||
ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas
|
||||
ALTER TABLE regular_schema.parent_attach_test_citus_local ATTACH PARTITION tenant_4.child_attach_test FOR VALUES FROM (1) TO (2);
|
||||
ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas
|
||||
ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas
|
||||
ALTER TABLE regular_schema.parent_attach_test_dist ATTACH PARTITION tenant_4.child_attach_test FOR VALUES FROM (1) TO (2);
|
||||
ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas
|
||||
ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas
|
||||
ALTER TABLE tenant_4.parent_attach_test ATTACH PARTITION tenant_4.child_attach_test FOR VALUES FROM (1) TO (2);
|
||||
-- verify that we don't allow multi-level partitioning on tenant tables
|
||||
CREATE TABLE tenant_4.multi_level_test(a int, b text) PARTITION BY RANGE (a);
|
||||
|
@ -1330,7 +1330,7 @@ DROP ROLE test_non_super_user;
|
|||
\c - - - :worker_1_port
|
||||
-- test creating a tenant table from workers
|
||||
CREATE TABLE tenant_3.tbl_1(a int, b text);
|
||||
ERROR: cannot create a tenant table from a worker node
|
||||
ERROR: cannot create tables in a distributed schema from a worker node
|
||||
HINT: Connect to the coordinator node and try again.
|
||||
-- test creating a tenant schema from workers
|
||||
SET citus.enable_schema_based_sharding TO ON;
|
||||
|
|
|
@ -114,6 +114,8 @@ ORDER BY 1;
|
|||
function citus_remote_connection_stats()
|
||||
function citus_remove_node(text,integer)
|
||||
function citus_run_local_command(text)
|
||||
function citus_schema_distribute(regnamespace)
|
||||
function citus_schema_undistribute(regnamespace)
|
||||
function citus_server_id()
|
||||
function citus_set_coordinator_host(text,integer,noderole,name)
|
||||
function citus_set_default_rebalance_strategy(text)
|
||||
|
@ -334,5 +336,5 @@ ORDER BY 1;
|
|||
view citus_stat_tenants_local
|
||||
view pg_dist_shard_placement
|
||||
view time_partitions
|
||||
(326 rows)
|
||||
(328 rows)
|
||||
|
||||
|
|
|
@ -77,6 +77,7 @@ test: isolation_global_pid
|
|||
test: isolation_citus_locks
|
||||
test: isolation_reference_table
|
||||
test: isolation_schema_based_sharding
|
||||
test: isolation_citus_schema_distribute_undistribute
|
||||
|
||||
# Rebalancer
|
||||
test: isolation_blocking_move_single_shard_commands
|
||||
|
|
|
@ -36,6 +36,7 @@ test: create_single_shard_table
|
|||
# don't parallelize single_shard_table_udfs to make sure colocation ids are sequential
|
||||
test: single_shard_table_udfs
|
||||
test: schema_based_sharding
|
||||
test: citus_schema_distribute_undistribute
|
||||
|
||||
test: multi_test_catalog_views
|
||||
test: multi_table_ddl
|
||||
|
|
|
@ -0,0 +1,171 @@
|
|||
setup
|
||||
{
|
||||
SELECT citus_set_coordinator_host('localhost', 57636);
|
||||
|
||||
CREATE SCHEMA tenant1;
|
||||
CREATE TABLE tenant1.table1(id int PRIMARY KEY, name text, col bigint);
|
||||
INSERT INTO tenant1.table1 SELECT i, 'asd', i*1000 FROM generate_series(11, 20) i;
|
||||
|
||||
CREATE TABLE tenant1.table2(id int PRIMARY KEY, name text, col bigint);
|
||||
|
||||
CREATE TABLE public.ref(id int PRIMARY KEY);
|
||||
SELECT create_reference_table('public.ref');
|
||||
|
||||
CREATE TABLE tenant1.table3(id int PRIMARY KEY, name text, col1 int, col int REFERENCES public.ref(id));
|
||||
SELECT citus_add_local_table_to_metadata('tenant1.table3');
|
||||
}
|
||||
|
||||
teardown
|
||||
{
|
||||
DROP TABLE public.ref CASCADE;
|
||||
DROP SCHEMA IF EXISTS tenant1, tenant2 CASCADE;
|
||||
SELECT citus_remove_node('localhost', 57636);
|
||||
}
|
||||
|
||||
session "s1"
|
||||
|
||||
step "s1-begin"
|
||||
{
|
||||
BEGIN;
|
||||
}
|
||||
|
||||
step "s1-schema-distribute"
|
||||
{
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
}
|
||||
|
||||
step "s1-schema-undistribute"
|
||||
{
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
}
|
||||
|
||||
step "s1-verify-distributed-schema"
|
||||
{
|
||||
SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid;
|
||||
}
|
||||
|
||||
step "s1-commit"
|
||||
{
|
||||
COMMIT;
|
||||
}
|
||||
|
||||
session "s2"
|
||||
|
||||
step "s2-drop-schema"
|
||||
{
|
||||
DROP SCHEMA tenant1 CASCADE;
|
||||
}
|
||||
|
||||
step "s2-rename-schema"
|
||||
{
|
||||
ALTER SCHEMA tenant1 RENAME TO tenant2;
|
||||
}
|
||||
|
||||
step "s2-add-table"
|
||||
{
|
||||
CREATE TABLE tenant1.table4(id int PRIMARY KEY, name text, col bigint);
|
||||
}
|
||||
|
||||
step "s2-drop-table"
|
||||
{
|
||||
DROP TABLE tenant1.table3;
|
||||
}
|
||||
|
||||
step "s2-alter-col-type"
|
||||
{
|
||||
ALTER TABLE tenant1.table3 ALTER COLUMN col1 TYPE text;
|
||||
}
|
||||
|
||||
step "s2-add-foreign-key"
|
||||
{
|
||||
ALTER TABLE tenant1.table3 ADD CONSTRAINT table3_fk1 FOREIGN KEY (id) REFERENCES tenant1.table2 (id);
|
||||
}
|
||||
|
||||
step "s2-drop-foreign-key"
|
||||
{
|
||||
ALTER TABLE tenant1.table3 DROP CONSTRAINT table3_col_fkey;
|
||||
}
|
||||
|
||||
step "s2-create-unique-index"
|
||||
{
|
||||
CREATE UNIQUE INDEX idx_2 ON tenant1.table3 (col);
|
||||
}
|
||||
|
||||
step "s2-create-unique-index-concurrently"
|
||||
{
|
||||
CREATE UNIQUE INDEX CONCURRENTLY idx_3 ON tenant1.table3 (col);
|
||||
}
|
||||
|
||||
step "s2-reindex-unique-concurrently"
|
||||
{
|
||||
REINDEX INDEX CONCURRENTLY tenant1.idx_2;
|
||||
}
|
||||
|
||||
step "s2-insert"
|
||||
{
|
||||
// we insert into public.ref table as well to prevent fkey violation
|
||||
INSERT INTO public.ref SELECT i FROM generate_series(11, 20) i;
|
||||
INSERT INTO tenant1.table3 SELECT i, 'asd', i*1000 FROM generate_series(11, 20) i;
|
||||
}
|
||||
|
||||
step "s2-delete"
|
||||
{
|
||||
DELETE FROM tenant1.table3;
|
||||
}
|
||||
|
||||
step "s2-update"
|
||||
{
|
||||
UPDATE tenant1.table3 SET col = 11 WHERE col = 11;
|
||||
}
|
||||
|
||||
// DROP SCHEMA
|
||||
permutation "s1-begin" "s1-schema-distribute" "s2-drop-schema" "s1-commit" "s1-verify-distributed-schema"
|
||||
permutation "s1-schema-distribute" "s1-begin" "s1-schema-undistribute" "s2-drop-schema" "s1-commit" "s1-verify-distributed-schema"
|
||||
|
||||
// RENAME SCHEMA
|
||||
permutation "s1-begin" "s1-schema-distribute" "s2-rename-schema" "s1-commit" "s1-verify-distributed-schema"
|
||||
permutation "s1-schema-distribute" "s1-begin" "s1-schema-undistribute" "s2-rename-schema" "s1-commit" "s1-verify-distributed-schema"
|
||||
|
||||
// CREATE TABLE
|
||||
permutation "s1-begin" "s1-schema-distribute" "s2-add-table" "s1-commit" "s1-verify-distributed-schema"
|
||||
permutation "s1-schema-distribute" "s1-begin" "s1-schema-undistribute" "s2-add-table" "s1-commit" "s1-verify-distributed-schema"
|
||||
|
||||
// DROP TABLE
|
||||
permutation "s1-begin" "s1-schema-distribute" "s2-drop-table" "s1-commit" "s1-verify-distributed-schema"
|
||||
permutation "s1-schema-distribute" "s1-begin" "s1-schema-undistribute" "s2-drop-table" "s1-commit" "s1-verify-distributed-schema"
|
||||
|
||||
// ALTER TABLE ALTER COLUMN TYPE
|
||||
permutation "s1-begin" "s1-schema-distribute" "s2-alter-col-type" "s1-commit" "s1-verify-distributed-schema"
|
||||
permutation "s1-schema-distribute" "s1-begin" "s1-schema-undistribute" "s2-alter-col-type" "s1-commit" "s1-verify-distributed-schema"
|
||||
|
||||
// ADD FOREIGN KEY
|
||||
permutation "s1-begin" "s1-schema-distribute" "s2-add-foreign-key" "s1-commit" "s1-verify-distributed-schema"
|
||||
permutation "s1-schema-distribute" "s1-begin" "s1-schema-undistribute" "s2-add-foreign-key" "s1-commit" "s1-verify-distributed-schema"
|
||||
|
||||
// DROP FOREIGN KEY
|
||||
permutation "s1-begin" "s1-schema-distribute" "s2-drop-foreign-key" "s1-commit" "s1-verify-distributed-schema"
|
||||
permutation "s1-schema-distribute" "s1-begin" "s1-schema-undistribute" "s2-drop-foreign-key" "s1-commit" "s1-verify-distributed-schema"
|
||||
|
||||
// CREATE UNIQUE INDEX
|
||||
permutation "s1-begin" "s1-schema-distribute" "s2-create-unique-index" "s1-commit" "s1-verify-distributed-schema"
|
||||
permutation "s1-schema-distribute" "s1-begin" "s1-schema-undistribute" "s2-create-unique-index" "s1-commit" "s1-verify-distributed-schema"
|
||||
|
||||
// CREATE UNIQUE INDEX CONCURRENTLY
|
||||
permutation "s1-begin" "s1-schema-distribute" "s2-create-unique-index-concurrently" "s1-commit" "s1-verify-distributed-schema"
|
||||
permutation "s1-schema-distribute" "s1-begin" "s1-schema-undistribute" "s2-create-unique-index-concurrently" "s1-commit" "s1-verify-distributed-schema"
|
||||
|
||||
// REINDEX CONCURRENTLY
|
||||
permutation "s2-create-unique-index" "s1-begin" "s1-schema-distribute" "s2-reindex-unique-concurrently" "s1-commit" "s1-verify-distributed-schema"
|
||||
permutation "s2-create-unique-index" "s1-schema-distribute" "s1-begin" "s1-schema-undistribute" "s2-reindex-unique-concurrently" "s1-commit" "s1-verify-distributed-schema"
|
||||
|
||||
// INSERT
|
||||
permutation "s1-begin" "s1-schema-distribute" "s2-insert" "s1-commit" "s1-verify-distributed-schema"
|
||||
permutation "s1-schema-distribute" "s1-begin" "s1-schema-undistribute" "s2-insert" "s1-commit" "s1-verify-distributed-schema"
|
||||
|
||||
// UPDATE
|
||||
permutation "s2-insert" "s1-begin" "s1-schema-distribute" "s2-update" "s1-commit" "s1-verify-distributed-schema"
|
||||
permutation "s2-insert" "s1-schema-distribute" "s1-begin" "s1-schema-undistribute" "s2-update" "s1-commit" "s1-verify-distributed-schema"
|
||||
|
||||
// DELETE
|
||||
permutation "s2-insert" "s1-begin" "s1-schema-distribute" "s2-delete" "s1-commit" "s1-verify-distributed-schema"
|
||||
permutation "s2-insert" "s1-schema-distribute" "s1-begin" "s1-schema-undistribute" "s2-delete" "s1-commit" "s1-verify-distributed-schema"
|
|
@ -0,0 +1,437 @@
|
|||
SET citus.next_shard_id TO 1730000;
|
||||
SET citus.shard_replication_factor TO 1;
|
||||
SET client_min_messages TO WARNING;
|
||||
SET citus.enable_schema_based_sharding TO off;
|
||||
|
||||
SELECT 1 FROM citus_add_node('localhost', :master_port, groupid => 0);
|
||||
|
||||
CREATE USER tenantuser superuser;
|
||||
SET role tenantuser;
|
||||
|
||||
-- check invalid input
|
||||
SELECT citus_schema_distribute(1);
|
||||
SELECT citus_schema_undistribute(1);
|
||||
|
||||
-- noop
|
||||
SELECT citus_schema_distribute(null);
|
||||
SELECT citus_schema_undistribute(null);
|
||||
|
||||
-- public and some others cannot be distributed as a tenant schema, but check what happens
|
||||
-- if we try to call citus_schema_undistribute() for such a schema.
|
||||
SELECT citus_schema_undistribute('public');
|
||||
|
||||
-- create non-tenant schema
|
||||
CREATE SCHEMA citus_schema_distribute_undistribute;
|
||||
|
||||
-- create tenant schema
|
||||
CREATE SCHEMA tenant1;
|
||||
|
||||
CREATE TABLE tenant1.table1(id int PRIMARY KEY, name text);
|
||||
INSERT INTO tenant1.table1 SELECT i, 'asd'::text FROM generate_series(1,20) i;
|
||||
|
||||
CREATE TABLE tenant1.table2(id int REFERENCES tenant1.table1(id), num bigint UNIQUE);
|
||||
INSERT INTO tenant1.table2 SELECT i, i FROM generate_series(1,20) i;
|
||||
|
||||
CREATE TABLE citus_schema_distribute_undistribute.ref(id int PRIMARY KEY);
|
||||
SELECT create_reference_table('citus_schema_distribute_undistribute.ref');
|
||||
INSERT INTO citus_schema_distribute_undistribute.ref SELECT i FROM generate_series(1,100) i;
|
||||
|
||||
-- autoconverted to Citus local table due to foreign key to reference table
|
||||
CREATE TABLE tenant1.table3(id int REFERENCES citus_schema_distribute_undistribute.ref(id));
|
||||
INSERT INTO tenant1.table3 SELECT i FROM generate_series(1,100) i;
|
||||
|
||||
-- Citus local table with autoconverted=false
|
||||
CREATE TABLE tenant1.table3x(id int PRIMARY KEY REFERENCES citus_schema_distribute_undistribute.ref(id));
|
||||
SELECT citus_add_local_table_to_metadata('tenant1.table3x');
|
||||
INSERT INTO tenant1.table3x SELECT i FROM generate_series(1,100) i;
|
||||
|
||||
-- foreign key to another local table in the same schema
|
||||
CREATE TABLE tenant1.table3y(id int PRIMARY KEY REFERENCES tenant1.table3x(id));
|
||||
SELECT citus_add_local_table_to_metadata('tenant1.table3y');
|
||||
INSERT INTO tenant1.table3y SELECT i FROM generate_series(1,100) i;
|
||||
|
||||
-- table with composite type
|
||||
CREATE TYPE tenant1.catname AS ENUM ('baby', 'teen', 'mid');
|
||||
CREATE TYPE tenant1.agecat AS (below_age int, name tenant1.catname);
|
||||
CREATE TABLE tenant1.table4(id int, age tenant1.agecat);
|
||||
|
||||
-- create autoconverted partitioned table
|
||||
CREATE TABLE tenant1.partitioned_table(id int REFERENCES citus_schema_distribute_undistribute.ref(id)) PARTITION BY RANGE(id);
|
||||
CREATE TABLE tenant1.partition1 PARTITION OF tenant1.partitioned_table FOR VALUES FROM (1) TO (11);
|
||||
CREATE TABLE tenant1.partition2 PARTITION OF tenant1.partitioned_table FOR VALUES FROM (11) TO (21);
|
||||
INSERT INTO tenant1.partitioned_table SELECT i FROM generate_series(1,20) i;
|
||||
|
||||
-- create view
|
||||
CREATE VIEW tenant1.view1 AS SELECT * FROM tenant1.table1 JOIN tenant1.table2 USING(id);
|
||||
|
||||
-- create view in regular schema
|
||||
CREATE VIEW citus_schema_distribute_undistribute.view2 AS SELECT * FROM tenant1.view1;
|
||||
|
||||
-- create materialized view
|
||||
CREATE MATERIALIZED VIEW tenant1.view2 AS SELECT * FROM tenant1.table1;
|
||||
|
||||
-- create collation
|
||||
CREATE COLLATION citus_schema_distribute_undistribute.german_phonebook (provider = icu, locale = 'de-u-co-phonebk');
|
||||
|
||||
-- create type
|
||||
CREATE TYPE citus_schema_distribute_undistribute.pair_type AS (a int, b int);
|
||||
|
||||
-- Create function
|
||||
CREATE FUNCTION citus_schema_distribute_undistribute.one_as_result() RETURNS INT LANGUAGE SQL AS
|
||||
$$
|
||||
SELECT 1;
|
||||
$$;
|
||||
|
||||
-- create text search dictionary
|
||||
CREATE TEXT SEARCH DICTIONARY citus_schema_distribute_undistribute.my_german_dict (
|
||||
template = snowball,
|
||||
language = german,
|
||||
stopwords = german
|
||||
);
|
||||
|
||||
-- create text search config
|
||||
CREATE TEXT SEARCH CONFIGURATION citus_schema_distribute_undistribute.my_ts_config ( parser = default );
|
||||
ALTER TEXT SEARCH CONFIGURATION citus_schema_distribute_undistribute.my_ts_config ALTER MAPPING FOR asciiword WITH citus_schema_distribute_undistribute.my_german_dict;
|
||||
|
||||
-- create sequence
|
||||
CREATE SEQUENCE citus_schema_distribute_undistribute.seq;
|
||||
|
||||
-- create complex table
|
||||
CREATE TABLE tenant1.complextable (id int PRIMARY KEY default nextval('citus_schema_distribute_undistribute.seq'), col int default (citus_schema_distribute_undistribute.one_as_result()), myserial serial, phone text COLLATE citus_schema_distribute_undistribute.german_phonebook, initials citus_schema_distribute_undistribute.pair_type);
|
||||
CREATE SEQUENCE tenant1.seq_owned OWNED BY tenant1.complextable.id;
|
||||
|
||||
-- not allowed from workers
|
||||
SELECT run_command_on_workers($$SELECT citus_schema_distribute('tenant1');$$);
|
||||
SELECT run_command_on_workers($$SELECT citus_schema_undistribute('tenant1');$$);
|
||||
|
||||
-- inherited table not allowed
|
||||
CREATE TABLE citus_schema_distribute_undistribute.cities (
|
||||
name text,
|
||||
population real,
|
||||
elevation int
|
||||
);
|
||||
CREATE TABLE citus_schema_distribute_undistribute.capitals (
|
||||
state char(2) UNIQUE NOT NULL
|
||||
) INHERITS (citus_schema_distribute_undistribute.cities);
|
||||
|
||||
-- temporarily move "cities" into tenant1 (not a tenant schema yet) to test citus_schema_distribute() with a table that is inherited
|
||||
ALTER TABLE citus_schema_distribute_undistribute.cities SET SCHEMA tenant1;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
ALTER TABLE tenant1.cities SET SCHEMA citus_schema_distribute_undistribute;
|
||||
|
||||
-- temporarily move "capitals" into tenant1 (not a tenant schema yet) to test citus_schema_distribute() with a table that inherits
|
||||
ALTER TABLE citus_schema_distribute_undistribute.capitals SET SCHEMA tenant1;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
ALTER TABLE tenant1.capitals SET SCHEMA citus_schema_distribute_undistribute;
|
||||
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
|
||||
CREATE TABLE citus_schema_distribute_undistribute.illegal_partitioned_table(id int) PARTITION BY RANGE(id);
|
||||
CREATE TABLE citus_schema_distribute_undistribute.illegal_partition1 PARTITION OF citus_schema_distribute_undistribute.illegal_partitioned_table FOR VALUES FROM (1) TO (11);
|
||||
|
||||
-- temporarily move "illegal_partitioned_table" into tenant1 (not a tenant schema yet) to test citus_schema_distribute() with a partition table whose parent is created in another schema
|
||||
ALTER TABLE citus_schema_distribute_undistribute.illegal_partitioned_table SET SCHEMA tenant1;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
ALTER TABLE tenant1.illegal_partitioned_table SET SCHEMA citus_schema_distribute_undistribute;
|
||||
|
||||
-- temporarily move "illegal_partition1" into tenant1 (not a tenant schema yet) to test citus_schema_distribute() with a parent table whose partition is created in another schema
|
||||
ALTER TABLE citus_schema_distribute_undistribute.illegal_partition1 SET SCHEMA tenant1;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
ALTER TABLE tenant1.illegal_partition1 SET SCHEMA citus_schema_distribute_undistribute;
|
||||
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
|
||||
-- foreign key to a local table in another schema is not allowed
|
||||
CREATE TABLE citus_schema_distribute_undistribute.tbl1(id int PRIMARY KEY);
|
||||
CREATE TABLE tenant1.table3z(id int PRIMARY KEY REFERENCES citus_schema_distribute_undistribute.tbl1(id));
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
SELECT create_reference_table('citus_schema_distribute_undistribute.tbl1');
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
|
||||
-- foreign key to a distributed table in another schema is not allowed
|
||||
CREATE TABLE citus_schema_distribute_undistribute.tbl2(id int PRIMARY KEY);
|
||||
SELECT create_distributed_table('citus_schema_distribute_undistribute.tbl2','id');
|
||||
CREATE TABLE tenant1.table3w(id int PRIMARY KEY REFERENCES citus_schema_distribute_undistribute.tbl2(id));
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
DROP TABLE citus_schema_distribute_undistribute.tbl2 CASCADE;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
|
||||
-- foreign key from a local table in another schema is not allowed
|
||||
CREATE TABLE tenant1.table3q(id int PRIMARY KEY);
|
||||
CREATE TABLE citus_schema_distribute_undistribute.tbl3(id int PRIMARY KEY REFERENCES tenant1.table3q(id));
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
DROP TABLE citus_schema_distribute_undistribute.tbl3 CASCADE;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
|
||||
-- foreign key from a reference table in another schema is not allowed
|
||||
CREATE TABLE tenant1.table3t(id int PRIMARY KEY);
|
||||
CREATE TABLE citus_schema_distribute_undistribute.tbl4(id int PRIMARY KEY REFERENCES tenant1.table3t(id));
|
||||
SELECT create_reference_table('citus_schema_distribute_undistribute.tbl4');
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
DROP TABLE citus_schema_distribute_undistribute.tbl4 CASCADE;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
|
||||
-- only allowed for schema owner or superuser
|
||||
CREATE USER dummyregular;
|
||||
SET role dummyregular;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
|
||||
-- assign all tables to dummyregular except table5
|
||||
SET role tenantuser;
|
||||
SELECT result FROM run_command_on_all_nodes($$ REASSIGN OWNED BY tenantuser TO dummyregular; $$);
|
||||
CREATE TABLE tenant1.table5(id int);
|
||||
|
||||
-- table owner check fails the distribution
|
||||
SET role dummyregular;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
|
||||
-- alter table owner, then redistribute
|
||||
SET role tenantuser;
|
||||
ALTER TABLE tenant1.table5 OWNER TO dummyregular;
|
||||
SET role dummyregular;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
|
||||
-- show the schema is a tenant schema now
|
||||
SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset
|
||||
-- below query verifies the same colocationid in pg_dist_tenant_schema, pg_dist_colocation and all entries in pg_dist_partition at the same time
|
||||
SELECT '$$' ||
|
||||
' SELECT colocationid = ' || :tenant1_colocid ||
|
||||
' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' ||
|
||||
' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''tenant1%'')' ||
|
||||
'$$'
|
||||
AS verify_tenant_query \gset
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*) AS total FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1%' $$);
|
||||
SELECT result FROM run_command_on_all_nodes(:verify_tenant_query);
|
||||
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
|
||||
-- show the schema is a regular schema
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$);
|
||||
-- below query verifies the tenant colocationid is removed from both pg_dist_colocation and all entries in pg_dist_partition at the same time
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$);
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$);
|
||||
|
||||
RESET role;
|
||||
SELECT result FROM run_command_on_all_nodes($$ REASSIGN OWNED BY dummyregular TO tenantuser; $$);
|
||||
DROP USER dummyregular;
|
||||
|
||||
CREATE USER dummysuper superuser;
|
||||
SET role dummysuper;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
|
||||
-- show the schema is a tenant schema now
|
||||
SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset
|
||||
SELECT '$$' ||
|
||||
' SELECT colocationid = ' || :tenant1_colocid ||
|
||||
' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' ||
|
||||
' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''tenant1%'')' ||
|
||||
'$$'
|
||||
AS verify_tenant_query \gset
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*) AS total FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1%' $$);
|
||||
SELECT result FROM run_command_on_all_nodes(:verify_tenant_query);
|
||||
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
|
||||
-- show the schema is a regular schema
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$);
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$);
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$);
|
||||
|
||||
RESET role;
|
||||
DROP USER dummysuper;
|
||||
|
||||
-- foreign table
|
||||
CREATE TABLE tenant1.foreign_table_test (id integer NOT NULL, data text, a bigserial);
|
||||
INSERT INTO tenant1.foreign_table_test SELECT i FROM generate_series(1,100) i;
|
||||
CREATE EXTENSION postgres_fdw;
|
||||
CREATE SERVER foreign_server
|
||||
FOREIGN DATA WRAPPER postgres_fdw
|
||||
OPTIONS (host 'localhost', port :'master_port', dbname 'regression');
|
||||
CREATE USER MAPPING FOR CURRENT_USER
|
||||
SERVER foreign_server
|
||||
OPTIONS (user 'postgres');
|
||||
CREATE FOREIGN TABLE tenant1.foreign_table (
|
||||
id integer NOT NULL,
|
||||
data text,
|
||||
a bigserial
|
||||
)
|
||||
SERVER foreign_server
|
||||
OPTIONS (schema_name 'tenant1', table_name 'foreign_table_test');
|
||||
|
||||
-- foreign table not allowed
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
ALTER FOREIGN TABLE tenant1.foreign_table SET SCHEMA citus_schema_distribute_undistribute;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
|
||||
-- already have distributed table error
|
||||
CREATE TABLE tenant1.dist(id int);
|
||||
SELECT create_distributed_table('tenant1.dist', 'id');
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
SELECT undistribute_table('tenant1.dist');
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
|
||||
CREATE TABLE tenant1.ref2(id int);
|
||||
SELECT create_reference_table('tenant1.ref2');
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
SELECT undistribute_table('tenant1.ref2');
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
|
||||
BEGIN;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
ROLLBACK;
|
||||
|
||||
-- show the schema is a regular schema
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$);
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$);
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$);
|
||||
|
||||
-- errors not a tenant schema
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
|
||||
-- make it a tenant schema
|
||||
BEGIN;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
COMMIT;
|
||||
|
||||
-- show the schema is a tenant schema now
|
||||
SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset
|
||||
SELECT '$$' ||
|
||||
' SELECT colocationid = ' || :tenant1_colocid ||
|
||||
' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' ||
|
||||
' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''tenant1%'')' ||
|
||||
'$$'
|
||||
AS verify_tenant_query \gset
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*) AS total FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1%' $$);
|
||||
SELECT result FROM run_command_on_all_nodes(:verify_tenant_query);
|
||||
|
||||
-- already a tenant schema notice
|
||||
SET client_min_messages TO NOTICE;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
SET client_min_messages TO WARNING;
|
||||
|
||||
-- convert back to a regular schema
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
|
||||
-- show the schema is a regular schema now
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$);
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$);
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$);
|
||||
|
||||
-- tables still have valid data
|
||||
SELECT COUNT(*) FROM tenant1.partitioned_table;
|
||||
SELECT COUNT(*) FROM citus_schema_distribute_undistribute.foreign_table;
|
||||
SELECT COUNT(*) FROM tenant1.table3;
|
||||
TRUNCATE citus_schema_distribute_undistribute.ref CASCADE;
|
||||
SELECT COUNT(*) FROM tenant1.table3;
|
||||
|
||||
-- disallowed namespaces
|
||||
SELECT citus_schema_distribute('public');
|
||||
SELECT citus_schema_distribute('pg_catalog');
|
||||
SELECT citus_schema_distribute('pg_toast');
|
||||
CREATE TEMP TABLE xx(id int); -- create a temp table in case we do not have any pg_temp_xx schema yet
|
||||
SELECT nspname AS temp_schema_name FROM pg_namespace WHERE nspname LIKE 'pg_temp%' LIMIT 1 \gset
|
||||
SELECT nspname AS temp_toast_schema_name FROM pg_namespace WHERE nspname LIKE 'pg_toast_temp%' LIMIT 1 \gset
|
||||
SELECT citus_schema_distribute(:'temp_schema_name');
|
||||
SELECT citus_schema_distribute(:'temp_toast_schema_name');
|
||||
SELECT citus_schema_distribute('citus');
|
||||
CREATE SCHEMA extensionschema;
|
||||
CREATE EXTENSION citext SCHEMA extensionschema;
|
||||
SELECT citus_schema_distribute('extensionschema');
|
||||
DROP SCHEMA extensionschema CASCADE;
|
||||
ALTER EXTENSION citus ADD TABLE tenant1.table1;
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
ALTER EXTENSION citus DROP TABLE tenant1.table1;
|
||||
|
||||
-- weird schema and table names
|
||||
CREATE SCHEMA "CiTuS.TeeN";
|
||||
CREATE TABLE "CiTuS.TeeN"."TeeNTabLE.1!?!"(id int PRIMARY KEY, name text);
|
||||
INSERT INTO "CiTuS.TeeN"."TeeNTabLE.1!?!" SELECT i, 'asd'::text FROM generate_series(1,20) i;
|
||||
SELECT citus_schema_distribute('"CiTuS.TeeN"');
|
||||
|
||||
-- show the schema is a tenant schema now
|
||||
SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset
|
||||
SELECT '$$' ||
|
||||
' SELECT colocationid = ' || :tenant1_colocid ||
|
||||
' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' ||
|
||||
' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''"CiTuS.TeeN"%'')' ||
|
||||
'$$'
|
||||
AS verify_tenant_query \gset
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*) AS total FROM pg_dist_partition WHERE logicalrelid::text LIKE '"CiTuS.TeeN"%' $$);
|
||||
SELECT result FROM run_command_on_all_nodes(:verify_tenant_query);
|
||||
|
||||
SELECT citus_schema_undistribute('"CiTuS.TeeN"');
|
||||
|
||||
-- show the schema is a regular schema
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$);
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$);
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$);
|
||||
|
||||
-- try setting the schema again after adding a distributed table into the schema. It should complain about distributed table.
|
||||
CREATE TABLE tenant1.new_dist(id int);
|
||||
SELECT create_distributed_table('tenant1.new_dist', 'id');
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
SELECT undistribute_table('tenant1.new_dist');
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
|
||||
-- try setting the schema again after adding a single shard table into the schema. It should complain about distributed table.
|
||||
CREATE TABLE tenant1.single_shard_t(id int);
|
||||
SELECT create_distributed_table('tenant1.single_shard_t', NULL);
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
SELECT undistribute_table('tenant1.single_shard_t');
|
||||
|
||||
-- try setting the schema again. It should succeed now.
|
||||
SELECT citus_schema_distribute('tenant1');
|
||||
|
||||
-- show the schema is a tenant schema now
|
||||
SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset
|
||||
SELECT '$$' ||
|
||||
' SELECT colocationid = ' || :tenant1_colocid ||
|
||||
' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' ||
|
||||
' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''tenant1%'')' ||
|
||||
'$$'
|
||||
AS verify_tenant_query \gset
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*) AS total FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1%' $$);
|
||||
SELECT result FROM run_command_on_all_nodes(:verify_tenant_query);
|
||||
|
||||
SELECT citus_schema_undistribute('tenant1');
|
||||
|
||||
-- show the schema is a regular schema now
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$);
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$);
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$);
|
||||
|
||||
-- create an empty tenant schema to verify colocation id is removed successfully after we undistribute it
|
||||
CREATE SCHEMA empty_tenant;
|
||||
SELECT citus_schema_distribute('empty_tenant');
|
||||
|
||||
-- show the schema is a tenant schema now
|
||||
SELECT colocationid AS empty_tenant_colocid FROM pg_dist_tenant_schema schemaid \gset
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace FROM pg_dist_colocation JOIN pg_dist_tenant_schema USING(colocationid) $$);
|
||||
|
||||
SELECT citus_schema_undistribute('empty_tenant');
|
||||
|
||||
-- show the schema is a regular schema now
|
||||
SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$);
|
||||
SELECT '$$' || 'SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = ' || :empty_tenant_colocid || '$$'
|
||||
AS verify_empty_tenant_query \gset
|
||||
SELECT result FROM run_command_on_all_nodes(:verify_empty_tenant_query);
|
||||
|
||||
-- cleanup
|
||||
DROP SCHEMA "CiTuS.TeeN" CASCADE;
|
||||
DROP SCHEMA tenant1 CASCADE;
|
||||
DROP SCHEMA empty_tenant CASCADE;
|
||||
DROP EXTENSION postgres_fdw CASCADE;
|
||||
DROP SCHEMA citus_schema_distribute_undistribute CASCADE;
|
||||
DROP USER tenantuser;
|
||||
SELECT citus_remove_node('localhost', :master_port);
|
Loading…
Reference in New Issue