From d138bb89bf7efaf452226ef5106e85dfd92f419e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philip=20Dub=C3=A9?= Date: Wed, 11 Sep 2019 06:38:01 +0000 Subject: [PATCH] Support creating collations as part of dependency resolution. Propagate ALTER/DROP on distributed collations Propagate CREATE COLLATION when outside transaction --- src/backend/distributed/commands/collation.c | 601 ++++++++++++++++++ .../distributed/commands/dependencies.c | 5 + src/backend/distributed/commands/extension.c | 2 +- src/backend/distributed/commands/function.c | 2 +- src/backend/distributed/commands/schema.c | 11 + src/backend/distributed/commands/type.c | 6 +- .../distributed/commands/utility_hook.c | 27 + src/backend/distributed/deparser/deparse.c | 26 +- .../deparser/deparse_collation_stmts.c | 177 ++++++ .../distributed/deparser/objectaddress.c | 35 +- src/backend/distributed/deparser/qualify.c | 44 ++ .../deparser/qualify_collation_stmt.c | 129 ++++ src/backend/distributed/metadata/dependency.c | 3 +- .../worker/worker_create_or_replace.c | 51 ++ src/include/distributed/commands.h | 19 + src/include/distributed/deparser.h | 14 +- .../expected/distributed_collations.out | 107 ++++ .../distributed_collations_conflict.out | 96 +++ .../expected/multi_mx_create_table.out | 4 - .../regress/expected/multi_schema_support.out | 112 ++-- src/test/regress/multi_schedule | 1 + .../regress/sql/distributed_collations.sql | 79 +++ .../sql/distributed_collations_conflict.sql | 73 +++ .../regress/sql/multi_mx_create_table.sql | 7 - src/test/regress/sql/multi_schema_support.sql | 8 - 25 files changed, 1547 insertions(+), 92 deletions(-) create mode 100644 src/backend/distributed/commands/collation.c create mode 100644 src/backend/distributed/deparser/deparse_collation_stmts.c create mode 100644 src/backend/distributed/deparser/qualify_collation_stmt.c create mode 100644 src/test/regress/expected/distributed_collations.out create mode 100644 src/test/regress/expected/distributed_collations_conflict.out create mode 100644 src/test/regress/sql/distributed_collations.sql create mode 100644 src/test/regress/sql/distributed_collations_conflict.sql diff --git a/src/backend/distributed/commands/collation.c b/src/backend/distributed/commands/collation.c new file mode 100644 index 000000000..e9b773ae5 --- /dev/null +++ b/src/backend/distributed/commands/collation.c @@ -0,0 +1,601 @@ +/*------------------------------------------------------------------------- + * collation.c + * + * This file contains functions to create, alter and drop policies on + * distributed tables. + * + * Copyright (c) 2019, Citus Data, Inc. + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/htup_details.h" +#include "access/xact.h" +#include "catalog/pg_collation.h" +#include "distributed/commands/utility_hook.h" +#include "distributed/commands.h" +#include "distributed/deparser.h" +#include "distributed/master_metadata_utility.h" +#include "distributed/metadata/distobject.h" +#include "distributed/metadata_sync.h" +#include "distributed/multi_executor.h" +#include "distributed/relation_access_tracking.h" +#include "distributed/worker_create_or_replace.h" +#include "distributed/worker_manager.h" +#include "parser/parse_type.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/syscache.h" +#include "miscadmin.h" + + +static char * CreateCollationDDLInternal(Oid collationId, Oid *collowner, + char **quotedCollationName); +static List * FilterNameListForDistributedCollations(List *objects, bool missing_ok, + List **addresses); +static void EnsureSequentialModeForCollationDDL(void); + + +/* + * GetCreateCollationDDLInternal returns a CREATE COLLATE sql string for the + * given collationId. + * + * It includes 2 out parameters to assist creation of ALTER COLLATION OWNER. + * quotedCollationName must not be NULL. + */ +static char * +CreateCollationDDLInternal(Oid collationId, Oid *collowner, char **quotedCollationName) +{ + char *schemaName = NULL; + StringInfoData collationNameDef; + const char *providerString = NULL; + HeapTuple heapTuple = NULL; + Form_pg_collation collationForm = NULL; + char collprovider; + const char *collcollate; + const char *collctype; + const char *collname; + Oid collnamespace; +#if PG_VERSION_NUM >= 120000 + bool collisdeterministic; +#endif + + heapTuple = SearchSysCache1(COLLOID, ObjectIdGetDatum(collationId)); + if (!HeapTupleIsValid(heapTuple)) + { + elog(ERROR, "citus cache lookup failed for collation %u", collationId); + } + + collationForm = (Form_pg_collation) GETSTRUCT(heapTuple); + collprovider = collationForm->collprovider; + collcollate = NameStr(collationForm->collcollate); + collctype = NameStr(collationForm->collctype); + collnamespace = collationForm->collnamespace; + collname = NameStr(collationForm->collname); +#if PG_VERSION_NUM >= 120000 + collisdeterministic = collationForm->collisdeterministic; +#endif + + if (collowner != NULL) + { + *collowner = collationForm->collowner; + } + + ReleaseSysCache(heapTuple); + schemaName = get_namespace_name(collnamespace); + *quotedCollationName = quote_qualified_identifier(schemaName, collname); + providerString = + collprovider == COLLPROVIDER_DEFAULT ? "default" : + collprovider == COLLPROVIDER_ICU ? "icu" : + collprovider == COLLPROVIDER_LIBC ? "libc" : NULL; + + if (providerString == NULL) + { + elog(ERROR, "unknown collation provider: %c", collprovider); + } + + initStringInfo(&collationNameDef); + appendStringInfo(&collationNameDef, + "CREATE COLLATION %s (provider = '%s'", + *quotedCollationName, providerString); + + if (strcmp(collcollate, collctype)) + { + appendStringInfo(&collationNameDef, + ", locale = %s", + quote_literal_cstr(collcollate)); + } + else + { + appendStringInfo(&collationNameDef, + ", lc_collate = %s, lc_ctype = %s", + quote_literal_cstr(collcollate), + quote_literal_cstr(collctype)); + } + +#if PG_VERSION_NUM >= 120000 + if (!collisdeterministic) + { + appendStringInfoString(&collationNameDef, ", deterministic = false"); + } +#endif + + + appendStringInfoChar(&collationNameDef, ')'); + return collationNameDef.data; +} + + +/* + * CreateCollationDDL wrap CreateCollationDDLInternal to hide the out parameters. + */ +char * +CreateCollationDDL(Oid collationId) +{ + char *quotedCollationName = NULL; + return CreateCollationDDLInternal(collationId, NULL, "edCollationName); +} + + +/* + * CreateCollationDDLsIdempotent returns a List of cstrings for creating the collation + * using create_or_replace_object & includes ALTER COLLATION ROLE. + */ +List * +CreateCollationDDLsIdempotent(Oid collationId) +{ + StringInfoData collationAlterOwnerCommand; + Oid collowner = InvalidOid; + char *quotedCollationName = NULL; + char *createCollationCommand = CreateCollationDDLInternal(collationId, &collowner, + "edCollationName); + + initStringInfo(&collationAlterOwnerCommand); + appendStringInfo(&collationAlterOwnerCommand, + "ALTER COLLATION %s OWNER TO %s", + quotedCollationName, + quote_identifier(GetUserNameFromId(collowner, false))); + + return list_make2(WrapCreateOrReplace(createCollationCommand), + collationAlterOwnerCommand.data); +} + + +ObjectAddress +AlterCollationOwnerObjectAddress(AlterOwnerStmt *stmt) +{ + Relation relation; + + Assert(stmt->objectType == OBJECT_COLLATION); + + return get_object_address(stmt->objectType, stmt->object, &relation, + AccessExclusiveLock, false); +} + + +/* + * FilterNameListForDistributedCollations takes a list of objects to delete. + * This list is filtered against the collations that are distributed. + * + * The original list will not be touched, a new list will be created with only the objects + * in there. + * + * objectAddresses is replaced with a list of object addresses for the filtered objects. + */ +static List * +FilterNameListForDistributedCollations(List *objects, bool missing_ok, + List **objectAddresses) +{ + ListCell *objectCell = NULL; + List *result = NIL; + + *objectAddresses = NIL; + + foreach(objectCell, objects) + { + List *collName = lfirst(objectCell); + Oid collOid = get_collation_oid(collName, true); + ObjectAddress collAddress = { 0 }; + + if (!OidIsValid(collOid)) + { + continue; + } + + ObjectAddressSet(collAddress, CollationRelationId, collOid); + if (IsObjectDistributed(&collAddress)) + { + ObjectAddress *address = palloc0(sizeof(ObjectAddress)); + *address = collAddress; + *objectAddresses = lappend(*objectAddresses, address); + result = lappend(result, collName); + } + } + return result; +} + + +List * +PlanDropCollationStmt(DropStmt *stmt) +{ + /* + * We swap the list of objects to remove during deparse so we need a reference back to + * the old list to put back + */ + ListCell *addressCell = NULL; + List *distributedTypeAddresses = NIL; + + if (!ShouldPropagate()) + { + return NIL; + } + + QualifyTreeNode((Node *) stmt); + + List *oldCollations = stmt->objects; + List *distributedCollations = + FilterNameListForDistributedCollations(oldCollations, stmt->missing_ok, + &distributedTypeAddresses); + if (list_length(distributedCollations) <= 0) + { + /* no distributed types to drop */ + return NIL; + } + + /* + * managing collations can only be done on the coordinator if ddl propagation is on. when + * it is off we will never get here. MX workers don't have a notion of distributed + * collations, so we block the call. + */ + EnsureCoordinator(); + + /* + * remove the entries for the distributed objects on dropping + */ + foreach(addressCell, distributedTypeAddresses) + { + ObjectAddress *address = (ObjectAddress *) lfirst(addressCell); + UnmarkObjectDistributed(address); + } + + /* + * temporary swap the lists of objects to delete with the distributed objects and + * deparse to an executable sql statement for the workers + */ + stmt->objects = distributedCollations; + char *dropStmtSql = DeparseTreeNode((Node *) stmt); + stmt->objects = oldCollations; + + EnsureSequentialModeForCollationDDL(); + + /* to prevent recursion with mx we disable ddl propagation */ + List *commands = list_make3(DISABLE_DDL_PROPAGATION, + (void *) dropStmtSql, + ENABLE_DDL_PROPAGATION); + + return NodeDDLTaskList(ALL_WORKERS, commands); +} + + +/* + * PlanAlterCollationOwnerStmt is called for change of ownership of collations + * before the ownership is changed on the local instance. + * + * If the type for which the owner is changed is distributed we execute the change on all + * the workers to keep the type in sync across the cluster. + */ +List * +PlanAlterCollationOwnerStmt(AlterOwnerStmt *stmt, const char *queryString) +{ + Assert(stmt->objectType == OBJECT_COLLATION); + + ObjectAddress *collationAddress = GetObjectAddressFromParseTree((Node *) stmt, false); + if (!ShouldPropagateObject(collationAddress)) + { + return NIL; + } + + EnsureCoordinator(); + + QualifyTreeNode((Node *) stmt); + char *sql = DeparseTreeNode((Node *) stmt); + + EnsureSequentialModeForCollationDDL(); + List *commands = list_make3(DISABLE_DDL_PROPAGATION, + (void *) sql, + ENABLE_DDL_PROPAGATION); + + return NodeDDLTaskList(ALL_WORKERS, commands); +} + + +/* + * PlanRenameCollationStmt is called when the user is renaming the collation. The invocation happens + * before the statement is applied locally. + * + * As the collation already exists we have access to the ObjectAddress for the collation, this is + * used to check if the collation is distributed. If the collation is distributed the rename is + * executed on all the workers to keep the collation in sync across the cluster. + */ +List * +PlanRenameCollationStmt(RenameStmt *stmt, const char *queryString) +{ + ObjectAddress *collationAddress = GetObjectAddressFromParseTree((Node *) stmt, false); + if (!ShouldPropagateObject(collationAddress)) + { + return NIL; + } + + /* fully qualify */ + QualifyTreeNode((Node *) stmt); + + /* deparse sql*/ + char *renameStmtSql = DeparseTreeNode((Node *) stmt); + + EnsureSequentialModeForCollationDDL(); + + /* to prevent recursion with mx we disable ddl propagation */ + List *commands = list_make3(DISABLE_DDL_PROPAGATION, + (void *) renameStmtSql, + ENABLE_DDL_PROPAGATION); + + return NodeDDLTaskList(ALL_WORKERS, commands); +} + + +/* + * PlanAlterCollationSchemaStmt is executed before the statement is applied to the local + * postgres instance. + * + * In this stage we can prepare the commands that need to be run on all workers. + */ +List * +PlanAlterCollationSchemaStmt(AlterObjectSchemaStmt *stmt, const char *queryString) +{ + Assert(stmt->objectType == OBJECT_COLLATION); + + ObjectAddress *collationAddress = GetObjectAddressFromParseTree((Node *) stmt, false); + if (!ShouldPropagateObject(collationAddress)) + { + return NIL; + } + + EnsureCoordinator(); + + QualifyTreeNode((Node *) stmt); + char *sql = DeparseTreeNode((Node *) stmt); + + EnsureSequentialModeForCollationDDL(); + + List *commands = list_make3(DISABLE_DDL_PROPAGATION, + (void *) sql, + ENABLE_DDL_PROPAGATION); + + return NodeDDLTaskList(ALL_WORKERS, commands); +} + + +/* + * ProcessAlterCollationSchemaStmt is executed after the change has been applied locally, we + * can now use the new dependencies of the type to ensure all its dependencies exist on + * the workers before we apply the commands remotely. + */ +void +ProcessAlterCollationSchemaStmt(AlterObjectSchemaStmt *stmt, const char *queryString) +{ + Assert(stmt->objectType == OBJECT_COLLATION); + + ObjectAddress *collationAddress = GetObjectAddressFromParseTree((Node *) stmt, false); + if (!ShouldPropagateObject(collationAddress)) + { + return; + } + + /* dependencies have changed (schema) let's ensure they exist */ + EnsureDependenciesExistsOnAllNodes(collationAddress); +} + + +/* + * RenameCollationStmtObjectAddress returns the ObjectAddress of the type that is the object + * of the RenameStmt. Errors if missing_ok is false. + */ +ObjectAddress * +RenameCollationStmtObjectAddress(RenameStmt *stmt, bool missing_ok) +{ + Assert(stmt->renameType == OBJECT_COLLATION); + + ObjectAddress *address = palloc0(sizeof(ObjectAddress)); + Oid collationOid = get_collation_oid((List *) stmt->object, missing_ok); + + ObjectAddressSet(*address, CollationRelationId, collationOid); + return address; +} + + +/* + * AlterCollationSchemaStmtObjectAddress returns the ObjectAddress of the type that is the + * subject of the AlterObjectSchemaStmt. Errors if missing_ok is false. + * + * This could be called both before or after it has been applied locally. It will look in + * the old schema first, if the type cannot be found in that schema it will look in the + * new schema. Errors if missing_ok is false and the type cannot be found in either of the + * schemas. + */ +ObjectAddress * +AlterCollationSchemaStmtObjectAddress(AlterObjectSchemaStmt *stmt, bool missing_ok) +{ + Assert(stmt->objectType == OBJECT_COLLATION); + + List *name = (List *) stmt->object; + Oid collationOid = get_collation_oid(name, true); + + if (collationOid == InvalidOid) + { + List *newName = list_make2(makeString(stmt->newschema), lfirst(list_tail(name))); + + collationOid = get_collation_oid(newName, true); + + if (!missing_ok && collationOid == InvalidOid) + { + ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("type \"%s\" does not exist", + NameListToString(name)))); + } + } + + ObjectAddress *address = palloc0(sizeof(ObjectAddress)); + ObjectAddressSet(*address, CollationRelationId, collationOid); + return address; +} + + +/* + * EnsureSequentialModeForCollationDDL makes sure that the current transaction is already in + * sequential mode, or can still safely be put in sequential mode, it errors if that is + * not possible. The error contains information for the user to retry the transaction with + * sequential mode set from the begining. + * + * As collations are node scoped objects there exists only 1 instance of the collation used by + * potentially multiple shards. To make sure all shards in the transaction can interact + * with the type the type needs to be visible on all connections used by the transaction, + * meaning we can only use 1 connection per node. + */ +static void +EnsureSequentialModeForCollationDDL(void) +{ + if (!IsTransactionBlock()) + { + /* we do not need to switch to sequential mode if we are not in a transaction */ + return; + } + + if (ParallelQueryExecutedInTransaction()) + { + ereport(ERROR, (errmsg("cannot create or modify collation because there was a " + "parallel operation on a distributed table in the " + "transaction"), + errdetail("When creating or altering a collation, Citus needs to " + "perform all operations over a single connection per " + "node to ensure consistency."), + errhint("Try re-running the transaction with " + "\"SET LOCAL citus.multi_shard_modify_mode TO " + "\'sequential\';\""))); + } + + ereport(DEBUG1, (errmsg("switching to sequential query execution mode"), + errdetail("Collation is created or altered. To make sure subsequent " + "commands see the collation correctly we need to make sure to " + "use only one connection for all future commands"))); + SetLocalMultiShardModifyModeToSequential(); +} + + +/* + * GenerateBackupNameForCollationCollision generates a new collation name for an existing collation. + * The name is generated in such a way that the new name doesn't overlap with an existing collation + * by adding a suffix with incrementing number after the new name. + */ +char * +GenerateBackupNameForCollationCollision(const ObjectAddress *address) +{ + char *newName = palloc0(NAMEDATALEN); + char suffix[NAMEDATALEN] = { 0 }; + int count = 0; + char *baseName = get_collation_name(address->objectId); + int baseLength = strlen(baseName); + HeapTuple collationTuple = SearchSysCache1(COLLOID, address->objectId); + + if (!HeapTupleIsValid(collationTuple)) + { + elog(ERROR, "citus cache lookup failed"); + return NULL; + } + Form_pg_collation collationForm = (Form_pg_collation) GETSTRUCT(collationTuple); + Value *namespace = makeString(get_namespace_name(collationForm->collnamespace)); + ReleaseSysCache(collationTuple); + + while (true) + { + int suffixLength = snprintf(suffix, NAMEDATALEN - 1, "(citus_backup_%d)", + count); + + /* trim the base name at the end to leave space for the suffix and trailing \0 */ + baseLength = Min(baseLength, NAMEDATALEN - suffixLength - 1); + + /* clear newName before copying the potentially trimmed baseName and suffix */ + memset(newName, 0, NAMEDATALEN); + strncpy(newName, baseName, baseLength); + strncpy(newName + baseLength, suffix, suffixLength); + + List *newCollationName = list_make2(namespace, makeString(newName)); + + /* don't need to rename if the input arguments don't match */ + Oid existingCollationId = get_collation_oid(newCollationName, true); + + if (existingCollationId == InvalidOid) + { + return newName; + } + + count++; + } +} + + +ObjectAddress * +DefineCollationStmtObjectAddress(DefineStmt *stmt, bool missing_ok) +{ + Assert(stmt->kind == OBJECT_COLLATION); + + Oid collOid = get_collation_oid(stmt->defnames, missing_ok); + ObjectAddress *address = palloc0(sizeof(ObjectAddress)); + + ObjectAddressSet(*address, CollationRelationId, collOid); + + return address; +} + + +/* + * ProcessCollationDefineStmt executed after the extension has been + * created locally and before we create it on the worker nodes. + * As we now have access to ObjectAddress of the extension that is just + * created, we can mark it as distributed to make sure that its + * dependencies exist on all nodes. + */ +List * +ProcessCollationDefineStmt(DefineStmt *stmt, const char *queryString) +{ + Assert(stmt->kind == OBJECT_COLLATION); + + if (!ShouldPropagate()) + { + return NIL; + } + + /* + * If the create collation command is a part of a multi-statement transaction, + * do not propagate it + */ + if (IsMultiStatementTransaction()) + { + return NIL; + } + + ObjectAddress *collationAddress = + DefineCollationStmtObjectAddress(stmt, false); + + if (collationAddress->objectId == InvalidOid) + { + return NIL; + } + + EnsureDependenciesExistsOnAllNodes(collationAddress); + + MarkObjectDistributed(collationAddress); + + return NodeDDLTaskList(ALL_WORKERS, CreateCollationDDLsIdempotent( + collationAddress->objectId)); +} diff --git a/src/backend/distributed/commands/dependencies.c b/src/backend/distributed/commands/dependencies.c index 70ba7e355..1fcb5aa8e 100644 --- a/src/backend/distributed/commands/dependencies.c +++ b/src/backend/distributed/commands/dependencies.c @@ -158,6 +158,11 @@ GetDependencyCreateDDLCommands(const ObjectAddress *dependency) break; } + case OCLASS_COLLATION: + { + return CreateCollationDDLsIdempotent(dependency->objectId); + } + case OCLASS_PROC: { return CreateFunctionDDLCommandsIdempotent(dependency); diff --git a/src/backend/distributed/commands/extension.c b/src/backend/distributed/commands/extension.c index 64e757109..60139a42c 100644 --- a/src/backend/distributed/commands/extension.c +++ b/src/backend/distributed/commands/extension.c @@ -130,7 +130,7 @@ PlanCreateExtensionStmt(CreateExtensionStmt *createExtensionStmt, const char *qu } /* - * If the extension command is a part of a bigger multi-statement transaction, + * If the extension command is a part of a multi-statement transaction, * do not propagate it */ if (IsMultiStatementTransaction()) diff --git a/src/backend/distributed/commands/function.c b/src/backend/distributed/commands/function.c index 9bcd655c0..62ddbd5f2 100644 --- a/src/backend/distributed/commands/function.c +++ b/src/backend/distributed/commands/function.c @@ -1567,7 +1567,7 @@ ProcessAlterFunctionSchemaStmt(AlterObjectSchemaStmt *stmt, const char *queryStr return; } - /* dependencies have changed (schema) lets ensure they exist */ + /* dependencies have changed (schema) let's ensure they exist */ EnsureDependenciesExistsOnAllNodes(address); } diff --git a/src/backend/distributed/commands/schema.c b/src/backend/distributed/commands/schema.c index 569415816..dcfcc8b7b 100644 --- a/src/backend/distributed/commands/schema.c +++ b/src/backend/distributed/commands/schema.c @@ -121,6 +121,11 @@ PlanAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt, const char *queryString) return PlanAlterTypeSchemaStmt(stmt, queryString); } + case OBJECT_COLLATION: + { + return PlanAlterCollationSchemaStmt(stmt, queryString); + } + case OBJECT_PROCEDURE: case OBJECT_AGGREGATE: case OBJECT_FUNCTION: @@ -200,6 +205,12 @@ ProcessAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt, const char *queryStrin return; } + case OBJECT_COLLATION: + { + ProcessAlterCollationSchemaStmt(stmt, queryString); + return; + } + case OBJECT_PROCEDURE: case OBJECT_AGGREGATE: case OBJECT_FUNCTION: diff --git a/src/backend/distributed/commands/type.c b/src/backend/distributed/commands/type.c index 8dd4f09c1..406700f70 100644 --- a/src/backend/distributed/commands/type.c +++ b/src/backend/distributed/commands/type.c @@ -489,9 +489,9 @@ PlanDropTypeStmt(DropStmt *stmt, const char *queryString) const char *dropStmtSql = DeparseTreeNode((Node *) stmt); stmt->objects = oldTypes; - /* to prevent recursion with mx we disable ddl propagation */ EnsureSequentialModeForTypeDDL(); + /* to prevent recursion with mx we disable ddl propagation */ List *commands = list_make3(DISABLE_DDL_PROPAGATION, (void *) dropStmtSql, ENABLE_DDL_PROPAGATION); @@ -524,9 +524,9 @@ PlanRenameTypeStmt(RenameStmt *stmt, const char *queryString) /* deparse sql*/ const char *renameStmtSql = DeparseTreeNode((Node *) stmt); - /* to prevent recursion with mx we disable ddl propagation */ EnsureSequentialModeForTypeDDL(); + /* to prevent recursion with mx we disable ddl propagation */ List *commands = list_make3(DISABLE_DDL_PROPAGATION, (void *) renameStmtSql, ENABLE_DDL_PROPAGATION); @@ -618,7 +618,7 @@ ProcessAlterTypeSchemaStmt(AlterObjectSchemaStmt *stmt, const char *queryString) return; } - /* dependencies have changed (schema) lets ensure they exist */ + /* dependencies have changed (schema) let's ensure they exist */ EnsureDependenciesExistsOnAllNodes(typeAddress); } diff --git a/src/backend/distributed/commands/utility_hook.c b/src/backend/distributed/commands/utility_hook.c index ddf7416ae..87eb0becf 100644 --- a/src/backend/distributed/commands/utility_hook.c +++ b/src/backend/distributed/commands/utility_hook.c @@ -393,6 +393,12 @@ multi_ProcessUtility(PlannedStmt *pstmt, DropStmt *dropStatement = (DropStmt *) parsetree; switch (dropStatement->removeType) { + case OBJECT_COLLATION: + { + ddlJobs = PlanDropCollationStmt(dropStatement); + break; + } + case OBJECT_INDEX: { ddlJobs = PlanDropIndexStmt(dropStatement, queryString); @@ -475,6 +481,12 @@ multi_ProcessUtility(PlannedStmt *pstmt, switch (renameStmt->renameType) { + case OBJECT_COLLATION: + { + ddlJobs = PlanRenameCollationStmt(renameStmt, queryString); + break; + } + case OBJECT_TYPE: { ddlJobs = PlanRenameTypeStmt(renameStmt, queryString); @@ -741,6 +753,16 @@ multi_ProcessUtility(PlannedStmt *pstmt, ProcessCreateEnumStmt(castNode(CreateEnumStmt, parsetree), queryString); } + if (IsA(parsetree, DefineStmt)) + { + DefineStmt *defineStmt = castNode(DefineStmt, parsetree); + + if (defineStmt->kind == OBJECT_COLLATION) + { + ddlJobs = ProcessCollationDefineStmt(defineStmt, queryString); + } + } + if (IsA(parsetree, AlterObjectSchemaStmt)) { ProcessAlterObjectSchemaStmt(castNode(AlterObjectSchemaStmt, parsetree), @@ -933,6 +955,11 @@ PlanAlterOwnerStmt(AlterOwnerStmt *stmt, const char *queryString) { switch (stmt->objectType) { + case OBJECT_COLLATION: + { + return PlanAlterCollationOwnerStmt(stmt, queryString); + } + case OBJECT_TYPE: { return PlanAlterTypeOwnerStmt(stmt, queryString); diff --git a/src/backend/distributed/deparser/deparse.c b/src/backend/distributed/deparser/deparse.c index 5e19228e5..674245b12 100644 --- a/src/backend/distributed/deparser/deparse.c +++ b/src/backend/distributed/deparser/deparse.c @@ -34,10 +34,8 @@ static char * DeparseAlterObjectDependsStmt(AlterObjectDependsStmt *stmt); * - CREATE TYPE, ALTER TYPE, DROP TYPE * - ALTER FUNCTION, ALTER PROCEDURE, ALTER AGGREGATE * - DROP FUNCTION, DROP PROCEDURE, DROP AGGREGATE - * - * - CREATE EXTENSION - * - ALTER EXTENSION - * - DROP EXTENSION + * - CREATE EXTENSION, ALTER EXTENSION, DROP EXTENSION + * - ALTER COLLATION, DROP COLLATION */ char * DeparseTreeNode(Node *stmt) @@ -133,6 +131,11 @@ DeparseDropStmt(DropStmt *stmt) return DeparseDropTypeStmt(stmt); } + case OBJECT_COLLATION: + { + return DeparseDropCollationStmt(stmt); + } + case OBJECT_PROCEDURE: case OBJECT_AGGREGATE: case OBJECT_FUNCTION: @@ -205,6 +208,11 @@ DeparseRenameStmt(RenameStmt *stmt) return DeparseRenameAttributeStmt(stmt); } + case OBJECT_COLLATION: + { + return DeparseRenameCollationStmt(stmt); + } + case OBJECT_PROCEDURE: case OBJECT_AGGREGATE: case OBJECT_FUNCTION: @@ -259,6 +267,11 @@ DeparseAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt) return DeparseAlterTypeSchemaStmt(stmt); } + case OBJECT_COLLATION: + { + return DeparseAlterCollationSchemaStmt(stmt); + } + case OBJECT_PROCEDURE: case OBJECT_AGGREGATE: case OBJECT_FUNCTION: @@ -297,6 +310,11 @@ DeparseAlterOwnerStmt(AlterOwnerStmt *stmt) return DeparseAlterTypeOwnerStmt(stmt); } + case OBJECT_COLLATION: + { + return DeparseAlterCollationOwnerStmt(stmt); + } + case OBJECT_PROCEDURE: case OBJECT_AGGREGATE: case OBJECT_FUNCTION: diff --git a/src/backend/distributed/deparser/deparse_collation_stmts.c b/src/backend/distributed/deparser/deparse_collation_stmts.c new file mode 100644 index 000000000..35578de12 --- /dev/null +++ b/src/backend/distributed/deparser/deparse_collation_stmts.c @@ -0,0 +1,177 @@ +/*------------------------------------------------------------------------- + * + * deparse_collation_stmts.c + * All routines to deparse collation statements. + * This file contains all entry points specific for type statement deparsing as well as + * functions that are currently only used for deparsing of the type statements. + * + * Copyright (c) Citus Data, Inc. + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "catalog/namespace.h" +#include "lib/stringinfo.h" +#include "nodes/value.h" +#include "utils/builtins.h" + +#include "distributed/deparser.h" +#include "distributed/citus_ruleutils.h" + +static void AppendDropCollationStmt(StringInfo buf, DropStmt *stmt); +static void AppendRenameCollationStmt(StringInfo buf, RenameStmt *stmt); +static void AppendAlterCollationSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt); +static void AppendAlterCollationOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt); +static void AppendNameList(StringInfo buf, List *objects); + + +/* + * DeparseDropCollationStmt builds and returns a string representing the DropStmt + */ +char * +DeparseDropCollationStmt(DropStmt *stmt) +{ + StringInfoData str = { 0 }; + initStringInfo(&str); + + Assert(stmt->removeType == OBJECT_COLLATION); + + AppendDropCollationStmt(&str, stmt); + + return str.data; +} + + +/* + * AppendDropCollationStmt appends a string representing the DropStmt to a buffer + */ +static void +AppendDropCollationStmt(StringInfo buf, DropStmt *stmt) +{ + appendStringInfoString(buf, "DROP COLLATION "); + if (stmt->missing_ok) + { + appendStringInfoString(buf, "IF EXISTS "); + } + AppendNameList(buf, stmt->objects); + if (stmt->behavior == DROP_CASCADE) + { + appendStringInfoString(buf, " CASCADE"); + } +} + + +/* + * DeparseRenameCollationStmt builds and returns a string representing the RenameStmt + */ +char * +DeparseRenameCollationStmt(RenameStmt *stmt) +{ + StringInfoData str = { 0 }; + initStringInfo(&str); + + Assert(stmt->renameType == OBJECT_COLLATION); + + AppendRenameCollationStmt(&str, stmt); + + return str.data; +} + + +/* + * AppendRenameCollationStmt appends a string representing the RenameStmt to a buffer + */ +static void +AppendRenameCollationStmt(StringInfo buf, RenameStmt *stmt) +{ + List *names = (List *) stmt->object; + + appendStringInfo(buf, "ALTER COLLATION %s RENAME TO %s;", NameListToQuotedString( + names), + quote_identifier(stmt->newname)); +} + + +/* + * DeparseAlterCollationSchemaStmt builds and returns a string representing the AlterObjectSchemaStmt + */ +char * +DeparseAlterCollationSchemaStmt(AlterObjectSchemaStmt *stmt) +{ + StringInfoData str = { 0 }; + initStringInfo(&str); + + Assert(stmt->objectType == OBJECT_COLLATION); + + AppendAlterCollationSchemaStmt(&str, stmt); + + return str.data; +} + + +/* + * AppendAlterCollationSchemaStmt appends a string representing the AlterObjectSchemaStmt to a buffer + */ +static void +AppendAlterCollationSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt) +{ + Assert(stmt->objectType == OBJECT_COLLATION); + + List *names = (List *) stmt->object; + appendStringInfo(buf, "ALTER COLLATION %s SET SCHEMA %s;", NameListToQuotedString( + names), + quote_identifier(stmt->newschema)); +} + + +/* + * DeparseAlterCollationOwnerStmt builds and returns a string representing the AlterOwnerStmt + */ +char * +DeparseAlterCollationOwnerStmt(AlterOwnerStmt *stmt) +{ + StringInfoData str = { 0 }; + initStringInfo(&str); + + Assert(stmt->objectType == OBJECT_COLLATION); + + AppendAlterCollationOwnerStmt(&str, stmt); + + return str.data; +} + + +/* + * AppendAlterCollationOwnerStmt appends a string representing the AlterOwnerStmt to a buffer + */ +static void +AppendAlterCollationOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt) +{ + Assert(stmt->objectType == OBJECT_COLLATION); + + List *names = (List *) stmt->object; + appendStringInfo(buf, "ALTER COLLATION %s OWNER TO %s;", NameListToQuotedString( + names), + RoleSpecString(stmt->newowner, true)); +} + + +static void +AppendNameList(StringInfo buf, List *objects) +{ + ListCell *objectCell = NULL; + + foreach(objectCell, objects) + { + List *name = castNode(List, lfirst(objectCell)); + + if (objectCell != list_head(objects)) + { + appendStringInfo(buf, ", "); + } + + appendStringInfoString(buf, NameListToQuotedString(name)); + } +} diff --git a/src/backend/distributed/deparser/objectaddress.c b/src/backend/distributed/deparser/objectaddress.c index f06752df7..70f1a5813 100644 --- a/src/backend/distributed/deparser/objectaddress.c +++ b/src/backend/distributed/deparser/objectaddress.c @@ -103,9 +103,23 @@ GetObjectAddressFromParseTree(Node *parseTree, bool missing_ok) case T_DefineStmt: { DefineStmt *stmt = castNode(DefineStmt, parseTree); - if (stmt->kind == OBJECT_AGGREGATE) + + switch (stmt->kind) { - return DefineAggregateStmtObjectAddress(stmt, missing_ok); + case OBJECT_AGGREGATE: + { + return DefineAggregateStmtObjectAddress(stmt, missing_ok); + } + + case OBJECT_COLLATION: + { + return DefineCollationStmtObjectAddress(stmt, missing_ok); + } + + default: + { + break; + } } ereport(ERROR, (errmsg( @@ -172,6 +186,11 @@ RenameStmtObjectAddress(RenameStmt *stmt, bool missing_ok) return RenameAttributeStmtObjectAddress(stmt, missing_ok); } + case OBJECT_COLLATION: + { + return RenameCollationStmtObjectAddress(stmt, missing_ok); + } + case OBJECT_PROCEDURE: case OBJECT_AGGREGATE: case OBJECT_FUNCTION: @@ -198,6 +217,11 @@ AlterObjectSchemaStmtObjectAddress(AlterObjectSchemaStmt *stmt, bool missing_ok) return AlterTypeSchemaStmtObjectAddress(stmt, missing_ok); } + case OBJECT_COLLATION: + { + return AlterCollationSchemaStmtObjectAddress(stmt, missing_ok); + } + case OBJECT_PROCEDURE: case OBJECT_AGGREGATE: case OBJECT_FUNCTION: @@ -245,6 +269,13 @@ AlterOwnerStmtObjectAddress(AlterOwnerStmt *stmt, bool missing_ok) { switch (stmt->objectType) { + case OBJECT_COLLATION: + { + ObjectAddress *address = palloc(sizeof(ObjectAddress)); + *address = AlterCollationOwnerObjectAddress(stmt); + return address; + } + case OBJECT_TYPE: { return AlterTypeOwnerObjectAddress(stmt, missing_ok); diff --git a/src/backend/distributed/deparser/qualify.c b/src/backend/distributed/deparser/qualify.c index 825b4cfc1..c6a98f90c 100644 --- a/src/backend/distributed/deparser/qualify.c +++ b/src/backend/distributed/deparser/qualify.c @@ -30,6 +30,7 @@ static void QualifyAlterTableStmt(AlterTableStmt *stmt); static void QualifyAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt); static void QualifyAlterOwnerStmt(AlterOwnerStmt *stmt); static void QualifyAlterObjectDependsStmt(AlterObjectDependsStmt *stmt); +static void QualifyDropObjectStmt(DropStmt *stmt); /* * QualifyTreeNode transforms the statement in place and makes all (supported) statements @@ -95,6 +96,12 @@ QualifyTreeNode(Node *stmt) return; } + case T_DropStmt: + { + QualifyDropObjectStmt(castNode(DropStmt, stmt)); + break; + } + default: { /* skip unsupported statements */ @@ -125,6 +132,12 @@ QualifyRenameStmt(RenameStmt *stmt) return; } + case OBJECT_COLLATION: + { + QualifyRenameCollationStmt(stmt); + return; + } + case OBJECT_AGGREGATE: case OBJECT_FUNCTION: case OBJECT_PROCEDURE: @@ -198,6 +211,12 @@ QualifyAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt) return; } + case OBJECT_COLLATION: + { + QualifyAlterCollationSchemaStmt(stmt); + return; + } + case OBJECT_AGGREGATE: case OBJECT_FUNCTION: case OBJECT_PROCEDURE: @@ -226,6 +245,12 @@ QualifyAlterOwnerStmt(AlterOwnerStmt *stmt) return; } + case OBJECT_COLLATION: + { + QualifyAlterCollationOwnerStmt(stmt); + return; + } + case OBJECT_AGGREGATE: case OBJECT_FUNCTION: case OBJECT_PROCEDURE: @@ -260,3 +285,22 @@ QualifyAlterObjectDependsStmt(AlterObjectDependsStmt *stmt) } } } + + +static void +QualifyDropObjectStmt(DropStmt *stmt) +{ + switch (stmt->removeType) + { + case OBJECT_COLLATION: + { + QualifyDropCollationStmt(stmt); + return; + } + + default: + { + return; + } + } +} diff --git a/src/backend/distributed/deparser/qualify_collation_stmt.c b/src/backend/distributed/deparser/qualify_collation_stmt.c new file mode 100644 index 000000000..84785aa11 --- /dev/null +++ b/src/backend/distributed/deparser/qualify_collation_stmt.c @@ -0,0 +1,129 @@ +/*------------------------------------------------------------------------- + * + * qualify_collation_stmt.c + * Functions specialized in fully qualifying all collation statements. These + * functions are dispatched from qualify.c + * + * Goal would be that the deparser functions for these statements can + * serialize the statement without any external lookups. + * + * Copyright (c), Citus Data, Inc. + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "access/htup_details.h" +#include "catalog/namespace.h" +#include "catalog/pg_collation.h" +#include "utils/lsyscache.h" +#include "utils/syscache.h" + +#include "distributed/deparser.h" +#include "distributed/listutils.h" + +static Node * QualifyCollationName(List *func); + + +/* + * QualifyRenameCollationStmt transforms a + * ALTER COLLATION .. RENAME TO .. + * statement in place and makes the collation name fully qualified. + */ +void +QualifyRenameCollationStmt(RenameStmt *stmt) +{ + Assert(stmt->renameType == OBJECT_COLLATION); + + stmt->object = QualifyCollationName(castNode(List, stmt->object)); +} + + +/* + * QualifyAlterCollationSchemaStmt transforms a + * ALTER COLLATION .. SET SCHEMA .. + * statement in place and makes the collation name fully qualified. + */ +void +QualifyAlterCollationSchemaStmt(AlterObjectSchemaStmt *stmt) +{ + Assert(stmt->objectType == OBJECT_COLLATION); + + stmt->object = QualifyCollationName(castNode(List, stmt->object)); +} + + +/* + * QualifyAlterCollationOwnerStmt transforms a + * ALTER COLLATION .. OWNER TO .. + * statement in place and makes the collation name fully qualified. + */ +void +QualifyAlterCollationOwnerStmt(AlterOwnerStmt *stmt) +{ + Assert(stmt->objectType == OBJECT_COLLATION); + + stmt->object = QualifyCollationName(castNode(List, stmt->object)); +} + + +/* + * QualifyDropCollationStmt transforms a + * DROP COLLATION .. + * statement in place and makes the collation name fully qualified. + */ +void +QualifyDropCollationStmt(DropStmt *stmt) +{ + List *names = NIL; + List *name = NIL; + + foreach_ptr(name, stmt->objects) + { + names = lappend(names, QualifyCollationName(name)); + } + + stmt->objects = names; +} + + +/* + * QualifyCollation transforms a collation in place and makes its name fully qualified. + */ +Node * +QualifyCollationName(List *name) +{ + char *collationName = NULL; + char *schemaName = NULL; + + /* check if the collation name is already qualified */ + DeconstructQualifiedName(name, &schemaName, &collationName); + + /* do a lookup for the schema name if the statement does not include one */ + if (schemaName == NULL) + { + Oid collid = get_collation_oid(name, true); + + if (collid == InvalidOid) + { + return (Node *) name; + } + + HeapTuple colltup = SearchSysCache1(COLLOID, collid); + + if (!HeapTupleIsValid(colltup)) + { + return (Node *) name; + } + Form_pg_collation collationForm = + (Form_pg_collation) GETSTRUCT(colltup); + + schemaName = get_namespace_name(collationForm->collnamespace); + collationName = NameStr(collationForm->collname); + name = list_make2(makeString(schemaName), makeString(collationName)); + ReleaseSysCache(colltup); + } + + return (Node *) name; +} diff --git a/src/backend/distributed/metadata/dependency.c b/src/backend/distributed/metadata/dependency.c index 3323fd8e7..57d77f931 100644 --- a/src/backend/distributed/metadata/dependency.c +++ b/src/backend/distributed/metadata/dependency.c @@ -210,7 +210,7 @@ recurse_pg_depend(const ObjectAddress *target, relation_close(depRel, AccessShareLock); /* - * concat expended entries if applicable + * concat expanded entries if applicable */ if (expand != NULL) { @@ -389,6 +389,7 @@ SupportedDependencyByCitus(const ObjectAddress *address) */ switch (getObjectClass(address)) { + case OCLASS_COLLATION: case OCLASS_SCHEMA: { return true; diff --git a/src/backend/distributed/worker/worker_create_or_replace.c b/src/backend/distributed/worker/worker_create_or_replace.c index ed27300c2..0b66f5e75 100644 --- a/src/backend/distributed/worker/worker_create_or_replace.c +++ b/src/backend/distributed/worker/worker_create_or_replace.c @@ -9,7 +9,9 @@ #include "postgres.h" +#include "access/htup_details.h" #include "catalog/dependency.h" +#include "catalog/pg_collation.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" #include "fmgr.h" @@ -19,6 +21,7 @@ #include "tcop/dest.h" #include "tcop/utility.h" #include "utils/builtins.h" +#include "utils/syscache.h" #include "utils/lsyscache.h" #include "utils/regproc.h" @@ -134,6 +137,11 @@ CreateStmtByObjectAddress(const ObjectAddress *address) { switch (getObjectClass(address)) { + case OCLASS_COLLATION: + { + return CreateCollationDDL(address->objectId); + } + case OCLASS_PROC: { return GetFunctionDDLCommand(address->objectId, false); @@ -163,6 +171,11 @@ GenerateBackupNameForCollision(const ObjectAddress *address) { switch (getObjectClass(address)) { + case OCLASS_COLLATION: + { + return GenerateBackupNameForCollationCollision(address); + } + case OCLASS_PROC: { return GenerateBackupNameForProcCollision(address); @@ -183,6 +196,39 @@ GenerateBackupNameForCollision(const ObjectAddress *address) } +/* + * CreateRenameTypeStmt creates a rename statement for a type based on its ObjectAddress. + * The rename statement will rename the existing object on its address to the value + * provided in newName. + */ +static RenameStmt * +CreateRenameCollationStmt(const ObjectAddress *address, char *newName) +{ + RenameStmt *stmt = makeNode(RenameStmt); + Oid collid = address->objectId; + + HeapTuple colltup = SearchSysCache1(COLLOID, collid); + if (!HeapTupleIsValid(colltup)) + { + elog(ERROR, "citus cache lookup error"); + return NULL; + } + Form_pg_collation collationForm = + (Form_pg_collation) GETSTRUCT(colltup); + + char *schemaName = get_namespace_name(collationForm->collnamespace); + char *collationName = NameStr(collationForm->collname); + List *name = list_make2(makeString(schemaName), makeString(collationName)); + ReleaseSysCache(colltup); + + stmt->renameType = OBJECT_COLLATION; + stmt->object = (Node *) name; + stmt->newname = newName; + + return stmt; +} + + /* * CreateRenameTypeStmt creates a rename statement for a type based on its ObjectAddress. * The rename statement will rename the existing object on its address to the value @@ -253,6 +299,11 @@ CreateRenameStatement(const ObjectAddress *address, char *newName) { switch (getObjectClass(address)) { + case OCLASS_COLLATION: + { + return CreateRenameCollationStmt(address, newName); + } + case OCLASS_PROC: { return CreateRenameProcStmt(address, newName); diff --git a/src/include/distributed/commands.h b/src/include/distributed/commands.h index 23b6ac027..3667985ea 100644 --- a/src/include/distributed/commands.h +++ b/src/include/distributed/commands.h @@ -27,6 +27,25 @@ extern List * PlanClusterStmt(ClusterStmt *clusterStmt, const char *clusterComma /* call.c */ extern bool CallDistributedProcedureRemotely(CallStmt *callStmt, DestReceiver *dest); +/* collation.c - forward declarations */ +extern char * CreateCollationDDL(Oid collationId); +extern List * CreateCollationDDLsIdempotent(Oid collationId); +extern ObjectAddress AlterCollationOwnerObjectAddress(AlterOwnerStmt *stmt); +extern List * PlanDropCollationStmt(DropStmt *stmt); +extern List * PlanAlterCollationOwnerStmt(AlterOwnerStmt *stmt, const char *queryString); +extern List * PlanAlterCollationSchemaStmt(AlterObjectSchemaStmt *stmt, + const char *queryString); +extern List * PlanRenameCollationStmt(RenameStmt *stmt, const char *queryString); +extern ObjectAddress * RenameCollationStmtObjectAddress(RenameStmt *stmt, + bool missing_ok); +extern ObjectAddress * AlterCollationSchemaStmtObjectAddress(AlterObjectSchemaStmt *stmt, + bool missing_ok); +extern void ProcessAlterCollationSchemaStmt(AlterObjectSchemaStmt *stmt, + const char *queryString); +extern char * GenerateBackupNameForCollationCollision(const ObjectAddress *address); +extern ObjectAddress * DefineCollationStmtObjectAddress(DefineStmt *stmt, bool + missing_ok); +extern List * ProcessCollationDefineStmt(DefineStmt *stmt, const char *queryString); /* extension.c - forward declarations */ extern bool IsCreateAlterExtensionUpdateCitusStmt(Node *parsetree); diff --git a/src/include/distributed/deparser.h b/src/include/distributed/deparser.h index 673712a14..a46b5768d 100644 --- a/src/include/distributed/deparser.h +++ b/src/include/distributed/deparser.h @@ -32,6 +32,17 @@ extern void AssertObjectTypeIsFunctional(ObjectType type); extern void QualifyTreeNode(Node *stmt); extern char * DeparseTreeNode(Node *stmt); +/* forward declarations for deparse_collation_stmts.c */ +extern char * DeparseDropCollationStmt(DropStmt *stmt); +extern char * DeparseRenameCollationStmt(RenameStmt *stmt); +extern char * DeparseAlterCollationSchemaStmt(AlterObjectSchemaStmt *stmt); +extern char * DeparseAlterCollationOwnerStmt(AlterOwnerStmt *stmt); + +extern void QualifyDropCollationStmt(DropStmt *stmt); +extern void QualifyRenameCollationStmt(RenameStmt *stmt); +extern void QualifyAlterCollationSchemaStmt(AlterObjectSchemaStmt *stmt); +extern void QualifyAlterCollationOwnerStmt(AlterOwnerStmt *stmt); + /* forward declarations for deparse_type_stmts.c */ extern char * DeparseCompositeTypeStmt(CompositeTypeStmt *stmt); extern char * DeparseCreateEnumStmt(CreateEnumStmt *stmt); @@ -52,8 +63,7 @@ extern void QualifyCreateEnumStmt(CreateEnumStmt *stmt); extern void QualifyAlterTypeSchemaStmt(AlterObjectSchemaStmt *stmt); extern void QualifyAlterTypeOwnerStmt(AlterOwnerStmt *stmt); -extern ObjectAddress * GetObjectAddressFromParseTree(Node *parseTree, bool - missing_ok); +extern ObjectAddress * GetObjectAddressFromParseTree(Node *parseTree, bool missing_ok); /* forward declarations for deparse_function_stmts.c */ extern char * DeparseDropFunctionStmt(DropStmt *stmt); diff --git a/src/test/regress/expected/distributed_collations.out b/src/test/regress/expected/distributed_collations.out new file mode 100644 index 000000000..561bcabee --- /dev/null +++ b/src/test/regress/expected/distributed_collations.out @@ -0,0 +1,107 @@ +SET citus.next_shard_id TO 20050000; +CREATE USER collationuser; +NOTICE: not propagating CREATE ROLE/USER commands to worker nodes +HINT: Connect to worker nodes directly to manually create all necessary users and roles. +SELECT run_command_on_workers($$CREATE USER collationuser;$$); + run_command_on_workers +----------------------------------- + (localhost,57637,t,"CREATE ROLE") + (localhost,57638,t,"CREATE ROLE") +(2 rows) + +CREATE SCHEMA collation_tests AUTHORIZATION collationuser; +CREATE SCHEMA collation_tests2 AUTHORIZATION collationuser; +SET search_path to collation_tests; +CREATE COLLATION german_phonebook (provider = icu, locale = 'de-u-co-phonebk'); +SET citus.enable_ddl_propagation TO off; +CREATE COLLATION german_phonebook_unpropagated (provider = icu, locale = 'de-u-co-phonebk'); +SET citus.enable_ddl_propagation TO on; +\c - - - :worker_1_port +SELECT c.collname, nsp.nspname, a.rolname +FROM pg_collation c +JOIN pg_namespace nsp ON nsp.oid = c.collnamespace +JOIN pg_authid a ON a.oid = c.collowner +WHERE collname like 'german_phonebook%' +ORDER BY 1,2,3; + collname | nspname | rolname +------------------+-----------------+---------- + german_phonebook | collation_tests | postgres +(1 row) + +\c - - - :master_port +SET search_path to collation_tests; +CREATE TABLE test_propagate(id int, t1 text COLLATE german_phonebook, + t2 text COLLATE german_phonebook_unpropagated); +INSERT INTO test_propagate VALUES (1, 'aesop', U&'\00E4sop'), (2, U&'Vo\1E9Er', 'Vossr'); +SELECT create_distributed_table('test_propagate', 'id'); +NOTICE: Copying data from local table... + create_distributed_table +-------------------------- + +(1 row) + +-- Test COLLATE is pushed down +SELECT * FROM collation_tests.test_propagate WHERE t2 < 'b'; + id | t1 | t2 +----+-------+------ + 1 | aesop | äsop +(1 row) + +SELECT * FROM collation_tests.test_propagate WHERE t2 < 'b' COLLATE "C"; + id | t1 | t2 +----+------+------- + 2 | Voẞr | Vossr +(1 row) + +\c - - - :worker_1_port +SELECT c.collname, nsp.nspname, a.rolname +FROM pg_collation c +JOIN pg_namespace nsp ON nsp.oid = c.collnamespace +JOIN pg_authid a ON a.oid = c.collowner +WHERE collname like 'german_phonebook%' +ORDER BY 1,2,3; + collname | nspname | rolname +-------------------------------+-----------------+---------- + german_phonebook | collation_tests | postgres + german_phonebook_unpropagated | collation_tests | postgres +(2 rows) + +\c - - - :master_port +ALTER COLLATION collation_tests.german_phonebook RENAME TO german_phonebook2; +ALTER COLLATION collation_tests.german_phonebook2 SET SCHEMA collation_tests2; +ALTER COLLATION collation_tests2.german_phonebook2 OWNER TO collationuser; +\c - - - :worker_1_port +SELECT c.collname, nsp.nspname, a.rolname +FROM pg_collation c +JOIN pg_namespace nsp ON nsp.oid = c.collnamespace +JOIN pg_authid a ON a.oid = c.collowner +WHERE collname like 'german_phonebook%' +ORDER BY 1,2,3; + collname | nspname | rolname +-------------------------------+------------------+--------------- + german_phonebook2 | collation_tests2 | collationuser + german_phonebook_unpropagated | collation_tests | postgres +(2 rows) + +\c - - - :master_port +SET client_min_messages TO error; -- suppress cascading objects dropping +DROP SCHEMA collation_tests CASCADE; +DROP SCHEMA collation_tests2 CASCADE; +-- This is hacky, but we should clean-up the resources as below +\c - - - :worker_1_port +SET client_min_messages TO error; -- suppress cascading objects dropping +DROP SCHEMA collation_tests CASCADE; +DROP SCHEMA collation_tests2 CASCADE; +\c - - - :worker_2_port +SET client_min_messages TO error; -- suppress cascading objects dropping +DROP SCHEMA collation_tests CASCADE; +DROP SCHEMA collation_tests2 CASCADE; +\c - - - :master_port +DROP USER collationuser; +SELECT run_command_on_workers($$DROP USER collationuser;$$); + run_command_on_workers +--------------------------------- + (localhost,57637,t,"DROP ROLE") + (localhost,57638,t,"DROP ROLE") +(2 rows) + diff --git a/src/test/regress/expected/distributed_collations_conflict.out b/src/test/regress/expected/distributed_collations_conflict.out new file mode 100644 index 000000000..77ac86d42 --- /dev/null +++ b/src/test/regress/expected/distributed_collations_conflict.out @@ -0,0 +1,96 @@ +CREATE SCHEMA collation_conflict; +SELECT run_command_on_workers($$CREATE SCHEMA collation_conflict;$$); + run_command_on_workers +------------------------------------- + (localhost,57637,t,"CREATE SCHEMA") + (localhost,57638,t,"CREATE SCHEMA") +(2 rows) + +\c - - - :worker_1_port +SET search_path TO collation_conflict; +CREATE COLLATION caseinsensitive ( + provider = icu, + locale = 'und-u-ks-level2' +); +\c - - - :master_port +SET search_path TO collation_conflict; +CREATE COLLATION caseinsensitive ( + provider = icu, + locale = 'und-u-ks-level2' +); +CREATE TABLE tblcoll(val text COLLATE caseinsensitive); +SELECT create_reference_table('tblcoll'); + create_reference_table +------------------------ + +(1 row) + +\c - - - :worker_1_port +SELECT c.collname, nsp.nspname, a.rolname +FROM pg_collation c +JOIN pg_namespace nsp ON nsp.oid = c.collnamespace +JOIN pg_authid a ON a.oid = c.collowner +WHERE collname like 'caseinsensitive%' +ORDER BY 1,2,3; + collname | nspname | rolname +-----------------+--------------------+---------- + caseinsensitive | collation_conflict | postgres +(1 row) + +\c - - - :master_port +SET search_path TO collation_conflict; +-- Now drop & recreate in order to make sure rename detects the existing renamed objects +-- hide cascades +--SET client_min_messages TO error; +DROP TABLE tblcoll; +DROP COLLATION caseinsensitive; +\c - - - :worker_1_port +SET search_path TO collation_conflict; +CREATE COLLATION caseinsensitive ( + provider = icu, + locale = 'und-u-ks-level1' +); +\c - - - :master_port +SET search_path TO collation_conflict; +CREATE COLLATION caseinsensitive ( + provider = icu, + locale = 'und-u-ks-level2' +); +CREATE TABLE tblcoll(val text COLLATE caseinsensitive); +SELECT create_reference_table('tblcoll'); + create_reference_table +------------------------ + +(1 row) + +\c - - - :worker_1_port +SELECT c.collname, nsp.nspname, a.rolname +FROM pg_collation c +JOIN pg_namespace nsp ON nsp.oid = c.collnamespace +JOIN pg_authid a ON a.oid = c.collowner +WHERE collname like 'caseinsensitive%' +ORDER BY 1,2,3; + collname | nspname | rolname +---------------------------------+--------------------+---------- + caseinsensitive | collation_conflict | postgres + caseinsensitive(citus_backup_0) | collation_conflict | postgres +(2 rows) + +\c - - - :master_port +SET search_path TO collation_conflict; +-- now test worker_create_or_replace_object directly +SELECT worker_create_or_replace_object($$CREATE COLLATION collation_conflict.caseinsensitive (provider = 'icu', lc_collate = 'und-u-ks-level2', lc_ctype = 'und-u-ks-level2')$$); + worker_create_or_replace_object +--------------------------------- + f +(1 row) + +SELECT worker_create_or_replace_object($$CREATE COLLATION collation_conflict.caseinsensitive (provider = 'icu', lc_collate = 'und-u-ks-level2', lc_ctype = 'und-u-ks-level2')$$); + worker_create_or_replace_object +--------------------------------- + f +(1 row) + +-- hide cascades +SET client_min_messages TO error; +DROP SCHEMA collation_conflict CASCADE; diff --git a/src/test/regress/expected/multi_mx_create_table.out b/src/test/regress/expected/multi_mx_create_table.out index bf9d5a0ce..9ad854dd0 100644 --- a/src/test/regress/expected/multi_mx_create_table.out +++ b/src/test/regress/expected/multi_mx_create_table.out @@ -94,8 +94,6 @@ CREATE OPERATOR citus_mx_test_schema.=== ( NEGATOR = !==, HASHES, MERGES ); -SET search_path TO public; -CREATE COLLATION citus_mx_test_schema.english (LOCALE=:current_locale); -- now create required stuff in the worker 2 \c - - - :worker_2_port -- create schema to test schema support @@ -134,8 +132,6 @@ CREATE OPERATOR citus_mx_test_schema.=== ( NEGATOR = !==, HASHES, MERGES ); -SET search_path TO public; -CREATE COLLATION citus_mx_test_schema.english (LOCALE=:current_locale); -- connect back to the master, and do some more tests \c - - - :master_port SET citus.shard_replication_factor TO 1; diff --git a/src/test/regress/expected/multi_schema_support.out b/src/test/regress/expected/multi_schema_support.out index c076b5224..492e5855c 100644 --- a/src/test/regress/expected/multi_schema_support.out +++ b/src/test/regress/expected/multi_schema_support.out @@ -144,7 +144,7 @@ SELECT master_create_worker_shards('test_schema_support.nation_hash', 4, 2); -- test cursors SET search_path TO public; BEGIN; -DECLARE test_cursor CURSOR FOR +DECLARE test_cursor CURSOR FOR SELECT * FROM test_schema_support.nation_append WHERE n_nationkey = 1; @@ -170,7 +170,7 @@ END; -- test with search_path is set SET search_path TO test_schema_support; BEGIN; -DECLARE test_cursor CURSOR FOR +DECLARE test_cursor CURSOR FOR SELECT * FROM nation_append WHERE n_nationkey = 1; @@ -402,11 +402,11 @@ SET search_path TO public; UPDATE test_schema_support.nation_hash SET n_regionkey = n_regionkey + 1; --verify modification SELECT * FROM test_schema_support.nation_hash ORDER BY 1,2,3,4; - n_nationkey | n_name | n_regionkey | n_comment --------------+---------------------------+-------------+------------------------------------------------------------------------------------------------------------- + n_nationkey | n_name | n_regionkey | n_comment +-------------+---------------------------+-------------+------------------------------------------------------------------------------------------------------------ 0 | ALGERIA | 1 | haggle. carefully final deposits detect slyly agai 1 | ARGENTINA | 2 | al foxes promise slyly according to the regular accounts. bold requests alon - 2 | BRAZIL | 2 | y alongside of the pending deposits. carefully special packages are about the ironic forges. slyly special + 2 | BRAZIL | 2 | y alongside of the pending deposits. carefully special packages are about the ironic forges. slyly special 3 | CANADA | 2 | eas hang ironic, silent packages. slyly regular packages are furiously over the tithes. fluffily bold 4 | EGYPT | 5 | y above the carefully unusual theodolites. final dugouts are quickly across the furiously regular d 5 | ETHIOPIA | 1 | ven packages wake quickly. regu @@ -419,11 +419,11 @@ SET search_path TO test_schema_support; UPDATE nation_hash SET n_regionkey = n_regionkey + 1; --verify modification SELECT * FROM nation_hash ORDER BY 1,2,3,4; - n_nationkey | n_name | n_regionkey | n_comment --------------+---------------------------+-------------+------------------------------------------------------------------------------------------------------------- + n_nationkey | n_name | n_regionkey | n_comment +-------------+---------------------------+-------------+------------------------------------------------------------------------------------------------------------ 0 | ALGERIA | 2 | haggle. carefully final deposits detect slyly agai 1 | ARGENTINA | 3 | al foxes promise slyly according to the regular accounts. bold requests alon - 2 | BRAZIL | 3 | y alongside of the pending deposits. carefully special packages are about the ironic forges. slyly special + 2 | BRAZIL | 3 | y alongside of the pending deposits. carefully special packages are about the ironic forges. slyly special 3 | CANADA | 3 | eas hang ironic, silent packages. slyly regular packages are furiously over the tithes. fluffily bold 4 | EGYPT | 6 | y above the carefully unusual theodolites. final dugouts are quickly across the furiously regular d 5 | ETHIOPIA | 2 | ven packages wake quickly. regu @@ -435,12 +435,6 @@ SELECT * FROM nation_hash ORDER BY 1,2,3,4; SET search_path TO public; SELECT quote_ident(current_setting('lc_collate')) as current_locale \gset CREATE COLLATION test_schema_support.english (LOCALE = :current_locale); --- create COLLATION in worker node 1 in schema -\c - - - :worker_1_port -CREATE COLLATION test_schema_support.english (LOCALE = :current_locale); --- create COLLATION in worker node 2 in schema -\c - - - :worker_2_port -CREATE COLLATION test_schema_support.english (LOCALE = :current_locale); \c - - - :master_port CREATE TABLE test_schema_support.nation_hash_collation( n_nationkey integer not null, @@ -469,25 +463,25 @@ SELECT master_create_worker_shards('test_schema_support.nation_hash_collation', \copy test_schema_support.nation_hash_collation FROM STDIN with delimiter '|'; SELECT * FROM test_schema_support.nation_hash_collation ORDER BY 1,2,3,4; - n_nationkey | n_name | n_regionkey | n_comment --------------+---------------------------+-------------+------------------------------------------------------------------------------------------------------------- + n_nationkey | n_name | n_regionkey | n_comment +-------------+---------------------------+-------------+------------------------------------------------------------------------------------------------------------ 0 | ALGERIA | 0 | haggle. carefully final deposits detect slyly agai 1 | ARGENTINA | 1 | al foxes promise slyly according to the regular accounts. bold requests alon - 2 | BRAZIL | 1 | y alongside of the pending deposits. carefully special packages are about the ironic forges. slyly special + 2 | BRAZIL | 1 | y alongside of the pending deposits. carefully special packages are about the ironic forges. slyly special 3 | CANADA | 1 | eas hang ironic, silent packages. slyly regular packages are furiously over the tithes. fluffily bold 4 | EGYPT | 4 | y above the carefully unusual theodolites. final dugouts are quickly across the furiously regular d 5 | ETHIOPIA | 0 | ven packages wake quickly. regu (6 rows) SELECT n_comment FROM test_schema_support.nation_hash_collation ORDER BY n_comment COLLATE test_schema_support.english; - n_comment -------------------------------------------------------------------------------------------------------------- + n_comment +------------------------------------------------------------------------------------------------------------ al foxes promise slyly according to the regular accounts. bold requests alon eas hang ironic, silent packages. slyly regular packages are furiously over the tithes. fluffily bold haggle. carefully final deposits detect slyly agai ven packages wake quickly. regu y above the carefully unusual theodolites. final dugouts are quickly across the furiously regular d - y alongside of the pending deposits. carefully special packages are about the ironic forges. slyly special + y alongside of the pending deposits. carefully special packages are about the ironic forges. slyly special (6 rows) --test with search_path is set @@ -512,25 +506,25 @@ SELECT master_create_worker_shards('nation_hash_collation_search_path', 4, 2); \copy nation_hash_collation_search_path FROM STDIN with delimiter '|'; SELECT * FROM nation_hash_collation_search_path ORDER BY 1 DESC, 2 DESC, 3 DESC, 4 DESC; - n_nationkey | n_name | n_regionkey | n_comment --------------+---------------------------+-------------+------------------------------------------------------------------------------------------------------------- + n_nationkey | n_name | n_regionkey | n_comment +-------------+---------------------------+-------------+------------------------------------------------------------------------------------------------------------ 5 | ETHIOPIA | 0 | ven packages wake quickly. regu 4 | EGYPT | 4 | y above the carefully unusual theodolites. final dugouts are quickly across the furiously regular d 3 | CANADA | 1 | eas hang ironic, silent packages. slyly regular packages are furiously over the tithes. fluffily bold - 2 | BRAZIL | 1 | y alongside of the pending deposits. carefully special packages are about the ironic forges. slyly special + 2 | BRAZIL | 1 | y alongside of the pending deposits. carefully special packages are about the ironic forges. slyly special 1 | ARGENTINA | 1 | al foxes promise slyly according to the regular accounts. bold requests alon 0 | ALGERIA | 0 | haggle. carefully final deposits detect slyly agai (6 rows) SELECT n_comment FROM nation_hash_collation_search_path ORDER BY n_comment COLLATE english; - n_comment -------------------------------------------------------------------------------------------------------------- + n_comment +------------------------------------------------------------------------------------------------------------ al foxes promise slyly according to the regular accounts. bold requests alon eas hang ironic, silent packages. slyly regular packages are furiously over the tithes. fluffily bold haggle. carefully final deposits detect slyly agai ven packages wake quickly. regu y above the carefully unusual theodolites. final dugouts are quickly across the furiously regular d - y alongside of the pending deposits. carefully special packages are about the ironic forges. slyly special + y alongside of the pending deposits. carefully special packages are about the ironic forges. slyly special (6 rows) --test composite types with schema @@ -838,10 +832,10 @@ SELECT create_distributed_table('test_schema_support_join_2.nation_hash', 'n_nat -- join of two tables which are in different schemas, -- join on partition column SET search_path TO public; -SELECT +SELECT count (*) FROM - test_schema_support_join_1.nation_hash n1, test_schema_support_join_2.nation_hash n2 + test_schema_support_join_1.nation_hash n1, test_schema_support_join_2.nation_hash n2 WHERE n1.n_nationkey = n2.n_nationkey; count @@ -853,10 +847,10 @@ WHERE -- join of two tables which are in different schemas, -- join on partition column SET search_path TO test_schema_support_join_1; -SELECT +SELECT count (*) FROM - nation_hash n1, test_schema_support_join_2.nation_hash n2 + nation_hash n1, test_schema_support_join_2.nation_hash n2 WHERE n1.n_nationkey = n2.n_nationkey; count @@ -868,10 +862,10 @@ WHERE -- join of two tables which are in same schemas, -- join on partition column SET search_path TO public; -SELECT +SELECT count (*) FROM - test_schema_support_join_1.nation_hash n1, test_schema_support_join_1.nation_hash_2 n2 + test_schema_support_join_1.nation_hash n1, test_schema_support_join_1.nation_hash_2 n2 WHERE n1.n_nationkey = n2.n_nationkey; count @@ -883,10 +877,10 @@ WHERE -- join of two tables which are in same schemas, -- join on partition column SET search_path TO test_schema_support_join_1; -SELECT +SELECT count (*) FROM - nation_hash n1, nation_hash_2 n2 + nation_hash n1, nation_hash_2 n2 WHERE n1.n_nationkey = n2.n_nationkey; count @@ -900,10 +894,10 @@ SET citus.task_executor_type TO "task-tracker"; -- join of two tables which are in different schemas, -- join on partition column and non-partition column SET search_path TO public; -SELECT +SELECT count (*) FROM - test_schema_support_join_1.nation_hash n1, test_schema_support_join_2.nation_hash n2 + test_schema_support_join_1.nation_hash n1, test_schema_support_join_2.nation_hash n2 WHERE n1.n_nationkey = n2.n_regionkey; count @@ -915,10 +909,10 @@ WHERE -- join of two tables which are in different schemas, -- join on partition column and non-partition column SET search_path TO test_schema_support_join_1; -SELECT +SELECT count (*) FROM - nation_hash n1, test_schema_support_join_2.nation_hash n2 + nation_hash n1, test_schema_support_join_2.nation_hash n2 WHERE n1.n_nationkey = n2.n_regionkey; count @@ -930,10 +924,10 @@ WHERE -- join of two tables which are in same schemas, -- join on partition column and non-partition column SET search_path TO test_schema_support_join_1; -SELECT +SELECT count (*) FROM - nation_hash n1, nation_hash_2 n2 + nation_hash n1, nation_hash_2 n2 WHERE n1.n_nationkey = n2.n_regionkey; count @@ -941,15 +935,15 @@ WHERE 6 (1 row) --- hash repartition joins +-- hash repartition joins -- check when search_path is public, -- join of two tables which are in different schemas, -- join on non-partition column SET search_path TO public; -SELECT +SELECT count (*) FROM - test_schema_support_join_1.nation_hash n1, test_schema_support_join_2.nation_hash n2 + test_schema_support_join_1.nation_hash n1, test_schema_support_join_2.nation_hash n2 WHERE n1.n_regionkey = n2.n_regionkey; count @@ -961,10 +955,10 @@ WHERE -- join of two tables which are in different schemas, -- join on non-partition column SET search_path TO test_schema_support_join_1; -SELECT +SELECT count (*) FROM - nation_hash n1, test_schema_support_join_2.nation_hash n2 + nation_hash n1, test_schema_support_join_2.nation_hash n2 WHERE n1.n_regionkey = n2.n_regionkey; count @@ -976,10 +970,10 @@ WHERE -- join of two tables which are in same schemas, -- join on non-partition column SET search_path TO test_schema_support_join_1; -SELECT +SELECT count (*) FROM - nation_hash n1, nation_hash_2 n2 + nation_hash n1, nation_hash_2 n2 WHERE n1.n_regionkey = n2.n_regionkey; count @@ -1108,8 +1102,8 @@ SELECT count(*) FROM "CiTUS.TEEN2"."CAPITAL_TABLE"; INSERT INTO "CiTuS.TeeN"."TeeNTabLE.1!?!" VALUES(1, 1),(1, 0),(0, 1),(2, 3),(3, 2),(4, 4); INSERT INTO "CiTUS.TEEN2"."CAPITAL_TABLE" VALUES(0, 1),(1, 0),(2, 1),(4, 3),(3, 2),(4, 4); -- join on tables with weird names -SELECT * -FROM "CiTuS.TeeN"."TeeNTabLE.1!?!", "CiTUS.TEEN2"."CAPITAL_TABLE" +SELECT * +FROM "CiTuS.TeeN"."TeeNTabLE.1!?!", "CiTUS.TEEN2"."CAPITAL_TABLE" WHERE "CiTUS.TEEN2"."CAPITAL_TABLE".i = "CiTuS.TeeN"."TeeNTabLE.1!?!"."TeNANt_Id" ORDER BY 1,2,3,4; id | TeNANt_Id | i | j @@ -1124,10 +1118,10 @@ ORDER BY 1,2,3,4; (7 rows) -- add group by, having, order by clauses -SELECT * -FROM "CiTuS.TeeN"."TeeNTabLE.1!?!", "CiTUS.TEEN2"."CAPITAL_TABLE" +SELECT * +FROM "CiTuS.TeeN"."TeeNTabLE.1!?!", "CiTUS.TEEN2"."CAPITAL_TABLE" WHERE "CiTUS.TEEN2"."CAPITAL_TABLE".i = "CiTuS.TeeN"."TeeNTabLE.1!?!"."TeNANt_Id" -GROUP BY "TeNANt_Id", id, i, j +GROUP BY "TeNANt_Id", id, i, j HAVING "TeNANt_Id" > 0 AND j >= id ORDER BY "TeNANt_Id"; id | TeNANt_Id | i | j ----+-----------+---+--- @@ -1136,10 +1130,10 @@ HAVING "TeNANt_Id" > 0 AND j >= id ORDER BY "TeNANt_Id"; 4 | 4 | 4 | 4 (3 rows) -SELECT * +SELECT * FROM "CiTuS.TeeN"."TeeNTabLE.1!?!" join "CiTUS.TEEN2"."CAPITAL_TABLE" on ("CiTUS.TEEN2"."CAPITAL_TABLE".i = "CiTuS.TeeN"."TeeNTabLE.1!?!"."TeNANt_Id") -GROUP BY "TeNANt_Id", id, i, j +GROUP BY "TeNANt_Id", id, i, j HAVING "TeNANt_Id" > 0 AND j >= id ORDER BY 1,2,3,4; id | TeNANt_Id | i | j @@ -1151,12 +1145,12 @@ ORDER BY 1,2,3,4; -- run with CTEs WITH "cTE" AS ( - SELECT * + SELECT * FROM "CiTuS.TeeN"."TeeNTabLE.1!?!" ) SELECT * FROM "cTE" join "CiTUS.TEEN2"."CAPITAL_TABLE" on ("cTE"."TeNANt_Id" = "CiTUS.TEEN2"."CAPITAL_TABLE".i) -GROUP BY "TeNANt_Id", id, i, j +GROUP BY "TeNANt_Id", id, i, j HAVING "TeNANt_Id" > 0 AND j >= id ORDER BY 1,2,3,4; id | TeNANt_Id | i | j @@ -1168,14 +1162,14 @@ ORDER BY 1,2,3,4; SET search_path to "CiTuS.TeeN"; -- and subqueries -SELECT * +SELECT * FROM ( - SELECT * + SELECT * FROM "TeeNTabLE.1!?!" ) "cTE" join "CiTUS.TEEN2"."CAPITAL_TABLE" on ("cTE"."TeNANt_Id" = "CiTUS.TEEN2"."CAPITAL_TABLE".i) -GROUP BY "TeNANt_Id", id, i, j +GROUP BY "TeNANt_Id", id, i, j HAVING "TeNANt_Id" > 0 AND j >= id ORDER BY 1,2,3,4; id | TeNANt_Id | i | j diff --git a/src/test/regress/multi_schedule b/src/test/regress/multi_schedule index 0d48602b2..221324404 100644 --- a/src/test/regress/multi_schedule +++ b/src/test/regress/multi_schedule @@ -302,6 +302,7 @@ test: ssl_by_default # --------- test: distributed_types distributed_types_conflict disable_object_propagation distributed_types_xact_add_enum_value test: distributed_functions distributed_functions_conflict +test: distributed_collations distributed_collations_conflict test: distributed_procedure # --------- diff --git a/src/test/regress/sql/distributed_collations.sql b/src/test/regress/sql/distributed_collations.sql new file mode 100644 index 000000000..85fc37814 --- /dev/null +++ b/src/test/regress/sql/distributed_collations.sql @@ -0,0 +1,79 @@ +SET citus.next_shard_id TO 20050000; + +CREATE USER collationuser; +SELECT run_command_on_workers($$CREATE USER collationuser;$$); + +CREATE SCHEMA collation_tests AUTHORIZATION collationuser; +CREATE SCHEMA collation_tests2 AUTHORIZATION collationuser; + +SET search_path to collation_tests; + +CREATE COLLATION german_phonebook (provider = icu, locale = 'de-u-co-phonebk'); + +SET citus.enable_ddl_propagation TO off; + +CREATE COLLATION german_phonebook_unpropagated (provider = icu, locale = 'de-u-co-phonebk'); + +SET citus.enable_ddl_propagation TO on; + +\c - - - :worker_1_port +SELECT c.collname, nsp.nspname, a.rolname +FROM pg_collation c +JOIN pg_namespace nsp ON nsp.oid = c.collnamespace +JOIN pg_authid a ON a.oid = c.collowner +WHERE collname like 'german_phonebook%' +ORDER BY 1,2,3; +\c - - - :master_port +SET search_path to collation_tests; + +CREATE TABLE test_propagate(id int, t1 text COLLATE german_phonebook, + t2 text COLLATE german_phonebook_unpropagated); +INSERT INTO test_propagate VALUES (1, 'aesop', U&'\00E4sop'), (2, U&'Vo\1E9Er', 'Vossr'); +SELECT create_distributed_table('test_propagate', 'id'); + +-- Test COLLATE is pushed down +SELECT * FROM collation_tests.test_propagate WHERE t2 < 'b'; +SELECT * FROM collation_tests.test_propagate WHERE t2 < 'b' COLLATE "C"; + +\c - - - :worker_1_port +SELECT c.collname, nsp.nspname, a.rolname +FROM pg_collation c +JOIN pg_namespace nsp ON nsp.oid = c.collnamespace +JOIN pg_authid a ON a.oid = c.collowner +WHERE collname like 'german_phonebook%' +ORDER BY 1,2,3; +\c - - - :master_port + +ALTER COLLATION collation_tests.german_phonebook RENAME TO german_phonebook2; +ALTER COLLATION collation_tests.german_phonebook2 SET SCHEMA collation_tests2; +ALTER COLLATION collation_tests2.german_phonebook2 OWNER TO collationuser; + +\c - - - :worker_1_port +SELECT c.collname, nsp.nspname, a.rolname +FROM pg_collation c +JOIN pg_namespace nsp ON nsp.oid = c.collnamespace +JOIN pg_authid a ON a.oid = c.collowner +WHERE collname like 'german_phonebook%' +ORDER BY 1,2,3; +\c - - - :master_port + +SET client_min_messages TO error; -- suppress cascading objects dropping +DROP SCHEMA collation_tests CASCADE; +DROP SCHEMA collation_tests2 CASCADE; + +-- This is hacky, but we should clean-up the resources as below + +\c - - - :worker_1_port +SET client_min_messages TO error; -- suppress cascading objects dropping +DROP SCHEMA collation_tests CASCADE; +DROP SCHEMA collation_tests2 CASCADE; + +\c - - - :worker_2_port +SET client_min_messages TO error; -- suppress cascading objects dropping +DROP SCHEMA collation_tests CASCADE; +DROP SCHEMA collation_tests2 CASCADE; + +\c - - - :master_port + +DROP USER collationuser; +SELECT run_command_on_workers($$DROP USER collationuser;$$); diff --git a/src/test/regress/sql/distributed_collations_conflict.sql b/src/test/regress/sql/distributed_collations_conflict.sql new file mode 100644 index 000000000..5f3063651 --- /dev/null +++ b/src/test/regress/sql/distributed_collations_conflict.sql @@ -0,0 +1,73 @@ +CREATE SCHEMA collation_conflict; +SELECT run_command_on_workers($$CREATE SCHEMA collation_conflict;$$); + +\c - - - :worker_1_port +SET search_path TO collation_conflict; + +CREATE COLLATION caseinsensitive ( + provider = icu, + locale = 'und-u-ks-level2' +); + +\c - - - :master_port +SET search_path TO collation_conflict; + +CREATE COLLATION caseinsensitive ( + provider = icu, + locale = 'und-u-ks-level2' +); +CREATE TABLE tblcoll(val text COLLATE caseinsensitive); +SELECT create_reference_table('tblcoll'); + +\c - - - :worker_1_port +SELECT c.collname, nsp.nspname, a.rolname +FROM pg_collation c +JOIN pg_namespace nsp ON nsp.oid = c.collnamespace +JOIN pg_authid a ON a.oid = c.collowner +WHERE collname like 'caseinsensitive%' +ORDER BY 1,2,3; +\c - - - :master_port +SET search_path TO collation_conflict; + +-- Now drop & recreate in order to make sure rename detects the existing renamed objects +-- hide cascades +--SET client_min_messages TO error; +DROP TABLE tblcoll; +DROP COLLATION caseinsensitive; + +\c - - - :worker_1_port +SET search_path TO collation_conflict; + +CREATE COLLATION caseinsensitive ( + provider = icu, + locale = 'und-u-ks-level1' +); + +\c - - - :master_port +SET search_path TO collation_conflict; + +CREATE COLLATION caseinsensitive ( + provider = icu, + locale = 'und-u-ks-level2' +); +CREATE TABLE tblcoll(val text COLLATE caseinsensitive); +SELECT create_reference_table('tblcoll'); + +\c - - - :worker_1_port +SELECT c.collname, nsp.nspname, a.rolname +FROM pg_collation c +JOIN pg_namespace nsp ON nsp.oid = c.collnamespace +JOIN pg_authid a ON a.oid = c.collowner +WHERE collname like 'caseinsensitive%' +ORDER BY 1,2,3; +\c - - - :master_port +SET search_path TO collation_conflict; + +-- now test worker_create_or_replace_object directly +SELECT worker_create_or_replace_object($$CREATE COLLATION collation_conflict.caseinsensitive (provider = 'icu', lc_collate = 'und-u-ks-level2', lc_ctype = 'und-u-ks-level2')$$); +SELECT worker_create_or_replace_object($$CREATE COLLATION collation_conflict.caseinsensitive (provider = 'icu', lc_collate = 'und-u-ks-level2', lc_ctype = 'und-u-ks-level2')$$); + +-- hide cascades +SET client_min_messages TO error; +DROP SCHEMA collation_conflict CASCADE; + diff --git a/src/test/regress/sql/multi_mx_create_table.sql b/src/test/regress/sql/multi_mx_create_table.sql index 9e7030921..da16122f2 100644 --- a/src/test/regress/sql/multi_mx_create_table.sql +++ b/src/test/regress/sql/multi_mx_create_table.sql @@ -100,9 +100,6 @@ CREATE OPERATOR citus_mx_test_schema.=== ( HASHES, MERGES ); -SET search_path TO public; -CREATE COLLATION citus_mx_test_schema.english (LOCALE=:current_locale); - -- now create required stuff in the worker 2 \c - - - :worker_2_port @@ -148,10 +145,6 @@ CREATE OPERATOR citus_mx_test_schema.=== ( HASHES, MERGES ); - -SET search_path TO public; -CREATE COLLATION citus_mx_test_schema.english (LOCALE=:current_locale); - -- connect back to the master, and do some more tests \c - - - :master_port diff --git a/src/test/regress/sql/multi_schema_support.sql b/src/test/regress/sql/multi_schema_support.sql index c54d95b50..48f26772c 100644 --- a/src/test/regress/sql/multi_schema_support.sql +++ b/src/test/regress/sql/multi_schema_support.sql @@ -322,14 +322,6 @@ SET search_path TO public; SELECT quote_ident(current_setting('lc_collate')) as current_locale \gset CREATE COLLATION test_schema_support.english (LOCALE = :current_locale); --- create COLLATION in worker node 1 in schema -\c - - - :worker_1_port -CREATE COLLATION test_schema_support.english (LOCALE = :current_locale); - --- create COLLATION in worker node 2 in schema -\c - - - :worker_2_port -CREATE COLLATION test_schema_support.english (LOCALE = :current_locale); - \c - - - :master_port CREATE TABLE test_schema_support.nation_hash_collation(