mirror of https://github.com/citusdata/citus.git
Propagate CREATE PUBLICATION statements
parent
e618345703
commit
b09d239809
|
@ -217,6 +217,7 @@ static bool WillRecreateForeignKeyToReferenceTable(Oid relationId,
|
||||||
CascadeToColocatedOption cascadeOption);
|
CascadeToColocatedOption cascadeOption);
|
||||||
static void WarningsForDroppingForeignKeysWithDistributedTables(Oid relationId);
|
static void WarningsForDroppingForeignKeysWithDistributedTables(Oid relationId);
|
||||||
static void ErrorIfUnsupportedCascadeObjects(Oid relationId);
|
static void ErrorIfUnsupportedCascadeObjects(Oid relationId);
|
||||||
|
static List * WrapTableDDLCommands(List *commandStrings);
|
||||||
static bool DoesCascadeDropUnsupportedObject(Oid classId, Oid id, HTAB *nodeMap);
|
static bool DoesCascadeDropUnsupportedObject(Oid classId, Oid id, HTAB *nodeMap);
|
||||||
static TableConversionReturn * CopyTableConversionReturnIntoCurrentContext(
|
static TableConversionReturn * CopyTableConversionReturnIntoCurrentContext(
|
||||||
TableConversionReturn *tableConversionReturn);
|
TableConversionReturn *tableConversionReturn);
|
||||||
|
@ -604,9 +605,18 @@ ConvertTableInternal(TableConversionState *con)
|
||||||
List *justBeforeDropCommands = NIL;
|
List *justBeforeDropCommands = NIL;
|
||||||
List *attachPartitionCommands = NIL;
|
List *attachPartitionCommands = NIL;
|
||||||
|
|
||||||
postLoadCommands =
|
List *createViewCommands = GetViewCreationCommandsOfTable(con->relationId);
|
||||||
list_concat(postLoadCommands,
|
|
||||||
GetViewCreationTableDDLCommandsOfTable(con->relationId));
|
postLoadCommands = list_concat(postLoadCommands,
|
||||||
|
WrapTableDDLCommands(createViewCommands));
|
||||||
|
|
||||||
|
/* need to add back to publications after dropping the original table */
|
||||||
|
bool isAdd = true;
|
||||||
|
List *alterPublicationCommands =
|
||||||
|
GetAlterPublicationDDLCommandsForTable(con->relationId, isAdd);
|
||||||
|
|
||||||
|
postLoadCommands = list_concat(postLoadCommands,
|
||||||
|
WrapTableDDLCommands(alterPublicationCommands));
|
||||||
|
|
||||||
List *foreignKeyCommands = NIL;
|
List *foreignKeyCommands = NIL;
|
||||||
if (con->conversionType == ALTER_DISTRIBUTED_TABLE)
|
if (con->conversionType == ALTER_DISTRIBUTED_TABLE)
|
||||||
|
@ -1493,17 +1503,16 @@ GetViewCreationCommandsOfTable(Oid relationId)
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* GetViewCreationTableDDLCommandsOfTable is the same as GetViewCreationCommandsOfTable,
|
* WrapTableDDLCommands takes a list of command strings and wraps them
|
||||||
* but the returned list includes objects of TableDDLCommand's, not strings.
|
* in TableDDLCommand structs.
|
||||||
*/
|
*/
|
||||||
List *
|
static List *
|
||||||
GetViewCreationTableDDLCommandsOfTable(Oid relationId)
|
WrapTableDDLCommands(List *commandStrings)
|
||||||
{
|
{
|
||||||
List *commands = GetViewCreationCommandsOfTable(relationId);
|
|
||||||
List *tableDDLCommands = NIL;
|
List *tableDDLCommands = NIL;
|
||||||
|
|
||||||
char *command = NULL;
|
char *command = NULL;
|
||||||
foreach_ptr(command, commands)
|
foreach_ptr(command, commandStrings)
|
||||||
{
|
{
|
||||||
tableDDLCommands = lappend(tableDDLCommands, makeTableDDLCommandString(command));
|
tableDDLCommands = lappend(tableDDLCommands, makeTableDDLCommandString(command));
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,6 +85,7 @@ static void DropRelationTruncateTriggers(Oid relationId);
|
||||||
static char * GetDropTriggerCommand(Oid relationId, char *triggerName);
|
static char * GetDropTriggerCommand(Oid relationId, char *triggerName);
|
||||||
static void DropViewsOnTable(Oid relationId);
|
static void DropViewsOnTable(Oid relationId);
|
||||||
static void DropIdentitiesOnTable(Oid relationId);
|
static void DropIdentitiesOnTable(Oid relationId);
|
||||||
|
static void DropTableFromPublications(Oid relationId);
|
||||||
static List * GetRenameStatsCommandList(List *statsOidList, uint64 shardId);
|
static List * GetRenameStatsCommandList(List *statsOidList, uint64 shardId);
|
||||||
static List * ReversedOidList(List *oidList);
|
static List * ReversedOidList(List *oidList);
|
||||||
static void AppendExplicitIndexIdsToList(Form_pg_index indexForm,
|
static void AppendExplicitIndexIdsToList(Form_pg_index indexForm,
|
||||||
|
@ -338,6 +339,10 @@ CreateCitusLocalTable(Oid relationId, bool cascadeViaForeignKeys, bool autoConve
|
||||||
List *shellTableDDLEvents = GetShellTableDDLEventsForCitusLocalTable(relationId);
|
List *shellTableDDLEvents = GetShellTableDDLEventsForCitusLocalTable(relationId);
|
||||||
List *tableViewCreationCommands = GetViewCreationCommandsOfTable(relationId);
|
List *tableViewCreationCommands = GetViewCreationCommandsOfTable(relationId);
|
||||||
|
|
||||||
|
bool isAdd = true;
|
||||||
|
List *alterPublicationCommands =
|
||||||
|
GetAlterPublicationDDLCommandsForTable(relationId, isAdd);
|
||||||
|
|
||||||
char *relationName = get_rel_name(relationId);
|
char *relationName = get_rel_name(relationId);
|
||||||
Oid relationSchemaId = get_rel_namespace(relationId);
|
Oid relationSchemaId = get_rel_namespace(relationId);
|
||||||
|
|
||||||
|
@ -347,6 +352,12 @@ CreateCitusLocalTable(Oid relationId, bool cascadeViaForeignKeys, bool autoConve
|
||||||
*/
|
*/
|
||||||
DropIdentitiesOnTable(relationId);
|
DropIdentitiesOnTable(relationId);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We do not want the shard to be in the publication (subscribers are
|
||||||
|
* unlikely to recognize it).
|
||||||
|
*/
|
||||||
|
DropTableFromPublications(relationId);
|
||||||
|
|
||||||
/* below we convert relation with relationId to the shard relation */
|
/* below we convert relation with relationId to the shard relation */
|
||||||
uint64 shardId = ConvertLocalTableToShard(relationId);
|
uint64 shardId = ConvertLocalTableToShard(relationId);
|
||||||
|
|
||||||
|
@ -363,6 +374,11 @@ CreateCitusLocalTable(Oid relationId, bool cascadeViaForeignKeys, bool autoConve
|
||||||
*/
|
*/
|
||||||
ExecuteAndLogUtilityCommandListInTableTypeConversionViaSPI(tableViewCreationCommands);
|
ExecuteAndLogUtilityCommandListInTableTypeConversionViaSPI(tableViewCreationCommands);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Execute the publication creation commands with the shell table.
|
||||||
|
*/
|
||||||
|
ExecuteAndLogUtilityCommandListInTableTypeConversionViaSPI(alterPublicationCommands);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set shellRelationId as the relation with relationId now points
|
* Set shellRelationId as the relation with relationId now points
|
||||||
* to the shard relation.
|
* to the shard relation.
|
||||||
|
@ -1163,6 +1179,21 @@ DropIdentitiesOnTable(Oid relationId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DropTableFromPublications drops the table from all of its publications.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
DropTableFromPublications(Oid relationId)
|
||||||
|
{
|
||||||
|
bool isAdd = false;
|
||||||
|
|
||||||
|
List *alterPublicationCommands =
|
||||||
|
GetAlterPublicationDDLCommandsForTable(relationId, isAdd);
|
||||||
|
|
||||||
|
ExecuteAndLogUtilityCommandList(alterPublicationCommands);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* DropViewsOnTable drops the views that depend on the given relation.
|
* DropViewsOnTable drops the views that depend on the given relation.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -438,6 +438,11 @@ GetDependencyCreateDDLCommands(const ObjectAddress *dependency)
|
||||||
return DDLCommands;
|
return DDLCommands;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case OCLASS_PUBLICATION:
|
||||||
|
{
|
||||||
|
return CreatePublicationDDLCommandsIdempotent(dependency);
|
||||||
|
}
|
||||||
|
|
||||||
case OCLASS_ROLE:
|
case OCLASS_ROLE:
|
||||||
{
|
{
|
||||||
return GenerateCreateOrAlterRoleCommand(dependency->objectId);
|
return GenerateCreateOrAlterRoleCommand(dependency->objectId);
|
||||||
|
|
|
@ -245,6 +245,15 @@ static DistributeObjectOps Any_CreatePolicy = {
|
||||||
.address = NULL,
|
.address = NULL,
|
||||||
.markDistributed = false,
|
.markDistributed = false,
|
||||||
};
|
};
|
||||||
|
static DistributeObjectOps Any_CreatePublication = {
|
||||||
|
.deparse = DeparseCreatePublicationStmt,
|
||||||
|
.qualify = QualifyCreatePublicationStmt,
|
||||||
|
.preprocess = NULL,
|
||||||
|
.postprocess = PostProcessCreatePublicationStmt,
|
||||||
|
.operationType = DIST_OPS_CREATE,
|
||||||
|
.address = CreatePublicationStmtObjectAddress,
|
||||||
|
.markDistributed = true,
|
||||||
|
};
|
||||||
static DistributeObjectOps Any_CreateRole = {
|
static DistributeObjectOps Any_CreateRole = {
|
||||||
.deparse = DeparseCreateRoleStmt,
|
.deparse = DeparseCreateRoleStmt,
|
||||||
.qualify = NULL,
|
.qualify = NULL,
|
||||||
|
@ -707,6 +716,45 @@ static DistributeObjectOps Procedure_Rename = {
|
||||||
.address = RenameFunctionStmtObjectAddress,
|
.address = RenameFunctionStmtObjectAddress,
|
||||||
.markDistributed = false,
|
.markDistributed = false,
|
||||||
};
|
};
|
||||||
|
static DistributeObjectOps Publication_Alter = {
|
||||||
|
.deparse = DeparseAlterPublicationStmt,
|
||||||
|
.qualify = QualifyAlterPublicationStmt,
|
||||||
|
.preprocess = PreprocessAlterPublicationStmt,
|
||||||
|
.postprocess = PostprocessAlterDistributedObjectStmt,
|
||||||
|
.objectType = OBJECT_PUBLICATION,
|
||||||
|
.operationType = DIST_OPS_ALTER,
|
||||||
|
.address = AlterPublicationStmtObjectAddress,
|
||||||
|
.markDistributed = false,
|
||||||
|
};
|
||||||
|
static DistributeObjectOps Publication_AlterOwner = {
|
||||||
|
.deparse = DeparseAlterPublicationOwnerStmt,
|
||||||
|
.qualify = NULL,
|
||||||
|
.preprocess = PreprocessAlterDistributedObjectStmt,
|
||||||
|
.postprocess = PostprocessAlterDistributedObjectStmt,
|
||||||
|
.objectType = OBJECT_PUBLICATION,
|
||||||
|
.operationType = DIST_OPS_ALTER,
|
||||||
|
.address = AlterPublicationOwnerStmtObjectAddress,
|
||||||
|
.markDistributed = false,
|
||||||
|
};
|
||||||
|
static DistributeObjectOps Publication_Drop = {
|
||||||
|
.deparse = DeparseDropPublicationStmt,
|
||||||
|
.qualify = NULL,
|
||||||
|
.preprocess = PreprocessDropDistributedObjectStmt,
|
||||||
|
.postprocess = NULL,
|
||||||
|
.operationType = DIST_OPS_DROP,
|
||||||
|
.address = NULL,
|
||||||
|
.markDistributed = false,
|
||||||
|
};
|
||||||
|
static DistributeObjectOps Publication_Rename = {
|
||||||
|
.deparse = DeparseRenamePublicationStmt,
|
||||||
|
.qualify = NULL,
|
||||||
|
.preprocess = PreprocessAlterDistributedObjectStmt,
|
||||||
|
.postprocess = NULL,
|
||||||
|
.objectType = OBJECT_PUBLICATION,
|
||||||
|
.operationType = DIST_OPS_ALTER,
|
||||||
|
.address = RenamePublicationStmtObjectAddress,
|
||||||
|
.markDistributed = false,
|
||||||
|
};
|
||||||
static DistributeObjectOps Routine_AlterObjectDepends = {
|
static DistributeObjectOps Routine_AlterObjectDepends = {
|
||||||
.deparse = DeparseAlterFunctionDependsStmt,
|
.deparse = DeparseAlterFunctionDependsStmt,
|
||||||
.qualify = QualifyAlterFunctionDependsStmt,
|
.qualify = QualifyAlterFunctionDependsStmt,
|
||||||
|
@ -1399,6 +1447,11 @@ GetDistributeObjectOps(Node *node)
|
||||||
return &Procedure_AlterOwner;
|
return &Procedure_AlterOwner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case OBJECT_PUBLICATION:
|
||||||
|
{
|
||||||
|
return &Publication_AlterOwner;
|
||||||
|
}
|
||||||
|
|
||||||
case OBJECT_ROUTINE:
|
case OBJECT_ROUTINE:
|
||||||
{
|
{
|
||||||
return &Routine_AlterOwner;
|
return &Routine_AlterOwner;
|
||||||
|
@ -1436,6 +1489,11 @@ GetDistributeObjectOps(Node *node)
|
||||||
return &Any_AlterPolicy;
|
return &Any_AlterPolicy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case T_AlterPublicationStmt:
|
||||||
|
{
|
||||||
|
return &Publication_Alter;
|
||||||
|
}
|
||||||
|
|
||||||
case T_AlterRoleStmt:
|
case T_AlterRoleStmt:
|
||||||
{
|
{
|
||||||
return &Any_AlterRole;
|
return &Any_AlterRole;
|
||||||
|
@ -1610,6 +1668,11 @@ GetDistributeObjectOps(Node *node)
|
||||||
return &Any_CreatePolicy;
|
return &Any_CreatePolicy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case T_CreatePublicationStmt:
|
||||||
|
{
|
||||||
|
return &Any_CreatePublication;
|
||||||
|
}
|
||||||
|
|
||||||
case T_CreateRoleStmt:
|
case T_CreateRoleStmt:
|
||||||
{
|
{
|
||||||
return &Any_CreateRole;
|
return &Any_CreateRole;
|
||||||
|
@ -1722,6 +1785,11 @@ GetDistributeObjectOps(Node *node)
|
||||||
return &Procedure_Drop;
|
return &Procedure_Drop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case OBJECT_PUBLICATION:
|
||||||
|
{
|
||||||
|
return &Publication_Drop;
|
||||||
|
}
|
||||||
|
|
||||||
case OBJECT_ROUTINE:
|
case OBJECT_ROUTINE:
|
||||||
{
|
{
|
||||||
return &Routine_Drop;
|
return &Routine_Drop;
|
||||||
|
@ -1901,6 +1969,11 @@ GetDistributeObjectOps(Node *node)
|
||||||
return &Procedure_Rename;
|
return &Procedure_Rename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case OBJECT_PUBLICATION:
|
||||||
|
{
|
||||||
|
return &Publication_Rename;
|
||||||
|
}
|
||||||
|
|
||||||
case OBJECT_ROUTINE:
|
case OBJECT_ROUTINE:
|
||||||
{
|
{
|
||||||
return &Routine_Rename;
|
return &Routine_Rename;
|
||||||
|
|
|
@ -0,0 +1,634 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* publication.c
|
||||||
|
* Commands for creating publications
|
||||||
|
*
|
||||||
|
* Copyright (c) Citus Data, Inc.
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "postgres.h"
|
||||||
|
#include "miscadmin.h"
|
||||||
|
|
||||||
|
#include "catalog/pg_publication.h"
|
||||||
|
#include "catalog/pg_publication_rel.h"
|
||||||
|
#include "distributed/commands.h"
|
||||||
|
#include "distributed/deparser.h"
|
||||||
|
#include "distributed/listutils.h"
|
||||||
|
#include "distributed/metadata_utility.h"
|
||||||
|
#include "distributed/metadata_sync.h"
|
||||||
|
#include "distributed/metadata/distobject.h"
|
||||||
|
#include "distributed/reference_table_utils.h"
|
||||||
|
#include "distributed/worker_create_or_replace.h"
|
||||||
|
#include "nodes/makefuncs.h"
|
||||||
|
#include "nodes/parsenodes.h"
|
||||||
|
#include "utils/builtins.h"
|
||||||
|
#include "utils/lsyscache.h"
|
||||||
|
#include "utils/syscache.h"
|
||||||
|
|
||||||
|
#include "pg_version_compat.h"
|
||||||
|
|
||||||
|
|
||||||
|
static CreatePublicationStmt * BuildCreatePublicationStmt(Oid publicationId);
|
||||||
|
#if (PG_VERSION_NUM >= PG_VERSION_15)
|
||||||
|
static PublicationObjSpec * BuildPublicationRelationObjSpec(Oid relationId,
|
||||||
|
Oid publicationId,
|
||||||
|
bool tableOnly);
|
||||||
|
#endif
|
||||||
|
static void AppendPublishOptionList(StringInfo str, List *strings);
|
||||||
|
static char * AlterPublicationOwnerCommand(Oid publicationId);
|
||||||
|
static bool ShouldPropagateCreatePublication(CreatePublicationStmt *stmt);
|
||||||
|
static List * ObjectAddressForPublicationName(char *publicationName, bool missingOk);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PostProcessCreatePublicationStmt handles CREATE PUBLICATION statements
|
||||||
|
* that contain distributed tables.
|
||||||
|
*/
|
||||||
|
List *
|
||||||
|
PostProcessCreatePublicationStmt(Node *node, const char *queryString)
|
||||||
|
{
|
||||||
|
CreatePublicationStmt *stmt = castNode(CreatePublicationStmt, node);
|
||||||
|
|
||||||
|
if (!ShouldPropagateCreatePublication(stmt))
|
||||||
|
{
|
||||||
|
/* should not propagate right now */
|
||||||
|
return NIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* call into CreatePublicationStmtObjectAddress */
|
||||||
|
List *publicationAddresses = GetObjectAddressListFromParseTree(node, false, true);
|
||||||
|
|
||||||
|
/* the code-path only supports a single object */
|
||||||
|
Assert(list_length(publicationAddresses) == 1);
|
||||||
|
|
||||||
|
if (IsAnyObjectAddressOwnedByExtension(publicationAddresses, NULL))
|
||||||
|
{
|
||||||
|
/* should not propagate publications owned by extensions */
|
||||||
|
return NIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnsureAllObjectDependenciesExistOnAllNodes(publicationAddresses);
|
||||||
|
|
||||||
|
const ObjectAddress *pubAddress = linitial(publicationAddresses);
|
||||||
|
|
||||||
|
List *commands = NIL;
|
||||||
|
commands = lappend(commands, DISABLE_DDL_PROPAGATION);
|
||||||
|
commands = lappend(commands, CreatePublicationDDLCommand(pubAddress->objectId));
|
||||||
|
commands = lappend(commands, ENABLE_DDL_PROPAGATION);
|
||||||
|
|
||||||
|
return NodeDDLTaskList(NON_COORDINATOR_NODES, commands);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CreatePublicationDDLCommandsIdempotent returns a list of DDL statements to be
|
||||||
|
* executed on a node to recreate the publication addressed by the publicationAddress.
|
||||||
|
*/
|
||||||
|
List *
|
||||||
|
CreatePublicationDDLCommandsIdempotent(const ObjectAddress *publicationAddress)
|
||||||
|
{
|
||||||
|
Assert(publicationAddress->classId == PublicationRelationId);
|
||||||
|
|
||||||
|
char *ddlCommand =
|
||||||
|
CreatePublicationDDLCommand(publicationAddress->objectId);
|
||||||
|
|
||||||
|
char *alterPublicationOwnerSQL =
|
||||||
|
AlterPublicationOwnerCommand(publicationAddress->objectId);
|
||||||
|
|
||||||
|
return list_make2(
|
||||||
|
WrapCreateOrReplace(ddlCommand),
|
||||||
|
alterPublicationOwnerSQL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CreatePublicationDDLCommand returns the CREATE PUBLICATION string that
|
||||||
|
* can be used to recreate a given publication.
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
CreatePublicationDDLCommand(Oid publicationId)
|
||||||
|
{
|
||||||
|
CreatePublicationStmt *createPubStmt = BuildCreatePublicationStmt(publicationId);
|
||||||
|
|
||||||
|
/* we took the WHERE clause from the catalog where it is already transformed */
|
||||||
|
bool whereClauseRequiresTransform = false;
|
||||||
|
|
||||||
|
/* only propagate Citus tables in publication */
|
||||||
|
bool includeLocalTables = false;
|
||||||
|
|
||||||
|
return DeparseCreatePublicationStmtExtended((Node *) createPubStmt,
|
||||||
|
whereClauseRequiresTransform,
|
||||||
|
includeLocalTables);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BuildCreatePublicationStmt constructs a CreatePublicationStmt struct for the
|
||||||
|
* given publication.
|
||||||
|
*/
|
||||||
|
static CreatePublicationStmt *
|
||||||
|
BuildCreatePublicationStmt(Oid publicationId)
|
||||||
|
{
|
||||||
|
CreatePublicationStmt *createPubStmt = makeNode(CreatePublicationStmt);
|
||||||
|
|
||||||
|
HeapTuple publicationTuple =
|
||||||
|
SearchSysCache1(PUBLICATIONOID, ObjectIdGetDatum(publicationId));
|
||||||
|
|
||||||
|
if (!HeapTupleIsValid(publicationTuple))
|
||||||
|
{
|
||||||
|
ereport(ERROR, (errmsg("cannot find publication with oid: %d", publicationId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Form_pg_publication publicationForm =
|
||||||
|
(Form_pg_publication) GETSTRUCT(publicationTuple);
|
||||||
|
|
||||||
|
/* CREATE PUBLICATION <name> */
|
||||||
|
createPubStmt->pubname = pstrdup(NameStr(publicationForm->pubname));
|
||||||
|
|
||||||
|
/* FOR ALL TABLES */
|
||||||
|
createPubStmt->for_all_tables = publicationForm->puballtables;
|
||||||
|
|
||||||
|
ReleaseSysCache(publicationTuple);
|
||||||
|
|
||||||
|
#if (PG_VERSION_NUM >= PG_VERSION_15)
|
||||||
|
List *schemaIds = GetPublicationSchemas(publicationId);
|
||||||
|
Oid schemaId = InvalidOid;
|
||||||
|
|
||||||
|
foreach_oid(schemaId, schemaIds)
|
||||||
|
{
|
||||||
|
char *schemaName = get_namespace_name(schemaId);
|
||||||
|
|
||||||
|
PublicationObjSpec *publicationObject = makeNode(PublicationObjSpec);
|
||||||
|
publicationObject->pubobjtype = PUBLICATIONOBJ_TABLES_IN_SCHEMA;
|
||||||
|
publicationObject->pubtable = NULL;
|
||||||
|
publicationObject->name = schemaName;
|
||||||
|
publicationObject->location = -1;
|
||||||
|
|
||||||
|
createPubStmt->pubobjects = lappend(createPubStmt->pubobjects, publicationObject);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
List *relationIds = GetPublicationRelations(publicationId,
|
||||||
|
publicationForm->pubviaroot ?
|
||||||
|
PUBLICATION_PART_ROOT :
|
||||||
|
PUBLICATION_PART_LEAF);
|
||||||
|
Oid relationId = InvalidOid;
|
||||||
|
int citusTableCount PG_USED_FOR_ASSERTS_ONLY = 0;
|
||||||
|
|
||||||
|
/* mainly for consistent ordering in test output */
|
||||||
|
relationIds = SortList(relationIds, CompareOids);
|
||||||
|
|
||||||
|
foreach_oid(relationId, relationIds)
|
||||||
|
{
|
||||||
|
#if (PG_VERSION_NUM >= PG_VERSION_15)
|
||||||
|
bool tableOnly = false;
|
||||||
|
|
||||||
|
/* since postgres 15, tables can have a column list and filter */
|
||||||
|
PublicationObjSpec *publicationObject =
|
||||||
|
BuildPublicationRelationObjSpec(relationId, publicationId, tableOnly);
|
||||||
|
|
||||||
|
createPubStmt->pubobjects = lappend(createPubStmt->pubobjects, publicationObject);
|
||||||
|
#else
|
||||||
|
|
||||||
|
/* before postgres 15, only full tables are supported */
|
||||||
|
char *schemaName = get_namespace_name(get_rel_namespace(relationId));
|
||||||
|
char *tableName = get_rel_name(relationId);
|
||||||
|
RangeVar *rangeVar = makeRangeVar(schemaName, tableName, -1);
|
||||||
|
|
||||||
|
createPubStmt->tables = lappend(createPubStmt->tables, rangeVar);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (IsCitusTable(relationId))
|
||||||
|
{
|
||||||
|
citusTableCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* WITH (publish_via_partition_root = true) option */
|
||||||
|
bool publishViaRoot = publicationForm->pubviaroot;
|
||||||
|
char *publishViaRootString = publishViaRoot ? "true" : "false";
|
||||||
|
DefElem *pubViaRootOption = makeDefElem("publish_via_partition_root",
|
||||||
|
(Node *) makeString(publishViaRootString),
|
||||||
|
-1);
|
||||||
|
createPubStmt->options = lappend(createPubStmt->options, pubViaRootOption);
|
||||||
|
|
||||||
|
/* WITH (publish = 'insert, update, delete, truncate') option */
|
||||||
|
List *publishList = NIL;
|
||||||
|
|
||||||
|
if (publicationForm->pubinsert)
|
||||||
|
{
|
||||||
|
publishList = lappend(publishList, makeString("insert"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (publicationForm->pubupdate)
|
||||||
|
{
|
||||||
|
publishList = lappend(publishList, makeString("update"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (publicationForm->pubdelete)
|
||||||
|
{
|
||||||
|
publishList = lappend(publishList, makeString("delete"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (publicationForm->pubtruncate)
|
||||||
|
{
|
||||||
|
publishList = lappend(publishList, makeString("truncate"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list_length(publishList) > 0)
|
||||||
|
{
|
||||||
|
StringInfo optionValue = makeStringInfo();
|
||||||
|
AppendPublishOptionList(optionValue, publishList);
|
||||||
|
|
||||||
|
DefElem *publishOption = makeDefElem("publish",
|
||||||
|
(Node *) makeString(optionValue->data), -1);
|
||||||
|
createPubStmt->options = lappend(createPubStmt->options, publishOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return createPubStmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendPublishOptionList appends a list of publication options in
|
||||||
|
* comma-separate form.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AppendPublishOptionList(StringInfo str, List *options)
|
||||||
|
{
|
||||||
|
ListCell *stringCell = NULL;
|
||||||
|
foreach(stringCell, options)
|
||||||
|
{
|
||||||
|
const char *string = strVal(lfirst(stringCell));
|
||||||
|
if (stringCell != list_head(options))
|
||||||
|
{
|
||||||
|
appendStringInfoString(str, ", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we cannot escape these strings */
|
||||||
|
appendStringInfoString(str, string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if (PG_VERSION_NUM >= PG_VERSION_15)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BuildPublicationRelationObjSpec returns a PublicationObjSpec that
|
||||||
|
* can be included in a CREATE or ALTER PUBLICATION statement.
|
||||||
|
*/
|
||||||
|
static PublicationObjSpec *
|
||||||
|
BuildPublicationRelationObjSpec(Oid relationId, Oid publicationId,
|
||||||
|
bool tableOnly)
|
||||||
|
{
|
||||||
|
HeapTuple pubRelationTuple = SearchSysCache2(PUBLICATIONRELMAP,
|
||||||
|
ObjectIdGetDatum(relationId),
|
||||||
|
ObjectIdGetDatum(publicationId));
|
||||||
|
if (!HeapTupleIsValid(pubRelationTuple))
|
||||||
|
{
|
||||||
|
ereport(ERROR, (errmsg("cannot find relation with oid %d in publication "
|
||||||
|
"with oid %d", relationId, publicationId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
List *columnNameList = NIL;
|
||||||
|
Node *whereClause = NULL;
|
||||||
|
|
||||||
|
/* build the column list */
|
||||||
|
if (!tableOnly)
|
||||||
|
{
|
||||||
|
bool isNull = false;
|
||||||
|
Datum attributesDatum = SysCacheGetAttr(PUBLICATIONRELMAP, pubRelationTuple,
|
||||||
|
Anum_pg_publication_rel_prattrs,
|
||||||
|
&isNull);
|
||||||
|
if (!isNull)
|
||||||
|
{
|
||||||
|
ArrayType *attributesArray = DatumGetArrayTypeP(attributesDatum);
|
||||||
|
int attributeCount = ARR_DIMS(attributesArray)[0];
|
||||||
|
int16 *elems = (int16 *) ARR_DATA_PTR(attributesArray);
|
||||||
|
|
||||||
|
for (int attNumIndex = 0; attNumIndex < attributeCount; attNumIndex++)
|
||||||
|
{
|
||||||
|
AttrNumber attributeNumber = elems[attNumIndex];
|
||||||
|
char *columnName = get_attname(relationId, attributeNumber, false);
|
||||||
|
|
||||||
|
columnNameList = lappend(columnNameList, makeString(columnName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* build the WHERE clause */
|
||||||
|
Datum whereClauseDatum = SysCacheGetAttr(PUBLICATIONRELMAP, pubRelationTuple,
|
||||||
|
Anum_pg_publication_rel_prqual,
|
||||||
|
&isNull);
|
||||||
|
if (!isNull)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We use the already-transformed parse tree form here, which does
|
||||||
|
* not match regular CreatePublicationStmt
|
||||||
|
*/
|
||||||
|
whereClause = stringToNode(TextDatumGetCString(whereClauseDatum));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReleaseSysCache(pubRelationTuple);
|
||||||
|
|
||||||
|
char *schemaName = get_namespace_name(get_rel_namespace(relationId));
|
||||||
|
char *tableName = get_rel_name(relationId);
|
||||||
|
RangeVar *rangeVar = makeRangeVar(schemaName, tableName, -1);
|
||||||
|
|
||||||
|
/* build the FOR TABLE */
|
||||||
|
PublicationTable *publicationTable =
|
||||||
|
makeNode(PublicationTable);
|
||||||
|
publicationTable->relation = rangeVar;
|
||||||
|
publicationTable->whereClause = whereClause;
|
||||||
|
publicationTable->columns = columnNameList;
|
||||||
|
|
||||||
|
PublicationObjSpec *publicationObject = makeNode(PublicationObjSpec);
|
||||||
|
publicationObject->pubobjtype = PUBLICATIONOBJ_TABLE;
|
||||||
|
publicationObject->pubtable = publicationTable;
|
||||||
|
publicationObject->name = NULL;
|
||||||
|
publicationObject->location = -1;
|
||||||
|
|
||||||
|
return publicationObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PreprocessAlterPublicationStmt handles ALTER PUBLICATION statements
|
||||||
|
* in a way that is mostly similar to PreprocessAlterDistributedObjectStmt,
|
||||||
|
* except we do not ensure sequential mode (publications do not interact with
|
||||||
|
* shards) and can handle NULL deparse commands for ALTER PUBLICATION commands
|
||||||
|
* that only involve local tables.
|
||||||
|
*/
|
||||||
|
List *
|
||||||
|
PreprocessAlterPublicationStmt(Node *stmt, const char *queryString,
|
||||||
|
ProcessUtilityContext processUtilityContext)
|
||||||
|
{
|
||||||
|
List *addresses = GetObjectAddressListFromParseTree(stmt, false, false);
|
||||||
|
|
||||||
|
/* the code-path only supports a single object */
|
||||||
|
Assert(list_length(addresses) == 1);
|
||||||
|
|
||||||
|
if (!ShouldPropagateAnyObject(addresses))
|
||||||
|
{
|
||||||
|
return NIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnsureCoordinator();
|
||||||
|
QualifyTreeNode(stmt);
|
||||||
|
|
||||||
|
const char *sql = DeparseTreeNode((Node *) stmt);
|
||||||
|
if (sql == NULL)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Deparsing logic decided that there is nothing to propagate, e.g.
|
||||||
|
* because the command only concerns local tables.
|
||||||
|
*/
|
||||||
|
return NIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
List *commands = list_make3(DISABLE_DDL_PROPAGATION,
|
||||||
|
(void *) sql,
|
||||||
|
ENABLE_DDL_PROPAGATION);
|
||||||
|
|
||||||
|
return NodeDDLTaskList(NON_COORDINATOR_NODES, commands);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GetAlterPublicationDDLCommandsForTable gets a list of ALTER PUBLICATION .. ADD/DROP
|
||||||
|
* commands for the given table.
|
||||||
|
*
|
||||||
|
* If isAdd is true, it return ALTER PUBLICATION .. ADD TABLE commands for all
|
||||||
|
* publications.
|
||||||
|
*
|
||||||
|
* Otherwise, it returns ALTER PUBLICATION .. DROP TABLE commands for all
|
||||||
|
* publications.
|
||||||
|
*/
|
||||||
|
List *
|
||||||
|
GetAlterPublicationDDLCommandsForTable(Oid relationId, bool isAdd)
|
||||||
|
{
|
||||||
|
List *commands = NIL;
|
||||||
|
|
||||||
|
List *publicationIds = GetRelationPublications(relationId);
|
||||||
|
Oid publicationId = InvalidOid;
|
||||||
|
|
||||||
|
foreach_oid(publicationId, publicationIds)
|
||||||
|
{
|
||||||
|
char *command = GetAlterPublicationTableDDLCommand(publicationId,
|
||||||
|
relationId, isAdd);
|
||||||
|
|
||||||
|
commands = lappend(commands, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
return commands;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GetAlterPublicationTableDDLCommand generates an ALTer PUBLICATION .. ADD/DROP TABLE
|
||||||
|
* command for the given publication and relation ID.
|
||||||
|
*
|
||||||
|
* If isAdd is true, it return an ALTER PUBLICATION .. ADD TABLE command.
|
||||||
|
* Otherwise, it returns ALTER PUBLICATION .. DROP TABLE command.
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
GetAlterPublicationTableDDLCommand(Oid publicationId, Oid relationId,
|
||||||
|
bool isAdd)
|
||||||
|
{
|
||||||
|
HeapTuple pubTuple = SearchSysCache1(PUBLICATIONOID,
|
||||||
|
ObjectIdGetDatum(publicationId));
|
||||||
|
if (!HeapTupleIsValid(pubTuple))
|
||||||
|
{
|
||||||
|
ereport(ERROR, (errmsg("cannot find publication with oid: %d",
|
||||||
|
publicationId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Form_pg_publication pubForm = (Form_pg_publication) GETSTRUCT(pubTuple);
|
||||||
|
|
||||||
|
AlterPublicationStmt *alterPubStmt = makeNode(AlterPublicationStmt);
|
||||||
|
alterPubStmt->pubname = pstrdup(NameStr(pubForm->pubname));
|
||||||
|
|
||||||
|
ReleaseSysCache(pubTuple);
|
||||||
|
|
||||||
|
#if (PG_VERSION_NUM >= PG_VERSION_15)
|
||||||
|
bool tableOnly = !isAdd;
|
||||||
|
|
||||||
|
/* since postgres 15, tables can have a column list and filter */
|
||||||
|
PublicationObjSpec *publicationObject =
|
||||||
|
BuildPublicationRelationObjSpec(relationId, publicationId, tableOnly);
|
||||||
|
|
||||||
|
alterPubStmt->pubobjects = lappend(alterPubStmt->pubobjects, publicationObject);
|
||||||
|
alterPubStmt->action = isAdd ? AP_AddObjects : AP_DropObjects;
|
||||||
|
#else
|
||||||
|
|
||||||
|
/* before postgres 15, only full tables are supported */
|
||||||
|
char *schemaName = get_namespace_name(get_rel_namespace(relationId));
|
||||||
|
char *tableName = get_rel_name(relationId);
|
||||||
|
RangeVar *rangeVar = makeRangeVar(schemaName, tableName, -1);
|
||||||
|
|
||||||
|
alterPubStmt->tables = lappend(alterPubStmt->tables, rangeVar);
|
||||||
|
alterPubStmt->tableAction = isAdd ? DEFELEM_ADD : DEFELEM_DROP;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* we take the WHERE clause from the catalog where it is already transformed */
|
||||||
|
bool whereClauseNeedsTransform = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We use these commands to restore publications before/after transforming a
|
||||||
|
* table, including transformations to/from local tables.
|
||||||
|
*/
|
||||||
|
bool includeLocalTables = true;
|
||||||
|
|
||||||
|
char *command = DeparseAlterPublicationStmtExtended((Node *) alterPubStmt,
|
||||||
|
whereClauseNeedsTransform,
|
||||||
|
includeLocalTables);
|
||||||
|
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AlterPublicationOwnerCommand returns "ALTER PUBLICATION .. OWNER TO .."
|
||||||
|
* statement for the specified publication.
|
||||||
|
*/
|
||||||
|
static char *
|
||||||
|
AlterPublicationOwnerCommand(Oid publicationId)
|
||||||
|
{
|
||||||
|
HeapTuple publicationTuple =
|
||||||
|
SearchSysCache1(PUBLICATIONOID, ObjectIdGetDatum(publicationId));
|
||||||
|
|
||||||
|
if (!HeapTupleIsValid(publicationTuple))
|
||||||
|
{
|
||||||
|
ereport(ERROR, (errmsg("cannot find publication with oid: %d",
|
||||||
|
publicationId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Form_pg_publication publicationForm =
|
||||||
|
(Form_pg_publication) GETSTRUCT(publicationTuple);
|
||||||
|
|
||||||
|
char *publicationName = NameStr(publicationForm->pubname);
|
||||||
|
Oid publicationOwnerId = publicationForm->pubowner;
|
||||||
|
|
||||||
|
char *publicationOwnerName = GetUserNameFromId(publicationOwnerId, false);
|
||||||
|
|
||||||
|
StringInfo alterCommand = makeStringInfo();
|
||||||
|
appendStringInfo(alterCommand, "ALTER PUBLICATION %s OWNER TO %s",
|
||||||
|
quote_identifier(publicationName),
|
||||||
|
quote_identifier(publicationOwnerName));
|
||||||
|
|
||||||
|
ReleaseSysCache(publicationTuple);
|
||||||
|
|
||||||
|
return alterCommand->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ShouldPropagateCreatePublication tests if we need to propagate a CREATE PUBLICATION
|
||||||
|
* statement.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
ShouldPropagateCreatePublication(CreatePublicationStmt *stmt)
|
||||||
|
{
|
||||||
|
if (!ShouldPropagate())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ShouldPropagateCreateInCoordinatedTransction())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AlterPublicationStmtObjectAddress generates the object address for the
|
||||||
|
* publication altered by a regular ALTER PUBLICATION .. statement.
|
||||||
|
*/
|
||||||
|
List *
|
||||||
|
AlterPublicationStmtObjectAddress(Node *node, bool missingOk, bool isPostProcess)
|
||||||
|
{
|
||||||
|
AlterPublicationStmt *stmt = castNode(AlterPublicationStmt, node);
|
||||||
|
|
||||||
|
return ObjectAddressForPublicationName(stmt->pubname, missingOk);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AlterPublicationOwnerStmtObjectAddress generates the object address for the
|
||||||
|
* publication altered by the given ALTER PUBLICATION .. OWNER TO statement.
|
||||||
|
*/
|
||||||
|
List *
|
||||||
|
AlterPublicationOwnerStmtObjectAddress(Node *node, bool missingOk, bool isPostProcess)
|
||||||
|
{
|
||||||
|
AlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);
|
||||||
|
|
||||||
|
return ObjectAddressForPublicationName(strVal(stmt->object), missingOk);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CreatePublicationStmtObjectAddress generates the object address for the
|
||||||
|
* publication created by the given CREATE PUBLICATION statement.
|
||||||
|
*/
|
||||||
|
List *
|
||||||
|
CreatePublicationStmtObjectAddress(Node *node, bool missingOk, bool isPostProcess)
|
||||||
|
{
|
||||||
|
CreatePublicationStmt *stmt = castNode(CreatePublicationStmt, node);
|
||||||
|
|
||||||
|
return ObjectAddressForPublicationName(stmt->pubname, missingOk);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RenamePublicationStmtObjectAddress generates the object address for the
|
||||||
|
* publication altered by the given ALter PUBLICATION .. RENAME TO statement.
|
||||||
|
*/
|
||||||
|
List *
|
||||||
|
RenamePublicationStmtObjectAddress(Node *node, bool missingOk, bool isPostprocess)
|
||||||
|
{
|
||||||
|
RenameStmt *stmt = castNode(RenameStmt, node);
|
||||||
|
|
||||||
|
return ObjectAddressForPublicationName(strVal(stmt->object), missingOk);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ObjectAddressForPublicationName returns the object address for a given publication
|
||||||
|
* name.
|
||||||
|
*/
|
||||||
|
static List *
|
||||||
|
ObjectAddressForPublicationName(char *publicationName, bool missingOk)
|
||||||
|
{
|
||||||
|
Oid publicationId = InvalidOid;
|
||||||
|
|
||||||
|
HeapTuple publicationTuple =
|
||||||
|
SearchSysCache1(PUBLICATIONNAME, CStringGetDatum(publicationName));
|
||||||
|
if (HeapTupleIsValid(publicationTuple))
|
||||||
|
{
|
||||||
|
Form_pg_publication publicationForm =
|
||||||
|
(Form_pg_publication) GETSTRUCT(publicationTuple);
|
||||||
|
publicationId = publicationForm->oid;
|
||||||
|
|
||||||
|
ReleaseSysCache(publicationTuple);
|
||||||
|
}
|
||||||
|
else if (!missingOk)
|
||||||
|
{
|
||||||
|
/* it should have just been created */
|
||||||
|
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||||
|
errmsg("publication \"%s\" does not exist", publicationName)));
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectAddress *address = palloc0(sizeof(ObjectAddress));
|
||||||
|
ObjectAddressSet(*address, PublicationRelationId, publicationId);
|
||||||
|
|
||||||
|
return list_make1(address);
|
||||||
|
}
|
|
@ -0,0 +1,690 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* deparse_publication_stmts.c
|
||||||
|
* All routines to deparse publication statements.
|
||||||
|
*
|
||||||
|
* Copyright (c) Citus Data, Inc.
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "access/relation.h"
|
||||||
|
#include "catalog/namespace.h"
|
||||||
|
#include "commands/defrem.h"
|
||||||
|
#include "distributed/citus_ruleutils.h"
|
||||||
|
#include "distributed/deparser.h"
|
||||||
|
#include "distributed/listutils.h"
|
||||||
|
#include "distributed/namespace_utils.h"
|
||||||
|
#include "lib/stringinfo.h"
|
||||||
|
#include "parser/parse_clause.h"
|
||||||
|
#include "parser/parse_collate.h"
|
||||||
|
#include "parser/parse_node.h"
|
||||||
|
#include "parser/parse_relation.h"
|
||||||
|
#include "nodes/value.h"
|
||||||
|
#include "utils/builtins.h"
|
||||||
|
#include "utils/lsyscache.h"
|
||||||
|
#include "utils/ruleutils.h"
|
||||||
|
|
||||||
|
|
||||||
|
static void AppendCreatePublicationStmt(StringInfo buf, CreatePublicationStmt *stmt,
|
||||||
|
bool whereClauseNeedsTransform,
|
||||||
|
bool includeLocalTables);
|
||||||
|
#if (PG_VERSION_NUM >= PG_VERSION_15)
|
||||||
|
static bool AppendPublicationObjects(StringInfo buf, List *publicationObjects,
|
||||||
|
bool whereClauseNeedsTransform,
|
||||||
|
bool includeLocalTables);
|
||||||
|
static void AppendWhereClauseExpression(StringInfo buf, RangeVar *tableName,
|
||||||
|
Node *whereClause,
|
||||||
|
bool whereClauseNeedsTransform);
|
||||||
|
static void AppendAlterPublicationAction(StringInfo buf, AlterPublicationAction action);
|
||||||
|
#else
|
||||||
|
static bool AppendTables(StringInfo buf, List *tables, bool includeLocalTables);
|
||||||
|
static void AppendDefElemAction(StringInfo buf, DefElemAction action);
|
||||||
|
#endif
|
||||||
|
static bool AppendAlterPublicationStmt(StringInfo buf, AlterPublicationStmt *stmt,
|
||||||
|
bool whereClauseNeedsTransform,
|
||||||
|
bool includeLocalTables);
|
||||||
|
static void AppendDropPublicationStmt(StringInfo buf, DropStmt *stmt);
|
||||||
|
static void AppendRenamePublicationStmt(StringInfo buf, RenameStmt *stmt);
|
||||||
|
static void AppendAlterPublicationOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt);
|
||||||
|
static void AppendPublicationOptions(StringInfo stringBuffer, List *optionList);
|
||||||
|
static void AppendIdentifierList(StringInfo buf, List *objects);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DeparseCreatePublicationStmt builds and returns a string representing a
|
||||||
|
* CreatePublicationStmt.
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
DeparseCreatePublicationStmt(Node *node)
|
||||||
|
{
|
||||||
|
/* regular deparsing function takes CREATE PUBLICATION from the parser */
|
||||||
|
bool whereClauseNeedsTransform = false;
|
||||||
|
|
||||||
|
/* for regular CREATE PUBLICATION we do not propagate local tables */
|
||||||
|
bool includeLocalTables = false;
|
||||||
|
|
||||||
|
return DeparseCreatePublicationStmtExtended(node, whereClauseNeedsTransform,
|
||||||
|
includeLocalTables);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DeparseCreatePublicationStmtExtended builds and returns a string representing a
|
||||||
|
* CreatePublicationStmt, which may have already-transformed expressions.
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
DeparseCreatePublicationStmtExtended(Node *node, bool whereClauseNeedsTransform,
|
||||||
|
bool includeLocalTables)
|
||||||
|
{
|
||||||
|
CreatePublicationStmt *stmt = castNode(CreatePublicationStmt, node);
|
||||||
|
|
||||||
|
StringInfoData str = { 0 };
|
||||||
|
initStringInfo(&str);
|
||||||
|
|
||||||
|
AppendCreatePublicationStmt(&str, stmt, whereClauseNeedsTransform,
|
||||||
|
includeLocalTables);
|
||||||
|
|
||||||
|
return str.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendCreatePublicationStmt appends a string representing a
|
||||||
|
* CreatePublicationStmt to a buffer.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AppendCreatePublicationStmt(StringInfo buf, CreatePublicationStmt *stmt,
|
||||||
|
bool whereClauseNeedsTransform,
|
||||||
|
bool includeLocalTables)
|
||||||
|
{
|
||||||
|
appendStringInfo(buf, "CREATE PUBLICATION %s",
|
||||||
|
quote_identifier(stmt->pubname));
|
||||||
|
|
||||||
|
if (stmt->for_all_tables)
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, " FOR ALL TABLES");
|
||||||
|
}
|
||||||
|
#if (PG_VERSION_NUM >= PG_VERSION_15)
|
||||||
|
else if (stmt->pubobjects != NIL)
|
||||||
|
{
|
||||||
|
bool hasObjects = false;
|
||||||
|
PublicationObjSpec *publicationObject = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check whether there are objects to propagate, mainly to know whether
|
||||||
|
* we should include "FOR".
|
||||||
|
*/
|
||||||
|
foreach_ptr(publicationObject, stmt->pubobjects)
|
||||||
|
{
|
||||||
|
if (publicationObject->pubobjtype == PUBLICATIONOBJ_TABLE)
|
||||||
|
{
|
||||||
|
/* FOR TABLE ... */
|
||||||
|
PublicationTable *publicationTable = publicationObject->pubtable;
|
||||||
|
|
||||||
|
if (includeLocalTables ||
|
||||||
|
IsCitusTableRangeVar(publicationTable->relation, NoLock, false))
|
||||||
|
{
|
||||||
|
hasObjects = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
hasObjects = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasObjects)
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, " FOR");
|
||||||
|
AppendPublicationObjects(buf, stmt->pubobjects, whereClauseNeedsTransform,
|
||||||
|
includeLocalTables);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
else if (stmt->tables != NIL)
|
||||||
|
{
|
||||||
|
bool hasTables = false;
|
||||||
|
RangeVar *rangeVar = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check whether there are tables to propagate, mainly to know whether
|
||||||
|
* we should include "FOR".
|
||||||
|
*/
|
||||||
|
foreach_ptr(rangeVar, stmt->tables)
|
||||||
|
{
|
||||||
|
if (includeLocalTables || IsCitusTableRangeVar(rangeVar, NoLock, false))
|
||||||
|
{
|
||||||
|
hasTables = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasTables)
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, " FOR");
|
||||||
|
AppendTables(buf, stmt->tables, includeLocalTables);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (stmt->options != NIL)
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, " WITH (");
|
||||||
|
AppendPublicationOptions(buf, stmt->options);
|
||||||
|
appendStringInfoString(buf, ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if (PG_VERSION_NUM >= PG_VERSION_15)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendPublicationObjects appends a string representing a list of publication
|
||||||
|
* objects to a buffer.
|
||||||
|
*
|
||||||
|
* For instance: TABLE users, departments, TABLES IN SCHEMA production
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
AppendPublicationObjects(StringInfo buf, List *publicationObjects,
|
||||||
|
bool whereClauseNeedsTransform,
|
||||||
|
bool includeLocalTables)
|
||||||
|
{
|
||||||
|
PublicationObjSpec *publicationObject = NULL;
|
||||||
|
bool appendedObject = false;
|
||||||
|
|
||||||
|
foreach_ptr(publicationObject, publicationObjects)
|
||||||
|
{
|
||||||
|
if (publicationObject->pubobjtype == PUBLICATIONOBJ_TABLE)
|
||||||
|
{
|
||||||
|
/* FOR TABLE ... */
|
||||||
|
PublicationTable *publicationTable = publicationObject->pubtable;
|
||||||
|
RangeVar *rangeVar = publicationTable->relation;
|
||||||
|
char *schemaName = rangeVar->schemaname;
|
||||||
|
char *tableName = rangeVar->relname;
|
||||||
|
|
||||||
|
if (!includeLocalTables && !IsCitusTableRangeVar(rangeVar, NoLock, false))
|
||||||
|
{
|
||||||
|
/* do not propagate local tables */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (schemaName != NULL)
|
||||||
|
{
|
||||||
|
/* qualified table name */
|
||||||
|
appendStringInfo(buf, "%s TABLE %s",
|
||||||
|
appendedObject ? "," : "",
|
||||||
|
quote_qualified_identifier(schemaName, tableName));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* unqualified table name */
|
||||||
|
appendStringInfo(buf, "%s TABLE %s",
|
||||||
|
appendedObject ? "," : "",
|
||||||
|
quote_identifier(tableName));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (publicationTable->columns != NIL)
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, " (");
|
||||||
|
AppendIdentifierList(buf, publicationTable->columns);
|
||||||
|
appendStringInfoString(buf, ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (publicationTable->whereClause != NULL)
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, " WHERE (");
|
||||||
|
|
||||||
|
AppendWhereClauseExpression(buf, rangeVar,
|
||||||
|
publicationTable->whereClause,
|
||||||
|
whereClauseNeedsTransform);
|
||||||
|
|
||||||
|
appendStringInfoString(buf, ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* FOR TABLES IN SCHEMA */
|
||||||
|
char *schemaName = publicationObject->name;
|
||||||
|
|
||||||
|
if (publicationObject->pubobjtype == PUBLICATIONOBJ_TABLES_IN_CUR_SCHEMA)
|
||||||
|
{
|
||||||
|
List *searchPath = fetch_search_path(false);
|
||||||
|
if (searchPath == NIL)
|
||||||
|
{
|
||||||
|
ereport(ERROR, errcode(ERRCODE_UNDEFINED_SCHEMA),
|
||||||
|
errmsg("no schema has been selected for "
|
||||||
|
"CURRENT_SCHEMA"));
|
||||||
|
}
|
||||||
|
|
||||||
|
schemaName = get_namespace_name(linitial_oid(searchPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
appendStringInfo(buf, "%s TABLES IN SCHEMA %s",
|
||||||
|
appendedObject ? "," : "",
|
||||||
|
quote_identifier(schemaName));
|
||||||
|
}
|
||||||
|
|
||||||
|
appendedObject = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return appendedObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendWhereClauseExpression appends a deparsed expression that can
|
||||||
|
* contain a filter on the given table. If whereClauseNeedsTransform is set
|
||||||
|
* the expression is first tranformed.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AppendWhereClauseExpression(StringInfo buf, RangeVar *tableName,
|
||||||
|
Node *whereClause, bool whereClauseNeedsTransform)
|
||||||
|
{
|
||||||
|
Relation relation = relation_openrv(tableName, AccessShareLock);
|
||||||
|
|
||||||
|
if (whereClauseNeedsTransform)
|
||||||
|
{
|
||||||
|
ParseState *pstate = make_parsestate(NULL);
|
||||||
|
pstate->p_sourcetext = "";
|
||||||
|
ParseNamespaceItem *nsitem = addRangeTableEntryForRelation(pstate,
|
||||||
|
relation,
|
||||||
|
AccessShareLock, NULL,
|
||||||
|
false, false);
|
||||||
|
addNSItemToQuery(pstate, nsitem, false, true, true);
|
||||||
|
|
||||||
|
whereClause = transformWhereClause(pstate,
|
||||||
|
copyObject(whereClause),
|
||||||
|
EXPR_KIND_WHERE,
|
||||||
|
"PUBLICATION WHERE");
|
||||||
|
|
||||||
|
assign_expr_collations(pstate, whereClause);
|
||||||
|
}
|
||||||
|
|
||||||
|
List *relationContext = deparse_context_for(tableName->relname, relation->rd_id);
|
||||||
|
|
||||||
|
PushOverrideEmptySearchPath(CurrentMemoryContext);
|
||||||
|
char *whereClauseString = deparse_expression(whereClause,
|
||||||
|
relationContext,
|
||||||
|
true, true);
|
||||||
|
PopOverrideSearchPath();
|
||||||
|
|
||||||
|
appendStringInfoString(buf, whereClauseString);
|
||||||
|
|
||||||
|
relation_close(relation, AccessShareLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendPublicationObjects appends a string representing a list of publication
|
||||||
|
* objects to a buffer.
|
||||||
|
*
|
||||||
|
* For instance: TABLE users, departments
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
AppendTables(StringInfo buf, List *tables, bool includeLocalTables)
|
||||||
|
{
|
||||||
|
RangeVar *rangeVar = NULL;
|
||||||
|
bool appendedObject = false;
|
||||||
|
|
||||||
|
foreach_ptr(rangeVar, tables)
|
||||||
|
{
|
||||||
|
if (!includeLocalTables &&
|
||||||
|
!IsCitusTableRangeVar(rangeVar, NoLock, false))
|
||||||
|
{
|
||||||
|
/* do not propagate local tables */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *schemaName = rangeVar->schemaname;
|
||||||
|
char *tableName = rangeVar->relname;
|
||||||
|
|
||||||
|
if (schemaName != NULL)
|
||||||
|
{
|
||||||
|
/* qualified table name */
|
||||||
|
appendStringInfo(buf, "%s %s",
|
||||||
|
appendedObject ? "," : " TABLE",
|
||||||
|
quote_qualified_identifier(schemaName, tableName));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* unqualified table name */
|
||||||
|
appendStringInfo(buf, "%s %s",
|
||||||
|
appendedObject ? "," : " TABLE",
|
||||||
|
quote_identifier(tableName));
|
||||||
|
}
|
||||||
|
|
||||||
|
appendedObject = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return appendedObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DeparseAlterPublicationSchemaStmt builds and returns a string representing
|
||||||
|
* an AlterPublicationStmt.
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
DeparseAlterPublicationStmt(Node *node)
|
||||||
|
{
|
||||||
|
/* regular deparsing function takes ALTER PUBLICATION from the parser */
|
||||||
|
bool whereClauseNeedsTransform = true;
|
||||||
|
|
||||||
|
/* for regular ALTER PUBLICATION we do not propagate local tables */
|
||||||
|
bool includeLocalTables = false;
|
||||||
|
|
||||||
|
return DeparseAlterPublicationStmtExtended(node, whereClauseNeedsTransform,
|
||||||
|
includeLocalTables);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DeparseAlterPublicationStmtExtended builds and returns a string representing a
|
||||||
|
* AlterPublicationStmt, which may have already-transformed expressions.
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
DeparseAlterPublicationStmtExtended(Node *node, bool whereClauseNeedsTransform,
|
||||||
|
bool includeLocalTables)
|
||||||
|
{
|
||||||
|
AlterPublicationStmt *stmt = castNode(AlterPublicationStmt, node);
|
||||||
|
StringInfoData str = { 0 };
|
||||||
|
initStringInfo(&str);
|
||||||
|
|
||||||
|
if (!AppendAlterPublicationStmt(&str, stmt, whereClauseNeedsTransform,
|
||||||
|
includeLocalTables))
|
||||||
|
{
|
||||||
|
Assert(!includeLocalTables);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When there are no objects to propagate, then there is no
|
||||||
|
* valid ALTER PUBLICATION to construct.
|
||||||
|
*/
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendAlterPublicationStmt appends a string representing an AlterPublicationStmt
|
||||||
|
* of the form ALTER PUBLICATION .. ADD/SET/DROP
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
AppendAlterPublicationStmt(StringInfo buf, AlterPublicationStmt *stmt,
|
||||||
|
bool whereClauseNeedsTransform,
|
||||||
|
bool includeLocalTables)
|
||||||
|
{
|
||||||
|
appendStringInfo(buf, "ALTER PUBLICATION %s",
|
||||||
|
quote_identifier(stmt->pubname));
|
||||||
|
|
||||||
|
if (stmt->options)
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, " SET (");
|
||||||
|
AppendPublicationOptions(buf, stmt->options);
|
||||||
|
appendStringInfoString(buf, ")");
|
||||||
|
|
||||||
|
/* changing options cannot be combined with other actions */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (PG_VERSION_NUM >= PG_VERSION_15)
|
||||||
|
AppendAlterPublicationAction(buf, stmt->action);
|
||||||
|
return AppendPublicationObjects(buf, stmt->pubobjects, whereClauseNeedsTransform,
|
||||||
|
includeLocalTables);
|
||||||
|
#else
|
||||||
|
AppendDefElemAction(buf, stmt->tableAction);
|
||||||
|
return AppendTables(buf, stmt->tables, includeLocalTables);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if (PG_VERSION_NUM >= PG_VERSION_15)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendAlterPublicationAction appends a string representing an AlterPublicationAction
|
||||||
|
* to a buffer.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AppendAlterPublicationAction(StringInfo buf, AlterPublicationAction action)
|
||||||
|
{
|
||||||
|
switch (action)
|
||||||
|
{
|
||||||
|
case AP_AddObjects:
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, " ADD");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AP_DropObjects:
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, " DROP");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case AP_SetObjects:
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, " SET");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
ereport(ERROR, (errmsg("unrecognized publication action: %d", action)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendDefElemAction appends a string representing a DefElemAction
|
||||||
|
* to a buffer.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AppendDefElemAction(StringInfo buf, DefElemAction action)
|
||||||
|
{
|
||||||
|
switch (action)
|
||||||
|
{
|
||||||
|
case DEFELEM_ADD:
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, " ADD");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DEFELEM_DROP:
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, " DROP");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DEFELEM_SET:
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, " SET");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
ereport(ERROR, (errmsg("unrecognized publication action: %d", action)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DeparseDropPublicationStmt builds and returns a string representing the DropStmt
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
DeparseDropPublicationStmt(Node *node)
|
||||||
|
{
|
||||||
|
DropStmt *stmt = castNode(DropStmt, node);
|
||||||
|
StringInfoData str = { 0 };
|
||||||
|
initStringInfo(&str);
|
||||||
|
|
||||||
|
Assert(stmt->removeType == OBJECT_PUBLICATION);
|
||||||
|
|
||||||
|
AppendDropPublicationStmt(&str, stmt);
|
||||||
|
|
||||||
|
return str.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendDropPublicationStmt appends a string representing the DropStmt to a buffer
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AppendDropPublicationStmt(StringInfo buf, DropStmt *stmt)
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, "DROP PUBLICATION ");
|
||||||
|
if (stmt->missing_ok)
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, "IF EXISTS ");
|
||||||
|
}
|
||||||
|
AppendIdentifierList(buf, stmt->objects);
|
||||||
|
if (stmt->behavior == DROP_CASCADE)
|
||||||
|
{
|
||||||
|
appendStringInfoString(buf, " CASCADE");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DeparseRenamePublicationStmt builds and returns a string representing the RenameStmt
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
DeparseRenamePublicationStmt(Node *node)
|
||||||
|
{
|
||||||
|
RenameStmt *stmt = castNode(RenameStmt, node);
|
||||||
|
StringInfoData str = { 0 };
|
||||||
|
initStringInfo(&str);
|
||||||
|
|
||||||
|
Assert(stmt->renameType == OBJECT_PUBLICATION);
|
||||||
|
|
||||||
|
AppendRenamePublicationStmt(&str, stmt);
|
||||||
|
|
||||||
|
return str.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendRenamePublicationStmt appends a string representing the RenameStmt to a buffer
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AppendRenamePublicationStmt(StringInfo buf, RenameStmt *stmt)
|
||||||
|
{
|
||||||
|
appendStringInfo(buf, "ALTER PUBLICATION %s RENAME TO %s;",
|
||||||
|
quote_identifier(strVal(stmt->object)),
|
||||||
|
quote_identifier(stmt->newname));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DeparseAlterPublicationOwnerStmt builds and returns a string representing the AlterOwnerStmt
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
DeparseAlterPublicationOwnerStmt(Node *node)
|
||||||
|
{
|
||||||
|
AlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node);
|
||||||
|
StringInfoData str = { 0 };
|
||||||
|
initStringInfo(&str);
|
||||||
|
|
||||||
|
Assert(stmt->objectType == OBJECT_PUBLICATION);
|
||||||
|
|
||||||
|
AppendAlterPublicationOwnerStmt(&str, stmt);
|
||||||
|
|
||||||
|
return str.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendAlterPublicationOwnerStmt appends a string representing the AlterOwnerStmt to a buffer
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AppendAlterPublicationOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt)
|
||||||
|
{
|
||||||
|
Assert(stmt->objectType == OBJECT_PUBLICATION);
|
||||||
|
|
||||||
|
appendStringInfo(buf, "ALTER PUBLICATION %s OWNER TO %s;",
|
||||||
|
quote_identifier(strVal(stmt->object)),
|
||||||
|
RoleSpecString(stmt->newowner, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendPublicationOptions appends a string representing a list of publication opions.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AppendPublicationOptions(StringInfo stringBuffer, List *optionList)
|
||||||
|
{
|
||||||
|
ListCell *optionCell = NULL;
|
||||||
|
bool firstOptionPrinted = false;
|
||||||
|
|
||||||
|
foreach(optionCell, optionList)
|
||||||
|
{
|
||||||
|
DefElem *option = (DefElem *) lfirst(optionCell);
|
||||||
|
char *optionName = option->defname;
|
||||||
|
char *optionValue = defGetString(option);
|
||||||
|
NodeTag valueType = nodeTag(option->arg);
|
||||||
|
|
||||||
|
if (firstOptionPrinted)
|
||||||
|
{
|
||||||
|
appendStringInfo(stringBuffer, ", ");
|
||||||
|
}
|
||||||
|
firstOptionPrinted = true;
|
||||||
|
|
||||||
|
appendStringInfo(stringBuffer, "%s = ",
|
||||||
|
quote_identifier(optionName));
|
||||||
|
|
||||||
|
#if (PG_VERSION_NUM >= PG_VERSION_15)
|
||||||
|
if (valueType == T_Integer || valueType == T_Float || valueType == T_Boolean)
|
||||||
|
#else
|
||||||
|
if (valueType == T_Integer || valueType == T_Float)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
/* string escaping is unnecessary for numeric types and can cause issues */
|
||||||
|
appendStringInfo(stringBuffer, "%s", optionValue);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
appendStringInfo(stringBuffer, "%s", quote_literal_cstr(optionValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AppendIdentifierList appends a string representing a list of
|
||||||
|
* identifiers (of String type).
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AppendIdentifierList(StringInfo buf, List *objects)
|
||||||
|
{
|
||||||
|
ListCell *objectCell = NULL;
|
||||||
|
|
||||||
|
foreach(objectCell, objects)
|
||||||
|
{
|
||||||
|
char *name = strVal(lfirst(objectCell));
|
||||||
|
|
||||||
|
if (objectCell != list_head(objects))
|
||||||
|
{
|
||||||
|
appendStringInfo(buf, ", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
appendStringInfoString(buf, quote_identifier(name));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* qualify_publication_stmt.c
|
||||||
|
* Functions specialized in fully qualifying all publication statements. These
|
||||||
|
* functions are dispatched from qualify.c
|
||||||
|
*
|
||||||
|
* Copyright (c), Citus Data, Inc.
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "catalog/namespace.h"
|
||||||
|
#include "distributed/deparser.h"
|
||||||
|
#include "distributed/listutils.h"
|
||||||
|
#include "nodes/nodes.h"
|
||||||
|
#include "utils/guc.h"
|
||||||
|
#include "utils/lsyscache.h"
|
||||||
|
|
||||||
|
#if (PG_VERSION_NUM >= PG_VERSION_15)
|
||||||
|
static void QualifyPublicationObjects(List *publicationObjects);
|
||||||
|
#else
|
||||||
|
static void QualifyTables(List *tables);
|
||||||
|
#endif
|
||||||
|
static void QualifyPublicationRangeVar(RangeVar *publication);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* QualifyCreatePublicationStmt quailifies the publication names of the
|
||||||
|
* CREATE PUBLICATION statement.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
QualifyCreatePublicationStmt(Node *node)
|
||||||
|
{
|
||||||
|
CreatePublicationStmt *stmt = castNode(CreatePublicationStmt, node);
|
||||||
|
|
||||||
|
#if (PG_VERSION_NUM >= PG_VERSION_15)
|
||||||
|
QualifyPublicationObjects(stmt->pubobjects);
|
||||||
|
#else
|
||||||
|
QualifyTables(stmt->tables);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if (PG_VERSION_NUM >= PG_VERSION_15)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* QualifyPublicationObjects ensures all table names in a list of
|
||||||
|
* publication objects are fully qualified.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
QualifyPublicationObjects(List *publicationObjects)
|
||||||
|
{
|
||||||
|
PublicationObjSpec *publicationObject = NULL;
|
||||||
|
|
||||||
|
foreach_ptr(publicationObject, publicationObjects)
|
||||||
|
{
|
||||||
|
if (publicationObject->pubobjtype == PUBLICATIONOBJ_TABLE)
|
||||||
|
{
|
||||||
|
/* FOR TABLE ... */
|
||||||
|
PublicationTable *publicationTable = publicationObject->pubtable;
|
||||||
|
|
||||||
|
QualifyPublicationRangeVar(publicationTable->relation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/*
|
||||||
|
* QualifyTables ensures all table names in a list are fully qualified.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
QualifyTables(List *tables)
|
||||||
|
{
|
||||||
|
RangeVar *rangeVar = NULL;
|
||||||
|
|
||||||
|
foreach_ptr(rangeVar, tables)
|
||||||
|
{
|
||||||
|
QualifyPublicationRangeVar(rangeVar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* QualifyPublicationObjects ensures all table names in a list of
|
||||||
|
* publication objects are fully qualified.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
QualifyAlterPublicationStmt(Node *node)
|
||||||
|
{
|
||||||
|
AlterPublicationStmt *stmt = castNode(AlterPublicationStmt, node);
|
||||||
|
|
||||||
|
#if (PG_VERSION_NUM >= PG_VERSION_15)
|
||||||
|
QualifyPublicationObjects(stmt->pubobjects);
|
||||||
|
#else
|
||||||
|
QualifyTables(stmt->tables);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* QualifyPublicationRangeVar qualifies the given publication RangeVar if it is not qualified.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
QualifyPublicationRangeVar(RangeVar *publication)
|
||||||
|
{
|
||||||
|
if (publication->schemaname == NULL)
|
||||||
|
{
|
||||||
|
Oid publicationOid = RelnameGetRelid(publication->relname);
|
||||||
|
Oid schemaOid = get_rel_namespace(publicationOid);
|
||||||
|
publication->schemaname = get_namespace_name(schemaOid);
|
||||||
|
}
|
||||||
|
}
|
|
@ -802,6 +802,11 @@ GetObjectTypeString(ObjectType objType)
|
||||||
return "function";
|
return "function";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case OBJECT_PUBLICATION:
|
||||||
|
{
|
||||||
|
return "publication";
|
||||||
|
}
|
||||||
|
|
||||||
case OBJECT_SCHEMA:
|
case OBJECT_SCHEMA:
|
||||||
{
|
{
|
||||||
return "schema";
|
return "schema";
|
||||||
|
|
|
@ -132,6 +132,7 @@ typedef struct ViewDependencyNode
|
||||||
static List * GetRelationSequenceDependencyList(Oid relationId);
|
static List * GetRelationSequenceDependencyList(Oid relationId);
|
||||||
static List * GetRelationFunctionDependencyList(Oid relationId);
|
static List * GetRelationFunctionDependencyList(Oid relationId);
|
||||||
static List * GetRelationTriggerFunctionDependencyList(Oid relationId);
|
static List * GetRelationTriggerFunctionDependencyList(Oid relationId);
|
||||||
|
static List * GetPublicationRelationsDependencyList(Oid relationId);
|
||||||
static List * GetRelationStatsSchemaDependencyList(Oid relationId);
|
static List * GetRelationStatsSchemaDependencyList(Oid relationId);
|
||||||
static List * GetRelationIndicesDependencyList(Oid relationId);
|
static List * GetRelationIndicesDependencyList(Oid relationId);
|
||||||
static DependencyDefinition * CreateObjectAddressDependencyDef(Oid classId, Oid objectId);
|
static DependencyDefinition * CreateObjectAddressDependencyDef(Oid classId, Oid objectId);
|
||||||
|
@ -722,6 +723,11 @@ SupportedDependencyByCitus(const ObjectAddress *address)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case OCLASS_PUBLICATION:
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
case OCLASS_TSCONFIG:
|
case OCLASS_TSCONFIG:
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
@ -1656,6 +1662,36 @@ ExpandCitusSupportedTypes(ObjectAddressCollector *collector, ObjectAddress targe
|
||||||
List *ruleRefDepList = GetViewRuleReferenceDependencyList(relationId);
|
List *ruleRefDepList = GetViewRuleReferenceDependencyList(relationId);
|
||||||
result = list_concat(result, ruleRefDepList);
|
result = list_concat(result, ruleRefDepList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PublicationRelationId:
|
||||||
|
{
|
||||||
|
Oid publicationId = target.objectId;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Publications do not depend directly on relations, because dropping
|
||||||
|
* the relation will only remove it from the publications. However,
|
||||||
|
* we add a dependency to ensure the relation is created first when
|
||||||
|
* adding a node.
|
||||||
|
*/
|
||||||
|
List *relationDependencyList =
|
||||||
|
GetPublicationRelationsDependencyList(publicationId);
|
||||||
|
result = list_concat(result, relationDependencyList);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* As of PostgreSQL 15, the same applies to schemas.
|
||||||
|
*/
|
||||||
|
#if PG_VERSION_NUM >= PG_VERSION_15
|
||||||
|
List *schemaIdList =
|
||||||
|
GetPublicationSchemas(publicationId);
|
||||||
|
List *schemaDependencyList =
|
||||||
|
CreateObjectAddressDependencyDefList(NamespaceRelationId, schemaIdList);
|
||||||
|
result = list_concat(result, schemaDependencyList);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -1923,6 +1959,33 @@ GetRelationTriggerFunctionDependencyList(Oid relationId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GetPublicationRelationsDependencyList creates a list of ObjectAddressDependencies for
|
||||||
|
* a publication on the Citus relations it contains. This helps make sure we distribute
|
||||||
|
* Citus tables before local tables.
|
||||||
|
*/
|
||||||
|
static List *
|
||||||
|
GetPublicationRelationsDependencyList(Oid publicationId)
|
||||||
|
{
|
||||||
|
List *allRelationIds = GetPublicationRelations(publicationId, PUBLICATION_PART_ROOT);
|
||||||
|
List *citusRelationIds = NIL;
|
||||||
|
|
||||||
|
Oid relationId = InvalidOid;
|
||||||
|
|
||||||
|
foreach_oid(relationId, allRelationIds)
|
||||||
|
{
|
||||||
|
if (!IsCitusTable(relationId))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
citusRelationIds = lappend_oid(citusRelationIds, relationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CreateObjectAddressDependencyDefList(RelationRelationId, citusRelationIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* GetTypeConstraintDependencyDefinition creates a list of constraint dependency
|
* GetTypeConstraintDependencyDefinition creates a list of constraint dependency
|
||||||
* definitions for a given type
|
* definitions for a given type
|
||||||
|
|
|
@ -590,6 +590,18 @@ IsCitusTable(Oid relationId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IsCitusTableRangeVar returns whether the table named in the given
|
||||||
|
* rangeVar is a Citus table.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
IsCitusTableRangeVar(RangeVar *rangeVar, LOCKMODE lockMode, bool missingOK)
|
||||||
|
{
|
||||||
|
Oid relationId = RangeVarGetRelid(rangeVar, lockMode, missingOK);
|
||||||
|
return IsCitusTable(relationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* IsCitusTableViaCatalog returns whether the given relation is a
|
* IsCitusTableViaCatalog returns whether the given relation is a
|
||||||
* distributed table or not.
|
* distributed table or not.
|
||||||
|
|
|
@ -100,6 +100,7 @@ static bool HasMetadataWorkers(void);
|
||||||
static void CreateShellTableOnWorkers(Oid relationId);
|
static void CreateShellTableOnWorkers(Oid relationId);
|
||||||
static void CreateTableMetadataOnWorkers(Oid relationId);
|
static void CreateTableMetadataOnWorkers(Oid relationId);
|
||||||
static void CreateDependingViewsOnWorkers(Oid relationId);
|
static void CreateDependingViewsOnWorkers(Oid relationId);
|
||||||
|
static void AddTableToPublications(Oid relationId);
|
||||||
static NodeMetadataSyncResult SyncNodeMetadataToNodesOptional(void);
|
static NodeMetadataSyncResult SyncNodeMetadataToNodesOptional(void);
|
||||||
static bool ShouldSyncTableMetadataInternal(bool hashDistributed,
|
static bool ShouldSyncTableMetadataInternal(bool hashDistributed,
|
||||||
bool citusTableWithNoDistKey);
|
bool citusTableWithNoDistKey);
|
||||||
|
@ -302,7 +303,8 @@ SyncNodeMetadataToNode(const char *nodeNameString, int32 nodePort)
|
||||||
* Our definition of metadata includes the shell table and its inter relations with
|
* Our definition of metadata includes the shell table and its inter relations with
|
||||||
* other shell tables, corresponding pg_dist_object, pg_dist_partiton, pg_dist_shard
|
* other shell tables, corresponding pg_dist_object, pg_dist_partiton, pg_dist_shard
|
||||||
* and pg_dist_shard placement entries. This function also propagates the views that
|
* and pg_dist_shard placement entries. This function also propagates the views that
|
||||||
* depend on the given relation, to the metadata workers.
|
* depend on the given relation, to the metadata workers, and adds the relation to
|
||||||
|
* the appropriate publications.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
SyncCitusTableMetadata(Oid relationId)
|
SyncCitusTableMetadata(Oid relationId)
|
||||||
|
@ -319,6 +321,7 @@ SyncCitusTableMetadata(Oid relationId)
|
||||||
}
|
}
|
||||||
|
|
||||||
CreateDependingViewsOnWorkers(relationId);
|
CreateDependingViewsOnWorkers(relationId);
|
||||||
|
AddTableToPublications(relationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -364,6 +367,49 @@ CreateDependingViewsOnWorkers(Oid relationId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AddTableToPublications adds the table to a publication on workers with metadata.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
AddTableToPublications(Oid relationId)
|
||||||
|
{
|
||||||
|
List *publicationIds = GetRelationPublications(relationId);
|
||||||
|
if (publicationIds == NIL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Oid publicationId = InvalidOid;
|
||||||
|
|
||||||
|
SendCommandToWorkersWithMetadata(DISABLE_DDL_PROPAGATION);
|
||||||
|
|
||||||
|
foreach_oid(publicationId, publicationIds)
|
||||||
|
{
|
||||||
|
ObjectAddress *publicationAddress = palloc0(sizeof(ObjectAddress));
|
||||||
|
ObjectAddressSet(*publicationAddress, PublicationRelationId, publicationId);
|
||||||
|
List *addresses = list_make1(publicationAddress);
|
||||||
|
|
||||||
|
if (!ShouldPropagateAnyObject(addresses))
|
||||||
|
{
|
||||||
|
/* skip non-distributed publications */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ensure schemas exist */
|
||||||
|
EnsureAllObjectDependenciesExistOnAllNodes(addresses);
|
||||||
|
|
||||||
|
bool isAdd = true;
|
||||||
|
char *alterPublicationCommand =
|
||||||
|
GetAlterPublicationTableDDLCommand(publicationId, relationId, isAdd);
|
||||||
|
|
||||||
|
/* send ALTER PUBLICATION .. ADD to workers with metadata */
|
||||||
|
SendCommandToWorkersWithMetadata(alterPublicationCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
SendCommandToWorkersWithMetadata(ENABLE_DDL_PROPAGATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* EnsureSequentialModeMetadataOperations makes sure that the current transaction is
|
* EnsureSequentialModeMetadataOperations makes sure that the current transaction is
|
||||||
* already in sequential mode, or can still safely be put in sequential mode,
|
* already in sequential mode, or can still safely be put in sequential mode,
|
||||||
|
|
|
@ -425,6 +425,7 @@ ErrorIfCurrentUserCanNotDistributeObject(char *textType, ObjectType type,
|
||||||
case OBJECT_COLLATION:
|
case OBJECT_COLLATION:
|
||||||
case OBJECT_VIEW:
|
case OBJECT_VIEW:
|
||||||
case OBJECT_ROLE:
|
case OBJECT_ROLE:
|
||||||
|
case OBJECT_PUBLICATION:
|
||||||
{
|
{
|
||||||
check_object_ownership(userId, type, *addr, node, *relation);
|
check_object_ownership(userId, type, *addr, node, *relation);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1338,7 +1338,9 @@ CreatePublications(MultiConnection *connection,
|
||||||
worker->groupId,
|
worker->groupId,
|
||||||
CLEANUP_ALWAYS);
|
CLEANUP_ALWAYS);
|
||||||
|
|
||||||
|
ExecuteCriticalRemoteCommand(connection, DISABLE_DDL_PROPAGATION);
|
||||||
ExecuteCriticalRemoteCommand(connection, createPublicationCommand->data);
|
ExecuteCriticalRemoteCommand(connection, createPublicationCommand->data);
|
||||||
|
ExecuteCriticalRemoteCommand(connection, ENABLE_DDL_PROPAGATION);
|
||||||
pfree(createPublicationCommand->data);
|
pfree(createPublicationCommand->data);
|
||||||
pfree(createPublicationCommand);
|
pfree(createPublicationCommand);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
-- citus--11.2-1--11.3-1
|
-- citus--11.2-1--11.3-1
|
||||||
#include "udfs/repl_origin_helper/11.3-1.sql"
|
#include "udfs/repl_origin_helper/11.3-1.sql"
|
||||||
-- bump version to 11.3-1
|
|
||||||
|
|
||||||
|
ALTER TABLE pg_catalog.pg_dist_authinfo REPLICA IDENTITY USING INDEX pg_dist_authinfo_identification_index;
|
||||||
|
ALTER TABLE pg_catalog.pg_dist_partition REPLICA IDENTITY USING INDEX pg_dist_partition_logical_relid_index;
|
||||||
|
ALTER TABLE pg_catalog.pg_dist_placement REPLICA IDENTITY USING INDEX pg_dist_placement_placementid_index;
|
||||||
|
ALTER TABLE pg_catalog.pg_dist_rebalance_strategy REPLICA IDENTITY USING INDEX pg_dist_rebalance_strategy_name_key;
|
||||||
|
ALTER TABLE pg_catalog.pg_dist_shard REPLICA IDENTITY USING INDEX pg_dist_shard_shardid_index;
|
||||||
|
ALTER TABLE pg_catalog.pg_dist_transaction REPLICA IDENTITY USING INDEX pg_dist_transaction_unique_constraint;
|
||||||
|
|
|
@ -1,4 +1,12 @@
|
||||||
-- citus--11.3-1--11.2-1
|
-- citus--11.3-1--11.2-1
|
||||||
|
|
||||||
DROP FUNCTION pg_catalog.citus_internal_start_replication_origin_tracking();
|
DROP FUNCTION pg_catalog.citus_internal_start_replication_origin_tracking();
|
||||||
DROP FUNCTION pg_catalog.citus_internal_stop_replication_origin_tracking();
|
DROP FUNCTION pg_catalog.citus_internal_stop_replication_origin_tracking();
|
||||||
DROP FUNCTION pg_catalog.citus_internal_is_replication_origin_tracking_active();
|
DROP FUNCTION pg_catalog.citus_internal_is_replication_origin_tracking_active();
|
||||||
|
|
||||||
|
ALTER TABLE pg_catalog.pg_dist_authinfo REPLICA IDENTITY NOTHING;
|
||||||
|
ALTER TABLE pg_catalog.pg_dist_partition REPLICA IDENTITY NOTHING;
|
||||||
|
ALTER TABLE pg_catalog.pg_dist_placement REPLICA IDENTITY NOTHING;
|
||||||
|
ALTER TABLE pg_catalog.pg_dist_rebalance_strategy REPLICA IDENTITY NOTHING;
|
||||||
|
ALTER TABLE pg_catalog.pg_dist_shard REPLICA IDENTITY NOTHING;
|
||||||
|
ALTER TABLE pg_catalog.pg_dist_transaction REPLICA IDENTITY NOTHING;
|
||||||
|
|
|
@ -35,8 +35,22 @@
|
||||||
#include "distributed/worker_create_or_replace.h"
|
#include "distributed/worker_create_or_replace.h"
|
||||||
#include "distributed/worker_protocol.h"
|
#include "distributed/worker_protocol.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* OnCollisionAction describes what to do when the created object
|
||||||
|
* and existing object do not match.
|
||||||
|
*/
|
||||||
|
typedef enum OnCollisionAction
|
||||||
|
{
|
||||||
|
ON_COLLISION_RENAME,
|
||||||
|
ON_COLLISION_DROP
|
||||||
|
} OnCollisionAction;
|
||||||
|
|
||||||
|
|
||||||
static List * CreateStmtListByObjectAddress(const ObjectAddress *address);
|
static List * CreateStmtListByObjectAddress(const ObjectAddress *address);
|
||||||
static bool CompareStringList(List *list1, List *list2);
|
static bool CompareStringList(List *list1, List *list2);
|
||||||
|
static OnCollisionAction GetOnCollisionAction(const ObjectAddress *address);
|
||||||
|
|
||||||
|
|
||||||
PG_FUNCTION_INFO_V1(worker_create_or_replace_object);
|
PG_FUNCTION_INFO_V1(worker_create_or_replace_object);
|
||||||
PG_FUNCTION_INFO_V1(worker_create_or_replace_object_array);
|
PG_FUNCTION_INFO_V1(worker_create_or_replace_object_array);
|
||||||
|
@ -192,7 +206,8 @@ WorkerCreateOrReplaceObject(List *sqlStatements)
|
||||||
/*
|
/*
|
||||||
* Object with name from statement is already found locally, check if states are
|
* Object with name from statement is already found locally, check if states are
|
||||||
* identical. If objects differ we will rename the old object (non- destructively)
|
* identical. If objects differ we will rename the old object (non- destructively)
|
||||||
* as to make room to create the new object according to the spec sent.
|
* or drop it (if safe) as to make room to create the new object according to the
|
||||||
|
* spec sent.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -213,11 +228,22 @@ WorkerCreateOrReplaceObject(List *sqlStatements)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *newName = GenerateBackupNameForCollision(address);
|
Node *utilityStmt = NULL;
|
||||||
|
|
||||||
RenameStmt *renameStmt = CreateRenameStatement(address, newName);
|
if (GetOnCollisionAction(address) == ON_COLLISION_DROP)
|
||||||
const char *sqlRenameStmt = DeparseTreeNode((Node *) renameStmt);
|
{
|
||||||
ProcessUtilityParseTree((Node *) renameStmt, sqlRenameStmt,
|
/* drop the existing object */
|
||||||
|
utilityStmt = (Node *) CreateDropStmt(address);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* rename the existing object */
|
||||||
|
char *newName = GenerateBackupNameForCollision(address);
|
||||||
|
utilityStmt = (Node *) CreateRenameStatement(address, newName);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *commandString = DeparseTreeNode(utilityStmt);
|
||||||
|
ProcessUtilityParseTree(utilityStmt, commandString,
|
||||||
PROCESS_UTILITY_QUERY,
|
PROCESS_UTILITY_QUERY,
|
||||||
NULL, None_Receiver, NULL);
|
NULL, None_Receiver, NULL);
|
||||||
}
|
}
|
||||||
|
@ -286,6 +312,11 @@ CreateStmtListByObjectAddress(const ObjectAddress *address)
|
||||||
return list_make1(GetFunctionDDLCommand(address->objectId, false));
|
return list_make1(GetFunctionDDLCommand(address->objectId, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case OCLASS_PUBLICATION:
|
||||||
|
{
|
||||||
|
return list_make1(CreatePublicationDDLCommand(address->objectId));
|
||||||
|
}
|
||||||
|
|
||||||
case OCLASS_TSCONFIG:
|
case OCLASS_TSCONFIG:
|
||||||
{
|
{
|
||||||
List *stmts = GetCreateTextSearchConfigStatements(address);
|
List *stmts = GetCreateTextSearchConfigStatements(address);
|
||||||
|
@ -312,6 +343,37 @@ CreateStmtListByObjectAddress(const ObjectAddress *address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GetOnCollisionAction decides what to do when the object already exists.
|
||||||
|
*/
|
||||||
|
static OnCollisionAction
|
||||||
|
GetOnCollisionAction(const ObjectAddress *address)
|
||||||
|
{
|
||||||
|
switch (getObjectClass(address))
|
||||||
|
{
|
||||||
|
case OCLASS_PUBLICATION:
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We prefer to drop publications because they can be
|
||||||
|
* harmful (cause update/delete failures) and are relatively
|
||||||
|
* safe to drop.
|
||||||
|
*/
|
||||||
|
return ON_COLLISION_DROP;
|
||||||
|
}
|
||||||
|
|
||||||
|
case OCLASS_COLLATION:
|
||||||
|
case OCLASS_PROC:
|
||||||
|
case OCLASS_TSCONFIG:
|
||||||
|
case OCLASS_TSDICT:
|
||||||
|
case OCLASS_TYPE:
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
return ON_COLLISION_RENAME;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* GenerateBackupNameForCollision calculate a backup name for a given object by its
|
* GenerateBackupNameForCollision calculate a backup name for a given object by its
|
||||||
* address. This name should be used when renaming an existing object before creating the
|
* address. This name should be used when renaming an existing object before creating the
|
||||||
|
@ -362,6 +424,64 @@ GenerateBackupNameForCollision(const ObjectAddress *address)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CreateDropPublicationStmt creates a DROP PUBLICATION statement for the
|
||||||
|
* publication at the given address.
|
||||||
|
*/
|
||||||
|
static DropStmt *
|
||||||
|
CreateDropPublicationStmt(const ObjectAddress *address)
|
||||||
|
{
|
||||||
|
Assert(address->classId == PublicationRelationId);
|
||||||
|
|
||||||
|
DropStmt *dropStmt = makeNode(DropStmt);
|
||||||
|
dropStmt->removeType = OBJECT_PUBLICATION;
|
||||||
|
dropStmt->behavior = DROP_RESTRICT;
|
||||||
|
|
||||||
|
HeapTuple publicationTuple =
|
||||||
|
SearchSysCache1(PUBLICATIONOID, ObjectIdGetDatum(address->objectId));
|
||||||
|
|
||||||
|
if (!HeapTupleIsValid(publicationTuple))
|
||||||
|
{
|
||||||
|
ereport(ERROR, (errmsg("cannot find publication with oid: %d",
|
||||||
|
address->objectId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Form_pg_publication publicationForm =
|
||||||
|
(Form_pg_publication) GETSTRUCT(publicationTuple);
|
||||||
|
|
||||||
|
char *publicationName = NameStr(publicationForm->pubname);
|
||||||
|
dropStmt->objects = list_make1(makeString(publicationName));
|
||||||
|
|
||||||
|
ReleaseSysCache(publicationTuple);
|
||||||
|
|
||||||
|
return dropStmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CreateDropStmt returns a DROP statement for the given object.
|
||||||
|
*/
|
||||||
|
DropStmt *
|
||||||
|
CreateDropStmt(const ObjectAddress *address)
|
||||||
|
{
|
||||||
|
switch (getObjectClass(address))
|
||||||
|
{
|
||||||
|
case OCLASS_PUBLICATION:
|
||||||
|
{
|
||||||
|
return CreateDropPublicationStmt(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ereport(ERROR, (errmsg("unsupported object to construct a drop statement"),
|
||||||
|
errdetail("unable to generate a parsetree for the drop")));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CreateRenameTypeStmt creates a rename statement for a type based on its ObjectAddress.
|
* 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
|
* The rename statement will rename the existing object on its address to the value
|
||||||
|
|
|
@ -409,6 +409,24 @@ extern void DropPolicyEventExtendNames(DropStmt *stmt, const char *schemaName, u
|
||||||
|
|
||||||
extern void AddRangeTableEntryToQueryCompat(ParseState *parseState, Relation relation);
|
extern void AddRangeTableEntryToQueryCompat(ParseState *parseState, Relation relation);
|
||||||
|
|
||||||
|
/* publication.c - forward declarations */
|
||||||
|
extern List * PostProcessCreatePublicationStmt(Node *node, const char *queryString);
|
||||||
|
extern List * CreatePublicationDDLCommandsIdempotent(const ObjectAddress *address);
|
||||||
|
extern char * CreatePublicationDDLCommand(Oid publicationId);
|
||||||
|
extern List * PreprocessAlterPublicationStmt(Node *stmt, const char *queryString,
|
||||||
|
ProcessUtilityContext processUtilityCtx);
|
||||||
|
extern List * GetAlterPublicationDDLCommandsForTable(Oid relationId, bool isAdd);
|
||||||
|
extern char * GetAlterPublicationTableDDLCommand(Oid publicationId, Oid relationId,
|
||||||
|
bool isAdd);
|
||||||
|
extern List * AlterPublicationOwnerStmtObjectAddress(Node *node, bool missingOk,
|
||||||
|
bool isPostProcess);
|
||||||
|
extern List * AlterPublicationStmtObjectAddress(Node *node, bool missingOk,
|
||||||
|
bool isPostProcess);
|
||||||
|
extern List * CreatePublicationStmtObjectAddress(Node *node, bool missingOk,
|
||||||
|
bool isPostProcess);
|
||||||
|
extern List * RenamePublicationStmtObjectAddress(Node *node, bool missingOk,
|
||||||
|
bool isPostProcess);
|
||||||
|
|
||||||
/* rename.c - forward declarations*/
|
/* rename.c - forward declarations*/
|
||||||
extern List * PreprocessRenameStmt(Node *renameStmt, const char *renameCommand,
|
extern List * PreprocessRenameStmt(Node *renameStmt, const char *renameCommand,
|
||||||
ProcessUtilityContext processUtilityContext);
|
ProcessUtilityContext processUtilityContext);
|
||||||
|
@ -657,7 +675,6 @@ extern List * PreprocessDropViewStmt(Node *node, const char *queryString,
|
||||||
extern List * DropViewStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess);
|
extern List * DropViewStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess);
|
||||||
extern char * CreateViewDDLCommand(Oid viewOid);
|
extern char * CreateViewDDLCommand(Oid viewOid);
|
||||||
extern List * GetViewCreationCommandsOfTable(Oid relationId);
|
extern List * GetViewCreationCommandsOfTable(Oid relationId);
|
||||||
extern List * GetViewCreationTableDDLCommandsOfTable(Oid relationId);
|
|
||||||
extern char * AlterViewOwnerCommand(Oid viewOid);
|
extern char * AlterViewOwnerCommand(Oid viewOid);
|
||||||
extern char * DeparseViewStmt(Node *node);
|
extern char * DeparseViewStmt(Node *node);
|
||||||
extern char * DeparseDropViewStmt(Node *node);
|
extern char * DeparseDropViewStmt(Node *node);
|
||||||
|
|
|
@ -210,6 +210,23 @@ extern char * DeparseAlterExtensionStmt(Node *stmt);
|
||||||
/* forward declarations for deparse_database_stmts.c */
|
/* forward declarations for deparse_database_stmts.c */
|
||||||
extern char * DeparseAlterDatabaseOwnerStmt(Node *node);
|
extern char * DeparseAlterDatabaseOwnerStmt(Node *node);
|
||||||
|
|
||||||
|
/* forward declaration for deparse_publication_stmts.c */
|
||||||
|
extern char * DeparseCreatePublicationStmt(Node *stmt);
|
||||||
|
extern char * DeparseCreatePublicationStmtExtended(Node *node,
|
||||||
|
bool whereClauseNeedsTransform,
|
||||||
|
bool includeLocalTables);
|
||||||
|
extern char * DeparseAlterPublicationStmt(Node *stmt);
|
||||||
|
extern char * DeparseAlterPublicationStmtExtended(Node *stmt,
|
||||||
|
bool whereClauseNeedsTransform,
|
||||||
|
bool includeLocalTables);
|
||||||
|
extern char * DeparseAlterPublicationOwnerStmt(Node *stmt);
|
||||||
|
extern char * DeparseAlterPublicationSchemaStmt(Node *node);
|
||||||
|
extern char * DeparseDropPublicationStmt(Node *stmt);
|
||||||
|
extern char * DeparseRenamePublicationStmt(Node *node);
|
||||||
|
|
||||||
|
extern void QualifyCreatePublicationStmt(Node *node);
|
||||||
|
extern void QualifyAlterPublicationStmt(Node *node);
|
||||||
|
|
||||||
/* forward declatations for deparse_text_search_stmts.c */
|
/* forward declatations for deparse_text_search_stmts.c */
|
||||||
extern void QualifyAlterTextSearchConfigurationOwnerStmt(Node *node);
|
extern void QualifyAlterTextSearchConfigurationOwnerStmt(Node *node);
|
||||||
extern void QualifyAlterTextSearchConfigurationSchemaStmt(Node *node);
|
extern void QualifyAlterTextSearchConfigurationSchemaStmt(Node *node);
|
||||||
|
|
|
@ -147,6 +147,7 @@ extern char * GetTableTypeName(Oid tableId);
|
||||||
extern void SetCreateCitusTransactionLevel(int val);
|
extern void SetCreateCitusTransactionLevel(int val);
|
||||||
extern int GetCitusCreationLevel(void);
|
extern int GetCitusCreationLevel(void);
|
||||||
extern bool IsCitusTable(Oid relationId);
|
extern bool IsCitusTable(Oid relationId);
|
||||||
|
extern bool IsCitusTableRangeVar(RangeVar *rangeVar, LOCKMODE lockMode, bool missingOk);
|
||||||
extern bool IsCitusTableViaCatalog(Oid relationId);
|
extern bool IsCitusTableViaCatalog(Oid relationId);
|
||||||
extern char PgDistPartitionViaCatalog(Oid relationId);
|
extern char PgDistPartitionViaCatalog(Oid relationId);
|
||||||
extern List * LookupDistShardTuples(Oid relationId);
|
extern List * LookupDistShardTuples(Oid relationId);
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
extern char * WrapCreateOrReplace(const char *sql);
|
extern char * WrapCreateOrReplace(const char *sql);
|
||||||
extern char * WrapCreateOrReplaceList(List *sqls);
|
extern char * WrapCreateOrReplaceList(List *sqls);
|
||||||
extern char * GenerateBackupNameForCollision(const ObjectAddress *address);
|
extern char * GenerateBackupNameForCollision(const ObjectAddress *address);
|
||||||
|
extern DropStmt * CreateDropStmt(const ObjectAddress *address);
|
||||||
extern RenameStmt * CreateRenameStatement(const ObjectAddress *address, char *newName);
|
extern RenameStmt * CreateRenameStatement(const ObjectAddress *address, char *newName);
|
||||||
|
|
||||||
#endif /* WORKER_CREATE_OR_REPLACE_H */
|
#endif /* WORKER_CREATE_OR_REPLACE_H */
|
||||||
|
|
|
@ -19,6 +19,7 @@ test: citus_local_tables_ent
|
||||||
test: remove_coordinator
|
test: remove_coordinator
|
||||||
# --------
|
# --------
|
||||||
|
|
||||||
|
test: publication
|
||||||
test: logical_replication
|
test: logical_replication
|
||||||
test: multi_create_table
|
test: multi_create_table
|
||||||
test: multi_create_table_superuser
|
test: multi_create_table_superuser
|
||||||
|
|
|
@ -25,7 +25,9 @@ NOTICE: localhost:xxxxx is the coordinator and already contains metadata, skipp
|
||||||
-- This allows us to test the cleanup logic at the start of the shard move.
|
-- This allows us to test the cleanup logic at the start of the shard move.
|
||||||
\c - - - :worker_1_port
|
\c - - - :worker_1_port
|
||||||
SET search_path TO logical_replication;
|
SET search_path TO logical_replication;
|
||||||
|
SET citus.enable_ddl_propagation TO off;
|
||||||
CREATE PUBLICATION citus_shard_move_publication_:postgres_oid FOR TABLE dist_6830000;
|
CREATE PUBLICATION citus_shard_move_publication_:postgres_oid FOR TABLE dist_6830000;
|
||||||
|
RESET citus.enable_ddl_propagation;
|
||||||
\c - - - :master_port
|
\c - - - :master_port
|
||||||
SET search_path TO logical_replication;
|
SET search_path TO logical_replication;
|
||||||
CREATE TABLE dist_6830000(
|
CREATE TABLE dist_6830000(
|
||||||
|
@ -155,6 +157,13 @@ SELECT count(*) from dist;
|
||||||
100
|
100
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
DROP PUBLICATION citus_shard_move_publication_:postgres_oid;
|
||||||
|
SELECT pg_drop_replication_slot('citus_shard_move_slot_' || :postgres_oid);
|
||||||
|
pg_drop_replication_slot
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
\c - - - :worker_2_port
|
\c - - - :worker_2_port
|
||||||
SET search_path TO logical_replication;
|
SET search_path TO logical_replication;
|
||||||
SELECT count(*) from pg_subscription;
|
SELECT count(*) from pg_subscription;
|
||||||
|
@ -188,3 +197,9 @@ ALTER SUBSCRIPTION citus_shard_move_subscription_:postgres_oid DISABLE;
|
||||||
ALTER SUBSCRIPTION citus_shard_move_subscription_:postgres_oid SET (slot_name = NONE);
|
ALTER SUBSCRIPTION citus_shard_move_subscription_:postgres_oid SET (slot_name = NONE);
|
||||||
DROP SUBSCRIPTION citus_shard_move_subscription_:postgres_oid;
|
DROP SUBSCRIPTION citus_shard_move_subscription_:postgres_oid;
|
||||||
DROP SCHEMA logical_replication CASCADE;
|
DROP SCHEMA logical_replication CASCADE;
|
||||||
|
SELECT public.wait_for_resource_cleanup();
|
||||||
|
wait_for_resource_cleanup
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
|
|
@ -713,13 +713,16 @@ BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;
|
||||||
|
|
||||||
SET application_name to 'citus_internal gpid=10000000001';
|
SET application_name to 'citus_internal gpid=10000000001';
|
||||||
\set VERBOSITY terse
|
\set VERBOSITY terse
|
||||||
CREATE TABLE publication_test_table(id int);
|
CREATE OPERATOR === (
|
||||||
CREATE PUBLICATION publication_test FOR TABLE publication_test_table;
|
LEFTARG = int,
|
||||||
|
RIGHTARG = int,
|
||||||
|
FUNCTION = int4eq
|
||||||
|
);
|
||||||
SET ROLE metadata_sync_helper_role;
|
SET ROLE metadata_sync_helper_role;
|
||||||
WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation)
|
WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation)
|
||||||
AS (VALUES ('publication', ARRAY['publication_test']::text[], ARRAY[]::text[], -1, 0, false))
|
AS (VALUES ('operator', ARRAY['===']::text[], ARRAY['int','int']::text[], -1, 0, false))
|
||||||
SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) FROM distributed_object_data;
|
SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) FROM distributed_object_data;
|
||||||
ERROR: publication object can not be distributed by Citus
|
ERROR: operator object can not be distributed by Citus
|
||||||
ROLLBACK;
|
ROLLBACK;
|
||||||
-- Show that citus_internal_add_object_metadata checks the priviliges
|
-- Show that citus_internal_add_object_metadata checks the priviliges
|
||||||
BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;
|
BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;
|
||||||
|
|
|
@ -22,7 +22,7 @@ SELECT nodeid AS worker_1_id FROM pg_dist_node WHERE nodename = 'localhost' AND
|
||||||
SELECT nodeid AS worker_2_id FROM pg_dist_node WHERE nodename = 'localhost' AND nodeport = :worker_2_port;
|
SELECT nodeid AS worker_2_id FROM pg_dist_node WHERE nodename = 'localhost' AND nodeport = :worker_2_port;
|
||||||
worker_2_id
|
worker_2_id
|
||||||
---------------------------------------------------------------------
|
---------------------------------------------------------------------
|
||||||
18
|
35
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
\gset
|
\gset
|
||||||
|
|
|
@ -16,7 +16,7 @@ SELECT nodeid AS worker_1_id FROM pg_dist_node WHERE nodename = 'localhost' AND
|
||||||
SELECT nodeid AS worker_2_id FROM pg_dist_node WHERE nodename = 'localhost' AND nodeport = :worker_2_port;
|
SELECT nodeid AS worker_2_id FROM pg_dist_node WHERE nodename = 'localhost' AND nodeport = :worker_2_port;
|
||||||
worker_2_id
|
worker_2_id
|
||||||
---------------------------------------------------------------------
|
---------------------------------------------------------------------
|
||||||
18
|
35
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
\gset
|
\gset
|
||||||
|
|
|
@ -0,0 +1,379 @@
|
||||||
|
CREATE SCHEMA publication;
|
||||||
|
CREATE SCHEMA "publication-1";
|
||||||
|
SET search_path TO publication;
|
||||||
|
SET citus.shard_replication_factor TO 1;
|
||||||
|
-- for citus_add_local_table_to_metadata / create_distributed_table_concurrently
|
||||||
|
SELECT citus_set_coordinator_host('localhost', :master_port);
|
||||||
|
citus_set_coordinator_host
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION activate_node_snapshot()
|
||||||
|
RETURNS text[]
|
||||||
|
LANGUAGE C STRICT
|
||||||
|
AS 'citus';
|
||||||
|
COMMENT ON FUNCTION activate_node_snapshot()
|
||||||
|
IS 'commands to activate node snapshot';
|
||||||
|
\c - - - :worker_1_port
|
||||||
|
SET citus.enable_ddl_propagation TO off;
|
||||||
|
CREATE OR REPLACE FUNCTION activate_node_snapshot()
|
||||||
|
RETURNS text[]
|
||||||
|
LANGUAGE C STRICT
|
||||||
|
AS 'citus';
|
||||||
|
COMMENT ON FUNCTION activate_node_snapshot()
|
||||||
|
IS 'commands to activate node snapshot';
|
||||||
|
\c - - - :worker_2_port
|
||||||
|
SET citus.enable_ddl_propagation TO off;
|
||||||
|
CREATE OR REPLACE FUNCTION activate_node_snapshot()
|
||||||
|
RETURNS text[]
|
||||||
|
LANGUAGE C STRICT
|
||||||
|
AS 'citus';
|
||||||
|
COMMENT ON FUNCTION activate_node_snapshot()
|
||||||
|
IS 'commands to activate node snapshot';
|
||||||
|
-- create some publications with conflicting names on worker node
|
||||||
|
-- publication will be different from coordinator
|
||||||
|
CREATE PUBLICATION "pub-all";
|
||||||
|
-- publication will be same as coordinator
|
||||||
|
CREATE PUBLICATION "pub-all-insertupdateonly" FOR ALL TABLES WITH (publish = 'insert, update');;
|
||||||
|
\c - - - :master_port
|
||||||
|
SET search_path TO publication;
|
||||||
|
SET citus.shard_replication_factor TO 1;
|
||||||
|
-- do not create publications on worker 2 initially
|
||||||
|
SELECT citus_remove_node('localhost', :worker_2_port);
|
||||||
|
citus_remove_node
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- create a non-distributed publication
|
||||||
|
SET citus.enable_ddl_propagation TO off;
|
||||||
|
CREATE PUBLICATION pubnotdistributed WITH (publish = 'delete');
|
||||||
|
RESET citus.enable_ddl_propagation;
|
||||||
|
ALTER PUBLICATION pubnotdistributed SET (publish = 'truncate');
|
||||||
|
-- create regular, distributed publications
|
||||||
|
CREATE PUBLICATION pubempty;
|
||||||
|
CREATE PUBLICATION pubinsertonly WITH (publish = 'insert');
|
||||||
|
CREATE PUBLICATION "pub-all" FOR ALL TABLES;
|
||||||
|
CREATE PUBLICATION "pub-all-insertupdateonly" FOR ALL TABLES WITH (publish = 'insert, update');
|
||||||
|
-- add worker 2 with publications
|
||||||
|
SELECT 1 FROM citus_add_node('localhost', :worker_2_port);
|
||||||
|
?column?
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- Check publications on all the nodes, if we see the same publication name twice then its definition differs
|
||||||
|
-- Note that publications are special in the sense that the coordinator object might differ from
|
||||||
|
-- worker objects due to the presence of regular tables.
|
||||||
|
SELECT DISTINCT c FROM (
|
||||||
|
SELECT unnest(result::text[]) c
|
||||||
|
FROM run_command_on_workers($$
|
||||||
|
SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' ORDER BY 1) s$$)
|
||||||
|
ORDER BY c) s;
|
||||||
|
c
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
SELECT worker_create_or_replace_object('CREATE PUBLICATION "pub-all" FOR ALL TABLES WITH (publish_via_partition_root = ''false'', publish = ''insert, update, delete, truncate'')');
|
||||||
|
SELECT worker_create_or_replace_object('CREATE PUBLICATION "pub-all-insertupdateonly" FOR ALL TABLES WITH (publish_via_partition_root = ''false'', publish = ''insert, update'')');
|
||||||
|
SELECT worker_create_or_replace_object('CREATE PUBLICATION pubempty WITH (publish_via_partition_root = ''false'', publish = ''insert, update, delete, truncate'')');
|
||||||
|
SELECT worker_create_or_replace_object('CREATE PUBLICATION pubinsertonly WITH (publish_via_partition_root = ''false'', publish = ''insert'')');
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
CREATE TABLE test (x int primary key, y int, "column-1" int, doc xml);
|
||||||
|
CREATE TABLE "test-pubs" (x int primary key, y int, "column-1" int);
|
||||||
|
CREATE TABLE "publication-1"."test-pubs" (x int primary key, y int, "column-1" int);
|
||||||
|
-- various operations on a publication with only local tables
|
||||||
|
CREATE PUBLICATION pubtables_orig FOR TABLE test, "test-pubs", "publication-1"."test-pubs" WITH (publish = 'insert, truncate');
|
||||||
|
ALTER PUBLICATION pubtables_orig DROP TABLE test;
|
||||||
|
ALTER PUBLICATION pubtables_orig ADD TABLE test;
|
||||||
|
-- publication will be empty on worker nodes, since all tables are local
|
||||||
|
SELECT DISTINCT c FROM (
|
||||||
|
SELECT unnest(result::text[]) c
|
||||||
|
FROM run_command_on_workers($$
|
||||||
|
SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' AND c LIKE '%pubtables%' ORDER BY 1) s$$)
|
||||||
|
ORDER BY c) s;
|
||||||
|
c
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
SELECT worker_create_or_replace_object('CREATE PUBLICATION pubtables_orig WITH (publish_via_partition_root = ''false'', publish = ''insert, truncate'')');
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- distribute a table, creating a mixed publication
|
||||||
|
SELECT create_distributed_table('test','x', colocate_with := 'none');
|
||||||
|
create_distributed_table
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- some generic operations
|
||||||
|
ALTER PUBLICATION pubtables_orig RENAME TO pubtables;
|
||||||
|
ALTER PUBLICATION pubtables SET (publish = 'insert, update, delete');
|
||||||
|
ALTER PUBLICATION pubtables OWNER TO postgres;
|
||||||
|
ALTER PUBLICATION pubtables SET (publish = 'inert, update, delete');
|
||||||
|
ERROR: unrecognized value for publication option "publish": "inert"
|
||||||
|
ALTER PUBLICATION pubtables ADD TABLE notexist;
|
||||||
|
ERROR: relation "notexist" does not exist
|
||||||
|
-- operations with a distributed table
|
||||||
|
ALTER PUBLICATION pubtables DROP TABLE test;
|
||||||
|
ALTER PUBLICATION pubtables ADD TABLE test;
|
||||||
|
ALTER PUBLICATION pubtables SET TABLE test, "test-pubs", "publication-1"."test-pubs";
|
||||||
|
-- operations with a local table in a mixed publication
|
||||||
|
ALTER PUBLICATION pubtables DROP TABLE "test-pubs";
|
||||||
|
ALTER PUBLICATION pubtables ADD TABLE "test-pubs";
|
||||||
|
SELECT create_distributed_table('"test-pubs"', 'x');
|
||||||
|
create_distributed_table
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- test and test-pubs will show up in worker nodes
|
||||||
|
SELECT DISTINCT c FROM (
|
||||||
|
SELECT unnest(result::text[]) c
|
||||||
|
FROM run_command_on_workers($$
|
||||||
|
SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' AND c LIKE '%pubtables%' ORDER BY 1) s$$)
|
||||||
|
ORDER BY c) s;
|
||||||
|
c
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
SELECT worker_create_or_replace_object('CREATE PUBLICATION pubtables FOR TABLE publication.test, TABLE publication."test-pubs" WITH (publish_via_partition_root = ''false'', publish = ''insert, update, delete'')');
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- operations with a strangely named distributed table in a mixed publication
|
||||||
|
ALTER PUBLICATION pubtables DROP TABLE "test-pubs";
|
||||||
|
ALTER PUBLICATION pubtables ADD TABLE "test-pubs";
|
||||||
|
-- create a publication with distributed and local tables
|
||||||
|
DROP PUBLICATION pubtables;
|
||||||
|
CREATE PUBLICATION pubtables FOR TABLE test, "test-pubs", "publication-1"."test-pubs";
|
||||||
|
-- change distributed tables
|
||||||
|
SELECT alter_distributed_table('test', shard_count := 5, cascade_to_colocated := true);
|
||||||
|
NOTICE: creating a new table for publication.test
|
||||||
|
NOTICE: moving the data of publication.test
|
||||||
|
NOTICE: dropping the old publication.test
|
||||||
|
NOTICE: renaming the new table to publication.test
|
||||||
|
NOTICE: creating a new table for publication."test-pubs"
|
||||||
|
NOTICE: moving the data of publication."test-pubs"
|
||||||
|
NOTICE: dropping the old publication."test-pubs"
|
||||||
|
NOTICE: renaming the new table to publication."test-pubs"
|
||||||
|
alter_distributed_table
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT undistribute_table('test');
|
||||||
|
NOTICE: creating a new table for publication.test
|
||||||
|
NOTICE: moving the data of publication.test
|
||||||
|
NOTICE: dropping the old publication.test
|
||||||
|
NOTICE: renaming the new table to publication.test
|
||||||
|
undistribute_table
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT citus_add_local_table_to_metadata('test');
|
||||||
|
citus_add_local_table_to_metadata
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT create_distributed_table_concurrently('test', 'x');
|
||||||
|
create_distributed_table_concurrently
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT undistribute_table('"test-pubs"');
|
||||||
|
NOTICE: creating a new table for publication."test-pubs"
|
||||||
|
NOTICE: moving the data of publication."test-pubs"
|
||||||
|
NOTICE: dropping the old publication."test-pubs"
|
||||||
|
NOTICE: renaming the new table to publication."test-pubs"
|
||||||
|
undistribute_table
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT create_reference_table('"test-pubs"');
|
||||||
|
create_reference_table
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- publications are unchanged despite various tranformations
|
||||||
|
SELECT DISTINCT c FROM (
|
||||||
|
SELECT unnest(result::text[]) c
|
||||||
|
FROM run_command_on_workers($$
|
||||||
|
SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' AND c LIKE '%pubtables%' ORDER BY 1) s$$)
|
||||||
|
ORDER BY c) s;
|
||||||
|
c
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
SELECT worker_create_or_replace_object('CREATE PUBLICATION pubtables FOR TABLE publication.test, TABLE publication."test-pubs" WITH (publish_via_partition_root = ''false'', publish = ''insert, update, delete, truncate'')');
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- partitioned table
|
||||||
|
CREATE TABLE testpub_partitioned (a int, b text, c text) PARTITION BY RANGE (a);
|
||||||
|
CREATE TABLE testpub_partitioned_0 PARTITION OF testpub_partitioned FOR VALUES FROM (1) TO (10);
|
||||||
|
ALTER TABLE testpub_partitioned_0 ADD PRIMARY KEY (a);
|
||||||
|
ALTER TABLE testpub_partitioned_0 REPLICA IDENTITY USING INDEX testpub_partitioned_0_pkey;
|
||||||
|
CREATE TABLE testpub_partitioned_1 PARTITION OF testpub_partitioned FOR VALUES FROM (11) TO (20);
|
||||||
|
ALTER TABLE testpub_partitioned_1 ADD PRIMARY KEY (a);
|
||||||
|
ALTER TABLE testpub_partitioned_1 REPLICA IDENTITY USING INDEX testpub_partitioned_1_pkey;
|
||||||
|
CREATE PUBLICATION pubpartitioned FOR TABLE testpub_partitioned WITH (publish_via_partition_root = 'true');
|
||||||
|
SELECT create_distributed_table('testpub_partitioned', 'a');
|
||||||
|
create_distributed_table
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT DISTINCT c FROM (
|
||||||
|
SELECT unnest(result::text[]) c
|
||||||
|
FROM run_command_on_workers($$
|
||||||
|
SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' AND c LIKE '%pubpartitioned%' ORDER BY 1) s$$)
|
||||||
|
ORDER BY c) s;
|
||||||
|
c
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
SELECT worker_create_or_replace_object('CREATE PUBLICATION pubpartitioned FOR TABLE publication.testpub_partitioned WITH (publish_via_partition_root = ''true'', publish = ''insert, update, delete, truncate'')');
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
DROP PUBLICATION pubpartitioned;
|
||||||
|
CREATE PUBLICATION pubpartitioned FOR TABLE testpub_partitioned WITH (publish_via_partition_root = 'true');
|
||||||
|
-- add a partition
|
||||||
|
ALTER PUBLICATION pubpartitioned ADD TABLE testpub_partitioned_1;
|
||||||
|
SELECT DISTINCT c FROM (
|
||||||
|
SELECT unnest(result::text[]) c
|
||||||
|
FROM run_command_on_workers($$
|
||||||
|
SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLIATION%' AND c LIKE '%pubpartitioned%' ORDER BY 1) s$$)
|
||||||
|
ORDER BY c) s;
|
||||||
|
ERROR: malformed array literal: ""
|
||||||
|
DETAIL: Array value must start with "{" or dimension information.
|
||||||
|
-- make sure we can sync all the publication metadata
|
||||||
|
SELECT start_metadata_sync_to_all_nodes();
|
||||||
|
start_metadata_sync_to_all_nodes
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
DROP PUBLICATION pubempty;
|
||||||
|
DROP PUBLICATION pubtables;
|
||||||
|
DROP PUBLICATION pubinsertonly;
|
||||||
|
DROP PUBLICATION "pub-all-insertupdateonly";
|
||||||
|
DROP PUBLICATION "pub-all";
|
||||||
|
DROP PUBLICATION pubpartitioned;
|
||||||
|
DROP PUBLICATION pubnotdistributed;
|
||||||
|
SHOW server_version \gset
|
||||||
|
SELECT substring(:'server_version', '\d+')::int >= 15 AS server_version_ge_15
|
||||||
|
\gset
|
||||||
|
\if :server_version_ge_15
|
||||||
|
\else
|
||||||
|
SET client_min_messages TO ERROR;
|
||||||
|
DROP SCHEMA publication CASCADE;
|
||||||
|
DROP SCHEMA "publication-1" CASCADE;
|
||||||
|
SELECT citus_remove_node('localhost', :master_port);
|
||||||
|
\q
|
||||||
|
\endif
|
||||||
|
-- recreate a mixed publication
|
||||||
|
CREATE PUBLICATION pubtables FOR TABLE test, "publication-1"."test-pubs";
|
||||||
|
-- operations on an existing distributed table
|
||||||
|
ALTER PUBLICATION pubtables DROP TABLE test;
|
||||||
|
ALTER PUBLICATION pubtables ADD TABLE test (y);
|
||||||
|
ALTER PUBLICATION pubtables SET TABLE test WHERE (doc IS DOCUMENT);
|
||||||
|
ALTER PUBLICATION pubtables SET TABLE test WHERE (xmlexists('//foo[text() = ''bar'']' PASSING BY VALUE doc));
|
||||||
|
ALTER PUBLICATION pubtables SET TABLE test WHERE (CASE x WHEN 5 THEN true ELSE false END);
|
||||||
|
SELECT DISTINCT c FROM (
|
||||||
|
SELECT unnest(result::text[]) c
|
||||||
|
FROM run_command_on_workers($$
|
||||||
|
SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' AND c LIKE '%pubtables%' ORDER BY 1) s$$)
|
||||||
|
ORDER BY c) s;
|
||||||
|
c
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
SELECT worker_create_or_replace_object('CREATE PUBLICATION pubtables FOR TABLE publication.test WHERE (CASE test.x WHEN 5 THEN true ELSE false END) WITH (publish_via_partition_root = ''false'', publish = ''insert, update, delete, truncate'')');
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
ALTER PUBLICATION pubtables SET TABLE test ("column-1", x) WHERE (x > "column-1"), "publication-1"."test-pubs";
|
||||||
|
-- operations on a local table
|
||||||
|
ALTER PUBLICATION pubtables DROP TABLE "publication-1"."test-pubs";
|
||||||
|
ALTER PUBLICATION pubtables ADD TABLE "publication-1"."test-pubs" (y);
|
||||||
|
-- mixed operations
|
||||||
|
ALTER PUBLICATION pubtables SET TABLE test, TABLES IN SCHEMA "publication-1", TABLES IN SCHEMA current_schema;
|
||||||
|
ALTER PUBLICATION pubtables SET TABLE "publication-1"."test-pubs", test ("column-1", x) WHERE (x > "column-1");
|
||||||
|
SELECT DISTINCT c FROM (
|
||||||
|
SELECT unnest(result::text[]) c
|
||||||
|
FROM run_command_on_workers($$
|
||||||
|
SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' AND c LIKE '%pubtables%' ORDER BY 1) s$$)
|
||||||
|
ORDER BY c) s;
|
||||||
|
c
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
SELECT worker_create_or_replace_object('CREATE PUBLICATION pubtables FOR TABLE publication.test (x, "column-1") WHERE ((test.x > test."column-1")) WITH (publish_via_partition_root = ''false'', publish = ''insert, update, delete, truncate'')');
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- publication with schemas
|
||||||
|
CREATE PUBLICATION "pub-mix" FOR TABLE test, TABLES IN SCHEMA current_schema, TABLE "publication-1"."test-pubs", TABLES IN SCHEMA "publication-1";
|
||||||
|
SELECT DISTINCT c FROM (
|
||||||
|
SELECT unnest(result::text[]) c
|
||||||
|
FROM run_command_on_workers($$
|
||||||
|
SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' AND c LIKE '%pub-mix%' ORDER BY 1) s$$)
|
||||||
|
ORDER BY c) s;
|
||||||
|
c
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
SELECT worker_create_or_replace_object('CREATE PUBLICATION "pub-mix" FOR TABLES IN SCHEMA publication, TABLES IN SCHEMA "publication-1", TABLE publication.test WITH (publish_via_partition_root = ''false'', publish = ''insert, update, delete, truncate'')');
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- publication on a partitioned table
|
||||||
|
CREATE PUBLICATION pubpartitioned FOR TABLE testpub_partitioned (a, b) WITH (publish_via_partition_root = 'true');
|
||||||
|
ALTER PUBLICATION pubpartitioned SET (publish_via_partition_root = 1);
|
||||||
|
SELECT alter_distributed_table('testpub_partitioned', shard_count := 6, cascade_to_colocated := true);
|
||||||
|
NOTICE: converting the partitions of publication.testpub_partitioned
|
||||||
|
NOTICE: creating a new table for publication.testpub_partitioned_0
|
||||||
|
NOTICE: moving the data of publication.testpub_partitioned_0
|
||||||
|
NOTICE: dropping the old publication.testpub_partitioned_0
|
||||||
|
NOTICE: renaming the new table to publication.testpub_partitioned_0
|
||||||
|
NOTICE: creating a new table for publication.testpub_partitioned_1
|
||||||
|
NOTICE: moving the data of publication.testpub_partitioned_1
|
||||||
|
NOTICE: dropping the old publication.testpub_partitioned_1
|
||||||
|
NOTICE: renaming the new table to publication.testpub_partitioned_1
|
||||||
|
NOTICE: creating a new table for publication.testpub_partitioned
|
||||||
|
NOTICE: dropping the old publication.testpub_partitioned
|
||||||
|
NOTICE: renaming the new table to publication.testpub_partitioned
|
||||||
|
NOTICE: creating a new table for publication.test
|
||||||
|
NOTICE: moving the data of publication.test
|
||||||
|
NOTICE: dropping the old publication.test
|
||||||
|
NOTICE: renaming the new table to publication.test
|
||||||
|
alter_distributed_table
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT DISTINCT c FROM (
|
||||||
|
SELECT unnest(result::text[]) c
|
||||||
|
FROM run_command_on_workers($$
|
||||||
|
SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' AND c LIKE '%pubpartitioned%' ORDER BY 1) s$$)
|
||||||
|
ORDER BY c) s;
|
||||||
|
c
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
SELECT worker_create_or_replace_object('CREATE PUBLICATION pubpartitioned FOR TABLE publication.testpub_partitioned (a, b) WITH (publish_via_partition_root = ''true'', publish = ''insert, update, delete, truncate'')');
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- make sure we propagate schema dependencies
|
||||||
|
SET citus.create_object_propagation TO 'deferred';
|
||||||
|
BEGIN;
|
||||||
|
CREATE SCHEMA deptest;
|
||||||
|
END;
|
||||||
|
CREATE PUBLICATION pubdep FOR TABLES IN SCHEMA deptest;
|
||||||
|
RESET citus.create_object_propagation;
|
||||||
|
DROP SCHEMA deptest CASCADE;
|
||||||
|
-- make sure we can sync all the publication metadata
|
||||||
|
SELECT start_metadata_sync_to_all_nodes();
|
||||||
|
start_metadata_sync_to_all_nodes
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
DROP PUBLICATION pubdep;
|
||||||
|
DROP PUBLICATION "pub-mix";
|
||||||
|
DROP PUBLICATION pubtables;
|
||||||
|
DROP PUBLICATION pubpartitioned;
|
||||||
|
SET client_min_messages TO ERROR;
|
||||||
|
DROP SCHEMA publication CASCADE;
|
||||||
|
DROP SCHEMA "publication-1" CASCADE;
|
||||||
|
SELECT citus_remove_node('localhost', :master_port);
|
||||||
|
citus_remove_node
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
|
@ -0,0 +1,273 @@
|
||||||
|
CREATE SCHEMA publication;
|
||||||
|
CREATE SCHEMA "publication-1";
|
||||||
|
SET search_path TO publication;
|
||||||
|
SET citus.shard_replication_factor TO 1;
|
||||||
|
-- for citus_add_local_table_to_metadata / create_distributed_table_concurrently
|
||||||
|
SELECT citus_set_coordinator_host('localhost', :master_port);
|
||||||
|
citus_set_coordinator_host
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION activate_node_snapshot()
|
||||||
|
RETURNS text[]
|
||||||
|
LANGUAGE C STRICT
|
||||||
|
AS 'citus';
|
||||||
|
COMMENT ON FUNCTION activate_node_snapshot()
|
||||||
|
IS 'commands to activate node snapshot';
|
||||||
|
\c - - - :worker_1_port
|
||||||
|
SET citus.enable_ddl_propagation TO off;
|
||||||
|
CREATE OR REPLACE FUNCTION activate_node_snapshot()
|
||||||
|
RETURNS text[]
|
||||||
|
LANGUAGE C STRICT
|
||||||
|
AS 'citus';
|
||||||
|
COMMENT ON FUNCTION activate_node_snapshot()
|
||||||
|
IS 'commands to activate node snapshot';
|
||||||
|
\c - - - :worker_2_port
|
||||||
|
SET citus.enable_ddl_propagation TO off;
|
||||||
|
CREATE OR REPLACE FUNCTION activate_node_snapshot()
|
||||||
|
RETURNS text[]
|
||||||
|
LANGUAGE C STRICT
|
||||||
|
AS 'citus';
|
||||||
|
COMMENT ON FUNCTION activate_node_snapshot()
|
||||||
|
IS 'commands to activate node snapshot';
|
||||||
|
-- create some publications with conflicting names on worker node
|
||||||
|
-- publication will be different from coordinator
|
||||||
|
CREATE PUBLICATION "pub-all";
|
||||||
|
-- publication will be same as coordinator
|
||||||
|
CREATE PUBLICATION "pub-all-insertupdateonly" FOR ALL TABLES WITH (publish = 'insert, update');;
|
||||||
|
\c - - - :master_port
|
||||||
|
SET search_path TO publication;
|
||||||
|
SET citus.shard_replication_factor TO 1;
|
||||||
|
-- do not create publications on worker 2 initially
|
||||||
|
SELECT citus_remove_node('localhost', :worker_2_port);
|
||||||
|
citus_remove_node
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- create a non-distributed publication
|
||||||
|
SET citus.enable_ddl_propagation TO off;
|
||||||
|
CREATE PUBLICATION pubnotdistributed WITH (publish = 'delete');
|
||||||
|
RESET citus.enable_ddl_propagation;
|
||||||
|
ALTER PUBLICATION pubnotdistributed SET (publish = 'truncate');
|
||||||
|
-- create regular, distributed publications
|
||||||
|
CREATE PUBLICATION pubempty;
|
||||||
|
CREATE PUBLICATION pubinsertonly WITH (publish = 'insert');
|
||||||
|
CREATE PUBLICATION "pub-all" FOR ALL TABLES;
|
||||||
|
CREATE PUBLICATION "pub-all-insertupdateonly" FOR ALL TABLES WITH (publish = 'insert, update');
|
||||||
|
-- add worker 2 with publications
|
||||||
|
SELECT 1 FROM citus_add_node('localhost', :worker_2_port);
|
||||||
|
?column?
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- Check publications on all the nodes, if we see the same publication name twice then its definition differs
|
||||||
|
-- Note that publications are special in the sense that the coordinator object might differ from
|
||||||
|
-- worker objects due to the presence of regular tables.
|
||||||
|
SELECT DISTINCT c FROM (
|
||||||
|
SELECT unnest(result::text[]) c
|
||||||
|
FROM run_command_on_workers($$
|
||||||
|
SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' ORDER BY 1) s$$)
|
||||||
|
ORDER BY c) s;
|
||||||
|
c
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
SELECT worker_create_or_replace_object('CREATE PUBLICATION "pub-all" FOR ALL TABLES WITH (publish_via_partition_root = ''false'', publish = ''insert, update, delete, truncate'')');
|
||||||
|
SELECT worker_create_or_replace_object('CREATE PUBLICATION "pub-all-insertupdateonly" FOR ALL TABLES WITH (publish_via_partition_root = ''false'', publish = ''insert, update'')');
|
||||||
|
SELECT worker_create_or_replace_object('CREATE PUBLICATION pubempty WITH (publish_via_partition_root = ''false'', publish = ''insert, update, delete, truncate'')');
|
||||||
|
SELECT worker_create_or_replace_object('CREATE PUBLICATION pubinsertonly WITH (publish_via_partition_root = ''false'', publish = ''insert'')');
|
||||||
|
(4 rows)
|
||||||
|
|
||||||
|
CREATE TABLE test (x int primary key, y int, "column-1" int, doc xml);
|
||||||
|
CREATE TABLE "test-pubs" (x int primary key, y int, "column-1" int);
|
||||||
|
CREATE TABLE "publication-1"."test-pubs" (x int primary key, y int, "column-1" int);
|
||||||
|
-- various operations on a publication with only local tables
|
||||||
|
CREATE PUBLICATION pubtables_orig FOR TABLE test, "test-pubs", "publication-1"."test-pubs" WITH (publish = 'insert, truncate');
|
||||||
|
ALTER PUBLICATION pubtables_orig DROP TABLE test;
|
||||||
|
ALTER PUBLICATION pubtables_orig ADD TABLE test;
|
||||||
|
-- publication will be empty on worker nodes, since all tables are local
|
||||||
|
SELECT DISTINCT c FROM (
|
||||||
|
SELECT unnest(result::text[]) c
|
||||||
|
FROM run_command_on_workers($$
|
||||||
|
SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' AND c LIKE '%pubtables%' ORDER BY 1) s$$)
|
||||||
|
ORDER BY c) s;
|
||||||
|
c
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
SELECT worker_create_or_replace_object('CREATE PUBLICATION pubtables_orig WITH (publish_via_partition_root = ''false'', publish = ''insert, truncate'')');
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- distribute a table, creating a mixed publication
|
||||||
|
SELECT create_distributed_table('test','x', colocate_with := 'none');
|
||||||
|
create_distributed_table
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- some generic operations
|
||||||
|
ALTER PUBLICATION pubtables_orig RENAME TO pubtables;
|
||||||
|
ALTER PUBLICATION pubtables SET (publish = 'insert, update, delete');
|
||||||
|
ALTER PUBLICATION pubtables OWNER TO postgres;
|
||||||
|
ALTER PUBLICATION pubtables SET (publish = 'inert, update, delete');
|
||||||
|
ERROR: unrecognized "publish" value: "inert"
|
||||||
|
ALTER PUBLICATION pubtables ADD TABLE notexist;
|
||||||
|
ERROR: relation "notexist" does not exist
|
||||||
|
-- operations with a distributed table
|
||||||
|
ALTER PUBLICATION pubtables DROP TABLE test;
|
||||||
|
ALTER PUBLICATION pubtables ADD TABLE test;
|
||||||
|
ALTER PUBLICATION pubtables SET TABLE test, "test-pubs", "publication-1"."test-pubs";
|
||||||
|
-- operations with a local table in a mixed publication
|
||||||
|
ALTER PUBLICATION pubtables DROP TABLE "test-pubs";
|
||||||
|
ALTER PUBLICATION pubtables ADD TABLE "test-pubs";
|
||||||
|
SELECT create_distributed_table('"test-pubs"', 'x');
|
||||||
|
create_distributed_table
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- test and test-pubs will show up in worker nodes
|
||||||
|
SELECT DISTINCT c FROM (
|
||||||
|
SELECT unnest(result::text[]) c
|
||||||
|
FROM run_command_on_workers($$
|
||||||
|
SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' AND c LIKE '%pubtables%' ORDER BY 1) s$$)
|
||||||
|
ORDER BY c) s;
|
||||||
|
c
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
SELECT worker_create_or_replace_object('CREATE PUBLICATION pubtables FOR TABLE publication.test, publication."test-pubs" WITH (publish_via_partition_root = ''false'', publish = ''insert, update, delete'')');
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- operations with a strangely named distributed table in a mixed publication
|
||||||
|
ALTER PUBLICATION pubtables DROP TABLE "test-pubs";
|
||||||
|
ALTER PUBLICATION pubtables ADD TABLE "test-pubs";
|
||||||
|
-- create a publication with distributed and local tables
|
||||||
|
DROP PUBLICATION pubtables;
|
||||||
|
CREATE PUBLICATION pubtables FOR TABLE test, "test-pubs", "publication-1"."test-pubs";
|
||||||
|
-- change distributed tables
|
||||||
|
SELECT alter_distributed_table('test', shard_count := 5, cascade_to_colocated := true);
|
||||||
|
NOTICE: creating a new table for publication.test
|
||||||
|
NOTICE: moving the data of publication.test
|
||||||
|
NOTICE: dropping the old publication.test
|
||||||
|
NOTICE: renaming the new table to publication.test
|
||||||
|
NOTICE: creating a new table for publication."test-pubs"
|
||||||
|
NOTICE: moving the data of publication."test-pubs"
|
||||||
|
NOTICE: dropping the old publication."test-pubs"
|
||||||
|
NOTICE: renaming the new table to publication."test-pubs"
|
||||||
|
alter_distributed_table
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT undistribute_table('test');
|
||||||
|
NOTICE: creating a new table for publication.test
|
||||||
|
NOTICE: moving the data of publication.test
|
||||||
|
NOTICE: dropping the old publication.test
|
||||||
|
NOTICE: renaming the new table to publication.test
|
||||||
|
undistribute_table
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT citus_add_local_table_to_metadata('test');
|
||||||
|
citus_add_local_table_to_metadata
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT create_distributed_table_concurrently('test', 'x');
|
||||||
|
create_distributed_table_concurrently
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT undistribute_table('"test-pubs"');
|
||||||
|
NOTICE: creating a new table for publication."test-pubs"
|
||||||
|
NOTICE: moving the data of publication."test-pubs"
|
||||||
|
NOTICE: dropping the old publication."test-pubs"
|
||||||
|
NOTICE: renaming the new table to publication."test-pubs"
|
||||||
|
undistribute_table
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT create_reference_table('"test-pubs"');
|
||||||
|
create_reference_table
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- publications are unchanged despite various tranformations
|
||||||
|
SELECT DISTINCT c FROM (
|
||||||
|
SELECT unnest(result::text[]) c
|
||||||
|
FROM run_command_on_workers($$
|
||||||
|
SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' AND c LIKE '%pubtables%' ORDER BY 1) s$$)
|
||||||
|
ORDER BY c) s;
|
||||||
|
c
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
SELECT worker_create_or_replace_object('CREATE PUBLICATION pubtables FOR TABLE publication.test, publication."test-pubs" WITH (publish_via_partition_root = ''false'', publish = ''insert, update, delete, truncate'')');
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- partitioned table
|
||||||
|
CREATE TABLE testpub_partitioned (a int, b text, c text) PARTITION BY RANGE (a);
|
||||||
|
CREATE TABLE testpub_partitioned_0 PARTITION OF testpub_partitioned FOR VALUES FROM (1) TO (10);
|
||||||
|
ALTER TABLE testpub_partitioned_0 ADD PRIMARY KEY (a);
|
||||||
|
ALTER TABLE testpub_partitioned_0 REPLICA IDENTITY USING INDEX testpub_partitioned_0_pkey;
|
||||||
|
CREATE TABLE testpub_partitioned_1 PARTITION OF testpub_partitioned FOR VALUES FROM (11) TO (20);
|
||||||
|
ALTER TABLE testpub_partitioned_1 ADD PRIMARY KEY (a);
|
||||||
|
ALTER TABLE testpub_partitioned_1 REPLICA IDENTITY USING INDEX testpub_partitioned_1_pkey;
|
||||||
|
CREATE PUBLICATION pubpartitioned FOR TABLE testpub_partitioned WITH (publish_via_partition_root = 'true');
|
||||||
|
SELECT create_distributed_table('testpub_partitioned', 'a');
|
||||||
|
create_distributed_table
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT DISTINCT c FROM (
|
||||||
|
SELECT unnest(result::text[]) c
|
||||||
|
FROM run_command_on_workers($$
|
||||||
|
SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' AND c LIKE '%pubpartitioned%' ORDER BY 1) s$$)
|
||||||
|
ORDER BY c) s;
|
||||||
|
c
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
SELECT worker_create_or_replace_object('CREATE PUBLICATION pubpartitioned FOR TABLE publication.testpub_partitioned WITH (publish_via_partition_root = ''true'', publish = ''insert, update, delete, truncate'')');
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
DROP PUBLICATION pubpartitioned;
|
||||||
|
CREATE PUBLICATION pubpartitioned FOR TABLE testpub_partitioned WITH (publish_via_partition_root = 'true');
|
||||||
|
-- add a partition
|
||||||
|
ALTER PUBLICATION pubpartitioned ADD TABLE testpub_partitioned_1;
|
||||||
|
SELECT DISTINCT c FROM (
|
||||||
|
SELECT unnest(result::text[]) c
|
||||||
|
FROM run_command_on_workers($$
|
||||||
|
SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLIATION%' AND c LIKE '%pubpartitioned%' ORDER BY 1) s$$)
|
||||||
|
ORDER BY c) s;
|
||||||
|
ERROR: malformed array literal: ""
|
||||||
|
DETAIL: Array value must start with "{" or dimension information.
|
||||||
|
-- make sure we can sync all the publication metadata
|
||||||
|
SELECT start_metadata_sync_to_all_nodes();
|
||||||
|
start_metadata_sync_to_all_nodes
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
DROP PUBLICATION pubempty;
|
||||||
|
DROP PUBLICATION pubtables;
|
||||||
|
DROP PUBLICATION pubinsertonly;
|
||||||
|
DROP PUBLICATION "pub-all-insertupdateonly";
|
||||||
|
DROP PUBLICATION "pub-all";
|
||||||
|
DROP PUBLICATION pubpartitioned;
|
||||||
|
DROP PUBLICATION pubnotdistributed;
|
||||||
|
SHOW server_version \gset
|
||||||
|
SELECT substring(:'server_version', '\d+')::int >= 15 AS server_version_ge_15
|
||||||
|
\gset
|
||||||
|
\if :server_version_ge_15
|
||||||
|
\else
|
||||||
|
SET client_min_messages TO ERROR;
|
||||||
|
DROP SCHEMA publication CASCADE;
|
||||||
|
DROP SCHEMA "publication-1" CASCADE;
|
||||||
|
SELECT citus_remove_node('localhost', :master_port);
|
||||||
|
citus_remove_node
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
\q
|
|
@ -61,7 +61,9 @@ SET search_path TO split_shard_replication_setup_schema;
|
||||||
CREATE TABLE table_to_split_2(id bigserial PRIMARY KEY, value char);
|
CREATE TABLE table_to_split_2(id bigserial PRIMARY KEY, value char);
|
||||||
CREATE TABLE table_to_split_3(id bigserial PRIMARY KEY, value char);
|
CREATE TABLE table_to_split_3(id bigserial PRIMARY KEY, value char);
|
||||||
-- Create publication at worker1
|
-- Create publication at worker1
|
||||||
|
SET citus.enable_ddl_propagation TO off;
|
||||||
CREATE PUBLICATION pub1 FOR TABLE table_to_split_1, table_to_split_2, table_to_split_3;
|
CREATE PUBLICATION pub1 FOR TABLE table_to_split_1, table_to_split_2, table_to_split_3;
|
||||||
|
RESET citus.enable_ddl_propagation;
|
||||||
SELECT count(*) FROM pg_catalog.worker_split_shard_replication_setup(ARRAY[
|
SELECT count(*) FROM pg_catalog.worker_split_shard_replication_setup(ARRAY[
|
||||||
ROW(1, 'id', 2, '-2147483648', '-1', :worker_2_node)::pg_catalog.split_shard_info,
|
ROW(1, 'id', 2, '-2147483648', '-1', :worker_2_node)::pg_catalog.split_shard_info,
|
||||||
ROW(1, 'id', 3, '0', '2147483647', :worker_2_node)::pg_catalog.split_shard_info
|
ROW(1, 'id', 3, '0', '2147483647', :worker_2_node)::pg_catalog.split_shard_info
|
||||||
|
@ -261,7 +263,9 @@ SELECT nodeid AS worker_2_node FROM pg_dist_node WHERE nodeport=:worker_2_port \
|
||||||
\c - - - :worker_1_port
|
\c - - - :worker_1_port
|
||||||
SET search_path TO split_shard_replication_setup_schema;
|
SET search_path TO split_shard_replication_setup_schema;
|
||||||
-- Create publication at worker1
|
-- Create publication at worker1
|
||||||
|
SET citus.enable_ddl_propagation TO off;
|
||||||
CREATE PUBLICATION pub1 FOR TABLE table_to_split_1, table_to_split_2, table_to_split_3;
|
CREATE PUBLICATION pub1 FOR TABLE table_to_split_1, table_to_split_2, table_to_split_3;
|
||||||
|
RESET citus.enable_ddl_propagation;
|
||||||
SELECT count(*) FROM pg_catalog.worker_split_shard_replication_setup(ARRAY[
|
SELECT count(*) FROM pg_catalog.worker_split_shard_replication_setup(ARRAY[
|
||||||
ROW(1, 'id', 2, '-2147483648', '-1', :worker_1_node)::pg_catalog.split_shard_info,
|
ROW(1, 'id', 2, '-2147483648', '-1', :worker_1_node)::pg_catalog.split_shard_info,
|
||||||
ROW(1, 'id', 3, '0', '2147483647', :worker_2_node)::pg_catalog.split_shard_info
|
ROW(1, 'id', 3, '0', '2147483647', :worker_2_node)::pg_catalog.split_shard_info
|
||||||
|
@ -428,7 +432,9 @@ SELECT nodeid AS worker_2_node FROM pg_dist_node WHERE nodeport=:worker_2_port \
|
||||||
SET search_path TO split_shard_replication_setup_schema;
|
SET search_path TO split_shard_replication_setup_schema;
|
||||||
SET client_min_messages TO ERROR;
|
SET client_min_messages TO ERROR;
|
||||||
-- Create publication at worker1
|
-- Create publication at worker1
|
||||||
|
SET citus.enable_ddl_propagation TO off;
|
||||||
CREATE PUBLICATION pub1 for table table_to_split_1, table_to_split_2, table_to_split_3;
|
CREATE PUBLICATION pub1 for table table_to_split_1, table_to_split_2, table_to_split_3;
|
||||||
|
RESET citus.enable_ddl_propagation;
|
||||||
-- Worker1 is target for table_to_split_2 and table_to_split_3
|
-- Worker1 is target for table_to_split_2 and table_to_split_3
|
||||||
SELECT count(*) FROM pg_catalog.worker_split_shard_replication_setup(ARRAY[
|
SELECT count(*) FROM pg_catalog.worker_split_shard_replication_setup(ARRAY[
|
||||||
ROW(1, 'id', 2, '-2147483648', '-1', :worker_1_node)::pg_catalog.split_shard_info,
|
ROW(1, 'id', 2, '-2147483648', '-1', :worker_1_node)::pg_catalog.split_shard_info,
|
||||||
|
@ -597,8 +603,10 @@ CREATE TABLE table_second_9(id bigserial PRIMARY KEY, value char);
|
||||||
-- Create publication at worker1
|
-- Create publication at worker1
|
||||||
\c - postgres - :worker_1_port
|
\c - postgres - :worker_1_port
|
||||||
SET search_path TO split_shard_replication_setup_schema;
|
SET search_path TO split_shard_replication_setup_schema;
|
||||||
|
SET citus.enable_ddl_propagation TO off;
|
||||||
CREATE PUBLICATION pub1 FOR TABLE table_first_4, table_first_5, table_first_6;
|
CREATE PUBLICATION pub1 FOR TABLE table_first_4, table_first_5, table_first_6;
|
||||||
CREATE PUBLICATION pub2 FOR TABLE table_second_7, table_second_8, table_second_9;
|
CREATE PUBLICATION pub2 FOR TABLE table_second_7, table_second_8, table_second_9;
|
||||||
|
RESET citus.enable_ddl_propagation;
|
||||||
SELECT count(*) FROM pg_catalog.worker_split_shard_replication_setup(ARRAY[
|
SELECT count(*) FROM pg_catalog.worker_split_shard_replication_setup(ARRAY[
|
||||||
ROW(4, 'id', 5, '-2147483648', '-1', :worker_2_node)::pg_catalog.split_shard_info,
|
ROW(4, 'id', 5, '-2147483648', '-1', :worker_2_node)::pg_catalog.split_shard_info,
|
||||||
ROW(4, 'id', 6, '0', '2147483647', :worker_2_node)::pg_catalog.split_shard_info,
|
ROW(4, 'id', 6, '0', '2147483647', :worker_2_node)::pg_catalog.split_shard_info,
|
||||||
|
|
|
@ -21,7 +21,9 @@ SELECT 1 from citus_add_node('localhost', :master_port, groupId := 0);
|
||||||
-- This allows us to test the cleanup logic at the start of the shard move.
|
-- This allows us to test the cleanup logic at the start of the shard move.
|
||||||
\c - - - :worker_1_port
|
\c - - - :worker_1_port
|
||||||
SET search_path TO logical_replication;
|
SET search_path TO logical_replication;
|
||||||
|
SET citus.enable_ddl_propagation TO off;
|
||||||
CREATE PUBLICATION citus_shard_move_publication_:postgres_oid FOR TABLE dist_6830000;
|
CREATE PUBLICATION citus_shard_move_publication_:postgres_oid FOR TABLE dist_6830000;
|
||||||
|
RESET citus.enable_ddl_propagation;
|
||||||
|
|
||||||
\c - - - :master_port
|
\c - - - :master_port
|
||||||
SET search_path TO logical_replication;
|
SET search_path TO logical_replication;
|
||||||
|
@ -72,6 +74,9 @@ SELECT count(*) from pg_publication;
|
||||||
SELECT count(*) from pg_replication_slots;
|
SELECT count(*) from pg_replication_slots;
|
||||||
SELECT count(*) from dist;
|
SELECT count(*) from dist;
|
||||||
|
|
||||||
|
DROP PUBLICATION citus_shard_move_publication_:postgres_oid;
|
||||||
|
SELECT pg_drop_replication_slot('citus_shard_move_slot_' || :postgres_oid);
|
||||||
|
|
||||||
\c - - - :worker_2_port
|
\c - - - :worker_2_port
|
||||||
SET search_path TO logical_replication;
|
SET search_path TO logical_replication;
|
||||||
|
|
||||||
|
@ -88,3 +93,4 @@ ALTER SUBSCRIPTION citus_shard_move_subscription_:postgres_oid DISABLE;
|
||||||
ALTER SUBSCRIPTION citus_shard_move_subscription_:postgres_oid SET (slot_name = NONE);
|
ALTER SUBSCRIPTION citus_shard_move_subscription_:postgres_oid SET (slot_name = NONE);
|
||||||
DROP SUBSCRIPTION citus_shard_move_subscription_:postgres_oid;
|
DROP SUBSCRIPTION citus_shard_move_subscription_:postgres_oid;
|
||||||
DROP SCHEMA logical_replication CASCADE;
|
DROP SCHEMA logical_replication CASCADE;
|
||||||
|
SELECT public.wait_for_resource_cleanup();
|
||||||
|
|
|
@ -429,12 +429,15 @@ BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;
|
||||||
SET application_name to 'citus_internal gpid=10000000001';
|
SET application_name to 'citus_internal gpid=10000000001';
|
||||||
\set VERBOSITY terse
|
\set VERBOSITY terse
|
||||||
|
|
||||||
CREATE TABLE publication_test_table(id int);
|
CREATE OPERATOR === (
|
||||||
CREATE PUBLICATION publication_test FOR TABLE publication_test_table;
|
LEFTARG = int,
|
||||||
|
RIGHTARG = int,
|
||||||
|
FUNCTION = int4eq
|
||||||
|
);
|
||||||
|
|
||||||
SET ROLE metadata_sync_helper_role;
|
SET ROLE metadata_sync_helper_role;
|
||||||
WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation)
|
WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation)
|
||||||
AS (VALUES ('publication', ARRAY['publication_test']::text[], ARRAY[]::text[], -1, 0, false))
|
AS (VALUES ('operator', ARRAY['===']::text[], ARRAY['int','int']::text[], -1, 0, false))
|
||||||
SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) FROM distributed_object_data;
|
SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) FROM distributed_object_data;
|
||||||
ROLLBACK;
|
ROLLBACK;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,269 @@
|
||||||
|
CREATE SCHEMA publication;
|
||||||
|
CREATE SCHEMA "publication-1";
|
||||||
|
SET search_path TO publication;
|
||||||
|
SET citus.shard_replication_factor TO 1;
|
||||||
|
|
||||||
|
-- for citus_add_local_table_to_metadata / create_distributed_table_concurrently
|
||||||
|
SELECT citus_set_coordinator_host('localhost', :master_port);
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION activate_node_snapshot()
|
||||||
|
RETURNS text[]
|
||||||
|
LANGUAGE C STRICT
|
||||||
|
AS 'citus';
|
||||||
|
COMMENT ON FUNCTION activate_node_snapshot()
|
||||||
|
IS 'commands to activate node snapshot';
|
||||||
|
|
||||||
|
\c - - - :worker_1_port
|
||||||
|
SET citus.enable_ddl_propagation TO off;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION activate_node_snapshot()
|
||||||
|
RETURNS text[]
|
||||||
|
LANGUAGE C STRICT
|
||||||
|
AS 'citus';
|
||||||
|
COMMENT ON FUNCTION activate_node_snapshot()
|
||||||
|
IS 'commands to activate node snapshot';
|
||||||
|
|
||||||
|
\c - - - :worker_2_port
|
||||||
|
SET citus.enable_ddl_propagation TO off;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION activate_node_snapshot()
|
||||||
|
RETURNS text[]
|
||||||
|
LANGUAGE C STRICT
|
||||||
|
AS 'citus';
|
||||||
|
COMMENT ON FUNCTION activate_node_snapshot()
|
||||||
|
IS 'commands to activate node snapshot';
|
||||||
|
|
||||||
|
-- create some publications with conflicting names on worker node
|
||||||
|
|
||||||
|
-- publication will be different from coordinator
|
||||||
|
CREATE PUBLICATION "pub-all";
|
||||||
|
-- publication will be same as coordinator
|
||||||
|
CREATE PUBLICATION "pub-all-insertupdateonly" FOR ALL TABLES WITH (publish = 'insert, update');;
|
||||||
|
|
||||||
|
\c - - - :master_port
|
||||||
|
SET search_path TO publication;
|
||||||
|
SET citus.shard_replication_factor TO 1;
|
||||||
|
|
||||||
|
-- do not create publications on worker 2 initially
|
||||||
|
SELECT citus_remove_node('localhost', :worker_2_port);
|
||||||
|
|
||||||
|
-- create a non-distributed publication
|
||||||
|
SET citus.enable_ddl_propagation TO off;
|
||||||
|
CREATE PUBLICATION pubnotdistributed WITH (publish = 'delete');
|
||||||
|
RESET citus.enable_ddl_propagation;
|
||||||
|
ALTER PUBLICATION pubnotdistributed SET (publish = 'truncate');
|
||||||
|
|
||||||
|
-- create regular, distributed publications
|
||||||
|
CREATE PUBLICATION pubempty;
|
||||||
|
CREATE PUBLICATION pubinsertonly WITH (publish = 'insert');
|
||||||
|
CREATE PUBLICATION "pub-all" FOR ALL TABLES;
|
||||||
|
CREATE PUBLICATION "pub-all-insertupdateonly" FOR ALL TABLES WITH (publish = 'insert, update');
|
||||||
|
|
||||||
|
-- add worker 2 with publications
|
||||||
|
SELECT 1 FROM citus_add_node('localhost', :worker_2_port);
|
||||||
|
|
||||||
|
-- Check publications on all the nodes, if we see the same publication name twice then its definition differs
|
||||||
|
-- Note that publications are special in the sense that the coordinator object might differ from
|
||||||
|
-- worker objects due to the presence of regular tables.
|
||||||
|
SELECT DISTINCT c FROM (
|
||||||
|
SELECT unnest(result::text[]) c
|
||||||
|
FROM run_command_on_workers($$
|
||||||
|
SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' ORDER BY 1) s$$)
|
||||||
|
ORDER BY c) s;
|
||||||
|
|
||||||
|
CREATE TABLE test (x int primary key, y int, "column-1" int, doc xml);
|
||||||
|
CREATE TABLE "test-pubs" (x int primary key, y int, "column-1" int);
|
||||||
|
CREATE TABLE "publication-1"."test-pubs" (x int primary key, y int, "column-1" int);
|
||||||
|
|
||||||
|
-- various operations on a publication with only local tables
|
||||||
|
CREATE PUBLICATION pubtables_orig FOR TABLE test, "test-pubs", "publication-1"."test-pubs" WITH (publish = 'insert, truncate');
|
||||||
|
ALTER PUBLICATION pubtables_orig DROP TABLE test;
|
||||||
|
ALTER PUBLICATION pubtables_orig ADD TABLE test;
|
||||||
|
|
||||||
|
-- publication will be empty on worker nodes, since all tables are local
|
||||||
|
SELECT DISTINCT c FROM (
|
||||||
|
SELECT unnest(result::text[]) c
|
||||||
|
FROM run_command_on_workers($$
|
||||||
|
SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' AND c LIKE '%pubtables%' ORDER BY 1) s$$)
|
||||||
|
ORDER BY c) s;
|
||||||
|
|
||||||
|
-- distribute a table, creating a mixed publication
|
||||||
|
SELECT create_distributed_table('test','x', colocate_with := 'none');
|
||||||
|
|
||||||
|
-- some generic operations
|
||||||
|
ALTER PUBLICATION pubtables_orig RENAME TO pubtables;
|
||||||
|
ALTER PUBLICATION pubtables SET (publish = 'insert, update, delete');
|
||||||
|
ALTER PUBLICATION pubtables OWNER TO postgres;
|
||||||
|
ALTER PUBLICATION pubtables SET (publish = 'inert, update, delete');
|
||||||
|
ALTER PUBLICATION pubtables ADD TABLE notexist;
|
||||||
|
|
||||||
|
-- operations with a distributed table
|
||||||
|
ALTER PUBLICATION pubtables DROP TABLE test;
|
||||||
|
ALTER PUBLICATION pubtables ADD TABLE test;
|
||||||
|
ALTER PUBLICATION pubtables SET TABLE test, "test-pubs", "publication-1"."test-pubs";
|
||||||
|
|
||||||
|
-- operations with a local table in a mixed publication
|
||||||
|
ALTER PUBLICATION pubtables DROP TABLE "test-pubs";
|
||||||
|
ALTER PUBLICATION pubtables ADD TABLE "test-pubs";
|
||||||
|
|
||||||
|
SELECT create_distributed_table('"test-pubs"', 'x');
|
||||||
|
|
||||||
|
-- test and test-pubs will show up in worker nodes
|
||||||
|
SELECT DISTINCT c FROM (
|
||||||
|
SELECT unnest(result::text[]) c
|
||||||
|
FROM run_command_on_workers($$
|
||||||
|
SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' AND c LIKE '%pubtables%' ORDER BY 1) s$$)
|
||||||
|
ORDER BY c) s;
|
||||||
|
|
||||||
|
-- operations with a strangely named distributed table in a mixed publication
|
||||||
|
ALTER PUBLICATION pubtables DROP TABLE "test-pubs";
|
||||||
|
ALTER PUBLICATION pubtables ADD TABLE "test-pubs";
|
||||||
|
|
||||||
|
-- create a publication with distributed and local tables
|
||||||
|
DROP PUBLICATION pubtables;
|
||||||
|
CREATE PUBLICATION pubtables FOR TABLE test, "test-pubs", "publication-1"."test-pubs";
|
||||||
|
|
||||||
|
-- change distributed tables
|
||||||
|
SELECT alter_distributed_table('test', shard_count := 5, cascade_to_colocated := true);
|
||||||
|
SELECT undistribute_table('test');
|
||||||
|
SELECT citus_add_local_table_to_metadata('test');
|
||||||
|
SELECT create_distributed_table_concurrently('test', 'x');
|
||||||
|
SELECT undistribute_table('"test-pubs"');
|
||||||
|
SELECT create_reference_table('"test-pubs"');
|
||||||
|
|
||||||
|
-- publications are unchanged despite various tranformations
|
||||||
|
SELECT DISTINCT c FROM (
|
||||||
|
SELECT unnest(result::text[]) c
|
||||||
|
FROM run_command_on_workers($$
|
||||||
|
SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' AND c LIKE '%pubtables%' ORDER BY 1) s$$)
|
||||||
|
ORDER BY c) s;
|
||||||
|
|
||||||
|
-- partitioned table
|
||||||
|
CREATE TABLE testpub_partitioned (a int, b text, c text) PARTITION BY RANGE (a);
|
||||||
|
CREATE TABLE testpub_partitioned_0 PARTITION OF testpub_partitioned FOR VALUES FROM (1) TO (10);
|
||||||
|
ALTER TABLE testpub_partitioned_0 ADD PRIMARY KEY (a);
|
||||||
|
ALTER TABLE testpub_partitioned_0 REPLICA IDENTITY USING INDEX testpub_partitioned_0_pkey;
|
||||||
|
CREATE TABLE testpub_partitioned_1 PARTITION OF testpub_partitioned FOR VALUES FROM (11) TO (20);
|
||||||
|
ALTER TABLE testpub_partitioned_1 ADD PRIMARY KEY (a);
|
||||||
|
ALTER TABLE testpub_partitioned_1 REPLICA IDENTITY USING INDEX testpub_partitioned_1_pkey;
|
||||||
|
CREATE PUBLICATION pubpartitioned FOR TABLE testpub_partitioned WITH (publish_via_partition_root = 'true');
|
||||||
|
|
||||||
|
SELECT create_distributed_table('testpub_partitioned', 'a');
|
||||||
|
|
||||||
|
SELECT DISTINCT c FROM (
|
||||||
|
SELECT unnest(result::text[]) c
|
||||||
|
FROM run_command_on_workers($$
|
||||||
|
SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' AND c LIKE '%pubpartitioned%' ORDER BY 1) s$$)
|
||||||
|
ORDER BY c) s;
|
||||||
|
|
||||||
|
DROP PUBLICATION pubpartitioned;
|
||||||
|
CREATE PUBLICATION pubpartitioned FOR TABLE testpub_partitioned WITH (publish_via_partition_root = 'true');
|
||||||
|
|
||||||
|
-- add a partition
|
||||||
|
ALTER PUBLICATION pubpartitioned ADD TABLE testpub_partitioned_1;
|
||||||
|
|
||||||
|
SELECT DISTINCT c FROM (
|
||||||
|
SELECT unnest(result::text[]) c
|
||||||
|
FROM run_command_on_workers($$
|
||||||
|
SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLIATION%' AND c LIKE '%pubpartitioned%' ORDER BY 1) s$$)
|
||||||
|
ORDER BY c) s;
|
||||||
|
|
||||||
|
-- make sure we can sync all the publication metadata
|
||||||
|
SELECT start_metadata_sync_to_all_nodes();
|
||||||
|
|
||||||
|
DROP PUBLICATION pubempty;
|
||||||
|
DROP PUBLICATION pubtables;
|
||||||
|
DROP PUBLICATION pubinsertonly;
|
||||||
|
DROP PUBLICATION "pub-all-insertupdateonly";
|
||||||
|
DROP PUBLICATION "pub-all";
|
||||||
|
DROP PUBLICATION pubpartitioned;
|
||||||
|
DROP PUBLICATION pubnotdistributed;
|
||||||
|
|
||||||
|
SHOW server_version \gset
|
||||||
|
SELECT substring(:'server_version', '\d+')::int >= 15 AS server_version_ge_15
|
||||||
|
\gset
|
||||||
|
\if :server_version_ge_15
|
||||||
|
\else
|
||||||
|
SET client_min_messages TO ERROR;
|
||||||
|
DROP SCHEMA publication CASCADE;
|
||||||
|
DROP SCHEMA "publication-1" CASCADE;
|
||||||
|
|
||||||
|
SELECT citus_remove_node('localhost', :master_port);
|
||||||
|
\q
|
||||||
|
\endif
|
||||||
|
|
||||||
|
-- recreate a mixed publication
|
||||||
|
CREATE PUBLICATION pubtables FOR TABLE test, "publication-1"."test-pubs";
|
||||||
|
|
||||||
|
-- operations on an existing distributed table
|
||||||
|
ALTER PUBLICATION pubtables DROP TABLE test;
|
||||||
|
ALTER PUBLICATION pubtables ADD TABLE test (y);
|
||||||
|
ALTER PUBLICATION pubtables SET TABLE test WHERE (doc IS DOCUMENT);
|
||||||
|
ALTER PUBLICATION pubtables SET TABLE test WHERE (xmlexists('//foo[text() = ''bar'']' PASSING BY VALUE doc));
|
||||||
|
ALTER PUBLICATION pubtables SET TABLE test WHERE (CASE x WHEN 5 THEN true ELSE false END);
|
||||||
|
|
||||||
|
SELECT DISTINCT c FROM (
|
||||||
|
SELECT unnest(result::text[]) c
|
||||||
|
FROM run_command_on_workers($$
|
||||||
|
SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' AND c LIKE '%pubtables%' ORDER BY 1) s$$)
|
||||||
|
ORDER BY c) s;
|
||||||
|
|
||||||
|
ALTER PUBLICATION pubtables SET TABLE test ("column-1", x) WHERE (x > "column-1"), "publication-1"."test-pubs";
|
||||||
|
|
||||||
|
-- operations on a local table
|
||||||
|
ALTER PUBLICATION pubtables DROP TABLE "publication-1"."test-pubs";
|
||||||
|
ALTER PUBLICATION pubtables ADD TABLE "publication-1"."test-pubs" (y);
|
||||||
|
|
||||||
|
-- mixed operations
|
||||||
|
ALTER PUBLICATION pubtables SET TABLE test, TABLES IN SCHEMA "publication-1", TABLES IN SCHEMA current_schema;
|
||||||
|
ALTER PUBLICATION pubtables SET TABLE "publication-1"."test-pubs", test ("column-1", x) WHERE (x > "column-1");
|
||||||
|
|
||||||
|
SELECT DISTINCT c FROM (
|
||||||
|
SELECT unnest(result::text[]) c
|
||||||
|
FROM run_command_on_workers($$
|
||||||
|
SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' AND c LIKE '%pubtables%' ORDER BY 1) s$$)
|
||||||
|
ORDER BY c) s;
|
||||||
|
|
||||||
|
-- publication with schemas
|
||||||
|
CREATE PUBLICATION "pub-mix" FOR TABLE test, TABLES IN SCHEMA current_schema, TABLE "publication-1"."test-pubs", TABLES IN SCHEMA "publication-1";
|
||||||
|
|
||||||
|
SELECT DISTINCT c FROM (
|
||||||
|
SELECT unnest(result::text[]) c
|
||||||
|
FROM run_command_on_workers($$
|
||||||
|
SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' AND c LIKE '%pub-mix%' ORDER BY 1) s$$)
|
||||||
|
ORDER BY c) s;
|
||||||
|
|
||||||
|
-- publication on a partitioned table
|
||||||
|
CREATE PUBLICATION pubpartitioned FOR TABLE testpub_partitioned (a, b) WITH (publish_via_partition_root = 'true');
|
||||||
|
ALTER PUBLICATION pubpartitioned SET (publish_via_partition_root = 1);
|
||||||
|
|
||||||
|
SELECT alter_distributed_table('testpub_partitioned', shard_count := 6, cascade_to_colocated := true);
|
||||||
|
|
||||||
|
SELECT DISTINCT c FROM (
|
||||||
|
SELECT unnest(result::text[]) c
|
||||||
|
FROM run_command_on_workers($$
|
||||||
|
SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' AND c LIKE '%pubpartitioned%' ORDER BY 1) s$$)
|
||||||
|
ORDER BY c) s;
|
||||||
|
|
||||||
|
-- make sure we propagate schema dependencies
|
||||||
|
SET citus.create_object_propagation TO 'deferred';
|
||||||
|
BEGIN;
|
||||||
|
CREATE SCHEMA deptest;
|
||||||
|
END;
|
||||||
|
CREATE PUBLICATION pubdep FOR TABLES IN SCHEMA deptest;
|
||||||
|
RESET citus.create_object_propagation;
|
||||||
|
DROP SCHEMA deptest CASCADE;
|
||||||
|
|
||||||
|
-- make sure we can sync all the publication metadata
|
||||||
|
SELECT start_metadata_sync_to_all_nodes();
|
||||||
|
|
||||||
|
DROP PUBLICATION pubdep;
|
||||||
|
DROP PUBLICATION "pub-mix";
|
||||||
|
DROP PUBLICATION pubtables;
|
||||||
|
DROP PUBLICATION pubpartitioned;
|
||||||
|
|
||||||
|
SET client_min_messages TO ERROR;
|
||||||
|
DROP SCHEMA publication CASCADE;
|
||||||
|
DROP SCHEMA "publication-1" CASCADE;
|
||||||
|
|
||||||
|
SELECT citus_remove_node('localhost', :master_port);
|
|
@ -64,7 +64,9 @@ CREATE TABLE table_to_split_2(id bigserial PRIMARY KEY, value char);
|
||||||
CREATE TABLE table_to_split_3(id bigserial PRIMARY KEY, value char);
|
CREATE TABLE table_to_split_3(id bigserial PRIMARY KEY, value char);
|
||||||
|
|
||||||
-- Create publication at worker1
|
-- Create publication at worker1
|
||||||
|
SET citus.enable_ddl_propagation TO off;
|
||||||
CREATE PUBLICATION pub1 FOR TABLE table_to_split_1, table_to_split_2, table_to_split_3;
|
CREATE PUBLICATION pub1 FOR TABLE table_to_split_1, table_to_split_2, table_to_split_3;
|
||||||
|
RESET citus.enable_ddl_propagation;
|
||||||
|
|
||||||
SELECT count(*) FROM pg_catalog.worker_split_shard_replication_setup(ARRAY[
|
SELECT count(*) FROM pg_catalog.worker_split_shard_replication_setup(ARRAY[
|
||||||
ROW(1, 'id', 2, '-2147483648', '-1', :worker_2_node)::pg_catalog.split_shard_info,
|
ROW(1, 'id', 2, '-2147483648', '-1', :worker_2_node)::pg_catalog.split_shard_info,
|
||||||
|
@ -176,7 +178,9 @@ SELECT nodeid AS worker_2_node FROM pg_dist_node WHERE nodeport=:worker_2_port \
|
||||||
SET search_path TO split_shard_replication_setup_schema;
|
SET search_path TO split_shard_replication_setup_schema;
|
||||||
|
|
||||||
-- Create publication at worker1
|
-- Create publication at worker1
|
||||||
|
SET citus.enable_ddl_propagation TO off;
|
||||||
CREATE PUBLICATION pub1 FOR TABLE table_to_split_1, table_to_split_2, table_to_split_3;
|
CREATE PUBLICATION pub1 FOR TABLE table_to_split_1, table_to_split_2, table_to_split_3;
|
||||||
|
RESET citus.enable_ddl_propagation;
|
||||||
|
|
||||||
SELECT count(*) FROM pg_catalog.worker_split_shard_replication_setup(ARRAY[
|
SELECT count(*) FROM pg_catalog.worker_split_shard_replication_setup(ARRAY[
|
||||||
ROW(1, 'id', 2, '-2147483648', '-1', :worker_1_node)::pg_catalog.split_shard_info,
|
ROW(1, 'id', 2, '-2147483648', '-1', :worker_1_node)::pg_catalog.split_shard_info,
|
||||||
|
@ -282,7 +286,9 @@ SET search_path TO split_shard_replication_setup_schema;
|
||||||
SET client_min_messages TO ERROR;
|
SET client_min_messages TO ERROR;
|
||||||
|
|
||||||
-- Create publication at worker1
|
-- Create publication at worker1
|
||||||
|
SET citus.enable_ddl_propagation TO off;
|
||||||
CREATE PUBLICATION pub1 for table table_to_split_1, table_to_split_2, table_to_split_3;
|
CREATE PUBLICATION pub1 for table table_to_split_1, table_to_split_2, table_to_split_3;
|
||||||
|
RESET citus.enable_ddl_propagation;
|
||||||
|
|
||||||
-- Worker1 is target for table_to_split_2 and table_to_split_3
|
-- Worker1 is target for table_to_split_2 and table_to_split_3
|
||||||
SELECT count(*) FROM pg_catalog.worker_split_shard_replication_setup(ARRAY[
|
SELECT count(*) FROM pg_catalog.worker_split_shard_replication_setup(ARRAY[
|
||||||
|
@ -401,8 +407,10 @@ CREATE TABLE table_second_9(id bigserial PRIMARY KEY, value char);
|
||||||
-- Create publication at worker1
|
-- Create publication at worker1
|
||||||
\c - postgres - :worker_1_port
|
\c - postgres - :worker_1_port
|
||||||
SET search_path TO split_shard_replication_setup_schema;
|
SET search_path TO split_shard_replication_setup_schema;
|
||||||
|
SET citus.enable_ddl_propagation TO off;
|
||||||
CREATE PUBLICATION pub1 FOR TABLE table_first_4, table_first_5, table_first_6;
|
CREATE PUBLICATION pub1 FOR TABLE table_first_4, table_first_5, table_first_6;
|
||||||
CREATE PUBLICATION pub2 FOR TABLE table_second_7, table_second_8, table_second_9;
|
CREATE PUBLICATION pub2 FOR TABLE table_second_7, table_second_8, table_second_9;
|
||||||
|
RESET citus.enable_ddl_propagation;
|
||||||
|
|
||||||
SELECT count(*) FROM pg_catalog.worker_split_shard_replication_setup(ARRAY[
|
SELECT count(*) FROM pg_catalog.worker_split_shard_replication_setup(ARRAY[
|
||||||
ROW(4, 'id', 5, '-2147483648', '-1', :worker_2_node)::pg_catalog.split_shard_info,
|
ROW(4, 'id', 5, '-2147483648', '-1', :worker_2_node)::pg_catalog.split_shard_info,
|
||||||
|
|
Loading…
Reference in New Issue