mirror of https://github.com/citusdata/citus.git
Add support for alter/drop role propagation from non-main databases (#7461)
DESCRIPTION: Adds support for distributed `ALTER/DROP ROLE` commands from the databases where Citus is not installed --------- Co-authored-by: Onur Tirtir <onurcantirtir@gmail.com>pull/7547/head
parent
f4242685e3
commit
51009d0191
|
@ -93,20 +93,40 @@
|
||||||
"SELECT citus_internal.start_management_transaction('%lu')"
|
"SELECT citus_internal.start_management_transaction('%lu')"
|
||||||
#define MARK_OBJECT_DISTRIBUTED \
|
#define MARK_OBJECT_DISTRIBUTED \
|
||||||
"SELECT citus_internal.mark_object_distributed(%d, %s, %d, %s)"
|
"SELECT citus_internal.mark_object_distributed(%d, %s, %d, %s)"
|
||||||
|
#define UNMARK_OBJECT_DISTRIBUTED \
|
||||||
|
"SELECT pg_catalog.citus_unmark_object_distributed(%d, %d, %d,%s)"
|
||||||
|
|
||||||
|
/* see NonMainDbDistributedStatementInfo for the explanation of these flags */
|
||||||
|
typedef enum DistObjectOperation
|
||||||
|
{
|
||||||
|
NO_DIST_OBJECT_OPERATION,
|
||||||
|
MARK_DISTRIBUTED_GLOBALLY,
|
||||||
|
UNMARK_DISTRIBUTED_LOCALLY
|
||||||
|
} DistObjectOperation;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NonMainDbDistributedStatementInfo is used to determine whether a statement is
|
* NonMainDbDistributedStatementInfo is used to determine whether a statement is
|
||||||
* supported from non-main databases and whether it should be marked as
|
* supported from non-main databases and whether it should be marked or unmarked
|
||||||
* distributed explicitly (*).
|
* as distributed.
|
||||||
*
|
*
|
||||||
* (*) We always have to mark such objects as "distributed" but while for some
|
* When creating a distributed object, we always have to mark such objects as
|
||||||
* object types we can delegate this to main database, for some others we have
|
* "distributed" but while for some object types we can delegate this to main
|
||||||
* to explicitly send a command to all nodes in this code-path to achieve this.
|
* database, for some others we have to explicitly send a command to all nodes
|
||||||
|
* in this code-path to achieve this. Callers need to provide
|
||||||
|
* MARK_DISTRIBUTED_GLOBALLY when that is the case.
|
||||||
|
*
|
||||||
|
* Similarly when dropping a distributed object, we always have to unmark such
|
||||||
|
* objects as "distributed" and our utility hook on remote nodes achieve this
|
||||||
|
* via UnmarkNodeWideObjectsDistributed() because the commands that we send to
|
||||||
|
* workers are executed via main db. However for the local node, this is not the
|
||||||
|
* case as we're not in the main db. For this reason, callers need to provide
|
||||||
|
* UNMARK_DISTRIBUTED_LOCALLY to unmark an object for local node as well.
|
||||||
*/
|
*/
|
||||||
typedef struct NonMainDbDistributedStatementInfo
|
typedef struct NonMainDbDistributedStatementInfo
|
||||||
{
|
{
|
||||||
int statementType;
|
int statementType;
|
||||||
bool explicitlyMarkAsDistributed;
|
DistObjectOperation DistObjectOperation;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* checkSupportedObjectTypes is a callback function that checks whether
|
* checkSupportedObjectTypes is a callback function that checks whether
|
||||||
|
@ -118,15 +138,16 @@ typedef struct NonMainDbDistributedStatementInfo
|
||||||
} NonMainDbDistributedStatementInfo;
|
} NonMainDbDistributedStatementInfo;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MarkObjectDistributedParams is used to pass parameters to the
|
* DistObjectOperationParams is used to pass parameters to the
|
||||||
* MarkObjectDistributedFromNonMainDb function.
|
* MarkObjectDistributedGloballyFromNonMainDb function and
|
||||||
|
* UnMarkObjectDistributedLocallyFromNonMainDb functions.
|
||||||
*/
|
*/
|
||||||
typedef struct MarkObjectDistributedParams
|
typedef struct DistObjectOperationParams
|
||||||
{
|
{
|
||||||
char *name;
|
char *name;
|
||||||
Oid id;
|
Oid id;
|
||||||
uint16 catalogRelId;
|
uint16 catalogRelId;
|
||||||
} MarkObjectDistributedParams;
|
} DistObjectOperationParams;
|
||||||
|
|
||||||
|
|
||||||
bool EnableDDLPropagation = true; /* ddl propagation is enabled */
|
bool EnableDDLPropagation = true; /* ddl propagation is enabled */
|
||||||
|
@ -166,9 +187,11 @@ static bool IsCommandToCreateOrDropMainDB(Node *parsetree);
|
||||||
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);
|
||||||
static bool StatementRequiresMarkDistributedFromNonMainDb(Node *parsetree);
|
static bool StatementRequiresMarkDistributedGloballyFromNonMainDb(Node *parsetree);
|
||||||
static void MarkObjectDistributedFromNonMainDb(Node *parsetree);
|
static bool StatementRequiresUnmarkDistributedLocallyFromNonMainDb(Node *parsetree);
|
||||||
static MarkObjectDistributedParams GetMarkObjectDistributedParams(Node *parsetree);
|
static void MarkObjectDistributedGloballyFromNonMainDb(Node *parsetree);
|
||||||
|
static void UnMarkObjectDistributedLocallyFromNonMainDb(List *unmarkDistributedList);
|
||||||
|
static List * GetDistObjectOperationParams(Node *parsetree);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* checkSupportedObjectTypes callbacks for
|
* checkSupportedObjectTypes callbacks for
|
||||||
|
@ -184,12 +207,15 @@ static bool NonMainDbCheckSupportedObjectTypeForSecLabel(Node *node);
|
||||||
*/
|
*/
|
||||||
ObjectType supportedObjectTypesForGrantStmt[] = { OBJECT_DATABASE };
|
ObjectType supportedObjectTypesForGrantStmt[] = { OBJECT_DATABASE };
|
||||||
static const NonMainDbDistributedStatementInfo NonMainDbSupportedStatements[] = {
|
static const NonMainDbDistributedStatementInfo NonMainDbSupportedStatements[] = {
|
||||||
{ T_GrantRoleStmt, false, NULL },
|
{ T_GrantRoleStmt, NO_DIST_OBJECT_OPERATION, NULL },
|
||||||
{ T_CreateRoleStmt, true, NULL },
|
{ T_CreateRoleStmt, MARK_DISTRIBUTED_GLOBALLY, NULL },
|
||||||
{ T_GrantStmt, false, NonMainDbCheckSupportedObjectTypeForGrant },
|
{ T_DropRoleStmt, UNMARK_DISTRIBUTED_LOCALLY, NULL },
|
||||||
{ T_CreatedbStmt, false, NULL },
|
{ T_AlterRoleStmt, NO_DIST_OBJECT_OPERATION, NULL },
|
||||||
{ T_DropdbStmt, false, NULL },
|
{ T_GrantStmt, NO_DIST_OBJECT_OPERATION, NonMainDbCheckSupportedObjectTypeForGrant },
|
||||||
{ T_SecLabelStmt, false, NonMainDbCheckSupportedObjectTypeForSecLabel },
|
{ T_CreatedbStmt, NO_DIST_OBJECT_OPERATION, NULL },
|
||||||
|
{ T_DropdbStmt, NO_DIST_OBJECT_OPERATION, NULL },
|
||||||
|
{ T_SecLabelStmt, NO_DIST_OBJECT_OPERATION,
|
||||||
|
NonMainDbCheckSupportedObjectTypeForSecLabel },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -1743,12 +1769,19 @@ RunPreprocessMainDBCommand(Node *parsetree)
|
||||||
START_MANAGEMENT_TRANSACTION,
|
START_MANAGEMENT_TRANSACTION,
|
||||||
GetCurrentFullTransactionId().value);
|
GetCurrentFullTransactionId().value);
|
||||||
RunCitusMainDBQuery(mainDBQuery->data);
|
RunCitusMainDBQuery(mainDBQuery->data);
|
||||||
|
|
||||||
mainDBQuery = makeStringInfo();
|
mainDBQuery = makeStringInfo();
|
||||||
appendStringInfo(mainDBQuery,
|
appendStringInfo(mainDBQuery,
|
||||||
EXECUTE_COMMAND_ON_REMOTE_NODES_AS_USER,
|
EXECUTE_COMMAND_ON_REMOTE_NODES_AS_USER,
|
||||||
quote_literal_cstr(queryString),
|
quote_literal_cstr(queryString),
|
||||||
quote_literal_cstr(CurrentUserName()));
|
quote_literal_cstr(CurrentUserName()));
|
||||||
RunCitusMainDBQuery(mainDBQuery->data);
|
RunCitusMainDBQuery(mainDBQuery->data);
|
||||||
|
|
||||||
|
if (StatementRequiresUnmarkDistributedLocallyFromNonMainDb(parsetree))
|
||||||
|
{
|
||||||
|
List *unmarkParams = GetDistObjectOperationParams(parsetree);
|
||||||
|
UnMarkObjectDistributedLocallyFromNonMainDb(unmarkParams);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1760,9 +1793,9 @@ static void
|
||||||
RunPostprocessMainDBCommand(Node *parsetree)
|
RunPostprocessMainDBCommand(Node *parsetree)
|
||||||
{
|
{
|
||||||
if (IsStatementSupportedFromNonMainDb(parsetree) &&
|
if (IsStatementSupportedFromNonMainDb(parsetree) &&
|
||||||
StatementRequiresMarkDistributedFromNonMainDb(parsetree))
|
StatementRequiresMarkDistributedGloballyFromNonMainDb(parsetree))
|
||||||
{
|
{
|
||||||
MarkObjectDistributedFromNonMainDb(parsetree);
|
MarkObjectDistributedGloballyFromNonMainDb(parsetree);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1793,11 +1826,11 @@ IsStatementSupportedFromNonMainDb(Node *parsetree)
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* StatementRequiresMarkDistributedFromNonMainDb returns true if the statement should be marked
|
* StatementRequiresMarkDistributedGloballyFromNonMainDb returns true if the statement should be marked
|
||||||
* as distributed when executed from a non-main database.
|
* as distributed when executed from a non-main database.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
StatementRequiresMarkDistributedFromNonMainDb(Node *parsetree)
|
StatementRequiresMarkDistributedGloballyFromNonMainDb(Node *parsetree)
|
||||||
{
|
{
|
||||||
NodeTag type = nodeTag(parsetree);
|
NodeTag type = nodeTag(parsetree);
|
||||||
|
|
||||||
|
@ -1806,7 +1839,8 @@ StatementRequiresMarkDistributedFromNonMainDb(Node *parsetree)
|
||||||
{
|
{
|
||||||
if (type == NonMainDbSupportedStatements[i].statementType)
|
if (type == NonMainDbSupportedStatements[i].statementType)
|
||||||
{
|
{
|
||||||
return NonMainDbSupportedStatements[i].explicitlyMarkAsDistributed;
|
return NonMainDbSupportedStatements[i].DistObjectOperation ==
|
||||||
|
MARK_DISTRIBUTED_GLOBALLY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1815,47 +1849,125 @@ StatementRequiresMarkDistributedFromNonMainDb(Node *parsetree)
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MarkObjectDistributedFromNonMainDb marks the given object as distributed on the
|
* StatementRequiresUnmarkDistributedLocallyFromNonMainDb returns true if the statement should be unmarked
|
||||||
* non-main database.
|
* as distributed when executed from a non-main database.
|
||||||
*/
|
*/
|
||||||
static void
|
static bool
|
||||||
MarkObjectDistributedFromNonMainDb(Node *parsetree)
|
StatementRequiresUnmarkDistributedLocallyFromNonMainDb(Node *parsetree)
|
||||||
{
|
{
|
||||||
MarkObjectDistributedParams markObjectDistributedParams =
|
NodeTag type = nodeTag(parsetree);
|
||||||
GetMarkObjectDistributedParams(parsetree);
|
|
||||||
StringInfo mainDBQuery = makeStringInfo();
|
for (int i = 0; i < sizeof(NonMainDbSupportedStatements) /
|
||||||
appendStringInfo(mainDBQuery,
|
sizeof(NonMainDbSupportedStatements[0]); i++)
|
||||||
MARK_OBJECT_DISTRIBUTED,
|
{
|
||||||
markObjectDistributedParams.catalogRelId,
|
if (type == NonMainDbSupportedStatements[i].statementType)
|
||||||
quote_literal_cstr(markObjectDistributedParams.name),
|
{
|
||||||
markObjectDistributedParams.id,
|
return NonMainDbSupportedStatements[i].DistObjectOperation ==
|
||||||
quote_literal_cstr(CurrentUserName()));
|
UNMARK_DISTRIBUTED_LOCALLY;
|
||||||
RunCitusMainDBQuery(mainDBQuery->data);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* GetMarkObjectDistributedParams returns MarkObjectDistributedParams for the target
|
* MarkObjectDistributedGloballyFromNonMainDb marks the given object as distributed on the
|
||||||
|
* non-main database.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
MarkObjectDistributedGloballyFromNonMainDb(Node *parsetree)
|
||||||
|
{
|
||||||
|
List *distObjectOperationParams =
|
||||||
|
GetDistObjectOperationParams(parsetree);
|
||||||
|
|
||||||
|
DistObjectOperationParams *distObjectOperationParam = NULL;
|
||||||
|
|
||||||
|
foreach_ptr(distObjectOperationParam, distObjectOperationParams)
|
||||||
|
{
|
||||||
|
StringInfo mainDBQuery = makeStringInfo();
|
||||||
|
appendStringInfo(mainDBQuery,
|
||||||
|
MARK_OBJECT_DISTRIBUTED,
|
||||||
|
distObjectOperationParam->catalogRelId,
|
||||||
|
quote_literal_cstr(distObjectOperationParam->name),
|
||||||
|
distObjectOperationParam->id,
|
||||||
|
quote_literal_cstr(CurrentUserName()));
|
||||||
|
RunCitusMainDBQuery(mainDBQuery->data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* UnMarkObjectDistributedLocallyFromNonMainDb unmarks the given object as distributed on the
|
||||||
|
* non-main database.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
UnMarkObjectDistributedLocallyFromNonMainDb(List *markObjectDistributedParamList)
|
||||||
|
{
|
||||||
|
DistObjectOperationParams *markObjectDistributedParam = NULL;
|
||||||
|
int subObjectId = 0;
|
||||||
|
char *checkObjectExistence = "false";
|
||||||
|
foreach_ptr(markObjectDistributedParam, markObjectDistributedParamList)
|
||||||
|
{
|
||||||
|
StringInfo query = makeStringInfo();
|
||||||
|
appendStringInfo(query,
|
||||||
|
UNMARK_OBJECT_DISTRIBUTED,
|
||||||
|
AuthIdRelationId,
|
||||||
|
markObjectDistributedParam->id,
|
||||||
|
subObjectId, checkObjectExistence);
|
||||||
|
RunCitusMainDBQuery(query->data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GetDistObjectOperationParams returns DistObjectOperationParams for the target
|
||||||
* object of given parsetree.
|
* object of given parsetree.
|
||||||
*/
|
*/
|
||||||
static MarkObjectDistributedParams
|
List *
|
||||||
GetMarkObjectDistributedParams(Node *parsetree)
|
GetDistObjectOperationParams(Node *parsetree)
|
||||||
{
|
{
|
||||||
|
List *paramsList = NIL;
|
||||||
if (IsA(parsetree, CreateRoleStmt))
|
if (IsA(parsetree, CreateRoleStmt))
|
||||||
{
|
{
|
||||||
CreateRoleStmt *stmt = castNode(CreateRoleStmt, parsetree);
|
CreateRoleStmt *stmt = castNode(CreateRoleStmt, parsetree);
|
||||||
MarkObjectDistributedParams info = {
|
DistObjectOperationParams *params =
|
||||||
.name = stmt->role,
|
(DistObjectOperationParams *) palloc(sizeof(DistObjectOperationParams));
|
||||||
.catalogRelId = AuthIdRelationId,
|
params->name = stmt->role;
|
||||||
.id = get_role_oid(stmt->role, false)
|
params->catalogRelId = AuthIdRelationId;
|
||||||
};
|
params->id = get_role_oid(stmt->role, false);
|
||||||
|
|
||||||
return info;
|
paramsList = lappend(paramsList, params);
|
||||||
|
}
|
||||||
|
else if (IsA(parsetree, DropRoleStmt))
|
||||||
|
{
|
||||||
|
DropRoleStmt *stmt = castNode(DropRoleStmt, parsetree);
|
||||||
|
RoleSpec *roleSpec;
|
||||||
|
foreach_ptr(roleSpec, stmt->roles)
|
||||||
|
{
|
||||||
|
DistObjectOperationParams *params = (DistObjectOperationParams *) palloc(
|
||||||
|
sizeof(DistObjectOperationParams));
|
||||||
|
|
||||||
|
Oid roleOid = get_role_oid(roleSpec->rolename, true);
|
||||||
|
|
||||||
|
if (roleOid == InvalidOid)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
params->id = roleOid;
|
||||||
|
params->name = roleSpec->rolename;
|
||||||
|
params->catalogRelId = AuthIdRelationId;
|
||||||
|
|
||||||
|
paramsList = lappend(paramsList, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
elog(ERROR, "unsupported statement type");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add else if branches for other statement types */
|
return paramsList;
|
||||||
|
|
||||||
elog(ERROR, "unsupported statement type");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -98,10 +98,10 @@ mark_object_distributed(PG_FUNCTION_ARGS)
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* citus_unmark_object_distributed(classid oid, objid oid, objsubid int)
|
* citus_unmark_object_distributed(classid oid, objid oid, objsubid int,checkobjectexistence bool)
|
||||||
*
|
*
|
||||||
* removes the entry for an object address from pg_dist_object. Only removes the entry if
|
* Removes the entry for an object address from pg_dist_object. If checkobjectexistence is true,
|
||||||
* the object does not exist anymore.
|
* throws an error if the object still exists.
|
||||||
*/
|
*/
|
||||||
Datum
|
Datum
|
||||||
citus_unmark_object_distributed(PG_FUNCTION_ARGS)
|
citus_unmark_object_distributed(PG_FUNCTION_ARGS)
|
||||||
|
@ -109,6 +109,12 @@ citus_unmark_object_distributed(PG_FUNCTION_ARGS)
|
||||||
Oid classid = PG_GETARG_OID(0);
|
Oid classid = PG_GETARG_OID(0);
|
||||||
Oid objid = PG_GETARG_OID(1);
|
Oid objid = PG_GETARG_OID(1);
|
||||||
int32 objsubid = PG_GETARG_INT32(2);
|
int32 objsubid = PG_GETARG_INT32(2);
|
||||||
|
bool checkObjectExistence = true;
|
||||||
|
if (!PG_ARGISNULL(3))
|
||||||
|
{
|
||||||
|
checkObjectExistence = PG_GETARG_BOOL(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ObjectAddress address = { 0 };
|
ObjectAddress address = { 0 };
|
||||||
ObjectAddressSubSet(address, classid, objid, objsubid);
|
ObjectAddressSubSet(address, classid, objid, objsubid);
|
||||||
|
@ -119,7 +125,7 @@ citus_unmark_object_distributed(PG_FUNCTION_ARGS)
|
||||||
PG_RETURN_VOID();
|
PG_RETURN_VOID();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ObjectExists(&address))
|
if (checkObjectExistence && ObjectExists(&address))
|
||||||
{
|
{
|
||||||
ereport(ERROR, (errmsg("object still exists"),
|
ereport(ERROR, (errmsg("object still exists"),
|
||||||
errdetail("the %s \"%s\" still exists",
|
errdetail("the %s \"%s\" still exists",
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
#include "udfs/start_management_transaction/12.2-1.sql"
|
#include "udfs/start_management_transaction/12.2-1.sql"
|
||||||
#include "udfs/execute_command_on_remote_nodes_as_user/12.2-1.sql"
|
#include "udfs/execute_command_on_remote_nodes_as_user/12.2-1.sql"
|
||||||
#include "udfs/mark_object_distributed/12.2-1.sql"
|
#include "udfs/mark_object_distributed/12.2-1.sql"
|
||||||
|
DROP FUNCTION pg_catalog.citus_unmark_object_distributed(oid, oid, int);
|
||||||
|
#include "udfs/citus_unmark_object_distributed/12.2-1.sql"
|
||||||
#include "udfs/commit_management_command_2pc/12.2-1.sql"
|
#include "udfs/commit_management_command_2pc/12.2-1.sql"
|
||||||
|
|
||||||
ALTER TABLE pg_catalog.pg_dist_transaction ADD COLUMN outer_xid xid8;
|
ALTER TABLE pg_catalog.pg_dist_transaction ADD COLUMN outer_xid xid8;
|
||||||
|
|
|
@ -18,6 +18,9 @@ DROP FUNCTION citus_internal.mark_object_distributed(
|
||||||
classId Oid, objectName text, objectId Oid, connectionUser text
|
classId Oid, objectName text, objectId Oid, connectionUser text
|
||||||
);
|
);
|
||||||
|
|
||||||
|
DROP FUNCTION pg_catalog.citus_unmark_object_distributed(oid,oid,int,boolean);
|
||||||
|
#include "../udfs/citus_unmark_object_distributed/10.0-1.sql"
|
||||||
|
|
||||||
DROP FUNCTION citus_internal.commit_management_command_2pc();
|
DROP FUNCTION citus_internal.commit_management_command_2pc();
|
||||||
|
|
||||||
ALTER TABLE pg_catalog.pg_dist_transaction DROP COLUMN outer_xid;
|
ALTER TABLE pg_catalog.pg_dist_transaction DROP COLUMN outer_xid;
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
CREATE FUNCTION pg_catalog.citus_unmark_object_distributed(classid oid, objid oid, objsubid int, checkobjectexistence boolean DEFAULT true)
|
||||||
|
RETURNS void
|
||||||
|
LANGUAGE C STRICT
|
||||||
|
AS 'MODULE_PATHNAME', $$citus_unmark_object_distributed$$;
|
||||||
|
COMMENT ON FUNCTION pg_catalog.citus_unmark_object_distributed(classid oid, objid oid, objsubid int, checkobjectexistence boolean)
|
||||||
|
IS 'Removes an object from citus.pg_dist_object after deletion. If checkobjectexistence is true, object existence check performed.'
|
||||||
|
'Otherwise, object existence check is skipped.';
|
|
@ -1,6 +1,7 @@
|
||||||
CREATE FUNCTION pg_catalog.citus_unmark_object_distributed(classid oid, objid oid, objsubid int)
|
CREATE FUNCTION pg_catalog.citus_unmark_object_distributed(classid oid, objid oid, objsubid int, checkobjectexistence boolean DEFAULT true)
|
||||||
RETURNS void
|
RETURNS void
|
||||||
LANGUAGE C STRICT
|
LANGUAGE C STRICT
|
||||||
AS 'MODULE_PATHNAME', $$citus_unmark_object_distributed$$;
|
AS 'MODULE_PATHNAME', $$citus_unmark_object_distributed$$;
|
||||||
COMMENT ON FUNCTION pg_catalog.citus_unmark_object_distributed(classid oid, objid oid, objsubid int)
|
COMMENT ON FUNCTION pg_catalog.citus_unmark_object_distributed(classid oid, objid oid, objsubid int, checkobjectexistence boolean)
|
||||||
IS 'remove an object address from citus.pg_dist_object once the object has been deleted';
|
IS 'Removes an object from citus.pg_dist_object after deletion. If checkobjectexistence is true, object existence check performed.'
|
||||||
|
'Otherwise, object existence check is skipped.';
|
||||||
|
|
|
@ -164,6 +164,149 @@ revoke CONNECT on database metadata_sync_2pc_db from "grant_role2pc'_user2";
|
||||||
revoke CREATE on database metadata_sync_2pc_db from "grant_role2pc'_user1";
|
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;
|
||||||
|
--test for user operations
|
||||||
|
--test for create user
|
||||||
|
\c regression - - :master_port
|
||||||
|
select 1 from citus_remove_node('localhost', :worker_2_port);
|
||||||
|
?column?
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
\c metadata_sync_2pc_db - - :master_port
|
||||||
|
CREATE ROLE test_role1 WITH LOGIN PASSWORD 'password1';
|
||||||
|
\c metadata_sync_2pc_db - - :worker_1_port
|
||||||
|
CREATE USER "test_role2-needs\!escape"
|
||||||
|
WITH
|
||||||
|
SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN REPLICATION BYPASSRLS CONNECTION
|
||||||
|
LIMIT 10 VALID UNTIL '2023-01-01' IN ROLE test_role1;
|
||||||
|
create role test_role3;
|
||||||
|
\c regression - - :master_port
|
||||||
|
select 1 from citus_add_node('localhost', :worker_2_port);
|
||||||
|
?column?
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- XXX: date is not correct on one of the workers due to https://github.com/citusdata/citus/issues/7533
|
||||||
|
select result FROM run_command_on_all_nodes($$
|
||||||
|
SELECT array_to_json(array_agg(row_to_json(t)))
|
||||||
|
FROM (
|
||||||
|
SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb,
|
||||||
|
rolcanlogin, rolreplication, rolbypassrls, rolconnlimit,
|
||||||
|
(rolpassword != '') as pass_not_empty, DATE(rolvaliduntil)
|
||||||
|
FROM pg_authid
|
||||||
|
WHERE rolname in ('test_role1', 'test_role2-needs\!escape','test_role3')
|
||||||
|
ORDER BY rolname
|
||||||
|
) t
|
||||||
|
$$);
|
||||||
|
result
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
[{"rolname":"test_role1","rolsuper":false,"rolinherit":true,"rolcreaterole":false,"rolcreatedb":false,"rolcanlogin":true,"rolreplication":false,"rolbypassrls":false,"rolconnlimit":-1,"pass_not_empty":true,"date":null},{"rolname":"test_role2-needs\\!escape","rolsuper":true,"rolinherit":true,"rolcreaterole":true,"rolcreatedb":true,"rolcanlogin":true,"rolreplication":true,"rolbypassrls":true,"rolconnlimit":10,"pass_not_empty":null,"date":"2023-01-01"},{"rolname":"test_role3","rolsuper":false,"rolinherit":true,"rolcreaterole":false,"rolcreatedb":false,"rolcanlogin":false,"rolreplication":false,"rolbypassrls":false,"rolconnlimit":-1,"pass_not_empty":null,"date":null}]
|
||||||
|
[{"rolname":"test_role1","rolsuper":false,"rolinherit":true,"rolcreaterole":false,"rolcreatedb":false,"rolcanlogin":true,"rolreplication":false,"rolbypassrls":false,"rolconnlimit":-1,"pass_not_empty":true,"date":null},{"rolname":"test_role2-needs\\!escape","rolsuper":true,"rolinherit":true,"rolcreaterole":true,"rolcreatedb":true,"rolcanlogin":true,"rolreplication":true,"rolbypassrls":true,"rolconnlimit":10,"pass_not_empty":null,"date":"2023-01-01"},{"rolname":"test_role3","rolsuper":false,"rolinherit":true,"rolcreaterole":false,"rolcreatedb":false,"rolcanlogin":false,"rolreplication":false,"rolbypassrls":false,"rolconnlimit":-1,"pass_not_empty":null,"date":null}]
|
||||||
|
[{"rolname":"test_role1","rolsuper":false,"rolinherit":true,"rolcreaterole":false,"rolcreatedb":false,"rolcanlogin":true,"rolreplication":false,"rolbypassrls":false,"rolconnlimit":-1,"pass_not_empty":true,"date":"infinity"},{"rolname":"test_role2-needs\\!escape","rolsuper":true,"rolinherit":true,"rolcreaterole":true,"rolcreatedb":true,"rolcanlogin":true,"rolreplication":true,"rolbypassrls":true,"rolconnlimit":10,"pass_not_empty":null,"date":"2023-01-01"},{"rolname":"test_role3","rolsuper":false,"rolinherit":true,"rolcreaterole":false,"rolcreatedb":false,"rolcanlogin":false,"rolreplication":false,"rolbypassrls":false,"rolconnlimit":-1,"pass_not_empty":null,"date":"infinity"}]
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
--test for alter user
|
||||||
|
select 1 from citus_remove_node('localhost', :worker_2_port);
|
||||||
|
?column?
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
\c metadata_sync_2pc_db - - :master_port
|
||||||
|
-- Test ALTER ROLE with various options
|
||||||
|
ALTER ROLE test_role1 WITH PASSWORD 'new_password1';
|
||||||
|
\c metadata_sync_2pc_db - - :worker_1_port
|
||||||
|
ALTER USER "test_role2-needs\!escape"
|
||||||
|
WITH
|
||||||
|
NOSUPERUSER NOCREATEDB NOCREATEROLE NOINHERIT NOLOGIN NOREPLICATION NOBYPASSRLS CONNECTION
|
||||||
|
LIMIT 5 VALID UNTIL '2024-01-01';
|
||||||
|
\c regression - - :master_port
|
||||||
|
select 1 from citus_add_node('localhost', :worker_2_port);
|
||||||
|
?column?
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- XXX: date is not correct on one of the workers due to https://github.com/citusdata/citus/issues/7533
|
||||||
|
select result FROM run_command_on_all_nodes($$
|
||||||
|
SELECT array_to_json(array_agg(row_to_json(t)))
|
||||||
|
FROM (
|
||||||
|
SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb,
|
||||||
|
rolcanlogin, rolreplication, rolbypassrls, rolconnlimit,
|
||||||
|
(rolpassword != '') as pass_not_empty, DATE(rolvaliduntil)
|
||||||
|
FROM pg_authid
|
||||||
|
WHERE rolname in ('test_role1', 'test_role2-needs\!escape','test_role3')
|
||||||
|
ORDER BY rolname
|
||||||
|
) t
|
||||||
|
$$);
|
||||||
|
result
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
[{"rolname":"test_role1","rolsuper":false,"rolinherit":true,"rolcreaterole":false,"rolcreatedb":false,"rolcanlogin":true,"rolreplication":false,"rolbypassrls":false,"rolconnlimit":-1,"pass_not_empty":true,"date":null},{"rolname":"test_role2-needs\\!escape","rolsuper":false,"rolinherit":false,"rolcreaterole":false,"rolcreatedb":false,"rolcanlogin":false,"rolreplication":false,"rolbypassrls":false,"rolconnlimit":5,"pass_not_empty":null,"date":"2024-01-01"},{"rolname":"test_role3","rolsuper":false,"rolinherit":true,"rolcreaterole":false,"rolcreatedb":false,"rolcanlogin":false,"rolreplication":false,"rolbypassrls":false,"rolconnlimit":-1,"pass_not_empty":null,"date":null}]
|
||||||
|
[{"rolname":"test_role1","rolsuper":false,"rolinherit":true,"rolcreaterole":false,"rolcreatedb":false,"rolcanlogin":true,"rolreplication":false,"rolbypassrls":false,"rolconnlimit":-1,"pass_not_empty":true,"date":null},{"rolname":"test_role2-needs\\!escape","rolsuper":false,"rolinherit":false,"rolcreaterole":false,"rolcreatedb":false,"rolcanlogin":false,"rolreplication":false,"rolbypassrls":false,"rolconnlimit":5,"pass_not_empty":null,"date":"2024-01-01"},{"rolname":"test_role3","rolsuper":false,"rolinherit":true,"rolcreaterole":false,"rolcreatedb":false,"rolcanlogin":false,"rolreplication":false,"rolbypassrls":false,"rolconnlimit":-1,"pass_not_empty":null,"date":null}]
|
||||||
|
[{"rolname":"test_role1","rolsuper":false,"rolinherit":true,"rolcreaterole":false,"rolcreatedb":false,"rolcanlogin":true,"rolreplication":false,"rolbypassrls":false,"rolconnlimit":-1,"pass_not_empty":true,"date":"infinity"},{"rolname":"test_role2-needs\\!escape","rolsuper":false,"rolinherit":false,"rolcreaterole":false,"rolcreatedb":false,"rolcanlogin":false,"rolreplication":false,"rolbypassrls":false,"rolconnlimit":5,"pass_not_empty":null,"date":"2024-01-01"},{"rolname":"test_role3","rolsuper":false,"rolinherit":true,"rolcreaterole":false,"rolcreatedb":false,"rolcanlogin":false,"rolreplication":false,"rolbypassrls":false,"rolconnlimit":-1,"pass_not_empty":null,"date":"infinity"}]
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
--test for drop user
|
||||||
|
select 1 from citus_remove_node('localhost', :worker_2_port);
|
||||||
|
?column?
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
\c metadata_sync_2pc_db - - :worker_1_port
|
||||||
|
DROP ROLE test_role1, "test_role2-needs\!escape";
|
||||||
|
\c metadata_sync_2pc_db - - :master_port
|
||||||
|
DROP ROLE test_role3;
|
||||||
|
\c regression - - :master_port
|
||||||
|
select 1 from citus_add_node('localhost', :worker_2_port);
|
||||||
|
?column?
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- XXX: date is not correct on one of the workers due to https://github.com/citusdata/citus/issues/7533
|
||||||
|
select result FROM run_command_on_all_nodes($$
|
||||||
|
SELECT array_to_json(array_agg(row_to_json(t)))
|
||||||
|
FROM (
|
||||||
|
SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb,
|
||||||
|
rolcanlogin, rolreplication, rolbypassrls, rolconnlimit,
|
||||||
|
(rolpassword != '') as pass_not_empty, DATE(rolvaliduntil)
|
||||||
|
FROM pg_authid
|
||||||
|
WHERE rolname in ('test_role1', 'test_role2-needs\!escape','test_role3')
|
||||||
|
ORDER BY rolname
|
||||||
|
) t
|
||||||
|
$$);
|
||||||
|
result
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
[{"rolname":"test_role1","rolsuper":false,"rolinherit":true,"rolcreaterole":false,"rolcreatedb":false,"rolcanlogin":true,"rolreplication":false,"rolbypassrls":false,"rolconnlimit":-1,"pass_not_empty":true,"date":"infinity"},{"rolname":"test_role2-needs\\!escape","rolsuper":false,"rolinherit":false,"rolcreaterole":false,"rolcreatedb":false,"rolcanlogin":false,"rolreplication":false,"rolbypassrls":false,"rolconnlimit":5,"pass_not_empty":null,"date":"2024-01-01"},{"rolname":"test_role3","rolsuper":false,"rolinherit":true,"rolcreaterole":false,"rolcreatedb":false,"rolcanlogin":false,"rolreplication":false,"rolbypassrls":false,"rolconnlimit":-1,"pass_not_empty":null,"date":"infinity"}]
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
-- Clean up: drop the database on worker node 2
|
||||||
|
\c regression - - :worker_2_port
|
||||||
|
DROP ROLE if exists test_role1, "test_role2-needs\!escape", test_role3;
|
||||||
|
\c regression - - :master_port
|
||||||
|
select result FROM run_command_on_all_nodes($$
|
||||||
|
SELECT array_to_json(array_agg(row_to_json(t)))
|
||||||
|
FROM (
|
||||||
|
SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb,
|
||||||
|
rolcanlogin, rolreplication, rolbypassrls, rolconnlimit,
|
||||||
|
(rolpassword != '') as pass_not_empty, DATE(rolvaliduntil)
|
||||||
|
FROM pg_authid
|
||||||
|
WHERE rolname in ('test_role1', 'test_role2-needs\!escape','test_role3')
|
||||||
|
ORDER BY rolname
|
||||||
|
) t
|
||||||
|
$$);
|
||||||
|
result
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
set citus.enable_create_database_propagation to on;
|
set citus.enable_create_database_propagation to on;
|
||||||
drop database metadata_sync_2pc_db;
|
drop database metadata_sync_2pc_db;
|
||||||
drop schema metadata_sync_2pc_schema;
|
drop schema metadata_sync_2pc_schema;
|
||||||
|
|
|
@ -1420,37 +1420,39 @@ SELECT * FROM multi_extension.print_extension_changes();
|
||||||
-- Snapshot of state at 12.2-1
|
-- Snapshot of state at 12.2-1
|
||||||
ALTER EXTENSION citus UPDATE TO '12.2-1';
|
ALTER EXTENSION citus UPDATE TO '12.2-1';
|
||||||
SELECT * FROM multi_extension.print_extension_changes();
|
SELECT * FROM multi_extension.print_extension_changes();
|
||||||
previous_object | current_object
|
previous_object | current_object
|
||||||
---------------------------------------------------------------------
|
---------------------------------------------------------------------
|
||||||
| function citus_internal.acquire_citus_advisory_object_class_lock(integer,cstring) void
|
function citus_unmark_object_distributed(oid,oid,integer) void |
|
||||||
| function citus_internal.add_colocation_metadata(integer,integer,integer,regtype,oid) void
|
| function citus_internal.acquire_citus_advisory_object_class_lock(integer,cstring) void
|
||||||
| function citus_internal.add_object_metadata(text,text[],text[],integer,integer,boolean) void
|
| function citus_internal.add_colocation_metadata(integer,integer,integer,regtype,oid) void
|
||||||
| function citus_internal.add_partition_metadata(regclass,"char",text,integer,"char") void
|
| function citus_internal.add_object_metadata(text,text[],text[],integer,integer,boolean) void
|
||||||
| function citus_internal.add_placement_metadata(bigint,bigint,integer,bigint) void
|
| function citus_internal.add_partition_metadata(regclass,"char",text,integer,"char") void
|
||||||
| function citus_internal.add_shard_metadata(regclass,bigint,"char",text,text) void
|
| function citus_internal.add_placement_metadata(bigint,bigint,integer,bigint) void
|
||||||
| function citus_internal.add_tenant_schema(oid,integer) void
|
| function citus_internal.add_shard_metadata(regclass,bigint,"char",text,text) void
|
||||||
| function citus_internal.adjust_local_clock_to_remote(cluster_clock) void
|
| function citus_internal.add_tenant_schema(oid,integer) void
|
||||||
| function citus_internal.commit_management_command_2pc() void
|
| function citus_internal.adjust_local_clock_to_remote(cluster_clock) void
|
||||||
| function citus_internal.database_command(text) void
|
| function citus_internal.commit_management_command_2pc() void
|
||||||
| function citus_internal.delete_colocation_metadata(integer) void
|
| function citus_internal.database_command(text) void
|
||||||
| function citus_internal.delete_partition_metadata(regclass) void
|
| function citus_internal.delete_colocation_metadata(integer) void
|
||||||
| function citus_internal.delete_placement_metadata(bigint) void
|
| function citus_internal.delete_partition_metadata(regclass) void
|
||||||
| function citus_internal.delete_shard_metadata(bigint) void
|
| function citus_internal.delete_placement_metadata(bigint) void
|
||||||
| function citus_internal.delete_tenant_schema(oid) void
|
| function citus_internal.delete_shard_metadata(bigint) void
|
||||||
| function citus_internal.execute_command_on_remote_nodes_as_user(text,text) void
|
| function citus_internal.delete_tenant_schema(oid) void
|
||||||
| function citus_internal.global_blocked_processes() SETOF record
|
| function citus_internal.execute_command_on_remote_nodes_as_user(text,text) void
|
||||||
| function citus_internal.is_replication_origin_tracking_active() boolean
|
| function citus_internal.global_blocked_processes() SETOF record
|
||||||
| function citus_internal.local_blocked_processes() SETOF record
|
| function citus_internal.is_replication_origin_tracking_active() boolean
|
||||||
| function citus_internal.mark_node_not_synced(integer,integer) void
|
| function citus_internal.local_blocked_processes() SETOF record
|
||||||
| function citus_internal.mark_object_distributed(oid,text,oid,text) void
|
| function citus_internal.mark_node_not_synced(integer,integer) void
|
||||||
| function citus_internal.start_management_transaction(xid8) void
|
| function citus_internal.mark_object_distributed(oid,text,oid,text) void
|
||||||
| function citus_internal.start_replication_origin_tracking() void
|
| function citus_internal.start_management_transaction(xid8) void
|
||||||
| function citus_internal.stop_replication_origin_tracking() void
|
| function citus_internal.start_replication_origin_tracking() void
|
||||||
| function citus_internal.unregister_tenant_schema_globally(oid,text) void
|
| function citus_internal.stop_replication_origin_tracking() void
|
||||||
| function citus_internal.update_none_dist_table_metadata(oid,"char",bigint,boolean) void
|
| function citus_internal.unregister_tenant_schema_globally(oid,text) void
|
||||||
| function citus_internal.update_placement_metadata(bigint,integer,integer) void
|
| function citus_internal.update_none_dist_table_metadata(oid,"char",bigint,boolean) void
|
||||||
| function citus_internal.update_relation_colocation(oid,integer) void
|
| function citus_internal.update_placement_metadata(bigint,integer,integer) void
|
||||||
(28 rows)
|
| function citus_internal.update_relation_colocation(oid,integer) void
|
||||||
|
| function citus_unmark_object_distributed(oid,oid,integer,boolean) void
|
||||||
|
(30 rows)
|
||||||
|
|
||||||
DROP TABLE multi_extension.prev_objects, multi_extension.extension_diff;
|
DROP TABLE multi_extension.prev_objects, multi_extension.extension_diff;
|
||||||
-- show running version
|
-- show running version
|
||||||
|
|
|
@ -0,0 +1,138 @@
|
||||||
|
-- Create a new database
|
||||||
|
set citus.enable_create_database_propagation to on;
|
||||||
|
CREATE DATABASE role_operations_test_db;
|
||||||
|
SET citus.superuser TO 'postgres';
|
||||||
|
-- Connect to the new database
|
||||||
|
\c role_operations_test_db
|
||||||
|
-- Test CREATE ROLE with various options
|
||||||
|
CREATE ROLE test_role1 WITH LOGIN PASSWORD 'password1';
|
||||||
|
\c role_operations_test_db - - :worker_1_port
|
||||||
|
CREATE USER "test_role2-needs\!escape"
|
||||||
|
WITH
|
||||||
|
SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN REPLICATION BYPASSRLS CONNECTION
|
||||||
|
LIMIT 10 VALID UNTIL '2023-01-01' IN ROLE test_role1;
|
||||||
|
\c regression - - :master_port
|
||||||
|
select result FROM run_command_on_all_nodes($$
|
||||||
|
SELECT array_to_json(array_agg(row_to_json(t)))
|
||||||
|
FROM (
|
||||||
|
SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb,
|
||||||
|
rolcanlogin, rolreplication, rolbypassrls, rolconnlimit,
|
||||||
|
(rolpassword != '') as pass_not_empty, DATE(rolvaliduntil)
|
||||||
|
FROM pg_authid
|
||||||
|
WHERE rolname in ('test_role1', 'test_role2-needs\!escape')
|
||||||
|
ORDER BY rolname
|
||||||
|
) t
|
||||||
|
$$);
|
||||||
|
result
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
[{"rolname":"test_role1","rolsuper":false,"rolinherit":true,"rolcreaterole":false,"rolcreatedb":false,"rolcanlogin":true,"rolreplication":false,"rolbypassrls":false,"rolconnlimit":-1,"pass_not_empty":true,"date":null},{"rolname":"test_role2-needs\\!escape","rolsuper":true,"rolinherit":true,"rolcreaterole":true,"rolcreatedb":true,"rolcanlogin":true,"rolreplication":true,"rolbypassrls":true,"rolconnlimit":10,"pass_not_empty":null,"date":"2023-01-01"}]
|
||||||
|
[{"rolname":"test_role1","rolsuper":false,"rolinherit":true,"rolcreaterole":false,"rolcreatedb":false,"rolcanlogin":true,"rolreplication":false,"rolbypassrls":false,"rolconnlimit":-1,"pass_not_empty":true,"date":null},{"rolname":"test_role2-needs\\!escape","rolsuper":true,"rolinherit":true,"rolcreaterole":true,"rolcreatedb":true,"rolcanlogin":true,"rolreplication":true,"rolbypassrls":true,"rolconnlimit":10,"pass_not_empty":null,"date":"2023-01-01"}]
|
||||||
|
[{"rolname":"test_role1","rolsuper":false,"rolinherit":true,"rolcreaterole":false,"rolcreatedb":false,"rolcanlogin":true,"rolreplication":false,"rolbypassrls":false,"rolconnlimit":-1,"pass_not_empty":true,"date":null},{"rolname":"test_role2-needs\\!escape","rolsuper":true,"rolinherit":true,"rolcreaterole":true,"rolcreatedb":true,"rolcanlogin":true,"rolreplication":true,"rolbypassrls":true,"rolconnlimit":10,"pass_not_empty":null,"date":"2023-01-01"}]
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
select result FROM run_command_on_all_nodes($$
|
||||||
|
SELECT array_to_json(array_agg(row_to_json(t)))
|
||||||
|
FROM (
|
||||||
|
SELECT r.rolname
|
||||||
|
FROM pg_dist_object d
|
||||||
|
JOIN pg_roles r ON d.objid = r.oid
|
||||||
|
WHERE r.rolname IN ('test_role1', 'test_role2-needs\!escape')
|
||||||
|
order by r.rolname
|
||||||
|
) t
|
||||||
|
$$);
|
||||||
|
result
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
[{"rolname":"test_role1"},{"rolname":"test_role2-needs\\!escape"}]
|
||||||
|
[{"rolname":"test_role1"},{"rolname":"test_role2-needs\\!escape"}]
|
||||||
|
[{"rolname":"test_role1"},{"rolname":"test_role2-needs\\!escape"}]
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
\c role_operations_test_db - - :master_port
|
||||||
|
-- Test ALTER ROLE with various options
|
||||||
|
ALTER ROLE test_role1 WITH PASSWORD 'new_password1';
|
||||||
|
\c role_operations_test_db - - :worker_1_port
|
||||||
|
ALTER USER "test_role2-needs\!escape"
|
||||||
|
WITH
|
||||||
|
NOSUPERUSER NOCREATEDB NOCREATEROLE NOINHERIT NOLOGIN NOREPLICATION NOBYPASSRLS CONNECTION
|
||||||
|
LIMIT 5 VALID UNTIL '2024-01-01';
|
||||||
|
\c regression - - :master_port
|
||||||
|
select result FROM run_command_on_all_nodes($$
|
||||||
|
SELECT array_to_json(array_agg(row_to_json(t)))
|
||||||
|
FROM (
|
||||||
|
SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb,
|
||||||
|
rolcanlogin, rolreplication, rolbypassrls, rolconnlimit,
|
||||||
|
(rolpassword != '') as pass_not_empty, DATE(rolvaliduntil)
|
||||||
|
FROM pg_authid
|
||||||
|
WHERE rolname in ('test_role1', 'test_role2-needs\!escape')
|
||||||
|
ORDER BY rolname
|
||||||
|
) t
|
||||||
|
$$);
|
||||||
|
result
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
[{"rolname":"test_role1","rolsuper":false,"rolinherit":true,"rolcreaterole":false,"rolcreatedb":false,"rolcanlogin":true,"rolreplication":false,"rolbypassrls":false,"rolconnlimit":-1,"pass_not_empty":true,"date":null},{"rolname":"test_role2-needs\\!escape","rolsuper":false,"rolinherit":false,"rolcreaterole":false,"rolcreatedb":false,"rolcanlogin":false,"rolreplication":false,"rolbypassrls":false,"rolconnlimit":5,"pass_not_empty":null,"date":"2024-01-01"}]
|
||||||
|
[{"rolname":"test_role1","rolsuper":false,"rolinherit":true,"rolcreaterole":false,"rolcreatedb":false,"rolcanlogin":true,"rolreplication":false,"rolbypassrls":false,"rolconnlimit":-1,"pass_not_empty":true,"date":null},{"rolname":"test_role2-needs\\!escape","rolsuper":false,"rolinherit":false,"rolcreaterole":false,"rolcreatedb":false,"rolcanlogin":false,"rolreplication":false,"rolbypassrls":false,"rolconnlimit":5,"pass_not_empty":null,"date":"2024-01-01"}]
|
||||||
|
[{"rolname":"test_role1","rolsuper":false,"rolinherit":true,"rolcreaterole":false,"rolcreatedb":false,"rolcanlogin":true,"rolreplication":false,"rolbypassrls":false,"rolconnlimit":-1,"pass_not_empty":true,"date":null},{"rolname":"test_role2-needs\\!escape","rolsuper":false,"rolinherit":false,"rolcreaterole":false,"rolcreatedb":false,"rolcanlogin":false,"rolreplication":false,"rolbypassrls":false,"rolconnlimit":5,"pass_not_empty":null,"date":"2024-01-01"}]
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
\c role_operations_test_db - - :master_port
|
||||||
|
-- Test DROP ROLE
|
||||||
|
DROP ROLE no_such_role; -- fails nicely
|
||||||
|
ERROR: role "no_such_role" does not exist
|
||||||
|
DROP ROLE IF EXISTS no_such_role; -- doesn't fail
|
||||||
|
NOTICE: role "no_such_role" does not exist, skipping
|
||||||
|
CREATE ROLE new_role;
|
||||||
|
DROP ROLE IF EXISTS no_such_role, new_role; -- doesn't fail
|
||||||
|
NOTICE: role "no_such_role" does not exist, skipping
|
||||||
|
DROP ROLE IF EXISTS test_role1, "test_role2-needs\!escape";
|
||||||
|
\c regression - - :master_port
|
||||||
|
--verify that roles and dist_object are dropped
|
||||||
|
select result FROM run_command_on_all_nodes($$
|
||||||
|
SELECT array_to_json(array_agg(row_to_json(t)))
|
||||||
|
FROM (
|
||||||
|
SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb,
|
||||||
|
rolcanlogin, rolreplication, rolbypassrls, rolconnlimit,
|
||||||
|
(rolpassword != '') as pass_not_empty, DATE(rolvaliduntil)
|
||||||
|
FROM pg_authid
|
||||||
|
WHERE rolname in ('test_role1', 'test_role2-needs\!escape','new_role','no_such_role')
|
||||||
|
ORDER BY rolname
|
||||||
|
) t
|
||||||
|
$$);
|
||||||
|
result
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
select result FROM run_command_on_all_nodes($$
|
||||||
|
SELECT array_to_json(array_agg(row_to_json(t)))
|
||||||
|
FROM (
|
||||||
|
SELECT r.rolname
|
||||||
|
FROM pg_roles r
|
||||||
|
WHERE r.rolname IN ('test_role1', 'test_role2-needs\!escape','new_role','no_such_role')
|
||||||
|
order by r.rolname
|
||||||
|
) t
|
||||||
|
$$);
|
||||||
|
result
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
SELECT result FROM run_command_on_all_nodes($$
|
||||||
|
SELECT count(*) leaked_pg_dist_object_records_for_roles
|
||||||
|
FROM pg_dist_object LEFT JOIN pg_authid ON (objid = oid)
|
||||||
|
WHERE classid = 1260 AND oid IS NULL
|
||||||
|
$$);
|
||||||
|
result
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
0
|
||||||
|
0
|
||||||
|
0
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
-- Clean up: drop the database
|
||||||
|
set citus.enable_create_database_propagation to on;
|
||||||
|
DROP DATABASE role_operations_test_db;
|
||||||
|
reset citus.enable_create_database_propagation;
|
|
@ -174,7 +174,7 @@ ORDER BY 1;
|
||||||
function citus_text_send_as_jsonb(text)
|
function citus_text_send_as_jsonb(text)
|
||||||
function citus_total_relation_size(regclass,boolean)
|
function citus_total_relation_size(regclass,boolean)
|
||||||
function citus_truncate_trigger()
|
function citus_truncate_trigger()
|
||||||
function citus_unmark_object_distributed(oid,oid,integer)
|
function citus_unmark_object_distributed(oid,oid,integer,boolean)
|
||||||
function citus_update_node(integer,text,integer,boolean,integer)
|
function citus_update_node(integer,text,integer,boolean,integer)
|
||||||
function citus_update_shard_statistics(bigint)
|
function citus_update_shard_statistics(bigint)
|
||||||
function citus_update_table_statistics(regclass)
|
function citus_update_table_statistics(regclass)
|
||||||
|
|
|
@ -108,7 +108,7 @@ test: object_propagation_debug
|
||||||
test: undistribute_table
|
test: undistribute_table
|
||||||
test: run_command_on_all_nodes
|
test: run_command_on_all_nodes
|
||||||
test: background_task_queue_monitor
|
test: background_task_queue_monitor
|
||||||
test: other_databases grant_role_from_non_maindb seclabel_non_maindb
|
test: other_databases grant_role_from_non_maindb role_operations_from_non_maindb seclabel_non_maindb
|
||||||
test: citus_internal_access
|
test: citus_internal_access
|
||||||
|
|
||||||
# Causal clock test
|
# Causal clock test
|
||||||
|
|
|
@ -80,9 +80,112 @@ 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;
|
||||||
|
--test for user operations
|
||||||
|
|
||||||
|
--test for create user
|
||||||
|
\c regression - - :master_port
|
||||||
|
select 1 from citus_remove_node('localhost', :worker_2_port);
|
||||||
|
|
||||||
|
\c metadata_sync_2pc_db - - :master_port
|
||||||
|
CREATE ROLE test_role1 WITH LOGIN PASSWORD 'password1';
|
||||||
|
|
||||||
|
\c metadata_sync_2pc_db - - :worker_1_port
|
||||||
|
CREATE USER "test_role2-needs\!escape"
|
||||||
|
WITH
|
||||||
|
SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN REPLICATION BYPASSRLS CONNECTION
|
||||||
|
LIMIT 10 VALID UNTIL '2023-01-01' IN ROLE test_role1;
|
||||||
|
|
||||||
|
create role test_role3;
|
||||||
|
|
||||||
|
\c regression - - :master_port
|
||||||
|
select 1 from citus_add_node('localhost', :worker_2_port);
|
||||||
|
|
||||||
|
-- XXX: date is not correct on one of the workers due to https://github.com/citusdata/citus/issues/7533
|
||||||
|
select result FROM run_command_on_all_nodes($$
|
||||||
|
SELECT array_to_json(array_agg(row_to_json(t)))
|
||||||
|
FROM (
|
||||||
|
SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb,
|
||||||
|
rolcanlogin, rolreplication, rolbypassrls, rolconnlimit,
|
||||||
|
(rolpassword != '') as pass_not_empty, DATE(rolvaliduntil)
|
||||||
|
FROM pg_authid
|
||||||
|
WHERE rolname in ('test_role1', 'test_role2-needs\!escape','test_role3')
|
||||||
|
ORDER BY rolname
|
||||||
|
) t
|
||||||
|
$$);
|
||||||
|
|
||||||
|
--test for alter user
|
||||||
|
select 1 from citus_remove_node('localhost', :worker_2_port);
|
||||||
|
\c metadata_sync_2pc_db - - :master_port
|
||||||
|
-- Test ALTER ROLE with various options
|
||||||
|
ALTER ROLE test_role1 WITH PASSWORD 'new_password1';
|
||||||
|
|
||||||
|
\c metadata_sync_2pc_db - - :worker_1_port
|
||||||
|
ALTER USER "test_role2-needs\!escape"
|
||||||
|
WITH
|
||||||
|
NOSUPERUSER NOCREATEDB NOCREATEROLE NOINHERIT NOLOGIN NOREPLICATION NOBYPASSRLS CONNECTION
|
||||||
|
LIMIT 5 VALID UNTIL '2024-01-01';
|
||||||
|
|
||||||
|
\c regression - - :master_port
|
||||||
|
select 1 from citus_add_node('localhost', :worker_2_port);
|
||||||
|
|
||||||
|
-- XXX: date is not correct on one of the workers due to https://github.com/citusdata/citus/issues/7533
|
||||||
|
select result FROM run_command_on_all_nodes($$
|
||||||
|
SELECT array_to_json(array_agg(row_to_json(t)))
|
||||||
|
FROM (
|
||||||
|
SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb,
|
||||||
|
rolcanlogin, rolreplication, rolbypassrls, rolconnlimit,
|
||||||
|
(rolpassword != '') as pass_not_empty, DATE(rolvaliduntil)
|
||||||
|
FROM pg_authid
|
||||||
|
WHERE rolname in ('test_role1', 'test_role2-needs\!escape','test_role3')
|
||||||
|
ORDER BY rolname
|
||||||
|
) t
|
||||||
|
$$);
|
||||||
|
|
||||||
|
--test for drop user
|
||||||
|
select 1 from citus_remove_node('localhost', :worker_2_port);
|
||||||
|
|
||||||
|
\c metadata_sync_2pc_db - - :worker_1_port
|
||||||
|
DROP ROLE test_role1, "test_role2-needs\!escape";
|
||||||
|
|
||||||
|
\c metadata_sync_2pc_db - - :master_port
|
||||||
|
DROP ROLE test_role3;
|
||||||
|
|
||||||
|
\c regression - - :master_port
|
||||||
|
select 1 from citus_add_node('localhost', :worker_2_port);
|
||||||
|
|
||||||
|
-- XXX: date is not correct on one of the workers due to https://github.com/citusdata/citus/issues/7533
|
||||||
|
select result FROM run_command_on_all_nodes($$
|
||||||
|
SELECT array_to_json(array_agg(row_to_json(t)))
|
||||||
|
FROM (
|
||||||
|
SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb,
|
||||||
|
rolcanlogin, rolreplication, rolbypassrls, rolconnlimit,
|
||||||
|
(rolpassword != '') as pass_not_empty, DATE(rolvaliduntil)
|
||||||
|
FROM pg_authid
|
||||||
|
WHERE rolname in ('test_role1', 'test_role2-needs\!escape','test_role3')
|
||||||
|
ORDER BY rolname
|
||||||
|
) t
|
||||||
|
$$);
|
||||||
|
|
||||||
|
-- Clean up: drop the database on worker node 2
|
||||||
|
\c regression - - :worker_2_port
|
||||||
|
DROP ROLE if exists test_role1, "test_role2-needs\!escape", test_role3;
|
||||||
|
|
||||||
|
\c regression - - :master_port
|
||||||
|
|
||||||
|
select result FROM run_command_on_all_nodes($$
|
||||||
|
SELECT array_to_json(array_agg(row_to_json(t)))
|
||||||
|
FROM (
|
||||||
|
SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb,
|
||||||
|
rolcanlogin, rolreplication, rolbypassrls, rolconnlimit,
|
||||||
|
(rolpassword != '') as pass_not_empty, DATE(rolvaliduntil)
|
||||||
|
FROM pg_authid
|
||||||
|
WHERE rolname in ('test_role1', 'test_role2-needs\!escape','test_role3')
|
||||||
|
ORDER BY rolname
|
||||||
|
) t
|
||||||
|
$$);
|
||||||
|
|
||||||
set citus.enable_create_database_propagation to on;
|
set citus.enable_create_database_propagation to on;
|
||||||
drop database metadata_sync_2pc_db;
|
drop database metadata_sync_2pc_db;
|
||||||
drop schema metadata_sync_2pc_schema;
|
drop schema metadata_sync_2pc_schema;
|
||||||
|
|
||||||
reset citus.enable_create_database_propagation;
|
reset citus.enable_create_database_propagation;
|
||||||
reset search_path;
|
reset search_path;
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
-- Create a new database
|
||||||
|
set citus.enable_create_database_propagation to on;
|
||||||
|
CREATE DATABASE role_operations_test_db;
|
||||||
|
SET citus.superuser TO 'postgres';
|
||||||
|
-- Connect to the new database
|
||||||
|
\c role_operations_test_db
|
||||||
|
-- Test CREATE ROLE with various options
|
||||||
|
CREATE ROLE test_role1 WITH LOGIN PASSWORD 'password1';
|
||||||
|
|
||||||
|
\c role_operations_test_db - - :worker_1_port
|
||||||
|
CREATE USER "test_role2-needs\!escape"
|
||||||
|
WITH
|
||||||
|
SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN REPLICATION BYPASSRLS CONNECTION
|
||||||
|
LIMIT 10 VALID UNTIL '2023-01-01' IN ROLE test_role1;
|
||||||
|
|
||||||
|
\c regression - - :master_port
|
||||||
|
|
||||||
|
select result FROM run_command_on_all_nodes($$
|
||||||
|
SELECT array_to_json(array_agg(row_to_json(t)))
|
||||||
|
FROM (
|
||||||
|
SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb,
|
||||||
|
rolcanlogin, rolreplication, rolbypassrls, rolconnlimit,
|
||||||
|
(rolpassword != '') as pass_not_empty, DATE(rolvaliduntil)
|
||||||
|
FROM pg_authid
|
||||||
|
WHERE rolname in ('test_role1', 'test_role2-needs\!escape')
|
||||||
|
ORDER BY rolname
|
||||||
|
) t
|
||||||
|
$$);
|
||||||
|
|
||||||
|
select result FROM run_command_on_all_nodes($$
|
||||||
|
SELECT array_to_json(array_agg(row_to_json(t)))
|
||||||
|
FROM (
|
||||||
|
SELECT r.rolname
|
||||||
|
FROM pg_dist_object d
|
||||||
|
JOIN pg_roles r ON d.objid = r.oid
|
||||||
|
WHERE r.rolname IN ('test_role1', 'test_role2-needs\!escape')
|
||||||
|
order by r.rolname
|
||||||
|
) t
|
||||||
|
$$);
|
||||||
|
|
||||||
|
\c role_operations_test_db - - :master_port
|
||||||
|
-- Test ALTER ROLE with various options
|
||||||
|
ALTER ROLE test_role1 WITH PASSWORD 'new_password1';
|
||||||
|
|
||||||
|
\c role_operations_test_db - - :worker_1_port
|
||||||
|
ALTER USER "test_role2-needs\!escape"
|
||||||
|
WITH
|
||||||
|
NOSUPERUSER NOCREATEDB NOCREATEROLE NOINHERIT NOLOGIN NOREPLICATION NOBYPASSRLS CONNECTION
|
||||||
|
LIMIT 5 VALID UNTIL '2024-01-01';
|
||||||
|
|
||||||
|
\c regression - - :master_port
|
||||||
|
select result FROM run_command_on_all_nodes($$
|
||||||
|
SELECT array_to_json(array_agg(row_to_json(t)))
|
||||||
|
FROM (
|
||||||
|
SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb,
|
||||||
|
rolcanlogin, rolreplication, rolbypassrls, rolconnlimit,
|
||||||
|
(rolpassword != '') as pass_not_empty, DATE(rolvaliduntil)
|
||||||
|
FROM pg_authid
|
||||||
|
WHERE rolname in ('test_role1', 'test_role2-needs\!escape')
|
||||||
|
ORDER BY rolname
|
||||||
|
) t
|
||||||
|
$$);
|
||||||
|
|
||||||
|
\c role_operations_test_db - - :master_port
|
||||||
|
-- Test DROP ROLE
|
||||||
|
DROP ROLE no_such_role; -- fails nicely
|
||||||
|
DROP ROLE IF EXISTS no_such_role; -- doesn't fail
|
||||||
|
|
||||||
|
CREATE ROLE new_role;
|
||||||
|
DROP ROLE IF EXISTS no_such_role, new_role; -- doesn't fail
|
||||||
|
DROP ROLE IF EXISTS test_role1, "test_role2-needs\!escape";
|
||||||
|
|
||||||
|
\c regression - - :master_port
|
||||||
|
--verify that roles and dist_object are dropped
|
||||||
|
select result FROM run_command_on_all_nodes($$
|
||||||
|
SELECT array_to_json(array_agg(row_to_json(t)))
|
||||||
|
FROM (
|
||||||
|
SELECT rolname, rolsuper, rolinherit, rolcreaterole, rolcreatedb,
|
||||||
|
rolcanlogin, rolreplication, rolbypassrls, rolconnlimit,
|
||||||
|
(rolpassword != '') as pass_not_empty, DATE(rolvaliduntil)
|
||||||
|
FROM pg_authid
|
||||||
|
WHERE rolname in ('test_role1', 'test_role2-needs\!escape','new_role','no_such_role')
|
||||||
|
ORDER BY rolname
|
||||||
|
) t
|
||||||
|
$$);
|
||||||
|
|
||||||
|
select result FROM run_command_on_all_nodes($$
|
||||||
|
SELECT array_to_json(array_agg(row_to_json(t)))
|
||||||
|
FROM (
|
||||||
|
SELECT r.rolname
|
||||||
|
FROM pg_roles r
|
||||||
|
WHERE r.rolname IN ('test_role1', 'test_role2-needs\!escape','new_role','no_such_role')
|
||||||
|
order by r.rolname
|
||||||
|
) t
|
||||||
|
$$);
|
||||||
|
|
||||||
|
SELECT result FROM run_command_on_all_nodes($$
|
||||||
|
SELECT count(*) leaked_pg_dist_object_records_for_roles
|
||||||
|
FROM pg_dist_object LEFT JOIN pg_authid ON (objid = oid)
|
||||||
|
WHERE classid = 1260 AND oid IS NULL
|
||||||
|
$$);
|
||||||
|
|
||||||
|
-- Clean up: drop the database
|
||||||
|
set citus.enable_create_database_propagation to on;
|
||||||
|
DROP DATABASE role_operations_test_db;
|
||||||
|
reset citus.enable_create_database_propagation;
|
Loading…
Reference in New Issue