diff --git a/src/backend/distributed/commands/database.c b/src/backend/distributed/commands/database.c index 78061aa63..71a726b91 100644 --- a/src/backend/distributed/commands/database.c +++ b/src/backend/distributed/commands/database.c @@ -212,3 +212,32 @@ PreprocessAlterDatabaseRefreshCollStmt(Node *node, const char *queryString, #endif + +/* + * PreprocessAlterDatabaseSetStmt is executed before the statement is applied to the local + * postgres instance. + * + * In this stage we can prepare the commands that need to be run on all workers to grant + * on databases. + */ +List * +PreprocessAlterDatabaseSetStmt(Node *node, const char *queryString, + ProcessUtilityContext processUtilityContext) +{ + if (!ShouldPropagate()) + { + return NIL; + } + + AlterDatabaseSetStmt *stmt = castNode(AlterDatabaseSetStmt, node); + + EnsureCoordinator(); + + char *sql = DeparseTreeNode((Node *) stmt); + + List *commands = list_make3(DISABLE_DDL_PROPAGATION, + (void *) sql, + ENABLE_DDL_PROPAGATION); + + return NodeDDLTaskList(NON_COORDINATOR_NODES, commands); +} diff --git a/src/backend/distributed/commands/distribute_object_ops.c b/src/backend/distributed/commands/distribute_object_ops.c index e31fda7b0..be34de2c2 100644 --- a/src/backend/distributed/commands/distribute_object_ops.c +++ b/src/backend/distributed/commands/distribute_object_ops.c @@ -468,6 +468,17 @@ static DistributeObjectOps Database_RefreshColl = { }; #endif +static DistributeObjectOps Database_Set = { + .deparse = DeparseAlterDatabaseSetStmt, + .qualify = NULL, + .preprocess = PreprocessAlterDatabaseSetStmt, + .postprocess = NULL, + .objectType = OBJECT_DATABASE, + .operationType = DIST_OPS_ALTER, + .address = NULL, + .markDistributed = false, +}; + static DistributeObjectOps Domain_Alter = { .deparse = DeparseAlterDomainStmt, .qualify = QualifyAlterDomainStmt, @@ -1318,6 +1329,12 @@ GetDistributeObjectOps(Node *node) } #endif + + case T_AlterDatabaseSetStmt: + { + return &Database_Set; + } + case T_AlterDomainStmt: { return &Domain_Alter; diff --git a/src/backend/distributed/deparser/citus_setutils.c b/src/backend/distributed/deparser/citus_setutils.c new file mode 100644 index 000000000..d384c9a5e --- /dev/null +++ b/src/backend/distributed/deparser/citus_setutils.c @@ -0,0 +1,172 @@ +#include "postgres.h" + +#include "pg_version_compat.h" + +#include "catalog/namespace.h" +#include "lib/stringinfo.h" +#include "nodes/parsenodes.h" +#include "utils/builtins.h" + +#include "distributed/deparser.h" +#include "distributed/citus_ruleutils.h" +#include "commands/defrem.h" +#include "distributed/log_utils.h" +#include "parser/parse_type.h" + + +void AppendVarSetValue(StringInfo buf, VariableSetStmt *setStmt); + +/* + * AppendVarSetValueDb deparses a VariableSetStmt with VAR_SET_VALUE kind. + * It takes from flatten_set_variable_args in postgres's utils/misc/guc.c, + * however flatten_set_variable_args does not apply correct quoting. + */ +void +AppendVarSetValue(StringInfo buf, VariableSetStmt *setStmt) +{ + ListCell *varArgCell = NULL; + ListCell *firstCell = list_head(setStmt->args); + + Assert(setStmt->kind == VAR_SET_VALUE); + + foreach (varArgCell, setStmt->args) + { + Node *varArgNode = lfirst(varArgCell); + A_Const *varArgConst = NULL; + TypeName *typeName = NULL; + + if (IsA(varArgNode, A_Const)) + { + varArgConst = (A_Const *)varArgNode; + } + else if (IsA(varArgNode, TypeCast)) + { + TypeCast *varArgTypeCast = (TypeCast *)varArgNode; + + varArgConst = castNode(A_Const, varArgTypeCast->arg); + typeName = varArgTypeCast->typeName; + } + else + { + elog(ERROR, "unrecognized node type: %d", varArgNode->type); + } + + /* don't know how to start SET until we inspect first arg */ + if (varArgCell != firstCell) + { + appendStringInfoChar(buf, ','); + } + else if (typeName != NULL) + { + appendStringInfoString(buf, " SET TIME ZONE"); + } + else + { + appendStringInfo(buf, " SET %s =", quote_identifier(setStmt->name)); + } + + Node *value = (Node *)&varArgConst->val; + switch (value->type) + { + case T_Integer: + { + appendStringInfo(buf, " %d", intVal(value)); + break; + } + + case T_Float: + { + appendStringInfo(buf, " %s", strVal(value)); + break; + } + + case T_String: + { + if (typeName != NULL) + { + /* + * Must be a ConstInterval argument for TIME ZONE. Coerce + * to interval and back to normalize the value and account + * for any typmod. + */ + Oid typoid = InvalidOid; + int32 typmod = -1; + + typenameTypeIdAndMod(NULL, typeName, &typoid, &typmod); + Assert(typoid == INTERVALOID); + + Datum interval = + DirectFunctionCall3(interval_in, + CStringGetDatum(strVal(value)), + ObjectIdGetDatum(InvalidOid), + Int32GetDatum(typmod)); + + char *intervalout = + DatumGetCString(DirectFunctionCall1(interval_out, + interval)); + appendStringInfo(buf, " INTERVAL '%s'", intervalout); + } + else + { + appendStringInfo(buf, " %s", quote_literal_cstr(strVal(value))); + } + break; + } + + default: + { + elog(ERROR, "Unexpected Value type in VAR_SET_VALUE arguments."); + break; + } + } + } +} + + +/* + * AppendVariableSetDb appends a string representing the VariableSetStmt to a buffer + */ +void AppendVariableSet(StringInfo buf, VariableSetStmt *setStmt) +{ + switch (setStmt->kind) + { + case VAR_SET_VALUE: + { + AppendVarSetValue(buf, setStmt); + break; + } + + case VAR_SET_CURRENT: + { + appendStringInfo(buf, " SET %s FROM CURRENT", quote_identifier(setStmt->name)); + break; + } + + case VAR_SET_DEFAULT: + { + appendStringInfo(buf, " SET %s TO DEFAULT", quote_identifier(setStmt->name)); + break; + } + + case VAR_RESET: + { + appendStringInfo(buf, " RESET %s", quote_identifier(setStmt->name)); + break; + } + + case VAR_RESET_ALL: + { + appendStringInfoString(buf, " RESET ALL"); + break; + } + + /* VAR_SET_MULTI is a special case for SET TRANSACTION that should not occur here */ + case VAR_SET_MULTI: + default: + { + ereport(ERROR, (errmsg("Unable to deparse SET statement"))); + break; + } + } +} + diff --git a/src/backend/distributed/deparser/deparse_database_stmts.c b/src/backend/distributed/deparser/deparse_database_stmts.c index 0cca2088e..697e7c09c 100644 --- a/src/backend/distributed/deparser/deparse_database_stmts.c +++ b/src/backend/distributed/deparser/deparse_database_stmts.c @@ -23,15 +23,19 @@ #include "commands/defrem.h" #include "distributed/deparser.h" #include "distributed/log_utils.h" +#include "parser/parse_type.h" static void AppendAlterDatabaseOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt); static void AppendAlterDatabaseStmt(StringInfo buf, AlterDatabaseStmt *stmt); +static void AppendVarSetValueDb(StringInfo buf, VariableSetStmt *setStmt); + +void AppendVariableSetDb(StringInfo buf, VariableSetStmt *setStmt); char * DeparseAlterDatabaseOwnerStmt(Node *node) { AlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node); - StringInfoData str = { 0 }; + StringInfoData str = {0}; initStringInfo(&str); Assert(stmt->objectType == OBJECT_DATABASE); @@ -41,7 +45,6 @@ DeparseAlterDatabaseOwnerStmt(Node *node) return str.data; } - static void AppendAlterDatabaseOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt) { @@ -49,18 +52,17 @@ AppendAlterDatabaseOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt) appendStringInfo(buf, "ALTER DATABASE %s OWNER TO %s;", - quote_identifier(strVal((String *) stmt->object)), + quote_identifier(strVal((String *)stmt->object)), RoleSpecString(stmt->newowner, true)); } - static void AppendGrantDatabases(StringInfo buf, GrantStmt *stmt) { ListCell *cell = NULL; appendStringInfo(buf, " ON DATABASE "); - foreach(cell, stmt->objects) + foreach (cell, stmt->objects) { char *database = strVal(lfirst(cell)); appendStringInfoString(buf, quote_identifier(database)); @@ -71,7 +73,6 @@ AppendGrantDatabases(StringInfo buf, GrantStmt *stmt) } } - static void AppendGrantOnDatabaseStmt(StringInfo buf, GrantStmt *stmt) { @@ -84,11 +85,17 @@ AppendGrantOnDatabaseStmt(StringInfo buf, GrantStmt *stmt) AppendGrantSharedSuffix(buf, stmt); } +static void +AppendDefElemIsTemplate(StringInfo buf, DefElem *def) +{ + appendStringInfo(buf, "WITH %s %s", quote_identifier(def->defname), + quote_literal_cstr(strVal(def->arg))); +} static void AppendDefElemConnLimit(StringInfo buf, DefElem *def) { - appendStringInfo(buf, " CONNECTION LIMIT %ld", (long int) defGetNumeric(def)); + appendStringInfo(buf, "WITH CONNECTION LIMIT %ld", (long int)defGetNumeric(def)); } @@ -101,18 +108,23 @@ AppendAlterDatabaseStmt(StringInfo buf, AlterDatabaseStmt *stmt) { ListCell *cell = NULL; appendStringInfo(buf, "WITH "); - foreach(cell, stmt->options) + foreach (cell, stmt->options) { DefElem *def = castNode(DefElem, lfirst(cell)); if (strcmp(def->defname, "is_template") == 0) { - appendStringInfo(buf, "%s %s", quote_identifier(def->defname), - quote_literal_cstr(strVal(def->arg))); + AppendDefElemIsTemplate(buf, def); } else if (strcmp(def->defname, "connection_limit") == 0) { AppendDefElemConnLimit(buf, def); } + else if (strcmp(def->defname, "tablespace") == 0) + { + + ereport(ERROR, + errmsg("SET tablespace is not supported")); + } else if (strcmp(def->defname, "allow_connections") == 0) { ereport(ERROR, @@ -130,14 +142,13 @@ AppendAlterDatabaseStmt(StringInfo buf, AlterDatabaseStmt *stmt) appendStringInfo(buf, ";"); } - char * DeparseGrantOnDatabaseStmt(Node *node) { GrantStmt *stmt = castNode(GrantStmt, node); Assert(stmt->objtype == OBJECT_DATABASE); - StringInfoData str = { 0 }; + StringInfoData str = {0}; initStringInfo(&str); AppendGrantOnDatabaseStmt(&str, stmt); @@ -145,13 +156,12 @@ DeparseGrantOnDatabaseStmt(Node *node) return str.data; } - char * DeparseAlterDatabaseStmt(Node *node) { AlterDatabaseStmt *stmt = castNode(AlterDatabaseStmt, node); - StringInfoData str = { 0 }; + StringInfoData str = {0}; initStringInfo(&str); AppendAlterDatabaseStmt(&str, stmt); @@ -159,12 +169,11 @@ DeparseAlterDatabaseStmt(Node *node) return str.data; } - #if PG_VERSION_NUM >= PG_VERSION_15 char * DeparseAlterDatabaseRefreshCollStmt(Node *node) { - AlterDatabaseRefreshCollStmt *stmt = (AlterDatabaseRefreshCollStmt *) node; + AlterDatabaseRefreshCollStmt *stmt = (AlterDatabaseRefreshCollStmt *)node; StringInfoData str; initStringInfo(&str); @@ -176,5 +185,29 @@ DeparseAlterDatabaseRefreshCollStmt(Node *node) return str.data; } - #endif + + + +static void +AppendAlterDatabaseSetStmt(StringInfo buf, AlterDatabaseSetStmt *stmt) +{ + appendStringInfo(buf, "ALTER DATABASE %s ", quote_identifier(stmt->dbname)); + + VariableSetStmt *varSetStmt = castNode(VariableSetStmt, stmt->setstmt); + + AppendVariableSet(buf, varSetStmt); +} + +char * +DeparseAlterDatabaseSetStmt(Node *node) +{ + AlterDatabaseSetStmt *stmt = castNode(AlterDatabaseSetStmt, node); + + StringInfoData str = {0}; + initStringInfo(&str); + + AppendAlterDatabaseSetStmt(&str, stmt); + + return str.data; +} diff --git a/src/backend/distributed/deparser/deparse_function_stmts.c b/src/backend/distributed/deparser/deparse_function_stmts.c index c1d7d3128..0612d794c 100644 --- a/src/backend/distributed/deparser/deparse_function_stmts.c +++ b/src/backend/distributed/deparser/deparse_function_stmts.c @@ -61,7 +61,6 @@ static void AppendDefElemRows(StringInfo buf, DefElem *def); static void AppendDefElemSet(StringInfo buf, DefElem *def); static void AppendDefElemSupport(StringInfo buf, DefElem *def); -static void AppendVarSetValue(StringInfo buf, VariableSetStmt *setStmt); static void AppendRenameFunctionStmt(StringInfo buf, RenameStmt *stmt); static void AppendAlterFunctionSchemaStmt(StringInfo buf, AlterObjectSchemaStmt *stmt); static void AppendAlterFunctionOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt); @@ -300,163 +299,6 @@ AppendDefElemSupport(StringInfo buf, DefElem *def) } -/* - * AppendVariableSet appends a string representing the VariableSetStmt to a buffer - */ -void -AppendVariableSet(StringInfo buf, VariableSetStmt *setStmt) -{ - switch (setStmt->kind) - { - case VAR_SET_VALUE: - { - AppendVarSetValue(buf, setStmt); - break; - } - - case VAR_SET_CURRENT: - { - appendStringInfo(buf, " SET %s FROM CURRENT", quote_identifier( - setStmt->name)); - break; - } - - case VAR_SET_DEFAULT: - { - appendStringInfo(buf, " SET %s TO DEFAULT", quote_identifier(setStmt->name)); - break; - } - - case VAR_RESET: - { - appendStringInfo(buf, " RESET %s", quote_identifier(setStmt->name)); - break; - } - - case VAR_RESET_ALL: - { - appendStringInfoString(buf, " RESET ALL"); - break; - } - - /* VAR_SET_MULTI is a special case for SET TRANSACTION that should not occur here */ - case VAR_SET_MULTI: - default: - { - ereport(ERROR, (errmsg("Unable to deparse SET statement"))); - break; - } - } -} - - -/* - * AppendVarSetValue deparses a VariableSetStmt with VAR_SET_VALUE kind. - * It takes from flatten_set_variable_args in postgres's utils/misc/guc.c, - * however flatten_set_variable_args does not apply correct quoting. - */ -static void -AppendVarSetValue(StringInfo buf, VariableSetStmt *setStmt) -{ - ListCell *varArgCell = NULL; - ListCell *firstCell = list_head(setStmt->args); - - Assert(setStmt->kind == VAR_SET_VALUE); - - foreach(varArgCell, setStmt->args) - { - Node *varArgNode = lfirst(varArgCell); - A_Const *varArgConst = NULL; - TypeName *typeName = NULL; - - if (IsA(varArgNode, A_Const)) - { - varArgConst = (A_Const *) varArgNode; - } - else if (IsA(varArgNode, TypeCast)) - { - TypeCast *varArgTypeCast = (TypeCast *) varArgNode; - - varArgConst = castNode(A_Const, varArgTypeCast->arg); - typeName = varArgTypeCast->typeName; - } - else - { - elog(ERROR, "unrecognized node type: %d", varArgNode->type); - } - - /* don't know how to start SET until we inspect first arg */ - if (varArgCell != firstCell) - { - appendStringInfoChar(buf, ','); - } - else if (typeName != NULL) - { - appendStringInfoString(buf, " SET TIME ZONE"); - } - else - { - appendStringInfo(buf, " SET %s =", quote_identifier(setStmt->name)); - } - - Node *value = (Node *) &varArgConst->val; - switch (value->type) - { - case T_Integer: - { - appendStringInfo(buf, " %d", intVal(value)); - break; - } - - case T_Float: - { - appendStringInfo(buf, " %s", strVal(value)); - break; - } - - case T_String: - { - if (typeName != NULL) - { - /* - * Must be a ConstInterval argument for TIME ZONE. Coerce - * to interval and back to normalize the value and account - * for any typmod. - */ - Oid typoid = InvalidOid; - int32 typmod = -1; - - typenameTypeIdAndMod(NULL, typeName, &typoid, &typmod); - Assert(typoid == INTERVALOID); - - Datum interval = - DirectFunctionCall3(interval_in, - CStringGetDatum(strVal(value)), - ObjectIdGetDatum(InvalidOid), - Int32GetDatum(typmod)); - - char *intervalout = - DatumGetCString(DirectFunctionCall1(interval_out, - interval)); - appendStringInfo(buf, " INTERVAL '%s'", intervalout); - } - else - { - appendStringInfo(buf, " %s", quote_literal_cstr(strVal( - value))); - } - break; - } - - default: - { - elog(ERROR, "Unexpected Value type in VAR_SET_VALUE arguments."); - break; - } - } - } -} - /* * DeparseRenameFunctionStmt builds and returns a string representing the RenameStmt diff --git a/src/include/distributed/commands.h b/src/include/distributed/commands.h index 309149293..429016f9f 100644 --- a/src/include/distributed/commands.h +++ b/src/include/distributed/commands.h @@ -231,6 +231,10 @@ extern List * PreprocessAlterDatabaseRefreshCollStmt(Node *node, const char *que processUtilityContext); +extern List * PreprocessAlterDatabaseSetStmt(Node *node, const char *queryString, + ProcessUtilityContext processUtilityContext); + + /* domain.c - forward declarations */ extern List * CreateDomainStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess); diff --git a/src/include/distributed/deparser.h b/src/include/distributed/deparser.h index aeefc811d..35d372ec5 100644 --- a/src/include/distributed/deparser.h +++ b/src/include/distributed/deparser.h @@ -225,6 +225,7 @@ extern char * DeparseAlterDatabaseOwnerStmt(Node *node); extern char * DeparseGrantOnDatabaseStmt(Node *node); extern char * DeparseAlterDatabaseStmt(Node *node); extern char * DeparseAlterDatabaseRefreshCollStmt(Node *node); +extern char * DeparseAlterDatabaseSetStmt(Node *node); /* forward declaration for deparse_publication_stmts.c */ extern char * DeparseCreatePublicationStmt(Node *stmt);