Merge branch 'main' into fix_master_disable_node

pull/7492/head
Jelte Fennema-Nio 2024-02-21 11:23:55 +01:00 committed by GitHub
commit ea898fa294
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 947 additions and 88 deletions

View File

@ -107,8 +107,20 @@ typedef struct NonMainDbDistributedStatementInfo
{ {
int statementType; int statementType;
bool explicitlyMarkAsDistributed; bool explicitlyMarkAsDistributed;
/*
* checkSupportedObjectTypes is a callback function that checks whether
* type of the object referred to by given statement is supported.
*
* Can be NULL if not applicable for the statement type.
*/
bool (*checkSupportedObjectTypes)(Node *node);
} NonMainDbDistributedStatementInfo; } NonMainDbDistributedStatementInfo;
/*
* MarkObjectDistributedParams is used to pass parameters to the
* MarkObjectDistributedFromNonMainDb function.
*/
typedef struct MarkObjectDistributedParams typedef struct MarkObjectDistributedParams
{ {
char *name; char *name;
@ -116,15 +128,6 @@ typedef struct MarkObjectDistributedParams
uint16 catalogRelId; uint16 catalogRelId;
} MarkObjectDistributedParams; } MarkObjectDistributedParams;
/*
* NonMainDbSupportedStatements is an array of statements that are supported
* from non-main databases.
*/
static const NonMainDbDistributedStatementInfo NonMainDbSupportedStatements[] = {
{ T_GrantRoleStmt, false },
{ T_CreateRoleStmt, true }
};
bool EnableDDLPropagation = true; /* ddl propagation is enabled */ bool EnableDDLPropagation = true; /* ddl propagation is enabled */
int CreateObjectPropagationMode = CREATE_OBJECT_PROPAGATION_IMMEDIATE; int CreateObjectPropagationMode = CREATE_OBJECT_PROPAGATION_IMMEDIATE;
@ -153,6 +156,12 @@ static void PostStandardProcessUtility(Node *parsetree);
static void DecrementUtilityHookCountersIfNecessary(Node *parsetree); static void DecrementUtilityHookCountersIfNecessary(Node *parsetree);
static bool IsDropSchemaOrDB(Node *parsetree); static bool IsDropSchemaOrDB(Node *parsetree);
static bool ShouldCheckUndistributeCitusLocalTables(void); static bool ShouldCheckUndistributeCitusLocalTables(void);
/*
* Functions to support commands used to manage node-wide objects from non-main
* databases.
*/
static void RunPreprocessMainDBCommand(Node *parsetree); static void RunPreprocessMainDBCommand(Node *parsetree);
static void RunPostprocessMainDBCommand(Node *parsetree); static void RunPostprocessMainDBCommand(Node *parsetree);
static bool IsStatementSupportedFromNonMainDb(Node *parsetree); static bool IsStatementSupportedFromNonMainDb(Node *parsetree);
@ -160,6 +169,25 @@ static bool StatementRequiresMarkDistributedFromNonMainDb(Node *parsetree);
static void MarkObjectDistributedFromNonMainDb(Node *parsetree); static void MarkObjectDistributedFromNonMainDb(Node *parsetree);
static MarkObjectDistributedParams GetMarkObjectDistributedParams(Node *parsetree); static MarkObjectDistributedParams GetMarkObjectDistributedParams(Node *parsetree);
/*
* checkSupportedObjectTypes callbacks for
* NonMainDbDistributedStatementInfo objects.
*/
static bool NonMainDbCheckSupportedObjectTypeForGrant(Node *node);
/*
* NonMainDbSupportedStatements is an array of statements that are supported
* from non-main databases.
*/
ObjectType supportedObjectTypesForGrantStmt[] = { OBJECT_DATABASE };
static const NonMainDbDistributedStatementInfo NonMainDbSupportedStatements[] = {
{ T_GrantRoleStmt, false, NULL },
{ T_CreateRoleStmt, true, NULL },
{ T_GrantStmt, false, NonMainDbCheckSupportedObjectTypeForGrant }
};
/* /*
* ProcessUtilityParseTree is a convenience method to create a PlannedStmt out of * ProcessUtilityParseTree is a convenience method to create a PlannedStmt out of
* pieces of a utility statement before invoking ProcessUtility. * pieces of a utility statement before invoking ProcessUtility.
@ -1692,10 +1720,13 @@ IsStatementSupportedFromNonMainDb(Node *parsetree)
for (int i = 0; i < sizeof(NonMainDbSupportedStatements) / for (int i = 0; i < sizeof(NonMainDbSupportedStatements) /
sizeof(NonMainDbSupportedStatements[0]); i++) sizeof(NonMainDbSupportedStatements[0]); i++)
{ {
if (type == NonMainDbSupportedStatements[i].statementType) if (type != NonMainDbSupportedStatements[i].statementType)
{ {
return true; continue;
} }
return !NonMainDbSupportedStatements[i].checkSupportedObjectTypes ||
NonMainDbSupportedStatements[i].checkSupportedObjectTypes(parsetree);
} }
return false; return false;
@ -1767,3 +1798,15 @@ GetMarkObjectDistributedParams(Node *parsetree)
elog(ERROR, "unsupported statement type"); elog(ERROR, "unsupported statement type");
} }
/*
* NonMainDbCheckSupportedObjectTypeForGrant implements checkSupportedObjectTypes
* callback for GrantStmt.
*/
static bool
NonMainDbCheckSupportedObjectTypeForGrant(Node *node)
{
GrantStmt *stmt = castNode(GrantStmt, node);
return stmt->objtype == OBJECT_DATABASE;
}

View File

@ -426,10 +426,9 @@ ExecuteDropShardPlacementCommandRemotely(ShardPlacement *shardPlacement,
errdetail("Marking this shard placement for " errdetail("Marking this shard placement for "
"deletion"))); "deletion")));
InsertCleanupRecordInCurrentTransaction(CLEANUP_OBJECT_SHARD_PLACEMENT, InsertCleanupOnSuccessRecordInCurrentTransaction(CLEANUP_OBJECT_SHARD_PLACEMENT,
shardRelationName, shardRelationName,
shardPlacement->groupId, shardPlacement->groupId);
CLEANUP_DEFERRED_ON_SUCCESS);
return; return;
} }

View File

