mirror of https://github.com/citusdata/citus.git
Adds comment on database and role propagation (#7388)
DESCRIPTION: Adds comment on database and role propagation. Example commands are as below comment on database <db_name> is '<comment_text>' comment on database <db_name> is NULL comment on role <role_name> is '<comment_text>' comment on role <role_name> is NULL --------- Co-authored-by: Jelte Fennema-Nio <jelte.fennema@microsoft.com>pull/7433/head^2
parent
5ec056a172
commit
188614512f
|
@ -0,0 +1,131 @@
|
|||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* comment.c
|
||||
* Commands to interact with the comments for all database
|
||||
* object types.
|
||||
*
|
||||
* Copyright (c) Citus Data, Inc.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "access/genam.h"
|
||||
#include "access/htup_details.h"
|
||||
#include "access/table.h"
|
||||
#include "catalog/pg_shdescription.h"
|
||||
#include "nodes/parsenodes.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/fmgroids.h"
|
||||
#include "utils/rel.h"
|
||||
|
||||
#include "distributed/comment.h"
|
||||
|
||||
static char * GetCommentForObject(Oid classOid, Oid objectOid);
|
||||
|
||||
|
||||
List *
|
||||
GetCommentPropagationCommands(Oid classOid, Oid objOoid, char *objectName, ObjectType
|
||||
objectType)
|
||||
{
|
||||
List *commands = NIL;
|
||||
|
||||
StringInfo commentStmt = makeStringInfo();
|
||||
|
||||
/* Get the comment for the database */
|
||||
char *comment = GetCommentForObject(classOid, objOoid);
|
||||
char const *commentObjectType = ObjectTypeNames[objectType];
|
||||
|
||||
/* Create the SQL command to propagate the comment to other nodes */
|
||||
if (comment != NULL)
|
||||
{
|
||||
appendStringInfo(commentStmt, "COMMENT ON %s %s IS %s;", commentObjectType,
|
||||
quote_identifier(objectName),
|
||||
quote_literal_cstr(comment));
|
||||
}
|
||||
|
||||
|
||||
/* Add the command to the list */
|
||||
if (commentStmt->len > 0)
|
||||
{
|
||||
commands = list_make1(commentStmt->data);
|
||||
}
|
||||
|
||||
return commands;
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
GetCommentForObject(Oid classOid, Oid objectOid)
|
||||
{
|
||||
HeapTuple tuple;
|
||||
char *comment = NULL;
|
||||
|
||||
/* Open pg_shdescription catalog */
|
||||
Relation shdescRelation = table_open(SharedDescriptionRelationId, AccessShareLock);
|
||||
|
||||
/* Scan the table */
|
||||
ScanKeyData scanKey[2];
|
||||
|
||||
ScanKeyInit(&scanKey[0],
|
||||
Anum_pg_shdescription_objoid,
|
||||
BTEqualStrategyNumber, F_OIDEQ,
|
||||
ObjectIdGetDatum(objectOid));
|
||||
ScanKeyInit(&scanKey[1],
|
||||
Anum_pg_shdescription_classoid,
|
||||
BTEqualStrategyNumber, F_OIDEQ,
|
||||
ObjectIdGetDatum(classOid));
|
||||
bool indexOk = true;
|
||||
int scanKeyCount = 2;
|
||||
SysScanDesc scan = systable_beginscan(shdescRelation, SharedDescriptionObjIndexId,
|
||||
indexOk, NULL, scanKeyCount,
|
||||
scanKey);
|
||||
if ((tuple = systable_getnext(scan)) != NULL)
|
||||
{
|
||||
bool isNull = false;
|
||||
|
||||
TupleDesc tupdesc = RelationGetDescr(shdescRelation);
|
||||
|
||||
Datum descDatum = heap_getattr(tuple, Anum_pg_shdescription_description, tupdesc,
|
||||
&isNull);
|
||||
|
||||
|
||||
/* Add the command to the list */
|
||||
if (!isNull)
|
||||
{
|
||||
comment = TextDatumGetCString(descDatum);
|
||||
}
|
||||
else
|
||||
{
|
||||
comment = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* End the scan and close the catalog */
|
||||
systable_endscan(scan);
|
||||
table_close(shdescRelation, AccessShareLock);
|
||||
|
||||
return comment;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* CommentObjectAddress resolves the ObjectAddress for the object
|
||||
* on which the comment is placed. Optionally errors if the object does not
|
||||
* exist based on the missing_ok flag passed in by the caller.
|
||||
*/
|
||||
List *
|
||||
CommentObjectAddress(Node *node, bool missing_ok, bool isPostprocess)
|
||||
{
|
||||
CommentStmt *stmt = castNode(CommentStmt, node);
|
||||
Relation relation;
|
||||
|
||||
ObjectAddress objectAddress = get_object_address(stmt->objtype, stmt->object,
|
||||
&relation, AccessExclusiveLock,
|
||||
missing_ok);
|
||||
|
||||
ObjectAddress *objectAddressCopy = palloc0(sizeof(ObjectAddress));
|
||||
*objectAddressCopy = objectAddress;
|
||||
return list_make1(objectAddressCopy);
|
||||
}
|
|
@ -13,8 +13,10 @@
|
|||
|
||||
#include "miscadmin.h"
|
||||
|
||||
#include "access/genam.h"
|
||||
#include "access/heapam.h"
|
||||
#include "access/htup_details.h"
|
||||
#include "access/table.h"
|
||||
#include "access/xact.h"
|
||||
#include "catalog/objectaddress.h"
|
||||
#include "catalog/pg_collation.h"
|
||||
|
@ -25,6 +27,7 @@
|
|||
#include "commands/defrem.h"
|
||||
#include "nodes/parsenodes.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/fmgroids.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/rel.h"
|
||||
#include "utils/relcache.h"
|
||||
|
@ -33,6 +36,7 @@
|
|||
#include "distributed/adaptive_executor.h"
|
||||
#include "distributed/commands.h"
|
||||
#include "distributed/commands/utility_hook.h"
|
||||
#include "distributed/comment.h"
|
||||
#include "distributed/deparse_shard_query.h"
|
||||
#include "distributed/deparser.h"
|
||||
#include "distributed/listutils.h"
|
||||
|
@ -45,7 +49,6 @@
|
|||
#include "distributed/worker_protocol.h"
|
||||
#include "distributed/worker_transaction.h"
|
||||
|
||||
|
||||
/*
|
||||
* DatabaseCollationInfo is used to store collation related information of a database.
|
||||
*/
|
||||
|
@ -672,6 +675,31 @@ GetTablespaceName(Oid tablespaceOid)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* GetDatabaseMetadataSyncCommands returns a list of sql statements
|
||||
* for the given database id. The list contains the database ddl command,
|
||||
* grant commands and comment propagation commands.
|
||||
*/
|
||||
List *
|
||||
GetDatabaseMetadataSyncCommands(Oid dbOid)
|
||||
{
|
||||
char *databaseName = get_database_name(dbOid);
|
||||
char *databaseDDLCommand = CreateDatabaseDDLCommand(dbOid);
|
||||
|
||||
List *ddlCommands = list_make1(databaseDDLCommand);
|
||||
|
||||
List *grantDDLCommands = GrantOnDatabaseDDLCommands(dbOid);
|
||||
List *commentDDLCommands = GetCommentPropagationCommands(DatabaseRelationId, dbOid,
|
||||
databaseName,
|
||||
OBJECT_DATABASE);
|
||||
|
||||
ddlCommands = list_concat(ddlCommands, grantDDLCommands);
|
||||
ddlCommands = list_concat(ddlCommands, commentDDLCommands);
|
||||
|
||||
return ddlCommands;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* GetDatabaseCollation gets oid of a database and returns all the collation related information
|
||||
* We need this method since collation related info in Form_pg_database is not accessible.
|
||||
|
|
|
@ -584,15 +584,7 @@ GetDependencyCreateDDLCommands(const ObjectAddress *dependency)
|
|||
*/
|
||||
if (dependency->objectId != MyDatabaseId && EnableCreateDatabasePropagation)
|
||||
{
|
||||
char *databaseDDLCommand = CreateDatabaseDDLCommand(dependency->objectId);
|
||||
|
||||
List *ddlCommands = list_make1(databaseDDLCommand);
|
||||
|
||||
List *grantDDLCommands = GrantOnDatabaseDDLCommands(dependency->objectId);
|
||||
|
||||
ddlCommands = list_concat(ddlCommands, grantDDLCommands);
|
||||
|
||||
return ddlCommands;
|
||||
return GetDatabaseMetadataSyncCommands(dependency->objectId);
|
||||
}
|
||||
|
||||
return NIL;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "distributed/commands.h"
|
||||
#include "distributed/commands/utility_hook.h"
|
||||
#include "distributed/comment.h"
|
||||
#include "distributed/deparser.h"
|
||||
#include "distributed/version_compat.h"
|
||||
|
||||
|
@ -304,6 +305,17 @@ static DistributeObjectOps Any_DropRole = {
|
|||
.address = NULL,
|
||||
.markDistributed = false,
|
||||
};
|
||||
|
||||
static DistributeObjectOps Role_Comment = {
|
||||
.deparse = DeparseCommentStmt,
|
||||
.qualify = NULL,
|
||||
.preprocess = PreprocessAlterDistributedObjectStmt,
|
||||
.postprocess = NULL,
|
||||
.objectType = OBJECT_DATABASE,
|
||||
.operationType = DIST_OPS_ALTER,
|
||||
.address = CommentObjectAddress,
|
||||
.markDistributed = false,
|
||||
};
|
||||
static DistributeObjectOps Any_CreateForeignServer = {
|
||||
.deparse = DeparseCreateForeignServerStmt,
|
||||
.qualify = NULL,
|
||||
|
@ -533,6 +545,17 @@ static DistributeObjectOps Database_Set = {
|
|||
.markDistributed = false,
|
||||
};
|
||||
|
||||
static DistributeObjectOps Database_Comment = {
|
||||
.deparse = DeparseCommentStmt,
|
||||
.qualify = NULL,
|
||||
.preprocess = PreprocessAlterDistributedObjectStmt,
|
||||
.postprocess = NULL,
|
||||
.objectType = OBJECT_DATABASE,
|
||||
.operationType = DIST_OPS_ALTER,
|
||||
.address = CommentObjectAddress,
|
||||
.markDistributed = false,
|
||||
};
|
||||
|
||||
static DistributeObjectOps Database_Rename = {
|
||||
.deparse = DeparseAlterDatabaseRenameStmt,
|
||||
.qualify = NULL,
|
||||
|
@ -972,13 +995,18 @@ static DistributeObjectOps TextSearchConfig_AlterOwner = {
|
|||
.markDistributed = false,
|
||||
};
|
||||
static DistributeObjectOps TextSearchConfig_Comment = {
|
||||
.deparse = DeparseTextSearchConfigurationCommentStmt,
|
||||
.deparse = DeparseCommentStmt,
|
||||
|
||||
/* TODO: When adding new comment types we should create an abstracted
|
||||
* qualify function, just like we have an abstract deparse
|
||||
* and adress function
|
||||
*/
|
||||
.qualify = QualifyTextSearchConfigurationCommentStmt,
|
||||
.preprocess = PreprocessAlterDistributedObjectStmt,
|
||||
.postprocess = NULL,
|
||||
.objectType = OBJECT_TSCONFIGURATION,
|
||||
.operationType = DIST_OPS_ALTER,
|
||||
.address = TextSearchConfigurationCommentObjectAddress,
|
||||
.address = CommentObjectAddress,
|
||||
.markDistributed = false,
|
||||
};
|
||||
static DistributeObjectOps TextSearchConfig_Define = {
|
||||
|
@ -1041,13 +1069,13 @@ static DistributeObjectOps TextSearchDict_AlterOwner = {
|
|||
.markDistributed = false,
|
||||
};
|
||||
static DistributeObjectOps TextSearchDict_Comment = {
|
||||
.deparse = DeparseTextSearchDictionaryCommentStmt,
|
||||
.deparse = DeparseCommentStmt,
|
||||
.qualify = QualifyTextSearchDictionaryCommentStmt,
|
||||
.preprocess = PreprocessAlterDistributedObjectStmt,
|
||||
.postprocess = NULL,
|
||||
.objectType = OBJECT_TSDICTIONARY,
|
||||
.operationType = DIST_OPS_ALTER,
|
||||
.address = TextSearchDictCommentObjectAddress,
|
||||
.address = CommentObjectAddress,
|
||||
.markDistributed = false,
|
||||
};
|
||||
static DistributeObjectOps TextSearchDict_Define = {
|
||||
|
@ -1780,6 +1808,16 @@ GetDistributeObjectOps(Node *node)
|
|||
return &TextSearchDict_Comment;
|
||||
}
|
||||
|
||||
case OBJECT_DATABASE:
|
||||
{
|
||||
return &Database_Comment;
|
||||
}
|
||||
|
||||
case OBJECT_ROLE:
|
||||
{
|
||||
return &Role_Comment;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
return &NoDistributeOps;
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#include "distributed/citus_safe_lib.h"
|
||||
#include "distributed/commands.h"
|
||||
#include "distributed/commands/utility_hook.h"
|
||||
#include "distributed/comment.h"
|
||||
#include "distributed/coordinator_protocol.h"
|
||||
#include "distributed/deparser.h"
|
||||
#include "distributed/listutils.h"
|
||||
|
@ -582,6 +583,17 @@ GenerateCreateOrAlterRoleCommand(Oid roleOid)
|
|||
{
|
||||
completeRoleList = lappend(completeRoleList, DeparseTreeNode(stmt));
|
||||
}
|
||||
|
||||
/*
|
||||
* append COMMENT ON ROLE commands for this specific user
|
||||
* When we propagate user creation, we also want to make sure that we propagate
|
||||
* all the comments it has been given. For this, we check pg_shdescription
|
||||
* for the ROLE entry corresponding to roleOid, and generate the relevant
|
||||
* Comment stmts to be run in the new node.
|
||||
*/
|
||||
List *commentStmts = GetCommentPropagationCommands(AuthIdRelationId, roleOid,
|
||||
rolename, OBJECT_ROLE);
|
||||
completeRoleList = list_concat(completeRoleList, commentStmts);
|
||||
}
|
||||
|
||||
return completeRoleList;
|
||||
|
|
|
@ -790,45 +790,6 @@ AlterTextSearchDictionarySchemaStmtObjectAddress(Node *node, bool missing_ok, bo
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* TextSearchConfigurationCommentObjectAddress resolves the ObjectAddress for the TEXT
|
||||
* SEARCH CONFIGURATION on which the comment is placed. Optionally errors if the
|
||||
* configuration does not exist based on the missing_ok flag passed in by the caller.
|
||||
*/
|
||||
List *
|
||||
TextSearchConfigurationCommentObjectAddress(Node *node, bool missing_ok, bool
|
||||
isPostprocess)
|
||||
{
|
||||
CommentStmt *stmt = castNode(CommentStmt, node);
|
||||
Assert(stmt->objtype == OBJECT_TSCONFIGURATION);
|
||||
|
||||
Oid objid = get_ts_config_oid(castNode(List, stmt->object), missing_ok);
|
||||
|
||||
ObjectAddress *address = palloc0(sizeof(ObjectAddress));
|
||||
ObjectAddressSet(*address, TSConfigRelationId, objid);
|
||||
return list_make1(address);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* TextSearchDictCommentObjectAddress resolves the ObjectAddress for the TEXT SEARCH
|
||||
* DICTIONARY on which the comment is placed. Optionally errors if the dictionary does not
|
||||
* exist based on the missing_ok flag passed in by the caller.
|
||||
*/
|
||||
List *
|
||||
TextSearchDictCommentObjectAddress(Node *node, bool missing_ok, bool isPostprocess)
|
||||
{
|
||||
CommentStmt *stmt = castNode(CommentStmt, node);
|
||||
Assert(stmt->objtype == OBJECT_TSDICTIONARY);
|
||||
|
||||
Oid objid = get_ts_dict_oid(castNode(List, stmt->object), missing_ok);
|
||||
|
||||
ObjectAddress *address = palloc0(sizeof(ObjectAddress));
|
||||
ObjectAddressSet(*address, TSDictionaryRelationId, objid);
|
||||
return list_make1(address);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* AlterTextSearchConfigurationOwnerObjectAddress resolves the ObjectAddress for the TEXT
|
||||
* SEARCH CONFIGURATION for which the owner is changed. Optionally errors if the
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* deparse_coment_stmts.c
|
||||
*
|
||||
* All routines to deparse comment statements.
|
||||
*
|
||||
* Copyright (c), Citus Data, Inc.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "catalog/namespace.h"
|
||||
#include "commands/defrem.h"
|
||||
#include "lib/stringinfo.h"
|
||||
#include "nodes/parsenodes.h"
|
||||
#include "parser/parse_type.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/elog.h"
|
||||
|
||||
#include "pg_version_compat.h"
|
||||
|
||||
#include "distributed/citus_ruleutils.h"
|
||||
#include "distributed/commands.h"
|
||||
#include "distributed/comment.h"
|
||||
#include "distributed/deparser.h"
|
||||
#include "distributed/listutils.h"
|
||||
#include "distributed/log_utils.h"
|
||||
|
||||
|
||||
const char *ObjectTypeNames[] =
|
||||
{
|
||||
[OBJECT_DATABASE] = "DATABASE",
|
||||
[OBJECT_ROLE] = "ROLE",
|
||||
[OBJECT_TSCONFIGURATION] = "TEXT SEARCH CONFIGURATION",
|
||||
[OBJECT_TSDICTIONARY] = "TEXT SEARCH DICTIONARY",
|
||||
|
||||
/* When support for propagating comments to new objects is introduced, an entry for each
|
||||
* statement type should be added to this list. The first element in each entry is the 'object_type' keyword
|
||||
* that will be included in the 'COMMENT ON <object_type> ..' statement (i.e. DATABASE,). The second element is the type of
|
||||
* stmt->object, which represents the name of the propagated object.
|
||||
*/
|
||||
};
|
||||
|
||||
char *
|
||||
DeparseCommentStmt(Node *node)
|
||||
{
|
||||
CommentStmt *stmt = castNode(CommentStmt, node);
|
||||
StringInfoData str = { 0 };
|
||||
initStringInfo(&str);
|
||||
|
||||
const char *objectName = NULL;
|
||||
if (IsA(stmt->object, String))
|
||||
{
|
||||
objectName = quote_identifier(strVal(stmt->object));
|
||||
}
|
||||
else if (IsA(stmt->object, List))
|
||||
{
|
||||
objectName = NameListToQuotedString(castNode(List, stmt->object));
|
||||
}
|
||||
else
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INTERNAL_ERROR),
|
||||
errmsg("unknown object type")));
|
||||
}
|
||||
|
||||
const char *objectType = ObjectTypeNames[stmt->objtype];
|
||||
|
||||
char *comment = stmt->comment != NULL ? quote_literal_cstr(stmt->comment) : "NULL";
|
||||
|
||||
|
||||
appendStringInfo(&str, "COMMENT ON %s %s IS %s;", objectType, objectName, comment);
|
||||
|
||||
return str.data;
|
||||
}
|
|
@ -395,68 +395,6 @@ DeparseAlterTextSearchDictionarySchemaStmt(Node *node)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* DeparseTextSearchConfigurationCommentStmt returns the sql statement representing
|
||||
* COMMENT ON TEXT SEARCH CONFIGURATION ... IS ...
|
||||
*/
|
||||
char *
|
||||
DeparseTextSearchConfigurationCommentStmt(Node *node)
|
||||
{
|
||||
CommentStmt *stmt = castNode(CommentStmt, node);
|
||||
Assert(stmt->objtype == OBJECT_TSCONFIGURATION);
|
||||
|
||||
StringInfoData buf = { 0 };
|
||||
initStringInfo(&buf);
|
||||
|
||||
appendStringInfo(&buf, "COMMENT ON TEXT SEARCH CONFIGURATION %s IS ",
|
||||
NameListToQuotedString(castNode(List, stmt->object)));
|
||||
|
||||
if (stmt->comment == NULL)
|
||||
{
|
||||
appendStringInfoString(&buf, "NULL");
|
||||
}
|
||||
else
|
||||
{
|
||||
appendStringInfoString(&buf, quote_literal_cstr(stmt->comment));
|
||||
}
|
||||
|
||||
appendStringInfoString(&buf, ";");
|
||||
|
||||
return buf.data;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* DeparseTextSearchDictionaryCommentStmt returns the sql statement representing
|
||||
* COMMENT ON TEXT SEARCH DICTIONARY ... IS ...
|
||||
*/
|
||||
char *
|
||||
DeparseTextSearchDictionaryCommentStmt(Node *node)
|
||||
{
|
||||
CommentStmt *stmt = castNode(CommentStmt, node);
|
||||
Assert(stmt->objtype == OBJECT_TSDICTIONARY);
|
||||
|
||||
StringInfoData buf = { 0 };
|
||||
initStringInfo(&buf);
|
||||
|
||||
appendStringInfo(&buf, "COMMENT ON TEXT SEARCH DICTIONARY %s IS ",
|
||||
NameListToQuotedString(castNode(List, stmt->object)));
|
||||
|
||||
if (stmt->comment == NULL)
|
||||
{
|
||||
appendStringInfoString(&buf, "NULL");
|
||||
}
|
||||
else
|
||||
{
|
||||
appendStringInfoString(&buf, quote_literal_cstr(stmt->comment));
|
||||
}
|
||||
|
||||
appendStringInfoString(&buf, ";");
|
||||
|
||||
return buf.data;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* AppendStringInfoTokentypeList specializes in adding a comma separated list of
|
||||
* token_tyoe's to TEXT SEARCH CONFIGURATION commands
|
||||
|
|
|
@ -230,6 +230,7 @@ extern List * PreprocessAlterDatabaseStmt(Node *node, const char *queryString,
|
|||
extern List * PreprocessAlterDatabaseRefreshCollStmt(Node *node, const char *queryString,
|
||||
ProcessUtilityContext
|
||||
processUtilityContext);
|
||||
extern List * GetDatabaseMetadataSyncCommands(Oid dbOid);
|
||||
|
||||
|
||||
extern List * PreprocessAlterDatabaseSetStmt(Node *node, const char *queryString,
|
||||
|
@ -693,11 +694,6 @@ extern List * AlterTextSearchConfigurationSchemaStmtObjectAddress(Node *node,
|
|||
extern List * AlterTextSearchDictionarySchemaStmtObjectAddress(Node *node,
|
||||
bool missing_ok, bool
|
||||
isPostprocess);
|
||||
extern List * TextSearchConfigurationCommentObjectAddress(Node *node,
|
||||
bool missing_ok, bool
|
||||
isPostprocess);
|
||||
extern List * TextSearchDictCommentObjectAddress(Node *node,
|
||||
bool missing_ok, bool isPostprocess);
|
||||
extern List * AlterTextSearchConfigurationOwnerObjectAddress(Node *node,
|
||||
bool missing_ok, bool
|
||||
isPostprocess);
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* comment.h
|
||||
* Declarations for comment related operations.
|
||||
*
|
||||
* Copyright (c) Citus Data, Inc.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef COMMENT_H
|
||||
#define COMMENT_H
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "nodes/parsenodes.h"
|
||||
|
||||
|
||||
extern const char *ObjectTypeNames[];
|
||||
|
||||
|
||||
extern List * GetCommentPropagationCommands(Oid classOid, Oid oid, char *objectName,
|
||||
ObjectType objectType);
|
||||
extern List * CommentObjectAddress(Node *node, bool missing_ok, bool isPostprocess);
|
||||
|
||||
# endif /* COMMENT_H */
|
|
@ -143,6 +143,9 @@ extern void DefElemOptionToStatement(StringInfo buf, DefElem *option,
|
|||
const DefElemOptionFormat *opt_formats,
|
||||
int opt_formats_len);
|
||||
|
||||
/* forward declarations for deparse_comment_stmts.c */
|
||||
extern char * DeparseCommentStmt(Node *node);
|
||||
|
||||
|
||||
/* forward declarations for deparse_statistics_stmts.c */
|
||||
extern char * DeparseCreateStatisticsStmt(Node *node);
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
set citus.log_remote_commands to on;
|
||||
set citus.enable_create_database_propagation to on;
|
||||
set citus.grep_remote_commands to 'COMMENT ON DATABASE';
|
||||
create database "test1-\!escape";
|
||||
comment on DATABASE "test1-\!escape" is 'test-comment';
|
||||
SELECT result FROM run_command_on_all_nodes(
|
||||
$$
|
||||
SELECT ds.description AS database_comment
|
||||
FROM pg_database d
|
||||
LEFT JOIN pg_shdescription ds ON d.oid = ds.objoid
|
||||
WHERE d.datname = 'test1-\!escape';
|
||||
$$
|
||||
);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
test-comment
|
||||
test-comment
|
||||
test-comment
|
||||
(3 rows)
|
||||
|
||||
comment on DATABASE "test1-\!escape" is 'comment-needs\!escape';
|
||||
SELECT result FROM run_command_on_all_nodes(
|
||||
$$
|
||||
SELECT ds.description AS database_comment
|
||||
FROM pg_database d
|
||||
LEFT JOIN pg_shdescription ds ON d.oid = ds.objoid
|
||||
WHERE d.datname = 'test1-\!escape';
|
||||
$$
|
||||
);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
comment-needs\!escape
|
||||
comment-needs\!escape
|
||||
comment-needs\!escape
|
||||
(3 rows)
|
||||
|
||||
comment on DATABASE "test1-\!escape" is null;
|
||||
SELECT result FROM run_command_on_all_nodes(
|
||||
$$
|
||||
SELECT ds.description AS database_comment
|
||||
FROM pg_database d
|
||||
LEFT JOIN pg_shdescription ds ON d.oid = ds.objoid
|
||||
WHERE d.datname = 'test1-\!escape';
|
||||
$$
|
||||
);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
(3 rows)
|
||||
|
||||
drop DATABASE "test1-\!escape";
|
||||
--test metadata sync
|
||||
select 1 from citus_remove_node('localhost', :worker_2_port);
|
||||
?column?
|
||||
---------------------------------------------------------------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
create database "test1-\!escape";
|
||||
comment on DATABASE "test1-\!escape" is 'test-comment';
|
||||
SELECT result FROM run_command_on_all_nodes(
|
||||
$$
|
||||
SELECT ds.description AS database_comment
|
||||
FROM pg_database d
|
||||
LEFT JOIN pg_shdescription ds ON d.oid = ds.objoid
|
||||
WHERE d.datname = 'test1-\!escape';
|
||||
$$
|
||||
);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
test-comment
|
||||
test-comment
|
||||
(2 rows)
|
||||
|
||||
select 1 from citus_add_node('localhost', :worker_2_port);
|
||||
?column?
|
||||
---------------------------------------------------------------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT result FROM run_command_on_all_nodes(
|
||||
$$
|
||||
SELECT ds.description AS database_comment
|
||||
FROM pg_database d
|
||||
LEFT JOIN pg_shdescription ds ON d.oid = ds.objoid
|
||||
WHERE d.datname = 'test1-\!escape';
|
||||
$$
|
||||
);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
test-comment
|
||||
test-comment
|
||||
test-comment
|
||||
(3 rows)
|
||||
|
||||
drop DATABASE "test1-\!escape";
|
||||
reset citus.enable_create_database_propagation;
|
||||
reset citus.grep_remote_commands;
|
||||
reset citus.log_remote_commands;
|
|
@ -0,0 +1,99 @@
|
|||
set citus.log_remote_commands to on;
|
||||
set citus.grep_remote_commands to 'COMMENT ON ROLE';
|
||||
create role "role1-\!escape";
|
||||
comment on ROLE "role1-\!escape" is 'test-comment';
|
||||
SELECT result FROM run_command_on_all_nodes(
|
||||
$$
|
||||
SELECT ds.description AS role_comment
|
||||
FROM pg_roles r
|
||||
LEFT JOIN pg_shdescription ds ON r.oid = ds.objoid
|
||||
WHERE r.rolname = 'role1-\!escape';
|
||||
$$
|
||||
);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
test-comment
|
||||
test-comment
|
||||
test-comment
|
||||
(3 rows)
|
||||
|
||||
comment on role "role1-\!escape" is 'comment-needs\!escape';
|
||||
SELECT result FROM run_command_on_all_nodes(
|
||||
$$
|
||||
SELECT ds.description AS role_comment
|
||||
FROM pg_roles r
|
||||
LEFT JOIN pg_shdescription ds ON r.oid = ds.objoid
|
||||
WHERE r.rolname = 'role1-\!escape';
|
||||
$$
|
||||
);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
comment-needs\!escape
|
||||
comment-needs\!escape
|
||||
comment-needs\!escape
|
||||
(3 rows)
|
||||
|
||||
comment on role "role1-\!escape" is NULL;
|
||||
SELECT result FROM run_command_on_all_nodes(
|
||||
$$
|
||||
SELECT ds.description AS role_comment
|
||||
FROM pg_roles r
|
||||
LEFT JOIN pg_shdescription ds ON r.oid = ds.objoid
|
||||
WHERE r.rolname = 'role1-\!escape';
|
||||
$$
|
||||
);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
(3 rows)
|
||||
|
||||
drop role "role1-\!escape";
|
||||
--test metadata sync
|
||||
select 1 from citus_remove_node('localhost', :worker_2_port);
|
||||
?column?
|
||||
---------------------------------------------------------------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
create role "role1-\!escape";
|
||||
comment on ROLE "role1-\!escape" is 'test-comment';
|
||||
SELECT result FROM run_command_on_all_nodes(
|
||||
$$
|
||||
SELECT ds.description AS role_comment
|
||||
FROM pg_roles r
|
||||
LEFT JOIN pg_shdescription ds ON r.oid = ds.objoid
|
||||
WHERE r.rolname = 'role1-\!escape';
|
||||
$$
|
||||
);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
test-comment
|
||||
test-comment
|
||||
(2 rows)
|
||||
|
||||
select 1 from citus_add_node('localhost', :worker_2_port);
|
||||
?column?
|
||||
---------------------------------------------------------------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT result FROM run_command_on_all_nodes(
|
||||
$$
|
||||
SELECT ds.description AS role_comment
|
||||
FROM pg_roles r
|
||||
LEFT JOIN pg_shdescription ds ON r.oid = ds.objoid
|
||||
WHERE r.rolname = 'role1-\!escape';
|
||||
$$
|
||||
);
|
||||
result
|
||||
---------------------------------------------------------------------
|
||||
test-comment
|
||||
test-comment
|
||||
test-comment
|
||||
(3 rows)
|
||||
|
||||
drop role "role1-\!escape";
|
||||
reset citus.grep_remote_commands;
|
||||
reset citus.log_remote_commands;
|
|
@ -38,6 +38,8 @@ test: create_single_shard_table
|
|||
test: create_drop_database_propagation
|
||||
test: create_drop_database_propagation_pg15
|
||||
test: create_drop_database_propagation_pg16
|
||||
test: comment_on_database
|
||||
test: comment_on_role
|
||||
# don't parallelize single_shard_table_udfs to make sure colocation ids are sequential
|
||||
test: single_shard_table_udfs
|
||||
test: schema_based_sharding
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
set citus.log_remote_commands to on;
|
||||
|
||||
set citus.enable_create_database_propagation to on;
|
||||
set citus.grep_remote_commands to 'COMMENT ON DATABASE';
|
||||
|
||||
create database "test1-\!escape";
|
||||
|
||||
comment on DATABASE "test1-\!escape" is 'test-comment';
|
||||
|
||||
SELECT result FROM run_command_on_all_nodes(
|
||||
$$
|
||||
SELECT ds.description AS database_comment
|
||||
FROM pg_database d
|
||||
LEFT JOIN pg_shdescription ds ON d.oid = ds.objoid
|
||||
WHERE d.datname = 'test1-\!escape';
|
||||
$$
|
||||
);
|
||||
|
||||
comment on DATABASE "test1-\!escape" is 'comment-needs\!escape';
|
||||
|
||||
SELECT result FROM run_command_on_all_nodes(
|
||||
$$
|
||||
SELECT ds.description AS database_comment
|
||||
FROM pg_database d
|
||||
LEFT JOIN pg_shdescription ds ON d.oid = ds.objoid
|
||||
WHERE d.datname = 'test1-\!escape';
|
||||
$$
|
||||
);
|
||||
|
||||
comment on DATABASE "test1-\!escape" is null;
|
||||
|
||||
SELECT result FROM run_command_on_all_nodes(
|
||||
$$
|
||||
SELECT ds.description AS database_comment
|
||||
FROM pg_database d
|
||||
LEFT JOIN pg_shdescription ds ON d.oid = ds.objoid
|
||||
WHERE d.datname = 'test1-\!escape';
|
||||
$$
|
||||
);
|
||||
|
||||
drop DATABASE "test1-\!escape";
|
||||
|
||||
--test metadata sync
|
||||
select 1 from citus_remove_node('localhost', :worker_2_port);
|
||||
create database "test1-\!escape";
|
||||
comment on DATABASE "test1-\!escape" is 'test-comment';
|
||||
|
||||
SELECT result FROM run_command_on_all_nodes(
|
||||
$$
|
||||
SELECT ds.description AS database_comment
|
||||
FROM pg_database d
|
||||
LEFT JOIN pg_shdescription ds ON d.oid = ds.objoid
|
||||
WHERE d.datname = 'test1-\!escape';
|
||||
$$
|
||||
);
|
||||
|
||||
select 1 from citus_add_node('localhost', :worker_2_port);
|
||||
|
||||
SELECT result FROM run_command_on_all_nodes(
|
||||
$$
|
||||
SELECT ds.description AS database_comment
|
||||
FROM pg_database d
|
||||
LEFT JOIN pg_shdescription ds ON d.oid = ds.objoid
|
||||
WHERE d.datname = 'test1-\!escape';
|
||||
$$
|
||||
);
|
||||
|
||||
drop DATABASE "test1-\!escape";
|
||||
|
||||
|
||||
reset citus.enable_create_database_propagation;
|
||||
reset citus.grep_remote_commands;
|
||||
reset citus.log_remote_commands;
|
|
@ -0,0 +1,72 @@
|
|||
set citus.log_remote_commands to on;
|
||||
|
||||
set citus.grep_remote_commands to 'COMMENT ON ROLE';
|
||||
|
||||
create role "role1-\!escape";
|
||||
|
||||
comment on ROLE "role1-\!escape" is 'test-comment';
|
||||
|
||||
SELECT result FROM run_command_on_all_nodes(
|
||||
$$
|
||||
SELECT ds.description AS role_comment
|
||||
FROM pg_roles r
|
||||
LEFT JOIN pg_shdescription ds ON r.oid = ds.objoid
|
||||
WHERE r.rolname = 'role1-\!escape';
|
||||
$$
|
||||
);
|
||||
|
||||
comment on role "role1-\!escape" is 'comment-needs\!escape';
|
||||
|
||||
SELECT result FROM run_command_on_all_nodes(
|
||||
$$
|
||||
SELECT ds.description AS role_comment
|
||||
FROM pg_roles r
|
||||
LEFT JOIN pg_shdescription ds ON r.oid = ds.objoid
|
||||
WHERE r.rolname = 'role1-\!escape';
|
||||
$$
|
||||
);
|
||||
|
||||
comment on role "role1-\!escape" is NULL;
|
||||
|
||||
SELECT result FROM run_command_on_all_nodes(
|
||||
$$
|
||||
SELECT ds.description AS role_comment
|
||||
FROM pg_roles r
|
||||
LEFT JOIN pg_shdescription ds ON r.oid = ds.objoid
|
||||
WHERE r.rolname = 'role1-\!escape';
|
||||
$$
|
||||
);
|
||||
|
||||
drop role "role1-\!escape";
|
||||
|
||||
|
||||
--test metadata sync
|
||||
|
||||
select 1 from citus_remove_node('localhost', :worker_2_port);
|
||||
create role "role1-\!escape";
|
||||
comment on ROLE "role1-\!escape" is 'test-comment';
|
||||
|
||||
SELECT result FROM run_command_on_all_nodes(
|
||||
$$
|
||||
SELECT ds.description AS role_comment
|
||||
FROM pg_roles r
|
||||
LEFT JOIN pg_shdescription ds ON r.oid = ds.objoid
|
||||
WHERE r.rolname = 'role1-\!escape';
|
||||
$$
|
||||
);
|
||||
|
||||
select 1 from citus_add_node('localhost', :worker_2_port);
|
||||
|
||||
SELECT result FROM run_command_on_all_nodes(
|
||||
$$
|
||||
SELECT ds.description AS role_comment
|
||||
FROM pg_roles r
|
||||
LEFT JOIN pg_shdescription ds ON r.oid = ds.objoid
|
||||
WHERE r.rolname = 'role1-\!escape';
|
||||
$$
|
||||
);
|
||||
|
||||
drop role "role1-\!escape";
|
||||
|
||||
reset citus.grep_remote_commands;
|
||||
reset citus.log_remote_commands;
|
Loading…
Reference in New Issue