pull/7240/head
Onur Tirtir 2023-11-20 18:41:24 +03:00
parent 022b6268bf
commit 9241970e0c
12 changed files with 228 additions and 303 deletions

View File

@ -45,7 +45,7 @@
/*
* DatabaseCollationInfo is used to store collation related information of a database
* DatabaseCollationInfo is used to store collation related information of a database.
*/
typedef struct DatabaseCollationInfo
{
@ -62,7 +62,6 @@ typedef struct DatabaseCollationInfo
#endif
} DatabaseCollationInfo;
static void EnsureSupportedCreateDatabaseCommand(CreatedbStmt *stmt);
static char * GenerateCreateDatabaseStatementFromPgDatabase(Form_pg_database
databaseForm);
static DatabaseCollationInfo GetDatabaseCollation(Oid dbOid);
@ -71,10 +70,10 @@ static AlterOwnerStmt * RecreateAlterDatabaseOwnerStmt(Oid databaseOid);
static char * GetLocaleProviderString(char datlocprovider);
#endif
static char * GetTablespaceName(Oid tablespaceOid);
static ObjectAddress * GetDatabaseAddressFromDatabaseName(char *databaseName, bool
missingOk);
static ObjectAddress * GetDatabaseAddressFromDatabaseName(char *databaseName,
bool missingOk);
static Oid get_database_owner(Oid db_oid);
static Oid get_database_owner(Oid dbId);
/* controlled via GUC */
@ -137,13 +136,13 @@ RecreateAlterDatabaseOwnerStmt(Oid databaseOid)
* get_database_owner returns the Oid of the role owning the database
*/
static Oid
get_database_owner(Oid db_oid)
get_database_owner(Oid dbId)
{
HeapTuple tuple = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(db_oid));
HeapTuple tuple = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(dbId));
if (!HeapTupleIsValid(tuple))
{
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_DATABASE),
errmsg("database with OID %u does not exist", db_oid)));
errmsg("database with OID %u does not exist", dbId)));
}
Oid dba = ((Form_pg_database) GETSTRUCT(tuple))->datdba;
@ -286,61 +285,13 @@ PreprocessAlterDatabaseSetStmt(Node *node, const char *queryString,
}
/*
* This function validates the options provided for the CREATE DATABASE command.
* It iterates over each option in the stmt->options list and checks if it's supported.
* If an unsupported option is found, or if a supported option has an invalid value,
* it raises an error.
*
* Parameters:
* stmt: A CreatedbStmt struct representing a CREATE DATABASE command.
* The options field is a list of DefElem structs, each representing an option.
*
* Currently, this function checks for the following:
* - The "oid" option is not supported.
* - The "template" option is only supported with the value "template1".
* - The "strategy" option is only supported with the value "wal_log".
*
* If any of these checks fail, the function calls ereport to raise an error.
*/
static void
EnsureSupportedCreateDatabaseCommand(CreatedbStmt *stmt)
{
DefElem *option = NULL;
foreach_ptr(option, stmt->options)
{
if (strcmp(option->defname, "oid") == 0)
{
ereport(ERROR,
errmsg("CREATE DATABASE option \"%s\" is not supported",
option->defname));
}
char *optionValue = defGetString(option);
if (strcmp(option->defname, "template") == 0 && strcmp(optionValue,
"template1") != 0)
{
ereport(ERROR, errmsg("Only template1 is supported as template "
"parameter for CREATE DATABASE"));
}
if (strcmp(option->defname, "strategy") == 0 && strcmp(optionValue, "wal_log") !=
0)
{
ereport(ERROR, errmsg("Only wal_log is supported as strategy "
"parameter for CREATE DATABASE"));
}
}
}
/*
* PostprocessAlterDatabaseStmt is executed before the statement is applied to the local
* postgres instance.
* Postgres instance.
*
* In this stage, we can perform validations and prepare the commands that need to
* be run on all workers to create the database.
* In this stage, we perform validations that we want to ensure before delegating to
* previous utility hooks because it might not be convenient to throw an error in an
* implicit transaction that creates a database.
*/
List *
PreprocessCreateDatabaseStmt(Node *node, const char *queryString,
@ -353,7 +304,6 @@ PreprocessCreateDatabaseStmt(Node *node, const char *queryString,
EnsureCoordinator();
/*validate the statement*/
CreatedbStmt *stmt = castNode(CreatedbStmt, node);
EnsureSupportedCreateDatabaseCommand(stmt);
@ -363,7 +313,7 @@ PreprocessCreateDatabaseStmt(Node *node, const char *queryString,
/*
* PostprocessCreateDatabaseStmt is executed after the statement is applied to the local
* postgres instance. In this stage we can prepare the commands that need to be run on
* postgres instance. In this stage we prepare the commands that need to be run on
* all workers to create the database. Since the CREATE DATABASE statement gives error
* in a transaction block, we need to use NontransactionalNodeDDLTaskList to send the
* CREATE DATABASE statement to the workers.
@ -379,6 +329,16 @@ PostprocessCreateDatabaseStmt(Node *node, const char *queryString)
EnsureCoordinator();
/*
* Given that CREATE DATABASE doesn't support "IF NOT EXISTS" and we're
* in the post-process, database must exist, hence missingOk = false.
*/
bool missingOk = false;
bool isPostProcess = true;
List *addresses = GetObjectAddressListFromParseTree(node, missingOk,
isPostProcess);
EnsureAllObjectDependenciesExistOnAllNodes(addresses);
char *createDatabaseCommand = DeparseTreeNode(node);
List *commands = list_make3(DISABLE_DDL_PROPAGATION,
@ -435,20 +395,6 @@ PreprocessDropDatabaseStmt(Node *node, const char *queryString,
}
/*
* GetDatabaseAddressFromDatabaseName gets the database name and returns the ObjectAddress
* of the database.
*/
static ObjectAddress *
GetDatabaseAddressFromDatabaseName(char *databaseName, bool missingOk)
{
Oid databaseOid = get_database_oid(databaseName, missingOk);
ObjectAddress *dbObjectAddress = palloc0(sizeof(ObjectAddress));
ObjectAddressSet(*dbObjectAddress, DatabaseRelationId, databaseOid);
return dbObjectAddress;
}
/*
* DropDatabaseStmtObjectAddress gets the ObjectAddress of the database that is the
* object of the DropdbStmt.
@ -477,6 +423,65 @@ CreateDatabaseStmtObjectAddress(Node *node, bool missingOk, bool isPostprocess)
}
/*
* EnsureSupportedCreateDatabaseCommand validates the options provided for the CREATE
* DATABASE command.
*
* Parameters:
* stmt: A CreatedbStmt struct representing a CREATE DATABASE command.
* The options field is a list of DefElem structs, each representing an option.
*
* Currently, this function checks for the following:
* - The "oid" option is not supported.
* - The "template" option is only supported with the value "template1".
* - The "strategy" option is only supported with the value "wal_log".
*/
void
EnsureSupportedCreateDatabaseCommand(CreatedbStmt *stmt)
{
DefElem *option = NULL;
foreach_ptr(option, stmt->options)
{
if (strcmp(option->defname, "oid") == 0)
{
ereport(ERROR,
errmsg("CREATE DATABASE option \"%s\" is not supported",
option->defname));
}
char *optionValue = defGetString(option);
if (strcmp(option->defname, "template") == 0 &&
strcmp(optionValue, "template1") != 0)
{
ereport(ERROR, errmsg("Only template1 is supported as template "
"parameter for CREATE DATABASE"));
}
if (strcmp(option->defname, "strategy") == 0 &&
strcmp(optionValue, "wal_log") != 0)
{
ereport(ERROR, errmsg("Only wal_log is supported as strategy "
"parameter for CREATE DATABASE"));
}
}
}
/*
* GetDatabaseAddressFromDatabaseName gets the database name and returns the ObjectAddress
* of the database.
*/
static ObjectAddress *
GetDatabaseAddressFromDatabaseName(char *databaseName, bool missingOk)
{
Oid databaseOid = get_database_oid(databaseName, missingOk);
ObjectAddress *dbObjectAddress = palloc0(sizeof(ObjectAddress));
ObjectAddressSet(*dbObjectAddress, DatabaseRelationId, databaseOid);
return dbObjectAddress;
}
/*
* GetTablespaceName gets the tablespace oid and returns the tablespace name.
*/
@ -664,73 +669,24 @@ GenerateCreateDatabaseStatementFromPgDatabase(Form_pg_database databaseForm)
/*
* GrantOnDatabaseDDLCommands returns a list of sql statements to idempotently apply a
* GRANT on distributed databases.
*/
List *
GenerateGrantDatabaseCommandList(void)
{
List *grantCommands = NIL;
Relation pgDatabaseRel = table_open(DatabaseRelationId, AccessShareLock);
TableScanDesc scan = table_beginscan_catalog(pgDatabaseRel, 0, NULL);
HeapTuple tuple = NULL;
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
Form_pg_database databaseForm = (Form_pg_database) GETSTRUCT(tuple);
ObjectAddress *dbAddress = GetDatabaseAddressFromDatabaseName(
NameStr(databaseForm->datname), false);
/* skip databases that are not distributed */
if (!IsAnyObjectDistributed(list_make1(dbAddress)))
{
continue;
}
List *dbGrants = GrantOnDatabaseDDLCommands(databaseForm->oid);
/* append dbGrants into grantCommands*/
grantCommands = list_concat(grantCommands, dbGrants);
}
heap_endscan(scan);
table_close(pgDatabaseRel, AccessShareLock);
return grantCommands;
}
/*
* GenerateCreateDatabaseCommandList returns a list of CREATE DATABASE statements
* for all the databases.
* CreateDatabaseDDLCommand returns a CREATE DATABASE command to create given
* database
*
* Commands in the list are wrapped by citus_internal_database_command() UDF
* to avoid from transaction block restrictions that apply to database commands
* Command is wrapped by citus_internal_database_command() UDF
* to avoid from transaction block restrictions that apply to database commands.
*/
List *
GenerateCreateDatabaseCommandList(void)
char *
CreateDatabaseDDLCommand(Oid dbId)
{
List *commands = NIL;
Relation pgDatabaseRel = table_open(DatabaseRelationId, AccessShareLock);
TableScanDesc scan = table_beginscan_catalog(pgDatabaseRel, 0, NULL);
HeapTuple tuple = NULL;
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
HeapTuple tuple = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(dbId));
if (!HeapTupleIsValid(tuple))
{
Form_pg_database databaseForm = (Form_pg_database) GETSTRUCT(tuple);
ObjectAddress *dbAddress = GetDatabaseAddressFromDatabaseName(
NameStr(databaseForm->datname), false);
/* skip databases that are not distributed */
if (!IsAnyObjectDistributed(list_make1(dbAddress)))
{
continue;
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_DATABASE),
errmsg("database with OID %u does not exist", dbId)));
}
Form_pg_database databaseForm = (Form_pg_database) GETSTRUCT(tuple);
char *createStmt = GenerateCreateDatabaseStatementFromPgDatabase(databaseForm);
StringInfo outerDbStmt = makeStringInfo();
@ -738,15 +694,9 @@ GenerateCreateDatabaseCommandList(void)
/* Generate the CREATE DATABASE statement */
appendStringInfo(outerDbStmt,
"SELECT pg_catalog.citus_internal_database_command(%s)",
quote_literal_cstr(
createStmt));
quote_literal_cstr(createStmt));
/* Add the statement to the list of commands */
commands = lappend(commands, outerDbStmt->data);
}
heap_endscan(scan);
table_close(pgDatabaseRel, AccessShareLock);
return commands;
ReleaseSysCache(tuple);
return outerDbStmt->data;
}