@ -452,15 +452,15 @@ CompareCleanupRecordsByObjectType(const void *leftElement, const void *rightElem
/* /*
* InsertCleanupRecordInCurrentTransaction inserts a new pg_dist_cleanup entry * InsertCleanupOnSuccessRecordInCurrentTransaction inserts a new pg_dist_cleanup entry
* as part of the current transaction. This is primarily useful for deferred drop scenarios, * as part of the current transaction. This is primarily useful for deferred drop scenarios,
* since these records would roll back in case of operation failure. * since these records would roll back in case of operation failure. And for the same reason,
* always sets the policy type to CLEANUP_DEFERRED_ON_SUCCESS.
*/ */
void void
InsertCleanupRecordInCurrentTransaction(CleanupObject objectType, InsertCleanupOnSuccessRecordInCurrentTransaction(CleanupObject objectType,
char *objectName, char *objectName,
int nodeGroupId, int nodeGroupId)
CleanupPolicy policy)
{ {
/* We must have a valid OperationId. Any operation requring cleanup /* We must have a valid OperationId. Any operation requring cleanup
* will call RegisterOperationNeedingCleanup. * will call RegisterOperationNeedingCleanup.
@ -482,7 +482,8 @@ InsertCleanupRecordInCurrentTransaction(CleanupObject objectType,
values[Anum_pg_dist_cleanup_object_type - 1] = Int32GetDatum(objectType); values[Anum_pg_dist_cleanup_object_type - 1] = Int32GetDatum(objectType);
values[Anum_pg_dist_cleanup_object_name - 1] = CStringGetTextDatum(objectName); values[Anum_pg_dist_cleanup_object_name - 1] = CStringGetTextDatum(objectName);
values[Anum_pg_dist_cleanup_node_group_id - 1] = Int32GetDatum(nodeGroupId); values[Anum_pg_dist_cleanup_node_group_id - 1] = Int32GetDatum(nodeGroupId);
values[Anum_pg_dist_cleanup_policy_type - 1] = Int32GetDatum(policy); values[Anum_pg_dist_cleanup_policy_type - 1] =
Int32GetDatum(CLEANUP_DEFERRED_ON_SUCCESS);
/* open cleanup relation and insert new tuple */ /* open cleanup relation and insert new tuple */
Oid relationId = DistCleanupRelationId(); Oid relationId = DistCleanupRelationId();
@ -499,23 +500,27 @@ InsertCleanupRecordInCurrentTransaction(CleanupObject objectType,
/* /*
* InsertCleanupRecordInSubtransaction inserts a new pg_dist_cleanup entry in a * InsertCleanupRecordOutsideTransaction inserts a new pg_dist_cleanup entry in a
* separate transaction to ensure the record persists after rollback. We should * separate transaction to ensure the record persists after rollback. We should
* delete these records if the operation completes successfully. * delete these records if the operation completes successfully.
* *
* For failure scenarios, use a subtransaction (direct insert via localhost). * This is used in scenarios where we need to cleanup resources on operation
* completion (CLEANUP_ALWAYS) or on failure (CLEANUP_ON_FAILURE).
*/ */
void void
InsertCleanupRecordInSubtransaction(CleanupObject objectType, InsertCleanupRecordOutsideTransaction(CleanupObject objectType,
char *objectName, char *objectName,
int nodeGroupId, int nodeGroupId,
CleanupPolicy policy) CleanupPolicy policy)
{ {
/* We must have a valid OperationId. Any operation requring cleanup /* We must have a valid OperationId. Any operation requring cleanup
* will call RegisterOperationNeedingCleanup. * will call RegisterOperationNeedingCleanup.
*/ */
Assert(CurrentOperationId != INVALID_OPERATION_ID); Assert(CurrentOperationId != INVALID_OPERATION_ID);
/* assert the circumstance noted in function comment */
Assert(policy == CLEANUP_ALWAYS || policy == CLEANUP_ON_FAILURE);
StringInfo sequenceName = makeStringInfo(); StringInfo sequenceName = makeStringInfo();
appendStringInfo(sequenceName, "%s.%s", appendStringInfo(sequenceName, "%s.%s",
PG_CATALOG, PG_CATALOG,

View File

@ -733,11 +733,11 @@ CreateSplitShardsForShardGroup(List *shardGroupSplitIntervalListList,
workerPlacementNode->workerPort))); workerPlacementNode->workerPort)));
} }
InsertCleanupRecordInSubtransaction(CLEANUP_OBJECT_SHARD_PLACEMENT, InsertCleanupRecordOutsideTransaction(CLEANUP_OBJECT_SHARD_PLACEMENT,
ConstructQualifiedShardName( ConstructQualifiedShardName(
shardInterval), shardInterval),
workerPlacementNode->groupId, workerPlacementNode->groupId,
CLEANUP_ON_FAILURE); CLEANUP_ON_FAILURE);
/* Create new split child shard on the specified placement list */ /* Create new split child shard on the specified placement list */
CreateObjectOnPlacement(splitShardCreationCommandList, CreateObjectOnPlacement(splitShardCreationCommandList,
@ -1717,11 +1717,11 @@ CreateDummyShardsForShardGroup(HTAB *mapOfPlacementToDummyShardList,
/* Log shard in pg_dist_cleanup. Given dummy shards are transient resources, /* Log shard in pg_dist_cleanup. Given dummy shards are transient resources,
* we want to cleanup irrespective of operation success or failure. * we want to cleanup irrespective of operation success or failure.
*/ */
InsertCleanupRecordInSubtransaction(CLEANUP_OBJECT_SHARD_PLACEMENT, InsertCleanupRecordOutsideTransaction(CLEANUP_OBJECT_SHARD_PLACEMENT,
ConstructQualifiedShardName( ConstructQualifiedShardName(
shardInterval), shardInterval),
workerPlacementNode->groupId, workerPlacementNode->groupId,
CLEANUP_ALWAYS); CLEANUP_ALWAYS);
/* Create dummy source shard on the specified placement list */ /* Create dummy source shard on the specified placement list */
CreateObjectOnPlacement(splitShardCreationCommandList, CreateObjectOnPlacement(splitShardCreationCommandList,
@ -1780,11 +1780,11 @@ CreateDummyShardsForShardGroup(HTAB *mapOfPlacementToDummyShardList,
/* Log shard in pg_dist_cleanup. Given dummy shards are transient resources, /* Log shard in pg_dist_cleanup. Given dummy shards are transient resources,
* we want to cleanup irrespective of operation success or failure. * we want to cleanup irrespective of operation success or failure.
*/ */
InsertCleanupRecordInSubtransaction(CLEANUP_OBJECT_SHARD_PLACEMENT, InsertCleanupRecordOutsideTransaction(CLEANUP_OBJECT_SHARD_PLACEMENT,
ConstructQualifiedShardName( ConstructQualifiedShardName(
shardInterval), shardInterval),
sourceWorkerNode->groupId, sourceWorkerNode->groupId,
CLEANUP_ALWAYS); CLEANUP_ALWAYS);
/* Create dummy split child shard on source worker node */ /* Create dummy split child shard on source worker node */
CreateObjectOnPlacement(splitShardCreationCommandList, sourceWorkerNode); CreateObjectOnPlacement(splitShardCreationCommandList, sourceWorkerNode);

View File

@ -604,10 +604,10 @@ InsertDeferredDropCleanupRecordsForShards(List *shardIntervalList)
* We also log cleanup record in the current transaction. If the current transaction rolls back, * We also log cleanup record in the current transaction. If the current transaction rolls back,
* we do not generate a record at all. * we do not generate a record at all.
*/ */
InsertCleanupRecordInCurrentTransaction(CLEANUP_OBJECT_SHARD_PLACEMENT, InsertCleanupOnSuccessRecordInCurrentTransaction(
qualifiedShardName, CLEANUP_OBJECT_SHARD_PLACEMENT,
placement->groupId, qualifiedShardName,
CLEANUP_DEFERRED_ON_SUCCESS); placement->groupId);
} }
} }
} }
@ -634,10 +634,9 @@ InsertCleanupRecordsForShardPlacementsOnNode(List *shardIntervalList,
* We also log cleanup record in the current transaction. If the current transaction rolls back, * We also log cleanup record in the current transaction. If the current transaction rolls back,
* we do not generate a record at all. * we do not generate a record at all.
*/ */
InsertCleanupRecordInCurrentTransaction(CLEANUP_OBJECT_SHARD_PLACEMENT, InsertCleanupOnSuccessRecordInCurrentTransaction(CLEANUP_OBJECT_SHARD_PLACEMENT,
qualifiedShardName, qualifiedShardName,
groupId, groupId);
CLEANUP_DEFERRED_ON_SUCCESS);
} }
} }
@ -1393,10 +1392,11 @@ CopyShardTablesViaLogicalReplication(List *shardIntervalList, char *sourceNodeNa
char *tableOwner = TableOwner(shardInterval->relationId); char *tableOwner = TableOwner(shardInterval->relationId);
/* drop the shard we created on the target, in case of failure */ /* drop the shard we created on the target, in case of failure */
InsertCleanupRecordInSubtransaction(CLEANUP_OBJECT_SHARD_PLACEMENT, InsertCleanupRecordOutsideTransaction(CLEANUP_OBJECT_SHARD_PLACEMENT,
ConstructQualifiedShardName(shardInterval), ConstructQualifiedShardName(shardInterval),
GroupForNode(targetNodeName, targetNodePort), GroupForNode(targetNodeName,
CLEANUP_ON_FAILURE); targetNodePort),
CLEANUP_ON_FAILURE);
SendCommandListToWorkerOutsideTransaction(targetNodeName, targetNodePort, SendCommandListToWorkerOutsideTransaction(targetNodeName, targetNodePort,
tableOwner, tableOwner,
@ -1466,10 +1466,11 @@ CopyShardTablesViaBlockWrites(List *shardIntervalList, char *sourceNodeName,
char *tableOwner = TableOwner(shardInterval->relationId); char *tableOwner = TableOwner(shardInterval->relationId);
/* drop the shard we created on the target, in case of failure */ /* drop the shard we created on the target, in case of failure */
InsertCleanupRecordInSubtransaction(CLEANUP_OBJECT_SHARD_PLACEMENT, InsertCleanupRecordOutsideTransaction(CLEANUP_OBJECT_SHARD_PLACEMENT,
ConstructQualifiedShardName(shardInterval), ConstructQualifiedShardName(shardInterval),
GroupForNode(targetNodeName, targetNodePort), GroupForNode(targetNodeName,
CLEANUP_ON_FAILURE); targetNodePort),
CLEANUP_ON_FAILURE);
SendCommandListToWorkerOutsideTransaction(targetNodeName, targetNodePort, SendCommandListToWorkerOutsideTransaction(targetNodeName, targetNodePort,
tableOwner, ddlCommandList); tableOwner, ddlCommandList);

View File

@ -1335,10 +1335,10 @@ CreatePublications(MultiConnection *connection,
WorkerNode *worker = FindWorkerNode(connection->hostname, WorkerNode *worker = FindWorkerNode(connection->hostname,
connection->port); connection->port);
InsertCleanupRecordInSubtransaction(CLEANUP_OBJECT_PUBLICATION, InsertCleanupRecordOutsideTransaction(CLEANUP_OBJECT_PUBLICATION,
entry->name, entry->name,
worker->groupId, worker->groupId,
CLEANUP_ALWAYS); CLEANUP_ALWAYS);
ExecuteCriticalRemoteCommand(connection, DISABLE_DDL_PROPAGATION); ExecuteCriticalRemoteCommand(connection, DISABLE_DDL_PROPAGATION);
ExecuteCriticalRemoteCommand(connection, createPublicationCommand->data); ExecuteCriticalRemoteCommand(connection, createPublicationCommand->data);
@ -1435,10 +1435,10 @@ CreateReplicationSlots(MultiConnection *sourceConnection,
WorkerNode *worker = FindWorkerNode(sourceConnection->hostname, WorkerNode *worker = FindWorkerNode(sourceConnection->hostname,
sourceConnection->port); sourceConnection->port);
InsertCleanupRecordInSubtransaction(CLEANUP_OBJECT_REPLICATION_SLOT, InsertCleanupRecordOutsideTransaction(CLEANUP_OBJECT_REPLICATION_SLOT,
replicationSlot->name, replicationSlot->name,
worker->groupId, worker->groupId,
CLEANUP_ALWAYS); CLEANUP_ALWAYS);
if (!firstReplicationSlot) if (!firstReplicationSlot)
{ {
@ -1506,10 +1506,10 @@ CreateSubscriptions(MultiConnection *sourceConnection,
quote_identifier(GetUserNameFromId(ownerId, false)) quote_identifier(GetUserNameFromId(ownerId, false))
))); )));
InsertCleanupRecordInSubtransaction(CLEANUP_OBJECT_USER, InsertCleanupRecordOutsideTransaction(CLEANUP_OBJECT_USER,
target->subscriptionOwnerName, target->subscriptionOwnerName,
worker->groupId, worker->groupId,
CLEANUP_ALWAYS); CLEANUP_ALWAYS);
StringInfo conninfo = makeStringInfo(); StringInfo conninfo = makeStringInfo();
appendStringInfo(conninfo, "host='%s' port=%d user='%s' dbname='%s' " appendStringInfo(conninfo, "host='%s' port=%d user='%s' dbname='%s' "
@ -1567,10 +1567,10 @@ CreateSubscriptions(MultiConnection *sourceConnection,
pfree(createSubscriptionCommand->data); pfree(createSubscriptionCommand->data);
pfree(createSubscriptionCommand); pfree(createSubscriptionCommand);
InsertCleanupRecordInSubtransaction(CLEANUP_OBJECT_SUBSCRIPTION, InsertCleanupRecordOutsideTransaction(CLEANUP_OBJECT_SUBSCRIPTION,
target->subscriptionName, target->subscriptionName,
worker->groupId, worker->groupId,
CLEANUP_ALWAYS); CLEANUP_ALWAYS);
ExecuteCriticalRemoteCommand(target->superuserConnection, psprintf( ExecuteCriticalRemoteCommand(target->superuserConnection, psprintf(
"ALTER SUBSCRIPTION %s OWNER TO %s", "ALTER SUBSCRIPTION %s OWNER TO %s",

View File

@ -81,16 +81,16 @@ typedef enum CleanupPolicy
extern OperationId RegisterOperationNeedingCleanup(void); extern OperationId RegisterOperationNeedingCleanup(void);
/* /*
* InsertCleanupRecordInCurrentTransaction inserts a new pg_dist_cleanup entry * InsertCleanupOnSuccessRecordInCurrentTransaction inserts a new pg_dist_cleanup entry
* as part of the current transaction. * as part of the current transaction.
* *
* This is primarily useful for deferred cleanup (CLEANUP_DEFERRED_ON_SUCCESS) * This is primarily useful for deferred cleanup (CLEANUP_DEFERRED_ON_SUCCESS)
* scenarios, since the records would roll back in case of failure. * scenarios, since the records would roll back in case of failure. And for the
* same reason, always sets the policy type to CLEANUP_DEFERRED_ON_SUCCESS.
*/ */
extern void InsertCleanupRecordInCurrentTransaction(CleanupObject objectType, extern void InsertCleanupOnSuccessRecordInCurrentTransaction(CleanupObject objectType,
char *objectName, char *objectName,
int nodeGroupId, int nodeGroupId);
CleanupPolicy policy);
/* /*
* InsertCleanupRecordInSeparateTransaction inserts a new pg_dist_cleanup entry * InsertCleanupRecordInSeparateTransaction inserts a new pg_dist_cleanup entry
@ -99,10 +99,10 @@ extern void InsertCleanupRecordInCurrentTransaction(CleanupObject objectType,
* This is used in scenarios where we need to cleanup resources on operation * This is used in scenarios where we need to cleanup resources on operation
* completion (CLEANUP_ALWAYS) or on failure (CLEANUP_ON_FAILURE). * completion (CLEANUP_ALWAYS) or on failure (CLEANUP_ON_FAILURE).
*/ */
extern void InsertCleanupRecordInSubtransaction(CleanupObject objectType, extern void InsertCleanupRecordOutsideTransaction(CleanupObject objectType,
char *objectName, char *objectName,
int nodeGroupId, int nodeGroupId,
CleanupPolicy policy); CleanupPolicy policy);
/* /*
* FinalizeOperationNeedingCleanupOnSuccess is be called by an operation to signal * FinalizeOperationNeedingCleanupOnSuccess is be called by an operation to signal

View File

@ -0,0 +1,471 @@
-- Public role has connect,temp,temporary privileges on database
-- To test these scenarios, we need to revoke these privileges from public role
-- since public role privileges are inherited by new roles/users
set citus.enable_create_database_propagation to on;
create database test_2pc_db;
show citus.main_db;
citus.main_db
---------------------------------------------------------------------
regression
(1 row)
revoke connect,temp,temporary on database test_2pc_db from public;
CREATE SCHEMA grant_on_database_propagation_non_maindb;
SET search_path TO grant_on_database_propagation_non_maindb;
-- test grant/revoke CREATE privilege propagation on database
create user "myuser'_test";
\c test_2pc_db - - :master_port
grant create on database test_2pc_db to "myuser'_test";
\c regression - - :master_port;
select check_database_privileges('myuser''_test','test_2pc_db',ARRAY['CREATE']);
check_database_privileges
---------------------------------------------------------------------
(CREATE,t)
(CREATE,t)
(CREATE,t)
(3 rows)
\c test_2pc_db - - :master_port
revoke create on database test_2pc_db from "myuser'_test";
\c regression - - :master_port;
select check_database_privileges('myuser''_test','test_2pc_db',ARRAY['CREATE']);
check_database_privileges
---------------------------------------------------------------------
(CREATE,f)
(CREATE,f)
(CREATE,f)
(3 rows)
drop user "myuser'_test";
---------------------------------------------------------------------
-- test grant/revoke CONNECT privilege propagation on database
\c regression - - :master_port
create user myuser2;
\c test_2pc_db - - :master_port
grant CONNECT on database test_2pc_db to myuser2;
\c regression - - :master_port;
select check_database_privileges('myuser2','test_2pc_db',ARRAY['CONNECT']);
check_database_privileges
---------------------------------------------------------------------
(CONNECT,t)
(CONNECT,t)
(CONNECT,t)
(3 rows)
\c test_2pc_db - - :master_port
revoke connect on database test_2pc_db from myuser2;
\c regression - - :master_port
select check_database_privileges('myuser2','test_2pc_db',ARRAY['CONNECT']);
check_database_privileges
---------------------------------------------------------------------
(CONNECT,f)
(CONNECT,f)
(CONNECT,f)
(3 rows)
drop user myuser2;
---------------------------------------------------------------------
-- test grant/revoke TEMP privilege propagation on database
\c regression - - :master_port
create user myuser3;
-- test grant/revoke temp on database
\c test_2pc_db - - :master_port
grant TEMP on database test_2pc_db to myuser3;
\c regression - - :master_port;
select check_database_privileges('myuser3','test_2pc_db',ARRAY['TEMP']);
check_database_privileges
---------------------------------------------------------------------
(TEMP,t)
(TEMP,t)
(TEMP,t)
(3 rows)
\c test_2pc_db - - :worker_1_port
revoke TEMP on database test_2pc_db from myuser3;
\c regression - - :master_port;
select check_database_privileges('myuser3','test_2pc_db',ARRAY['TEMP']);
check_database_privileges
---------------------------------------------------------------------
(TEMP,f)
(TEMP,f)
(TEMP,f)
(3 rows)
drop user myuser3;
---------------------------------------------------------------------
\c regression - - :master_port
-- test temporary privilege on database
create user myuser4;
-- test grant/revoke temporary on database
\c test_2pc_db - - :worker_1_port
grant TEMPORARY on database test_2pc_db to myuser4;
\c regression - - :master_port
select check_database_privileges('myuser4','test_2pc_db',ARRAY['TEMPORARY']);
check_database_privileges
---------------------------------------------------------------------
(TEMPORARY,t)
(TEMPORARY,t)
(TEMPORARY,t)
(3 rows)
\c test_2pc_db - - :master_port
revoke TEMPORARY on database test_2pc_db from myuser4;
\c regression - - :master_port;
select check_database_privileges('myuser4','test_2pc_db',ARRAY['TEMPORARY']);
check_database_privileges
---------------------------------------------------------------------
(TEMPORARY,f)
(TEMPORARY,f)
(TEMPORARY,f)
(3 rows)
drop user myuser4;
---------------------------------------------------------------------
-- test ALL privileges with ALL statement on database
create user myuser5;
grant ALL on database test_2pc_db to myuser5;
\c regression - - :master_port
select check_database_privileges('myuser5','test_2pc_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
check_database_privileges
---------------------------------------------------------------------
(CREATE,t)
(CREATE,t)
(CREATE,t)
(CONNECT,t)
(CONNECT,t)
(CONNECT,t)
(TEMP,t)
(TEMP,t)
(TEMP,t)
(TEMPORARY,t)
(TEMPORARY,t)
(TEMPORARY,t)
(12 rows)
\c test_2pc_db - - :master_port
revoke ALL on database test_2pc_db from myuser5;
\c regression - - :master_port
select check_database_privileges('myuser5','test_2pc_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
check_database_privileges
---------------------------------------------------------------------
(CREATE,f)
(CREATE,f)
(CREATE,f)
(CONNECT,f)
(CONNECT,f)
(CONNECT,f)
(TEMP,f)
(TEMP,f)
(TEMP,f)
(TEMPORARY,f)
(TEMPORARY,f)
(TEMPORARY,f)
(12 rows)
drop user myuser5;
---------------------------------------------------------------------
-- test CREATE,CONNECT,TEMP,TEMPORARY privileges one by one on database
create user myuser6;
\c test_2pc_db - - :master_port
grant CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db to myuser6;
\c regression - - :master_port
select check_database_privileges('myuser6','test_2pc_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
check_database_privileges
---------------------------------------------------------------------
(CREATE,t)
(CREATE,t)
(CREATE,t)
(CONNECT,t)
(CONNECT,t)
(CONNECT,t)
(TEMP,t)
(TEMP,t)
(TEMP,t)
(TEMPORARY,t)
(TEMPORARY,t)
(TEMPORARY,t)
(12 rows)
\c test_2pc_db - - :master_port
revoke CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db from myuser6;
\c regression - - :master_port
select check_database_privileges('myuser6','test_2pc_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
check_database_privileges
---------------------------------------------------------------------
(CREATE,f)
(CREATE,f)
(CREATE,f)
(CONNECT,f)
(CONNECT,f)
(CONNECT,f)
(TEMP,f)
(TEMP,f)
(TEMP,f)
(TEMPORARY,f)
(TEMPORARY,f)
(TEMPORARY,f)
(12 rows)
drop user myuser6;
---------------------------------------------------------------------
-- test CREATE,CONNECT,TEMP,TEMPORARY privileges one by one on database with grant option
create user myuser7;
create user myuser_1;
\c test_2pc_db - - :master_port
grant CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db to myuser7;
set role myuser7;
--here since myuser7 does not have grant option, it should fail
grant CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db to myuser_1;
WARNING: no privileges were granted for "test_2pc_db"
\c regression - - :master_port
select check_database_privileges('myuser_1','test_2pc_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
check_database_privileges
---------------------------------------------------------------------
(CREATE,f)
(CREATE,f)
(CREATE,f)
(CONNECT,f)
(CONNECT,f)
(CONNECT,f)
(TEMP,f)
(TEMP,f)
(TEMP,f)
(TEMPORARY,f)
(TEMPORARY,f)
(TEMPORARY,f)
(12 rows)
\c test_2pc_db - - :master_port
RESET ROLE;
grant CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db to myuser7 with grant option;
set role myuser7;
--here since myuser have grant option, it should succeed
grant CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db to myuser_1 granted by myuser7;
\c regression - - :master_port
select check_database_privileges('myuser_1','test_2pc_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
check_database_privileges
---------------------------------------------------------------------
(CREATE,t)
(CREATE,t)
(CREATE,t)
(CONNECT,t)
(CONNECT,t)
(CONNECT,t)
(TEMP,t)
(TEMP,t)
(TEMP,t)
(TEMPORARY,t)
(TEMPORARY,t)
(TEMPORARY,t)
(12 rows)
\c test_2pc_db - - :master_port
RESET ROLE;
--below test should fail and should throw an error since myuser_1 still have the dependent privileges
revoke CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db from myuser7 restrict;
ERROR: dependent privileges exist
HINT: Use CASCADE to revoke them too.
--below test should fail and should throw an error since myuser_1 still have the dependent privileges
revoke grant option for CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db from myuser7 restrict ;
ERROR: dependent privileges exist
HINT: Use CASCADE to revoke them too.
--below test should succeed and should not throw any error since myuser_1 privileges are revoked with cascade
revoke grant option for CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db from myuser7 cascade ;
--here we test if myuser7 still have the privileges after revoke grant option for
\c regression - - :master_port
select check_database_privileges('myuser7','test_2pc_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
check_database_privileges
---------------------------------------------------------------------
(CREATE,t)
(CREATE,t)
(CREATE,t)
(CONNECT,t)
(CONNECT,t)
(CONNECT,t)
(TEMP,t)
(TEMP,t)
(TEMP,t)
(TEMPORARY,t)
(TEMPORARY,t)
(TEMPORARY,t)
(12 rows)
\c test_2pc_db - - :master_port
reset role;
revoke CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db from myuser7;
revoke CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db from myuser_1;
\c regression - - :master_port
drop user myuser_1;
drop user myuser7;
---------------------------------------------------------------------
-- test CREATE,CONNECT,TEMP,TEMPORARY privileges one by one on database multi database
-- and multi user
\c regression - - :master_port
create user myuser8;
create user myuser_2;
set citus.enable_create_database_propagation to on;
create database test_db;
revoke connect,temp,temporary on database test_db from public;
\c test_2pc_db - - :master_port
grant CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db,test_db to myuser8,myuser_2;
\c regression - - :master_port
select check_database_privileges('myuser8','test_2pc_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
check_database_privileges
---------------------------------------------------------------------
(CREATE,t)
(CREATE,t)
(CREATE,t)
(CONNECT,t)
(CONNECT,t)
(CONNECT,t)
(TEMP,t)
(TEMP,t)
(TEMP,t)
(TEMPORARY,t)
(TEMPORARY,t)
(TEMPORARY,t)
(12 rows)
select check_database_privileges('myuser8','test_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
check_database_privileges
---------------------------------------------------------------------
(CREATE,t)
(CREATE,t)
(CREATE,t)
(CONNECT,t)
(CONNECT,t)
(CONNECT,t)
(TEMP,t)
(TEMP,t)
(TEMP,t)
(TEMPORARY,t)
(TEMPORARY,t)
(TEMPORARY,t)
(12 rows)
select check_database_privileges('myuser_2','test_2pc_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
check_database_privileges
---------------------------------------------------------------------
(CREATE,t)
(CREATE,t)
(CREATE,t)
(CONNECT,t)
(CONNECT,t)
(CONNECT,t)
(TEMP,t)
(TEMP,t)
(TEMP,t)
(TEMPORARY,t)
(TEMPORARY,t)
(TEMPORARY,t)
(12 rows)
select check_database_privileges('myuser_2','test_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
check_database_privileges
---------------------------------------------------------------------
(CREATE,t)
(CREATE,t)
(CREATE,t)
(CONNECT,t)
(CONNECT,t)
(CONNECT,t)
(TEMP,t)
(TEMP,t)
(TEMP,t)
(TEMPORARY,t)
(TEMPORARY,t)
(TEMPORARY,t)
(12 rows)
\c test_2pc_db - - :master_port
RESET ROLE;
revoke CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db,test_db from myuser8 ;
--below test should succeed and should not throw any error
revoke CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db,test_db from myuser_2;
--below test should succeed and should not throw any error
revoke CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db,test_db from myuser8 cascade;
\c regression - - :master_port
select check_database_privileges('myuser8','test_2pc_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
check_database_privileges
---------------------------------------------------------------------
(CREATE,f)
(CREATE,f)
(CREATE,f)
(CONNECT,f)
(CONNECT,f)
(CONNECT,f)
(TEMP,f)
(TEMP,f)
(TEMP,f)
(TEMPORARY,f)
(TEMPORARY,f)
(TEMPORARY,f)
(12 rows)
select check_database_privileges('myuser8','test_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
check_database_privileges
---------------------------------------------------------------------
(CREATE,f)
(CREATE,f)
(CREATE,f)
(CONNECT,f)
(CONNECT,f)
(CONNECT,f)
(TEMP,f)
(TEMP,f)
(TEMP,f)
(TEMPORARY,f)
(TEMPORARY,f)
(TEMPORARY,f)
(12 rows)
select check_database_privileges('myuser_2','test_2pc_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
check_database_privileges
---------------------------------------------------------------------
(CREATE,f)
(CREATE,f)
(CREATE,f)
(CONNECT,f)
(CONNECT,f)
(CONNECT,f)
(TEMP,f)
(TEMP,f)
(TEMP,f)
(TEMPORARY,f)
(TEMPORARY,f)
(TEMPORARY,f)
(12 rows)
select check_database_privileges('myuser_2','test_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
check_database_privileges
---------------------------------------------------------------------
(CREATE,f)
(CREATE,f)
(CREATE,f)
(CONNECT,f)
(CONNECT,f)
(CONNECT,f)
(TEMP,f)
(TEMP,f)
(TEMP,f)
(TEMPORARY,f)
(TEMPORARY,f)
(TEMPORARY,f)
(12 rows)
\c test_2pc_db - - :master_port
reset role;
\c regression - - :master_port
drop user myuser_2;
drop user myuser8;
set citus.enable_create_database_propagation to on;
drop database test_db;
---------------------------------------------------------------------
-- rollbacks public role database privileges to original state
grant connect,temp,temporary on database test_2pc_db to public;
drop database test_2pc_db;
set citus.enable_create_database_propagation to off;
DROP SCHEMA grant_on_database_propagation_non_maindb CASCADE;
reset citus.enable_create_database_propagation;
reset search_path;
---------------------------------------------------------------------

View File

@ -2,6 +2,7 @@ CREATE SCHEMA metadata_sync_2pc_schema;
SET search_path TO metadata_sync_2pc_schema; SET search_path TO metadata_sync_2pc_schema;
set citus.enable_create_database_propagation to on; set citus.enable_create_database_propagation to on;
CREATE DATABASE metadata_sync_2pc_db; CREATE DATABASE metadata_sync_2pc_db;
revoke connect,temp,temporary on database metadata_sync_2pc_db from public;
\c metadata_sync_2pc_db \c metadata_sync_2pc_db
SHOW citus.main_db; SHOW citus.main_db;
citus.main_db citus.main_db
@ -24,7 +25,41 @@ select 1 from citus_remove_node('localhost', :worker_2_port);
\c metadata_sync_2pc_db \c metadata_sync_2pc_db
grant "grant_role2pc'_user1","grant_role2pc'_user2" to "grant_role2pc'_user3" WITH ADMIN OPTION; grant "grant_role2pc'_user1","grant_role2pc'_user2" to "grant_role2pc'_user3" WITH ADMIN OPTION;
grant "grant_role2pc'_user1","grant_role2pc'_user2" to grant_role2pc_user4,grant_role2pc_user5 granted by "grant_role2pc'_user3"; grant "grant_role2pc'_user1","grant_role2pc'_user2" to grant_role2pc_user4,grant_role2pc_user5 granted by "grant_role2pc'_user3";
--test for grant on database
\c metadata_sync_2pc_db - - :master_port
grant create on database metadata_sync_2pc_db to "grant_role2pc'_user1";
grant connect on database metadata_sync_2pc_db to "grant_role2pc'_user2";
grant ALL on database metadata_sync_2pc_db to "grant_role2pc'_user3";
\c regression \c regression
select check_database_privileges('grant_role2pc''_user1','metadata_sync_2pc_db',ARRAY['CREATE']);
check_database_privileges
---------------------------------------------------------------------
(CREATE,t)
(CREATE,t)
(2 rows)
select check_database_privileges('grant_role2pc''_user2','metadata_sync_2pc_db',ARRAY['CONNECT']);
check_database_privileges
---------------------------------------------------------------------
(CONNECT,t)
(CONNECT,t)
(2 rows)
select check_database_privileges('grant_role2pc''_user3','metadata_sync_2pc_db',ARRAY['CREATE','CONNECT','TEMP','TEMPORARY']);
check_database_privileges
---------------------------------------------------------------------
(CREATE,t)
(CREATE,t)
(CONNECT,t)
(CONNECT,t)
(TEMP,t)
(TEMP,t)
(TEMPORARY,t)
(TEMPORARY,t)
(8 rows)
\c regression
set citus.enable_create_database_propagation to on;
select 1 from citus_add_node('localhost', :worker_2_port); select 1 from citus_add_node('localhost', :worker_2_port);
?column? ?column?
--------------------------------------------------------------------- ---------------------------------------------------------------------
@ -48,10 +83,46 @@ $$);
[{"member":"\"grant_role2pc'_user3\"","role":"\"grant_role2pc'_user1\"","grantor":"postgres","admin_option":true},{"member":"\"grant_role2pc'_user3\"","role":"\"grant_role2pc'_user2\"","grantor":"postgres","admin_option":true},{"member":"grant_role2pc_user4","role":"\"grant_role2pc'_user1\"","grantor":"\"grant_role2pc'_user3\"","admin_option":false},{"member":"grant_role2pc_user4","role":"\"grant_role2pc'_user2\"","grantor":"\"grant_role2pc'_user3\"","admin_option":false},{"member":"grant_role2pc_user5","role":"\"grant_role2pc'_user1\"","grantor":"\"grant_role2pc'_user3\"","admin_option":false},{"member":"grant_role2pc_user5","role":"\"grant_role2pc'_user2\"","grantor":"\"grant_role2pc'_user3\"","admin_option":false}] [{"member":"\"grant_role2pc'_user3\"","role":"\"grant_role2pc'_user1\"","grantor":"postgres","admin_option":true},{"member":"\"grant_role2pc'_user3\"","role":"\"grant_role2pc'_user2\"","grantor":"postgres","admin_option":true},{"member":"grant_role2pc_user4","role":"\"grant_role2pc'_user1\"","grantor":"\"grant_role2pc'_user3\"","admin_option":false},{"member":"grant_role2pc_user4","role":"\"grant_role2pc'_user2\"","grantor":"\"grant_role2pc'_user3\"","admin_option":false},{"member":"grant_role2pc_user5","role":"\"grant_role2pc'_user1\"","grantor":"\"grant_role2pc'_user3\"","admin_option":false},{"member":"grant_role2pc_user5","role":"\"grant_role2pc'_user2\"","grantor":"\"grant_role2pc'_user3\"","admin_option":false}]
(3 rows) (3 rows)
select check_database_privileges('grant_role2pc''_user1','metadata_sync_2pc_db',ARRAY['CREATE']);
check_database_privileges
---------------------------------------------------------------------
(CREATE,t)
(CREATE,t)
(CREATE,t)
(3 rows)
select check_database_privileges('grant_role2pc''_user2','metadata_sync_2pc_db',ARRAY['CONNECT']);
check_database_privileges
---------------------------------------------------------------------
(CONNECT,t)
(CONNECT,t)
(CONNECT,t)
(3 rows)
select check_database_privileges('grant_role2pc''_user3','metadata_sync_2pc_db',ARRAY['CREATE','CONNECT','TEMP','TEMPORARY']);
check_database_privileges
---------------------------------------------------------------------
(CREATE,t)
(CREATE,t)
(CREATE,t)
(CONNECT,t)
(CONNECT,t)
(CONNECT,t)
(TEMP,t)
(TEMP,t)
(TEMP,t)
(TEMPORARY,t)
(TEMPORARY,t)
(TEMPORARY,t)
(12 rows)
\c metadata_sync_2pc_db \c metadata_sync_2pc_db
revoke "grant_role2pc'_user1","grant_role2pc'_user2" from grant_role2pc_user4,grant_role2pc_user5 granted by "grant_role2pc'_user3"; revoke "grant_role2pc'_user1","grant_role2pc'_user2" from grant_role2pc_user4,grant_role2pc_user5 granted by "grant_role2pc'_user3";
revoke admin option for "grant_role2pc'_user1","grant_role2pc'_user2" from "grant_role2pc'_user3"; revoke admin option for "grant_role2pc'_user1","grant_role2pc'_user2" from "grant_role2pc'_user3";
revoke "grant_role2pc'_user1","grant_role2pc'_user2" from "grant_role2pc'_user3"; revoke "grant_role2pc'_user1","grant_role2pc'_user2" from "grant_role2pc'_user3";
revoke ALL on database metadata_sync_2pc_db from "grant_role2pc'_user3";
revoke CONNECT on database metadata_sync_2pc_db from "grant_role2pc'_user2";
revoke CREATE on database metadata_sync_2pc_db from "grant_role2pc'_user1";
\c regression \c regression
drop user "grant_role2pc'_user1","grant_role2pc'_user2","grant_role2pc'_user3",grant_role2pc_user4,grant_role2pc_user5; drop user "grant_role2pc'_user1","grant_role2pc'_user2","grant_role2pc'_user3",grant_role2pc_user4,grant_role2pc_user5;
set citus.enable_create_database_propagation to on; set citus.enable_create_database_propagation to on;

View File

@ -634,7 +634,8 @@ DECLARE
BEGIN BEGIN
FOREACH permission IN ARRAY permissions FOREACH permission IN ARRAY permissions
LOOP LOOP
RETURN QUERY EXECUTE format($inner$SELECT '%s', result FROM run_command_on_all_nodes($$select has_database_privilege('%s','%s', '%s'); $$)$inner$, permission, role_name, db_name, permission); RETURN QUERY EXECUTE format($inner$SELECT %s, result FROM run_command_on_all_nodes($$select has_database_privilege(%s,%s,%s); $$)$inner$,
quote_literal(permission), quote_literal(role_name), quote_literal(db_name), quote_literal(permission));
END LOOP; END LOOP;
END; END;
$func$ LANGUAGE plpgsql; $func$ LANGUAGE plpgsql;

View File

@ -58,7 +58,7 @@ test: multi_metadata_attributes
test: multi_read_from_secondaries test: multi_read_from_secondaries
test: grant_on_database_propagation test: grant_on_database_propagation grant_on_database_propagation_from_non_maindb
test: alter_database_propagation test: alter_database_propagation
test: citus_shards test: citus_shards

View File

@ -0,0 +1,246 @@
-- Public role has connect,temp,temporary privileges on database
-- To test these scenarios, we need to revoke these privileges from public role
-- since public role privileges are inherited by new roles/users
set citus.enable_create_database_propagation to on;
create database test_2pc_db;
show citus.main_db;
revoke connect,temp,temporary on database test_2pc_db from public;
CREATE SCHEMA grant_on_database_propagation_non_maindb;
SET search_path TO grant_on_database_propagation_non_maindb;
-- test grant/revoke CREATE privilege propagation on database
create user "myuser'_test";
\c test_2pc_db - - :master_port
grant create on database test_2pc_db to "myuser'_test";
\c regression - - :master_port;
select check_database_privileges('myuser''_test','test_2pc_db',ARRAY['CREATE']);
\c test_2pc_db - - :master_port
revoke create on database test_2pc_db from "myuser'_test";
\c regression - - :master_port;
select check_database_privileges('myuser''_test','test_2pc_db',ARRAY['CREATE']);
drop user "myuser'_test";
-----------------------------------------------------------------------
-- test grant/revoke CONNECT privilege propagation on database
\c regression - - :master_port
create user myuser2;
\c test_2pc_db - - :master_port
grant CONNECT on database test_2pc_db to myuser2;
\c regression - - :master_port;
select check_database_privileges('myuser2','test_2pc_db',ARRAY['CONNECT']);
\c test_2pc_db - - :master_port
revoke connect on database test_2pc_db from myuser2;
\c regression - - :master_port
select check_database_privileges('myuser2','test_2pc_db',ARRAY['CONNECT']);
drop user myuser2;
-----------------------------------------------------------------------
-- test grant/revoke TEMP privilege propagation on database
\c regression - - :master_port
create user myuser3;
-- test grant/revoke temp on database
\c test_2pc_db - - :master_port
grant TEMP on database test_2pc_db to myuser3;
\c regression - - :master_port;
select check_database_privileges('myuser3','test_2pc_db',ARRAY['TEMP']);
\c test_2pc_db - - :worker_1_port
revoke TEMP on database test_2pc_db from myuser3;
\c regression - - :master_port;
select check_database_privileges('myuser3','test_2pc_db',ARRAY['TEMP']);
drop user myuser3;
-----------------------------------------------------------------------
\c regression - - :master_port
-- test temporary privilege on database
create user myuser4;
-- test grant/revoke temporary on database
\c test_2pc_db - - :worker_1_port
grant TEMPORARY on database test_2pc_db to myuser4;
\c regression - - :master_port
select check_database_privileges('myuser4','test_2pc_db',ARRAY['TEMPORARY']);
\c test_2pc_db - - :master_port
revoke TEMPORARY on database test_2pc_db from myuser4;
\c regression - - :master_port;
select check_database_privileges('myuser4','test_2pc_db',ARRAY['TEMPORARY']);
drop user myuser4;
-----------------------------------------------------------------------
-- test ALL privileges with ALL statement on database
create user myuser5;
grant ALL on database test_2pc_db to myuser5;
\c regression - - :master_port
select check_database_privileges('myuser5','test_2pc_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
\c test_2pc_db - - :master_port
revoke ALL on database test_2pc_db from myuser5;
\c regression - - :master_port
select check_database_privileges('myuser5','test_2pc_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
drop user myuser5;
-----------------------------------------------------------------------
-- test CREATE,CONNECT,TEMP,TEMPORARY privileges one by one on database
create user myuser6;
\c test_2pc_db - - :master_port
grant CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db to myuser6;
\c regression - - :master_port
select check_database_privileges('myuser6','test_2pc_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
\c test_2pc_db - - :master_port
revoke CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db from myuser6;
\c regression - - :master_port
select check_database_privileges('myuser6','test_2pc_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
drop user myuser6;
-----------------------------------------------------------------------
-- test CREATE,CONNECT,TEMP,TEMPORARY privileges one by one on database with grant option
create user myuser7;
create user myuser_1;
\c test_2pc_db - - :master_port
grant CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db to myuser7;
set role myuser7;
--here since myuser7 does not have grant option, it should fail
grant CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db to myuser_1;
\c regression - - :master_port
select check_database_privileges('myuser_1','test_2pc_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
\c test_2pc_db - - :master_port
RESET ROLE;
grant CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db to myuser7 with grant option;
set role myuser7;
--here since myuser have grant option, it should succeed
grant CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db to myuser_1 granted by myuser7;
\c regression - - :master_port
select check_database_privileges('myuser_1','test_2pc_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
\c test_2pc_db - - :master_port
RESET ROLE;
--below test should fail and should throw an error since myuser_1 still have the dependent privileges
revoke CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db from myuser7 restrict;
--below test should fail and should throw an error since myuser_1 still have the dependent privileges
revoke grant option for CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db from myuser7 restrict ;
--below test should succeed and should not throw any error since myuser_1 privileges are revoked with cascade
revoke grant option for CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db from myuser7 cascade ;
--here we test if myuser7 still have the privileges after revoke grant option for
\c regression - - :master_port
select check_database_privileges('myuser7','test_2pc_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
\c test_2pc_db - - :master_port
reset role;
revoke CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db from myuser7;
revoke CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db from myuser_1;
\c regression - - :master_port
drop user myuser_1;
drop user myuser7;
-----------------------------------------------------------------------
-- test CREATE,CONNECT,TEMP,TEMPORARY privileges one by one on database multi database
-- and multi user
\c regression - - :master_port
create user myuser8;
create user myuser_2;
set citus.enable_create_database_propagation to on;
create database test_db;
revoke connect,temp,temporary on database test_db from public;
\c test_2pc_db - - :master_port
grant CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db,test_db to myuser8,myuser_2;
\c regression - - :master_port
select check_database_privileges('myuser8','test_2pc_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
select check_database_privileges('myuser8','test_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
select check_database_privileges('myuser_2','test_2pc_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
select check_database_privileges('myuser_2','test_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
\c test_2pc_db - - :master_port
RESET ROLE;
revoke CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db,test_db from myuser8 ;
--below test should succeed and should not throw any error
revoke CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db,test_db from myuser_2;
--below test should succeed and should not throw any error
revoke CREATE,CONNECT,TEMP,TEMPORARY on database test_2pc_db,test_db from myuser8 cascade;
\c regression - - :master_port
select check_database_privileges('myuser8','test_2pc_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
select check_database_privileges('myuser8','test_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
select check_database_privileges('myuser_2','test_2pc_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
select check_database_privileges('myuser_2','test_db',ARRAY['CREATE', 'CONNECT', 'TEMP', 'TEMPORARY']);
\c test_2pc_db - - :master_port
reset role;
\c regression - - :master_port
drop user myuser_2;
drop user myuser8;
set citus.enable_create_database_propagation to on;
drop database test_db;
---------------------------------------------------------------------------
-- rollbacks public role database privileges to original state
grant connect,temp,temporary on database test_2pc_db to public;
drop database test_2pc_db;
set citus.enable_create_database_propagation to off;
DROP SCHEMA grant_on_database_propagation_non_maindb CASCADE;
reset citus.enable_create_database_propagation;
reset search_path;
---------------------------------------------------------------------------

View File

@ -3,6 +3,8 @@ SET search_path TO metadata_sync_2pc_schema;
set citus.enable_create_database_propagation to on; set citus.enable_create_database_propagation to on;
CREATE DATABASE metadata_sync_2pc_db; CREATE DATABASE metadata_sync_2pc_db;
revoke connect,temp,temporary on database metadata_sync_2pc_db from public;
\c metadata_sync_2pc_db \c metadata_sync_2pc_db
SHOW citus.main_db; SHOW citus.main_db;
@ -19,7 +21,19 @@ select 1 from citus_remove_node('localhost', :worker_2_port);
grant "grant_role2pc'_user1","grant_role2pc'_user2" to "grant_role2pc'_user3" WITH ADMIN OPTION; grant "grant_role2pc'_user1","grant_role2pc'_user2" to "grant_role2pc'_user3" WITH ADMIN OPTION;
grant "grant_role2pc'_user1","grant_role2pc'_user2" to grant_role2pc_user4,grant_role2pc_user5 granted by "grant_role2pc'_user3"; grant "grant_role2pc'_user1","grant_role2pc'_user2" to grant_role2pc_user4,grant_role2pc_user5 granted by "grant_role2pc'_user3";
--test for grant on database
\c metadata_sync_2pc_db - - :master_port
grant create on database metadata_sync_2pc_db to "grant_role2pc'_user1";
grant connect on database metadata_sync_2pc_db to "grant_role2pc'_user2";
grant ALL on database metadata_sync_2pc_db to "grant_role2pc'_user3";
\c regression \c regression
select check_database_privileges('grant_role2pc''_user1','metadata_sync_2pc_db',ARRAY['CREATE']);
select check_database_privileges('grant_role2pc''_user2','metadata_sync_2pc_db',ARRAY['CONNECT']);
select check_database_privileges('grant_role2pc''_user3','metadata_sync_2pc_db',ARRAY['CREATE','CONNECT','TEMP','TEMPORARY']);
\c regression
set citus.enable_create_database_propagation to on;
select 1 from citus_add_node('localhost', :worker_2_port); select 1 from citus_add_node('localhost', :worker_2_port);
select result FROM run_command_on_all_nodes($$ select result FROM run_command_on_all_nodes($$
@ -33,12 +47,19 @@ FROM (
) t ) t
$$); $$);
select check_database_privileges('grant_role2pc''_user1','metadata_sync_2pc_db',ARRAY['CREATE']);
select check_database_privileges('grant_role2pc''_user2','metadata_sync_2pc_db',ARRAY['CONNECT']);
select check_database_privileges('grant_role2pc''_user3','metadata_sync_2pc_db',ARRAY['CREATE','CONNECT','TEMP','TEMPORARY']);
\c metadata_sync_2pc_db \c metadata_sync_2pc_db
revoke "grant_role2pc'_user1","grant_role2pc'_user2" from grant_role2pc_user4,grant_role2pc_user5 granted by "grant_role2pc'_user3"; revoke "grant_role2pc'_user1","grant_role2pc'_user2" from grant_role2pc_user4,grant_role2pc_user5 granted by "grant_role2pc'_user3";
revoke admin option for "grant_role2pc'_user1","grant_role2pc'_user2" from "grant_role2pc'_user3"; revoke admin option for "grant_role2pc'_user1","grant_role2pc'_user2" from "grant_role2pc'_user3";
revoke "grant_role2pc'_user1","grant_role2pc'_user2" from "grant_role2pc'_user3"; revoke "grant_role2pc'_user1","grant_role2pc'_user2" from "grant_role2pc'_user3";
revoke ALL on database metadata_sync_2pc_db from "grant_role2pc'_user3";
revoke CONNECT on database metadata_sync_2pc_db from "grant_role2pc'_user2";
revoke CREATE on database metadata_sync_2pc_db from "grant_role2pc'_user1";
\c regression \c regression

View File

@ -661,7 +661,8 @@ DECLARE
BEGIN BEGIN
FOREACH permission IN ARRAY permissions FOREACH permission IN ARRAY permissions
LOOP LOOP
RETURN QUERY EXECUTE format($inner$SELECT '%s', result FROM run_command_on_all_nodes($$select has_database_privilege('%s','%s', '%s'); $$)$inner$, permission, role_name, db_name, permission); RETURN QUERY EXECUTE format($inner$SELECT %s, result FROM run_command_on_all_nodes($$select has_database_privilege(%s,%s,%s); $$)$inner$,
quote_literal(permission), quote_literal(role_name), quote_literal(db_name), quote_literal(permission));
END LOOP; END LOOP;
END; END;
$func$ LANGUAGE plpgsql; $func$ LANGUAGE plpgsql;