mirror of https://github.com/citusdata/citus.git
commit
parent
022b6268bf
commit
9241970e0c
|
@ -45,7 +45,7 @@
|
|||
|
||||
|
||||
/*
|
||||
* DatabaseCollationInfo is used to store collation related information of a database
|
||||
* DatabaseCollationInfo is used to store collation related information of a database.
|
||||
*/
|
||||
typedef struct DatabaseCollationInfo
|
||||
{
|
||||
|
@ -62,7 +62,6 @@ typedef struct DatabaseCollationInfo
|
|||
#endif
|
||||
} DatabaseCollationInfo;
|
||||
|
||||
static void EnsureSupportedCreateDatabaseCommand(CreatedbStmt *stmt);
|
||||
static char * GenerateCreateDatabaseStatementFromPgDatabase(Form_pg_database
|
||||
databaseForm);
|
||||
static DatabaseCollationInfo GetDatabaseCollation(Oid dbOid);
|
||||
|
@ -71,10 +70,10 @@ static AlterOwnerStmt * RecreateAlterDatabaseOwnerStmt(Oid databaseOid);
|
|||
static char * GetLocaleProviderString(char datlocprovider);
|
||||
#endif
|
||||
static char * GetTablespaceName(Oid tablespaceOid);
|
||||
static ObjectAddress * GetDatabaseAddressFromDatabaseName(char *databaseName, bool
|
||||
missingOk);
|
||||
static ObjectAddress * GetDatabaseAddressFromDatabaseName(char *databaseName,
|
||||
bool missingOk);
|
||||
|
||||
static Oid get_database_owner(Oid db_oid);
|
||||
static Oid get_database_owner(Oid dbId);
|
||||
|
||||
|
||||
/* controlled via GUC */
|
||||
|
@ -137,13 +136,13 @@ RecreateAlterDatabaseOwnerStmt(Oid databaseOid)
|
|||
* get_database_owner returns the Oid of the role owning the database
|
||||
*/
|
||||
static Oid
|
||||
get_database_owner(Oid db_oid)
|
||||
get_database_owner(Oid dbId)
|
||||
{
|
||||
HeapTuple tuple = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(db_oid));
|
||||
HeapTuple tuple = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(dbId));
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
{
|
||||
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_DATABASE),
|
||||
errmsg("database with OID %u does not exist", db_oid)));
|
||||
errmsg("database with OID %u does not exist", dbId)));
|
||||
}
|
||||
|
||||
Oid dba = ((Form_pg_database) GETSTRUCT(tuple))->datdba;
|
||||
|
@ -286,61 +285,13 @@ PreprocessAlterDatabaseSetStmt(Node *node, const char *queryString,
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* This function validates the options provided for the CREATE DATABASE command.
|
||||
* It iterates over each option in the stmt->options list and checks if it's supported.
|
||||
* If an unsupported option is found, or if a supported option has an invalid value,
|
||||
* it raises an error.
|
||||
*
|
||||
* Parameters:
|
||||
* stmt: A CreatedbStmt struct representing a CREATE DATABASE command.
|
||||
* The options field is a list of DefElem structs, each representing an option.
|
||||
*
|
||||
* Currently, this function checks for the following:
|
||||
* - The "oid" option is not supported.
|
||||
* - The "template" option is only supported with the value "template1".
|
||||
* - The "strategy" option is only supported with the value "wal_log".
|
||||
*
|
||||
* If any of these checks fail, the function calls ereport to raise an error.
|
||||
*/
|
||||
static void
|
||||
EnsureSupportedCreateDatabaseCommand(CreatedbStmt *stmt)
|
||||
{
|
||||
DefElem *option = NULL;
|
||||
foreach_ptr(option, stmt->options)
|
||||
{
|
||||
if (strcmp(option->defname, "oid") == 0)
|
||||
{
|
||||
ereport(ERROR,
|
||||
errmsg("CREATE DATABASE option \"%s\" is not supported",
|
||||
option->defname));
|
||||
}
|
||||
|
||||
char *optionValue = defGetString(option);
|
||||
|
||||
if (strcmp(option->defname, "template") == 0 && strcmp(optionValue,
|
||||
"template1") != 0)
|
||||
{
|
||||
ereport(ERROR, errmsg("Only template1 is supported as template "
|
||||
"parameter for CREATE DATABASE"));
|
||||
}
|
||||
|
||||
if (strcmp(option->defname, "strategy") == 0 && strcmp(optionValue, "wal_log") !=
|
||||
0)
|
||||
{
|
||||
ereport(ERROR, errmsg("Only wal_log is supported as strategy "
|
||||
"parameter for CREATE DATABASE"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* PostprocessAlterDatabaseStmt is executed before the statement is applied to the local
|
||||
* postgres instance.
|
||||
* Postgres instance.
|
||||
*
|
||||
* In this stage, we can perform validations and prepare the commands that need to
|
||||
* be run on all workers to create the database.
|
||||
* In this stage, we perform validations that we want to ensure before delegating to
|
||||
* previous utility hooks because it might not be convenient to throw an error in an
|
||||
* implicit transaction that creates a database.
|
||||
*/
|
||||
List *
|
||||
PreprocessCreateDatabaseStmt(Node *node, const char *queryString,
|
||||
|
@ -353,7 +304,6 @@ PreprocessCreateDatabaseStmt(Node *node, const char *queryString,
|
|||
|
||||
EnsureCoordinator();
|
||||
|
||||
/*validate the statement*/
|
||||
CreatedbStmt *stmt = castNode(CreatedbStmt, node);
|
||||
EnsureSupportedCreateDatabaseCommand(stmt);
|
||||
|
||||
|
@ -363,7 +313,7 @@ PreprocessCreateDatabaseStmt(Node *node, const char *queryString,
|
|||
|
||||
/*
|
||||
* PostprocessCreateDatabaseStmt is executed after the statement is applied to the local
|
||||
* postgres instance. In this stage we can prepare the commands that need to be run on
|
||||
* postgres instance. In this stage we prepare the commands that need to be run on
|
||||
* all workers to create the database. Since the CREATE DATABASE statement gives error
|
||||
* in a transaction block, we need to use NontransactionalNodeDDLTaskList to send the
|
||||
* CREATE DATABASE statement to the workers.
|
||||
|
@ -379,6 +329,16 @@ PostprocessCreateDatabaseStmt(Node *node, const char *queryString)
|
|||
|
||||
EnsureCoordinator();
|
||||
|
||||
/*
|
||||
* Given that CREATE DATABASE doesn't support "IF NOT EXISTS" and we're
|
||||
* in the post-process, database must exist, hence missingOk = false.
|
||||
*/
|
||||
bool missingOk = false;
|
||||
bool isPostProcess = true;
|
||||
List *addresses = GetObjectAddressListFromParseTree(node, missingOk,
|
||||
isPostProcess);
|
||||
EnsureAllObjectDependenciesExistOnAllNodes(addresses);
|
||||
|
||||
char *createDatabaseCommand = DeparseTreeNode(node);
|
||||
|
||||
List *commands = list_make3(DISABLE_DDL_PROPAGATION,
|
||||
|
@ -435,20 +395,6 @@ PreprocessDropDatabaseStmt(Node *node, const char *queryString,
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* GetDatabaseAddressFromDatabaseName gets the database name and returns the ObjectAddress
|
||||
* of the database.
|
||||
*/
|
||||
static ObjectAddress *
|
||||
GetDatabaseAddressFromDatabaseName(char *databaseName, bool missingOk)
|
||||
{
|
||||
Oid databaseOid = get_database_oid(databaseName, missingOk);
|
||||
ObjectAddress *dbObjectAddress = palloc0(sizeof(ObjectAddress));
|
||||
ObjectAddressSet(*dbObjectAddress, DatabaseRelationId, databaseOid);
|
||||
return dbObjectAddress;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* DropDatabaseStmtObjectAddress gets the ObjectAddress of the database that is the
|
||||
* object of the DropdbStmt.
|
||||
|
@ -477,6 +423,65 @@ CreateDatabaseStmtObjectAddress(Node *node, bool missingOk, bool isPostprocess)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* EnsureSupportedCreateDatabaseCommand validates the options provided for the CREATE
|
||||
* DATABASE command.
|
||||
*
|
||||
* Parameters:
|
||||
* stmt: A CreatedbStmt struct representing a CREATE DATABASE command.
|
||||
* The options field is a list of DefElem structs, each representing an option.
|
||||
*
|
||||
* Currently, this function checks for the following:
|
||||
* - The "oid" option is not supported.
|
||||
* - The "template" option is only supported with the value "template1".
|
||||
* - The "strategy" option is only supported with the value "wal_log".
|
||||
*/
|
||||
void
|
||||
EnsureSupportedCreateDatabaseCommand(CreatedbStmt *stmt)
|
||||
{
|
||||
DefElem *option = NULL;
|
||||
foreach_ptr(option, stmt->options)
|
||||
{
|
||||
if (strcmp(option->defname, "oid") == 0)
|
||||
{
|
||||
ereport(ERROR,
|
||||
errmsg("CREATE DATABASE option \"%s\" is not supported",
|
||||
option->defname));
|
||||
}
|
||||
|
||||
char *optionValue = defGetString(option);
|
||||
|
||||
if (strcmp(option->defname, "template") == 0 &&
|
||||
strcmp(optionValue, "template1") != 0)
|
||||
{
|
||||
ereport(ERROR, errmsg("Only template1 is supported as template "
|
||||
"parameter for CREATE DATABASE"));
|
||||
}
|
||||
|
||||
if (strcmp(option->defname, "strategy") == 0 &&
|
||||
strcmp(optionValue, "wal_log") != 0)
|
||||
{
|
||||
ereport(ERROR, errmsg("Only wal_log is supported as strategy "
|
||||
"parameter for CREATE DATABASE"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* GetDatabaseAddressFromDatabaseName gets the database name and returns the ObjectAddress
|
||||
* of the database.
|
||||
*/
|
||||
static ObjectAddress *
|
||||
GetDatabaseAddressFromDatabaseName(char *databaseName, bool missingOk)
|
||||
{
|
||||
Oid databaseOid = get_database_oid(databaseName, missingOk);
|
||||
ObjectAddress *dbObjectAddress = palloc0(sizeof(ObjectAddress));
|
||||
ObjectAddressSet(*dbObjectAddress, DatabaseRelationId, databaseOid);
|
||||
return dbObjectAddress;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* GetTablespaceName gets the tablespace oid and returns the tablespace name.
|
||||
*/
|
||||
|
@ -664,73 +669,24 @@ GenerateCreateDatabaseStatementFromPgDatabase(Form_pg_database databaseForm)
|
|||
|
||||
|
||||
/*
|
||||
* GrantOnDatabaseDDLCommands returns a list of sql statements to idempotently apply a
|
||||
* GRANT on distributed databases.
|
||||
*/
|
||||
List *
|
||||
GenerateGrantDatabaseCommandList(void)
|
||||
{
|
||||
List *grantCommands = NIL;
|
||||
|
||||
Relation pgDatabaseRel = table_open(DatabaseRelationId, AccessShareLock);
|
||||
TableScanDesc scan = table_beginscan_catalog(pgDatabaseRel, 0, NULL);
|
||||
|
||||
HeapTuple tuple = NULL;
|
||||
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
|
||||
{
|
||||
Form_pg_database databaseForm = (Form_pg_database) GETSTRUCT(tuple);
|
||||
|
||||
ObjectAddress *dbAddress = GetDatabaseAddressFromDatabaseName(
|
||||
NameStr(databaseForm->datname), false);
|
||||
|
||||
/* skip databases that are not distributed */
|
||||
if (!IsAnyObjectDistributed(list_make1(dbAddress)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
List *dbGrants = GrantOnDatabaseDDLCommands(databaseForm->oid);
|
||||
|
||||
/* append dbGrants into grantCommands*/
|
||||
grantCommands = list_concat(grantCommands, dbGrants);
|
||||
}
|
||||
|
||||
heap_endscan(scan);
|
||||
table_close(pgDatabaseRel, AccessShareLock);
|
||||
|
||||
return grantCommands;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* GenerateCreateDatabaseCommandList returns a list of CREATE DATABASE statements
|
||||
* for all the databases.
|
||||
* CreateDatabaseDDLCommand returns a CREATE DATABASE command to create given
|
||||
* database
|
||||
*
|
||||
* Commands in the list are wrapped by citus_internal_database_command() UDF
|
||||
* to avoid from transaction block restrictions that apply to database commands
|
||||
* Command is wrapped by citus_internal_database_command() UDF
|
||||
* to avoid from transaction block restrictions that apply to database commands.
|
||||
*/
|
||||
List *
|
||||
GenerateCreateDatabaseCommandList(void)
|
||||
char *
|
||||
CreateDatabaseDDLCommand(Oid dbId)
|
||||
{
|
||||
List *commands = NIL;
|
||||
|
||||
Relation pgDatabaseRel = table_open(DatabaseRelationId, AccessShareLock);
|
||||
TableScanDesc scan = table_beginscan_catalog(pgDatabaseRel, 0, NULL);
|
||||
|
||||
HeapTuple tuple = NULL;
|
||||
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
|
||||
HeapTuple tuple = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(dbId));
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
{
|
||||
Form_pg_database databaseForm = (Form_pg_database) GETSTRUCT(tuple);
|
||||
|
||||
ObjectAddress *dbAddress = GetDatabaseAddressFromDatabaseName(
|
||||
NameStr(databaseForm->datname), false);
|
||||
|
||||
/* skip databases that are not distributed */
|
||||
if (!IsAnyObjectDistributed(list_make1(dbAddress)))
|
||||
{
|
||||
continue;
|
||||
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_DATABASE),
|
||||
errmsg("database with OID %u does not exist", dbId)));
|
||||
}
|
||||
|
||||
Form_pg_database databaseForm = (Form_pg_database) GETSTRUCT(tuple);
|
||||
|
||||
char *createStmt = GenerateCreateDatabaseStatementFromPgDatabase(databaseForm);
|
||||
|
||||
StringInfo outerDbStmt = makeStringInfo();
|
||||
|
@ -738,15 +694,9 @@ GenerateCreateDatabaseCommandList(void)
|
|||
/* Generate the CREATE DATABASE statement */
|
||||
appendStringInfo(outerDbStmt,
|
||||
"SELECT pg_catalog.citus_internal_database_command(%s)",
|
||||
quote_literal_cstr(
|
||||
createStmt));
|
||||
quote_literal_cstr(createStmt));
|
||||
|
||||
/* Add the statement to the list of commands */
|
||||
commands = lappend(commands, outerDbStmt->data);
|
||||
}
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
heap_endscan(scan);
|
||||
table_close(pgDatabaseRel, AccessShareLock);
|
||||
|
||||
return commands;
|
||||
return outerDbStmt->data;
|
||||
}
|
||||
|
|
|
@ -457,16 +457,37 @@ GetDependencyCreateDDLCommands(const ObjectAddress *dependency)
|
|||
|
||||
case OCLASS_DATABASE:
|
||||
{
|
||||
List *databaseDDLCommands = NIL;
|
||||
|
||||
/* only propagate the ownership of the database when the feature is on */
|
||||
if (EnableAlterDatabaseOwner)
|
||||
/*
|
||||
* For the database where Citus is installed, only propagate the ownership of the
|
||||
* database, only when the feature is on.
|
||||
*
|
||||
* This is because this database must exist on all nodes already so we shouldn't
|
||||
* need to "CREATE" it on other nodes. However, we still need to correctly reflect
|
||||
* its owner on other nodes too.
|
||||
*/
|
||||
if (dependency->objectId == MyDatabaseId && EnableAlterDatabaseOwner)
|
||||
{
|
||||
List *ownerDDLCommands = DatabaseOwnerDDLCommands(dependency);
|
||||
databaseDDLCommands = list_concat(databaseDDLCommands, ownerDDLCommands);
|
||||
return DatabaseOwnerDDLCommands(dependency);
|
||||
}
|
||||
|
||||
return databaseDDLCommands;
|
||||
/*
|
||||
* For the other databases, create the database on all nodes, only when the feature
|
||||
* is on.
|
||||
*/
|
||||
if (dependency->objectId != MyDatabaseId && EnableCreateDatabasePropagation)
|
||||
{
|
||||
char *databaseDDLCommand = CreateDatabaseDDLCommand(dependency->objectId);
|
||||
|
||||
List *ddlCommands = list_make1(databaseDDLCommand);
|
||||
|
||||
List *grantDDLCommands = GrantOnDatabaseDDLCommands(dependency->objectId);
|
||||
|
||||
ddlCommands = list_concat(ddlCommands, grantDDLCommands);
|
||||
|
||||
return ddlCommands;
|
||||
}
|
||||
|
||||
return NIL;
|
||||
}
|
||||
|
||||
case OCLASS_PROC:
|
||||
|
|
|
@ -1347,7 +1347,7 @@ CreateRoleStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess)
|
|||
* with the role the role needs to be visible on all connections used by the transaction,
|
||||
* meaning we can only use 1 connection per node.
|
||||
*/
|
||||
void
|
||||
static void
|
||||
EnsureSequentialModeForRoleDDL(void)
|
||||
{
|
||||
if (!IsTransactionBlock())
|
||||
|
|
|
@ -70,15 +70,6 @@ DefElemOptionToStatement(StringInfo buf, DefElem *option,
|
|||
break;
|
||||
}
|
||||
|
||||
#if PG_VERSION_NUM >= PG_VERSION_15
|
||||
case OPTION_FORMAT_OBJECT_ID:
|
||||
{
|
||||
Oid value = defGetObjectId(option);
|
||||
appendStringInfo(buf, optionFormats[i].format, value);
|
||||
break;
|
||||
}
|
||||
|
||||
#endif
|
||||
case OPTION_FORMAT_LITERAL_CSTR:
|
||||
{
|
||||
char *value = defGetString(option);
|
||||
|
|
|
@ -13,17 +13,18 @@
|
|||
|
||||
#include "pg_version_compat.h"
|
||||
#include "catalog/namespace.h"
|
||||
#include "commands/defrem.h"
|
||||
#include "lib/stringinfo.h"
|
||||
#include "nodes/parsenodes.h"
|
||||
#include "parser/parse_type.h"
|
||||
#include "utils/builtins.h"
|
||||
|
||||
#include "commands/defrem.h"
|
||||
#include "distributed/deparser.h"
|
||||
#include "distributed/commands.h"
|
||||
#include "distributed/citus_ruleutils.h"
|
||||
#include "distributed/deparser.h"
|
||||
#include "distributed/listutils.h"
|
||||
#include "distributed/log_utils.h"
|
||||
#include "parser/parse_type.h"
|
||||
|
||||
|
||||
static void AppendAlterDatabaseOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt);
|
||||
|
@ -49,26 +50,9 @@ const DefElemOptionFormat create_database_option_formats[] = {
|
|||
{ "tablespace", " TABLESPACE %s", OPTION_FORMAT_STRING },
|
||||
{ "allow_connections", " ALLOW_CONNECTIONS %s", OPTION_FORMAT_BOOLEAN },
|
||||
{ "connection_limit", " CONNECTION LIMIT %d", OPTION_FORMAT_INTEGER },
|
||||
{ "is_template", " IS_TEMPLATE %s", OPTION_FORMAT_BOOLEAN },
|
||||
{ "oid", " OID %d", OPTION_FORMAT_OBJECT_ID }
|
||||
{ "is_template", " IS_TEMPLATE %s", OPTION_FORMAT_BOOLEAN }
|
||||
};
|
||||
|
||||
/*
|
||||
* DeparseAlterDatabaseOwnerStmt
|
||||
* Deparse an AlterDatabaseOwnerStmt node
|
||||
*
|
||||
* This function is responsible for producing a string representation of an
|
||||
* AlterDatabaseOwnerStmt node, which represents an ALTER DATABASE statement
|
||||
* that changes the owner of a database. The output string includes the ALTER
|
||||
* DATABASE keyword, the name of the database being altered, and the new owner
|
||||
* of the database.
|
||||
*
|
||||
* Parameters:
|
||||
* - node: a pointer to the AlterDatabaseOwnerStmt node to be deparsed
|
||||
*
|
||||
* Returns:
|
||||
* - a string representation of the ALTER DATABASE statement
|
||||
*/
|
||||
char *
|
||||
DeparseAlterDatabaseOwnerStmt(Node *node)
|
||||
{
|
||||
|
@ -84,15 +68,6 @@ DeparseAlterDatabaseOwnerStmt(Node *node)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* AppendAlterDatabaseOwnerStmt
|
||||
* Append an ALTER DATABASE statement for changing the owner of a database to the given StringInfo buffer.
|
||||
*
|
||||
* Parameters:
|
||||
* - buf: The StringInfo buffer to append the statement to.
|
||||
* - stmt: The AlterOwnerStmt representing the ALTER DATABASE statement to append.
|
||||
*/
|
||||
static void
|
||||
AppendAlterDatabaseOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt)
|
||||
{
|
||||
|
@ -258,26 +233,25 @@ DeparseAlterDatabaseSetStmt(Node *node)
|
|||
static void
|
||||
AppendCreateDatabaseStmt(StringInfo buf, CreatedbStmt *stmt)
|
||||
{
|
||||
/*
|
||||
* Make sure that we don't try to deparse something that this
|
||||
* function doesn't expect.
|
||||
*/
|
||||
EnsureSupportedCreateDatabaseCommand(stmt);
|
||||
|
||||
appendStringInfo(buf,
|
||||
"CREATE DATABASE %s",
|
||||
quote_identifier(stmt->dbname));
|
||||
|
||||
DefElem *option = NULL;
|
||||
|
||||
foreach_ptr(option, stmt->options)
|
||||
{
|
||||
/*ValidateCreateDatabaseOptions(option); */
|
||||
|
||||
DefElemOptionToStatement(buf, option, create_database_option_formats,
|
||||
lengthof(create_database_option_formats));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Converts a CreatedbStmt structure into a SQL command string.
|
||||
* Used in the deparsing of Create database statement.
|
||||
*/
|
||||
char *
|
||||
DeparseCreateDatabaseStmt(Node *node)
|
||||
{
|
||||
|
@ -300,20 +274,15 @@ AppendDropDatabaseStmt(StringInfo buf, DropdbStmt *stmt)
|
|||
ifExistsStatement,
|
||||
quote_identifier(stmt->dbname));
|
||||
|
||||
DefElem *option = NULL;
|
||||
|
||||
|
||||
foreach_ptr(option, stmt->options)
|
||||
if (list_length(stmt->options) > 1)
|
||||
{
|
||||
/* if it is the first option then append with "WITH" else append with "," */
|
||||
if (option == linitial(stmt->options))
|
||||
/* FORCE is the only option that can be provided for this command */
|
||||
elog(ERROR, "got unexpected number of options for DROP DATABASE");
|
||||
}
|
||||
else if (list_length(stmt->options) == 1)
|
||||
{
|
||||
DefElem *option = linitial(stmt->options);
|
||||
appendStringInfo(buf, " WITH ( ");
|
||||
}
|
||||
else
|
||||
{
|
||||
appendStringInfo(buf, ", ");
|
||||
}
|
||||
|
||||
if (strcmp(option->defname, "force") == 0)
|
||||
{
|
||||
|
@ -321,24 +290,17 @@ AppendDropDatabaseStmt(StringInfo buf, DropdbStmt *stmt)
|
|||
}
|
||||
else
|
||||
{
|
||||
/* FORCE is the only option that can be provided for this command */
|
||||
ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("unrecognized DROP DATABASE option \"%s\"",
|
||||
option->defname)));
|
||||
}
|
||||
|
||||
/* if it is the last option then append with ")" */
|
||||
if (option == llast(stmt->options))
|
||||
{
|
||||
appendStringInfo(buf, " )");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Converts a DropdbStmt structure into a SQL command string.
|
||||
* Used in the deparsing of drop database statement.
|
||||
*/
|
||||
char *
|
||||
DeparseDropDatabaseStmt(Node *node)
|
||||
{
|
||||
|
|
|
@ -698,7 +698,6 @@ SupportedDependencyByCitus(const ObjectAddress *address)
|
|||
|
||||
case OCLASS_DATABASE:
|
||||
{
|
||||
/* only to propagate its owner */
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -155,7 +155,6 @@ static char * RemoteSchemaIdExpressionByName(char *schemaName);
|
|||
static char * RemoteTypeIdExpression(Oid typeId);
|
||||
static char * RemoteCollationIdExpression(Oid colocationId);
|
||||
static char * RemoteTableIdExpression(Oid relationId);
|
||||
static void SendDatabaseGrantSyncCommands(MetadataSyncContext *context);
|
||||
|
||||
|
||||
PG_FUNCTION_INFO_V1(start_metadata_sync_to_all_nodes);
|
||||
|
@ -2049,6 +2048,10 @@ GenerateGrantOnSchemaQueriesFromAclItem(Oid schemaOid, AclItem *aclItem)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* GrantOnDatabaseDDLCommands creates a list of ddl command for replicating the permissions
|
||||
* of roles on databases.
|
||||
*/
|
||||
List *
|
||||
GrantOnDatabaseDDLCommands(Oid databaseOid)
|
||||
{
|
||||
|
@ -2079,6 +2082,10 @@ GrantOnDatabaseDDLCommands(Oid databaseOid)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* GenerateGrantOnDatabaseFromAclItem generates a query string for replicating a users permissions
|
||||
* on a database.
|
||||
*/
|
||||
List *
|
||||
GenerateGrantOnDatabaseFromAclItem(Oid databaseOid, AclItem *aclItem)
|
||||
{
|
||||
|
@ -4658,13 +4665,6 @@ PropagateNodeWideObjectsCommandList(void)
|
|||
ddlCommands = list_concat(ddlCommands, alterRoleSetCommands);
|
||||
}
|
||||
|
||||
if (EnableCreateDatabasePropagation)
|
||||
{
|
||||
/* get commands for database creation */
|
||||
List *createDatabaseCommands = GenerateCreateDatabaseCommandList();
|
||||
ddlCommands = list_concat(ddlCommands, createDatabaseCommands);
|
||||
}
|
||||
|
||||
return ddlCommands;
|
||||
}
|
||||
|
||||
|
@ -4736,13 +4736,6 @@ SyncDistributedObjects(MetadataSyncContext *context)
|
|||
* those tables.
|
||||
*/
|
||||
SendInterTableRelationshipCommands(context);
|
||||
|
||||
/*
|
||||
* After creation of databases and roles, send the grant database commands
|
||||
* to the workers.
|
||||
*/
|
||||
|
||||
SendDatabaseGrantSyncCommands(context);
|
||||
}
|
||||
|
||||
|
||||
|
@ -4768,34 +4761,6 @@ SendNodeWideObjectsSyncCommands(MetadataSyncContext *context)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* SendDatabaseGrantSyncCommands sends database grants to roles to workers with
|
||||
* transactional or nontransactional mode according to transactionMode inside
|
||||
* metadataSyncContext in case of EnableCreateDatabasePropagation GUC set.
|
||||
* This function is called after SendNodeWideObjectsSyncCommands and SendDependencyCreationCommands
|
||||
* because we need both databases and roles to be created on the worker.
|
||||
*
|
||||
*/
|
||||
static void
|
||||
SendDatabaseGrantSyncCommands(MetadataSyncContext *context)
|
||||
{
|
||||
if (EnableCreateDatabasePropagation)
|
||||
{
|
||||
/* propagate node wide objects. It includes only roles for now. */
|
||||
List *commandList = GenerateGrantDatabaseCommandList();
|
||||
|
||||
if (commandList == NIL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
commandList = lcons(DISABLE_DDL_PROPAGATION, commandList);
|
||||
commandList = lappend(commandList, ENABLE_DDL_PROPAGATION);
|
||||
SendOrCollectCommandListToActivatedNodes(context, commandList);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* SendShellTableDeletionCommands sends sequence, and shell table deletion
|
||||
* commands to workers with transactional or nontransactional mode according to
|
||||
|
|
|
@ -243,8 +243,8 @@ extern List * DropDatabaseStmtObjectAddress(Node *node, bool missingOk,
|
|||
bool isPostprocess);
|
||||
extern List * CreateDatabaseStmtObjectAddress(Node *node, bool missingOk,
|
||||
bool isPostprocess);
|
||||
extern List * GenerateCreateDatabaseCommandList(void);
|
||||
extern List * GenerateGrantDatabaseCommandList(void);
|
||||
extern void EnsureSupportedCreateDatabaseCommand(CreatedbStmt *stmt);
|
||||
extern char * CreateDatabaseDDLCommand(Oid dbId);
|
||||
|
||||
|
||||
/* domain.c - forward declarations */
|
||||
|
|
|
@ -135,8 +135,7 @@ typedef enum OptionFormatType
|
|||
OPTION_FORMAT_STRING,
|
||||
OPTION_FORMAT_LITERAL_CSTR,
|
||||
OPTION_FORMAT_BOOLEAN,
|
||||
OPTION_FORMAT_INTEGER,
|
||||
OPTION_FORMAT_OBJECT_ID
|
||||
OPTION_FORMAT_INTEGER
|
||||
} OptionFormatType;
|
||||
|
||||
|
||||
|
|
|
@ -910,6 +910,40 @@ SELECT result from run_command_on_all_nodes(
|
|||
|
||||
DROP ROLE db_role_grants_test_role_exists_on_node_2;
|
||||
DROP ROLE db_role_grants_test_role_missing_on_node_2;
|
||||
select 1 from citus_remove_node('localhost', :worker_2_port);
|
||||
?column?
|
||||
---------------------------------------------------------------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
set citus.enable_create_role_propagation TO off;
|
||||
create role non_propagated_role;
|
||||
NOTICE: not propagating CREATE ROLE/USER commands to other nodes
|
||||
HINT: Connect to other nodes directly to manually create all necessary users and roles.
|
||||
set citus.enable_create_role_propagation TO on;
|
||||
set citus.enable_create_database_propagation TO on;
|
||||
-- Make sure that we propagate non_propagated_role because it's a dependency of test_db.
|
||||
-- And hence it becomes a distributed object.
|
||||
create database test_db OWNER non_propagated_role;
|
||||
create role propagated_role;
|
||||
grant connect on database test_db to propagated_role;
|
||||
SELECT 1 FROM citus_add_node('localhost', :worker_2_port);
|
||||
?column?
|
||||
---------------------------------------------------------------------
|
||||
1
|
||||
(1 row)
|
||||
|
||||
SELECT * FROM public.check_database_on_all_nodes('test_db') ORDER BY node_type;
|
||||
node_type | result
|
||||
---------------------------------------------------------------------
|
||||
coordinator (local) | {"database_properties": {"datacl": ["=Tc/non_propagated_role", "non_propagated_role=CTc/non_propagated_role", "propagated_role=c/non_propagated_role"], "datname": "test_db", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "non_propagated_role", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false}
|
||||
worker node (remote) | {"database_properties": {"datacl": ["=Tc/non_propagated_role", "non_propagated_role=CTc/non_propagated_role", "propagated_role=c/non_propagated_role"], "datname": "test_db", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "non_propagated_role", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false}
|
||||
worker node (remote) | {"database_properties": {"datacl": ["=Tc/non_propagated_role", "non_propagated_role=CTc/non_propagated_role", "propagated_role=c/non_propagated_role"], "datname": "test_db", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "non_propagated_role", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false}
|
||||
(3 rows)
|
||||
|
||||
REVOKE CONNECT ON DATABASE test_db FROM propagated_role;
|
||||
DROP DATABASE test_db;
|
||||
DROP ROLE propagated_role, non_propagated_role;
|
||||
--clean up resources created by this test
|
||||
-- DROP TABLESPACE is not supported, so we need to drop it manually.
|
||||
SELECT result FROM run_command_on_all_nodes(
|
||||
|
|
|
@ -38,7 +38,6 @@ test: create_single_shard_table
|
|||
test: create_drop_database_propagation
|
||||
test: create_drop_database_propagation_pg15
|
||||
test: create_drop_database_propagation_pg16
|
||||
|
||||
# don't parallelize single_shard_table_udfs to make sure colocation ids are sequential
|
||||
test: single_shard_table_udfs
|
||||
test: schema_based_sharding
|
||||
|
|
|
@ -296,8 +296,6 @@ drop database distributed_db;
|
|||
set citus.enable_create_database_propagation TO off;
|
||||
drop database non_distributed_db;
|
||||
|
||||
|
||||
|
||||
-- test role grants on DATABASE in metadata sync
|
||||
|
||||
SELECT result from run_command_on_all_nodes(
|
||||
|
@ -314,11 +312,8 @@ SELECT result from run_command_on_all_nodes(
|
|||
|
||||
SET citus.enable_create_database_propagation TO on;
|
||||
|
||||
|
||||
|
||||
CREATE ROLE db_role_grants_test_role_exists_on_node_2;
|
||||
|
||||
|
||||
select 1 from citus_remove_node('localhost', :worker_2_port);
|
||||
|
||||
CREATE DATABASE db_role_grants_test;
|
||||
|
@ -332,15 +327,11 @@ CREATE ROLE db_role_grants_test_role_missing_on_node_2;
|
|||
RESET citus.log_remote_commands ;
|
||||
RESET citus.grep_remote_commands;
|
||||
|
||||
|
||||
|
||||
SET citus.log_remote_commands = true;
|
||||
set citus.grep_remote_commands = '%GRANT%';
|
||||
grant CONNECT,TEMPORARY,CREATE on DATABASE db_role_grants_test to db_role_grants_test_role_exists_on_node_2;
|
||||
grant CONNECT,TEMPORARY,CREATE on DATABASE db_role_grants_test to db_role_grants_test_role_missing_on_node_2;
|
||||
|
||||
|
||||
|
||||
grant CONNECT,TEMPORARY,CREATE on DATABASE db_role_grants_test_non_distributed to db_role_grants_test_role_exists_on_node_2;
|
||||
grant CONNECT,TEMPORARY,CREATE on DATABASE db_role_grants_test_non_distributed to db_role_grants_test_role_missing_on_node_2;
|
||||
|
||||
|
@ -374,7 +365,6 @@ SELECT result from run_command_on_all_nodes(
|
|||
$$
|
||||
) ORDER BY result;
|
||||
|
||||
|
||||
SELECT result from run_command_on_all_nodes(
|
||||
$$
|
||||
select has_database_privilege('db_role_grants_test_role_missing_on_node_2','db_role_grants_test', 'TEMPORARY')
|
||||
|
@ -416,7 +406,6 @@ SELECT result from run_command_on_all_nodes(
|
|||
$$
|
||||
) ORDER BY result;
|
||||
|
||||
|
||||
SELECT result from run_command_on_all_nodes(
|
||||
$$
|
||||
select has_database_privilege('db_role_grants_test_role_missing_on_node_2','db_role_grants_test_non_distributed', 'TEMPORARY')
|
||||
|
@ -429,13 +418,11 @@ SELECT result from run_command_on_all_nodes(
|
|||
$$
|
||||
) ORDER BY result;
|
||||
|
||||
|
||||
RESET citus.log_remote_commands;
|
||||
RESET citus.grep_remote_commands;
|
||||
|
||||
select 1 from citus_add_node('localhost', :worker_2_port);
|
||||
|
||||
|
||||
-- check the privileges after add_node for database db_role_grants_test,
|
||||
-- role db_role_grants_test_role_exists_on_node_2
|
||||
|
||||
|
@ -466,7 +453,6 @@ SELECT result from run_command_on_all_nodes(
|
|||
$$
|
||||
) ORDER BY result;
|
||||
|
||||
|
||||
SELECT result from run_command_on_all_nodes(
|
||||
$$
|
||||
select has_database_privilege('db_role_grants_test_role_missing_on_node_2','db_role_grants_test', 'TEMPORARY')
|
||||
|
@ -508,7 +494,6 @@ SELECT result from run_command_on_all_nodes(
|
|||
$$
|
||||
) ORDER BY result;
|
||||
|
||||
|
||||
SELECT result from run_command_on_all_nodes(
|
||||
$$
|
||||
select has_database_privilege('db_role_grants_test_role_missing_on_node_2','db_role_grants_test_non_distributed', 'TEMPORARY')
|
||||
|
@ -533,8 +518,28 @@ SELECT result from run_command_on_all_nodes(
|
|||
DROP ROLE db_role_grants_test_role_exists_on_node_2;
|
||||
DROP ROLE db_role_grants_test_role_missing_on_node_2;
|
||||
|
||||
select 1 from citus_remove_node('localhost', :worker_2_port);
|
||||
|
||||
set citus.enable_create_role_propagation TO off;
|
||||
create role non_propagated_role;
|
||||
set citus.enable_create_role_propagation TO on;
|
||||
|
||||
set citus.enable_create_database_propagation TO on;
|
||||
|
||||
-- Make sure that we propagate non_propagated_role because it's a dependency of test_db.
|
||||
-- And hence it becomes a distributed object.
|
||||
create database test_db OWNER non_propagated_role;
|
||||
|
||||
create role propagated_role;
|
||||
grant connect on database test_db to propagated_role;
|
||||
|
||||
SELECT 1 FROM citus_add_node('localhost', :worker_2_port);
|
||||
|
||||
SELECT * FROM public.check_database_on_all_nodes('test_db') ORDER BY node_type;
|
||||
|
||||
REVOKE CONNECT ON DATABASE test_db FROM propagated_role;
|
||||
DROP DATABASE test_db;
|
||||
DROP ROLE propagated_role, non_propagated_role;
|
||||
|
||||
--clean up resources created by this test
|
||||
|
||||
|
|
Loading…
Reference in New Issue