View File

@ -457,16 +457,37 @@ GetDependencyCreateDDLCommands(const ObjectAddress *dependency)
case OCLASS_DATABASE:
{
List *databaseDDLCommands = NIL;
/* only propagate the ownership of the database when the feature is on */
if (EnableAlterDatabaseOwner)
/*
* For the database where Citus is installed, only propagate the ownership of the
* database, only when the feature is on.
*
* This is because this database must exist on all nodes already so we shouldn't
* need to "CREATE" it on other nodes. However, we still need to correctly reflect
* its owner on other nodes too.
*/
if (dependency->objectId == MyDatabaseId && EnableAlterDatabaseOwner)
{
List *ownerDDLCommands = DatabaseOwnerDDLCommands(dependency);
databaseDDLCommands = list_concat(databaseDDLCommands, ownerDDLCommands);
return DatabaseOwnerDDLCommands(dependency);
}
return databaseDDLCommands;
/*
* For the other databases, create the database on all nodes, only when the feature
* is on.
*/
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 NIL;
}
case OCLASS_PROC:

View File

@ -1347,7 +1347,7 @@ CreateRoleStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)
* with the role the role needs to be visible on all connections used by the transaction,
* meaning we can only use 1 connection per node.
*/
void
static void
EnsureSequentialModeForRoleDDL(void)
{
if (!IsTransactionBlock())

View File

@ -70,15 +70,6 @@ DefElemOptionToStatement(StringInfo buf, DefElem *option,
break;
}
#if PG_VERSION_NUM >= PG_VERSION_15
case OPTION_FORMAT_OBJECT_ID:
{
Oid value = defGetObjectId(option);
appendStringInfo(buf, optionFormats[i].format, value);
break;
}
#endif
case OPTION_FORMAT_LITERAL_CSTR:
{
char *value = defGetString(option);

View File

@ -13,17 +13,18 @@
#include "pg_version_compat.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 "commands/defrem.h"
#include "distributed/deparser.h"
#include "distributed/commands.h"
#include "distributed/citus_ruleutils.h"
#include "distributed/deparser.h"
#include "distributed/listutils.h"
#include "distributed/log_utils.h"
#include "parser/parse_type.h"
static void AppendAlterDatabaseOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt);
@ -49,26 +50,9 @@ const DefElemOptionFormat create_database_option_formats[] = {
{ "tablespace", " TABLESPACE %s", OPTION_FORMAT_STRING },
{ "allow_connections", " ALLOW_CONNECTIONS %s", OPTION_FORMAT_BOOLEAN },
{ "connection_limit", " CONNECTION LIMIT %d", OPTION_FORMAT_INTEGER },
{ "is_template", " IS_TEMPLATE %s", OPTION_FORMAT_BOOLEAN },
{ "oid", " OID %d", OPTION_FORMAT_OBJECT_ID }
{ "is_template", " IS_TEMPLATE %s", OPTION_FORMAT_BOOLEAN }
};
/*
* DeparseAlterDatabaseOwnerStmt
* Deparse an AlterDatabaseOwnerStmt node
*
* This function is responsible for producing a string representation of an
* AlterDatabaseOwnerStmt node, which represents an ALTER DATABASE statement
* that changes the owner of a database. The output string includes the ALTER
* DATABASE keyword, the name of the database being altered, and the new owner
* of the database.
*
* Parameters:
* - node: a pointer to the AlterDatabaseOwnerStmt node to be deparsed
*
* Returns:
* - a string representation of the ALTER DATABASE statement
*/
char *
DeparseAlterDatabaseOwnerStmt(Node *node)
{
@ -84,15 +68,6 @@ DeparseAlterDatabaseOwnerStmt(Node *node)
}
/*
*
* AppendAlterDatabaseOwnerStmt
* Append an ALTER DATABASE statement for changing the owner of a database to the given StringInfo buffer.
*
* Parameters:
* - buf: The StringInfo buffer to append the statement to.
* - stmt: The AlterOwnerStmt representing the ALTER DATABASE statement to append.
*/
static void
AppendAlterDatabaseOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt)
{
@ -258,26 +233,25 @@ DeparseAlterDatabaseSetStmt(Node *node)
static void
AppendCreateDatabaseStmt(StringInfo buf, CreatedbStmt *stmt)
{
/*
* Make sure that we don't try to deparse something that this
* function doesn't expect.
*/
EnsureSupportedCreateDatabaseCommand(stmt);
appendStringInfo(buf,
"CREATE DATABASE %s",
quote_identifier(stmt->dbname));
DefElem *option = NULL;
foreach_ptr(option, stmt->options)
{
/*ValidateCreateDatabaseOptions(option); */
DefElemOptionToStatement(buf, option, create_database_option_formats,
lengthof(create_database_option_formats));
}
}
/*
* Converts a CreatedbStmt structure into a SQL command string.
* Used in the deparsing of Create database statement.
*/
char *
DeparseCreateDatabaseStmt(Node *node)
{
@ -300,20 +274,15 @@ AppendDropDatabaseStmt(StringInfo buf, DropdbStmt *stmt)
ifExistsStatement,
quote_identifier(stmt->dbname));
DefElem *option = NULL;
foreach_ptr(option, stmt->options)
if (list_length(stmt->options) > 1)
{
/* if it is the first option then append with "WITH" else append with "," */
if (option == linitial(stmt->options))
/* FORCE is the only option that can be provided for this command */
elog(ERROR, "got unexpected number of options for DROP DATABASE");
}
else if (list_length(stmt->options) == 1)
{
DefElem *option = linitial(stmt->options);
appendStringInfo(buf, " WITH ( ");
}
else
{
appendStringInfo(buf, ", ");
}
if (strcmp(option->defname, "force") == 0)
{
@ -321,24 +290,17 @@ AppendDropDatabaseStmt(StringInfo buf, DropdbStmt *stmt)
}
else
{
/* FORCE is the only option that can be provided for this command */
ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),
errmsg("unrecognized DROP DATABASE option \"%s\"",
option->defname)));
}
/* if it is the last option then append with ")" */
if (option == llast(stmt->options))
{
appendStringInfo(buf, " )");
}
}
}
/*
* Converts a DropdbStmt structure into a SQL command string.
* Used in the deparsing of drop database statement.
*/
char *
DeparseDropDatabaseStmt(Node *node)
{

View File

@ -698,7 +698,6 @@ SupportedDependencyByCitus(const ObjectAddress *address)
case OCLASS_DATABASE:
{
/* only to propagate its owner */
return true;
}

View File

@ -155,7 +155,6 @@ static char * RemoteSchemaIdExpressionByName(char *schemaName);
static char * RemoteTypeIdExpression(Oid typeId);
static char * RemoteCollationIdExpression(Oid colocationId);
static char * RemoteTableIdExpression(Oid relationId);
static void SendDatabaseGrantSyncCommands(MetadataSyncContext *context);
PG_FUNCTION_INFO_V1(start_metadata_sync_to_all_nodes);
@ -2049,6 +2048,10 @@ GenerateGrantOnSchemaQueriesFromAclItem(Oid schemaOid, AclItem *aclItem)
}
/*
* GrantOnDatabaseDDLCommands creates a list of ddl command for replicating the permissions
* of roles on databases.
*/
List *
GrantOnDatabaseDDLCommands(Oid databaseOid)
{
@ -2079,6 +2082,10 @@ GrantOnDatabaseDDLCommands(Oid databaseOid)
}
/*
* GenerateGrantOnDatabaseFromAclItem generates a query string for replicating a users permissions
* on a database.
*/
List *
GenerateGrantOnDatabaseFromAclItem(Oid databaseOid, AclItem *aclItem)
{
@ -4658,13 +4665,6 @@ PropagateNodeWideObjectsCommandList(void)
ddlCommands = list_concat(ddlCommands, alterRoleSetCommands);
}
if (EnableCreateDatabasePropagation)
{
/* get commands for database creation */
List *createDatabaseCommands = GenerateCreateDatabaseCommandList();
ddlCommands = list_concat(ddlCommands, createDatabaseCommands);
}
return ddlCommands;
}
@ -4736,13 +4736,6 @@ SyncDistributedObjects(MetadataSyncContext *context)
* those tables.
*/
SendInterTableRelationshipCommands(context);
/*
* After creation of databases and roles, send the grant database commands
* to the workers.
*/
SendDatabaseGrantSyncCommands(context);
}
@ -4768,34 +4761,6 @@ SendNodeWideObjectsSyncCommands(MetadataSyncContext *context)
}
/*
* SendDatabaseGrantSyncCommands sends database grants to roles to workers with
* transactional or nontransactional mode according to transactionMode inside
* metadataSyncContext in case of EnableCreateDatabasePropagation GUC set.
* This function is called after SendNodeWideObjectsSyncCommands and SendDependencyCreationCommands
* because we need both databases and roles to be created on the worker.
*
*/
static void
SendDatabaseGrantSyncCommands(MetadataSyncContext *context)
{
if (EnableCreateDatabasePropagation)
{
/* propagate node wide objects. It includes only roles for now. */
List *commandList = GenerateGrantDatabaseCommandList();
if (commandList == NIL)
{
return;
}
commandList = lcons(DISABLE_DDL_PROPAGATION, commandList);
commandList = lappend(commandList, ENABLE_DDL_PROPAGATION);
SendOrCollectCommandListToActivatedNodes(context, commandList);
}
}
/*
* SendShellTableDeletionCommands sends sequence, and shell table deletion
* commands to workers with transactional or nontransactional mode according to

View File

@ -243,8 +243,8 @@ extern List * DropDatabaseStmtObjectAddress(Node *node, bool missingOk,
bool isPostprocess);
extern List * CreateDatabaseStmtObjectAddress(Node *node, bool missingOk,
bool isPostprocess);
extern List * GenerateCreateDatabaseCommandList(void);
extern List * GenerateGrantDatabaseCommandList(void);
extern void EnsureSupportedCreateDatabaseCommand(CreatedbStmt *stmt);
extern char * CreateDatabaseDDLCommand(Oid dbId);
/* domain.c - forward declarations */

View File

@ -135,8 +135,7 @@ typedef enum OptionFormatType
OPTION_FORMAT_STRING,
OPTION_FORMAT_LITERAL_CSTR,
OPTION_FORMAT_BOOLEAN,
OPTION_FORMAT_INTEGER,
OPTION_FORMAT_OBJECT_ID
OPTION_FORMAT_INTEGER
} OptionFormatType;

View File

@ -910,6 +910,40 @@ SELECT result from run_command_on_all_nodes(
DROP ROLE db_role_grants_test_role_exists_on_node_2;
DROP ROLE db_role_grants_test_role_missing_on_node_2;
select 1 from citus_remove_node('localhost', :worker_2_port);
?column?
---------------------------------------------------------------------
1
(1 row)
set citus.enable_create_role_propagation TO off;
create role non_propagated_role;
NOTICE: not propagating CREATE ROLE/USER commands to other nodes
HINT: Connect to other nodes directly to manually create all necessary users and roles.
set citus.enable_create_role_propagation TO on;
set citus.enable_create_database_propagation TO on;
-- Make sure that we propagate non_propagated_role because it's a dependency of test_db.
-- And hence it becomes a distributed object.
create database test_db OWNER non_propagated_role;
create role propagated_role;
grant connect on database test_db to propagated_role;
SELECT 1 FROM citus_add_node('localhost', :worker_2_port);
?column?
---------------------------------------------------------------------
1
(1 row)
SELECT * FROM public.check_database_on_all_nodes('test_db') ORDER BY node_type;
node_type | result
---------------------------------------------------------------------
coordinator (local) | {"database_properties": {"datacl": ["=Tc/non_propagated_role", "non_propagated_role=CTc/non_propagated_role", "propagated_role=c/non_propagated_role"], "datname": "test_db", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "non_propagated_role", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false}
worker node (remote) | {"database_properties": {"datacl": ["=Tc/non_propagated_role", "non_propagated_role=CTc/non_propagated_role", "propagated_role=c/non_propagated_role"], "datname": "test_db", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "non_propagated_role", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false}
worker node (remote) | {"database_properties": {"datacl": ["=Tc/non_propagated_role", "non_propagated_role=CTc/non_propagated_role", "propagated_role=c/non_propagated_role"], "datname": "test_db", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "non_propagated_role", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false}
(3 rows)
REVOKE CONNECT ON DATABASE test_db FROM propagated_role;
DROP DATABASE test_db;
DROP ROLE propagated_role, non_propagated_role;
--clean up resources created by this test
-- DROP TABLESPACE is not supported, so we need to drop it manually.
SELECT result FROM run_command_on_all_nodes(

View File

@ -38,7 +38,6 @@ test: create_single_shard_table
test: create_drop_database_propagation
test: create_drop_database_propagation_pg15
test: create_drop_database_propagation_pg16
# don't parallelize single_shard_table_udfs to make sure colocation ids are sequential
test: single_shard_table_udfs
test: schema_based_sharding

View File

@ -296,8 +296,6 @@ drop database distributed_db;
set citus.enable_create_database_propagation TO off;
drop database non_distributed_db;
-- test role grants on DATABASE in metadata sync
SELECT result from run_command_on_all_nodes(
@ -314,11 +312,8 @@ SELECT result from run_command_on_all_nodes(
SET citus.enable_create_database_propagation TO on;
CREATE ROLE db_role_grants_test_role_exists_on_node_2;
select 1 from citus_remove_node('localhost', :worker_2_port);
CREATE DATABASE db_role_grants_test;
@ -332,15 +327,11 @@ CREATE ROLE db_role_grants_test_role_missing_on_node_2;
RESET citus.log_remote_commands ;
RESET citus.grep_remote_commands;
SET citus.log_remote_commands = true;
set citus.grep_remote_commands = '%GRANT%';
grant CONNECT,TEMPORARY,CREATE on DATABASE db_role_grants_test to db_role_grants_test_role_exists_on_node_2;
grant CONNECT,TEMPORARY,CREATE on DATABASE db_role_grants_test to db_role_grants_test_role_missing_on_node_2;
grant CONNECT,TEMPORARY,CREATE on DATABASE db_role_grants_test_non_distributed to db_role_grants_test_role_exists_on_node_2;
grant CONNECT,TEMPORARY,CREATE on DATABASE db_role_grants_test_non_distributed to db_role_grants_test_role_missing_on_node_2;
@ -374,7 +365,6 @@ SELECT result from run_command_on_all_nodes(
$$
) ORDER BY result;
SELECT result from run_command_on_all_nodes(
$$
select has_database_privilege('db_role_grants_test_role_missing_on_node_2','db_role_grants_test', 'TEMPORARY')
@ -416,7 +406,6 @@ SELECT result from run_command_on_all_nodes(
$$
) ORDER BY result;
SELECT result from run_command_on_all_nodes(
$$
select has_database_privilege('db_role_grants_test_role_missing_on_node_2','db_role_grants_test_non_distributed', 'TEMPORARY')
@ -429,13 +418,11 @@ SELECT result from run_command_on_all_nodes(
$$
) ORDER BY result;
RESET citus.log_remote_commands;
RESET citus.grep_remote_commands;
select 1 from citus_add_node('localhost', :worker_2_port);
-- check the privileges after add_node for database db_role_grants_test,
-- role db_role_grants_test_role_exists_on_node_2
@ -466,7 +453,6 @@ SELECT result from run_command_on_all_nodes(
$$
) ORDER BY result;
SELECT result from run_command_on_all_nodes(
$$
select has_database_privilege('db_role_grants_test_role_missing_on_node_2','db_role_grants_test', 'TEMPORARY')
@ -508,7 +494,6 @@ SELECT result from run_command_on_all_nodes(
$$
) ORDER BY result;
SELECT result from run_command_on_all_nodes(
$$
select has_database_privilege('db_role_grants_test_role_missing_on_node_2','db_role_grants_test_non_distributed', 'TEMPORARY')
@ -533,8 +518,28 @@ SELECT result from run_command_on_all_nodes(
DROP ROLE db_role_grants_test_role_exists_on_node_2;
DROP ROLE db_role_grants_test_role_missing_on_node_2;
select 1 from citus_remove_node('localhost', :worker_2_port);
set citus.enable_create_role_propagation TO off;
create role non_propagated_role;
set citus.enable_create_role_propagation TO on;
set citus.enable_create_database_propagation TO on;
-- Make sure that we propagate non_propagated_role because it's a dependency of test_db.
-- And hence it becomes a distributed object.
create database test_db OWNER non_propagated_role;
create role propagated_role;
grant connect on database test_db to propagated_role;
SELECT 1 FROM citus_add_node('localhost', :worker_2_port);
SELECT * FROM public.check_database_on_all_nodes('test_db') ORDER BY node_type;
REVOKE CONNECT ON DATABASE test_db FROM propagated_role;
DROP DATABASE test_db;
DROP ROLE propagated_role, non_propagated_role;
--clean up resources created by this test