diff --git a/.circleci/config.yml b/.circleci/config.yml index f7e912573..98807b173 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,7 +6,7 @@ orbs: parameters: image_suffix: type: string - default: '-v641cdcd' + default: '-v87fd773' pg14_version: type: string default: '14.9' @@ -15,10 +15,10 @@ parameters: default: '15.4' pg16_version: type: string - default: '16rc1' + default: '16.0' upgrade_pg_versions: type: string - default: '14.9-15.4-16rc1' + default: '14.9-15.4-16.0' style_checker_tools_version: type: string default: '0.8.18' diff --git a/.github/workflows/packaging-test-pipelines.yml b/.github/workflows/packaging-test-pipelines.yml index c66c5b4f7..9d3fb81be 100644 --- a/.github/workflows/packaging-test-pipelines.yml +++ b/.github/workflows/packaging-test-pipelines.yml @@ -44,12 +44,22 @@ jobs: # For this reason, we need to use a "matrix" to generate names of # rpm images, e.g. citus/packaging:centos-7-pg12 packaging_docker_image: - - oraclelinux-7 - oraclelinux-8 - - centos-7 - almalinux-8 - almalinux-9 POSTGRES_VERSION: ${{ fromJson(needs.get_postgres_versions_from_file.outputs.pg_versions) }} + # Postgres removed support for CentOS 7 in PG 16. Below block is needed to + # keep the build for CentOS 7 working for PG 14 and PG 15. + # Once dependent systems drop support for Centos 7, we can remove this block. + include: + - packaging_docker_image: centos-7 + POSTGRES_VERSION: 14 + - packaging_docker_image: centos-7 + POSTGRES_VERSION: 15 + - packaging_docker_image: oraclelinux-7 + POSTGRES_VERSION: 14 + - packaging_docker_image: oraclelinux-7 + POSTGRES_VERSION: 15 container: image: citus/packaging:${{ matrix.packaging_docker_image }}-pg${{ matrix.POSTGRES_VERSION }} @@ -125,7 +135,6 @@ jobs: - debian-bullseye-all - ubuntu-focal-all - ubuntu-jammy-all - - ubuntu-kinetic-all POSTGRES_VERSION: ${{ fromJson(needs.get_postgres_versions_from_file.outputs.pg_versions) }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 70eba8a09..3a2b2ce99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,55 @@ +### citus v12.1.0 (September 12, 2023) ### + +* Adds support for PostgreSQL 16.0 (#7173) + +* Add `citus_schema_move()` function which moves tables within a + distributed schema to another node (#7180) + +* Adds `citus_pause_node()` UDF that allows pausing the node with given id + (#7089) + +* Makes sure to enforce shard level colocation with the GUC + `citus.enable_non_colocated_router_query_pushdown` (#7076) + +* Allows creating reference / distributed-schema tables from local tables added + to metadata and that use identity columns (#7131) + +* Propagates `BUFFER_USAGE_LIMIT` option in `VACUUM` and `ANALYZE` (#7114) + +* Propagates `PROCESS_MAIN`, `SKIP_DATABASE_STATS`, `ONLY_DATABASE_STATS` + options in `VACUUM` (#7114) + +* Propagates `GENERIC_PLAN` option in `EXPLAIN` (#7141) + +* Propagates "rules" option in `CREATE COLLATION` (#7185) + +* Propagates `GRANT`/ `REVOKE` for database privileges (#7109) + +* Adds TRUNCATE trigger support on Citus foreign tables (#7170) + +* Removes `pg_send_cancellation` (#7135) + +* Prevents unnecessarily pulling the data into coordinator for some + `INSERT .. SELECT` queries that target a single-shard group (#7077) + +* Makes sure that rebalancer throws an error if replication factor is greater + than the shard allowed node count. Also makes sure to avoid moving a shard + to a node that it already exists on. (#7074) + +* Fixes a bug that may appear during 2PC recovery when there are multiple + databases (#7174) + +* Fixes a bug that could cause `COPY` logic to skip data in case of + out-of-memory (#7152) + +* Fixes a bug that causes an unexpected error when adding a column with + a `NULL` constraint (#7093) + +* Fixes `PROCESS_TOAST` default value to `true` (#7122) + +* Improves the error thrown when there is datatype mismatch in `MERGE ON` + (#7081) + ### citus v12.0.0 (July 11, 2023) ### * Adds support for schema-based sharding. diff --git a/configure b/configure index c98037d89..a0c978dea 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for Citus 12.1devel. +# Generated by GNU Autoconf 2.69 for Citus 12.2devel. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -579,8 +579,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='Citus' PACKAGE_TARNAME='citus' -PACKAGE_VERSION='12.1devel' -PACKAGE_STRING='Citus 12.1devel' +PACKAGE_VERSION='12.2devel' +PACKAGE_STRING='Citus 12.2devel' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -1262,7 +1262,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures Citus 12.1devel to adapt to many kinds of systems. +\`configure' configures Citus 12.2devel to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1324,7 +1324,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of Citus 12.1devel:";; + short | recursive ) echo "Configuration of Citus 12.2devel:";; esac cat <<\_ACEOF @@ -1429,7 +1429,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -Citus configure 12.1devel +Citus configure 12.2devel generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1912,7 +1912,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by Citus $as_me 12.1devel, which was +It was created by Citus $as_me 12.2devel, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -5393,7 +5393,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by Citus $as_me 12.1devel, which was +This file was extended by Citus $as_me 12.2devel, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -5455,7 +5455,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -Citus config.status 12.1devel +Citus config.status 12.2devel configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 7e5619857..2a4c7a21a 100644 --- a/configure.ac +++ b/configure.ac @@ -5,7 +5,7 @@ # everyone needing autoconf installed, the resulting files are checked # into the SCM. -AC_INIT([Citus], [12.1devel]) +AC_INIT([Citus], [12.2devel]) AC_COPYRIGHT([Copyright (c) Citus Data, Inc.]) # we'll need sed and awk for some of the version commands diff --git a/src/backend/columnar/citus_columnar.control b/src/backend/columnar/citus_columnar.control index d8a54923c..c96d839d1 100644 --- a/src/backend/columnar/citus_columnar.control +++ b/src/backend/columnar/citus_columnar.control @@ -1,6 +1,6 @@ # Columnar extension comment = 'Citus Columnar extension' -default_version = '11.3-1' +default_version = '12.2-1' module_pathname = '$libdir/citus_columnar' relocatable = false schema = pg_catalog diff --git a/src/backend/columnar/sql/citus_columnar--11.3-1--12.2-1.sql b/src/backend/columnar/sql/citus_columnar--11.3-1--12.2-1.sql new file mode 100644 index 000000000..364241dc3 --- /dev/null +++ b/src/backend/columnar/sql/citus_columnar--11.3-1--12.2-1.sql @@ -0,0 +1 @@ +-- citus_columnar--11.3-1--12.2-1 diff --git a/src/backend/columnar/sql/downgrades/citus_columnar--12.2-1--11.3-1.sql b/src/backend/columnar/sql/downgrades/citus_columnar--12.2-1--11.3-1.sql new file mode 100644 index 000000000..34a0c2459 --- /dev/null +++ b/src/backend/columnar/sql/downgrades/citus_columnar--12.2-1--11.3-1.sql @@ -0,0 +1 @@ +-- citus_columnar--12.2-1--11.3-1 diff --git a/src/backend/distributed/citus.control b/src/backend/distributed/citus.control index 831955ad8..91c0a3be7 100644 --- a/src/backend/distributed/citus.control +++ b/src/backend/distributed/citus.control @@ -1,6 +1,6 @@ # Citus extension comment = 'Citus distributed database' -default_version = '12.1-1' +default_version = '12.2-1' module_pathname = '$libdir/citus' relocatable = false schema = pg_catalog diff --git a/src/backend/distributed/commands/database.c b/src/backend/distributed/commands/database.c index ce46b2995..944ff627d 100644 --- a/src/backend/distributed/commands/database.c +++ b/src/backend/distributed/commands/database.c @@ -147,3 +147,98 @@ PreprocessGrantOnDatabaseStmt(Node *node, const char *queryString, return NodeDDLTaskList(NON_COORDINATOR_NODES, commands); } + + +/* + * PreprocessAlterDatabaseStmt 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 * +PreprocessAlterDatabaseStmt(Node *node, const char *queryString, + ProcessUtilityContext processUtilityContext) +{ + if (!ShouldPropagate()) + { + return NIL; + } + + AlterDatabaseStmt *stmt = castNode(AlterDatabaseStmt, 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); +} + + +#if PG_VERSION_NUM >= PG_VERSION_15 + +/* + * 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 * +PreprocessAlterDatabaseRefreshCollStmt(Node *node, const char *queryString, + ProcessUtilityContext processUtilityContext) +{ + if (!ShouldPropagate()) + { + return NIL; + } + + AlterDatabaseRefreshCollStmt *stmt = castNode(AlterDatabaseRefreshCollStmt, 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); +} + + +#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 607a6db31..a17d75e17 100644 --- a/src/backend/distributed/commands/distribute_object_ops.c +++ b/src/backend/distributed/commands/distribute_object_ops.c @@ -444,6 +444,42 @@ static DistributeObjectOps Database_Grant = { .markDistributed = false, }; +static DistributeObjectOps Database_Alter = { + .deparse = DeparseAlterDatabaseStmt, + .qualify = NULL, + .preprocess = PreprocessAlterDatabaseStmt, + .postprocess = NULL, + .objectType = OBJECT_DATABASE, + .operationType = DIST_OPS_ALTER, + .address = NULL, + .markDistributed = false, +}; + +#if PG_VERSION_NUM >= PG_VERSION_15 +static DistributeObjectOps Database_RefreshColl = { + .deparse = DeparseAlterDatabaseRefreshCollStmt, + .qualify = NULL, + .preprocess = PreprocessAlterDatabaseRefreshCollStmt, + .postprocess = NULL, + .objectType = OBJECT_DATABASE, + .operationType = DIST_OPS_ALTER, + .address = NULL, + .markDistributed = false, +}; +#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, @@ -1272,7 +1308,6 @@ static DistributeObjectOps Trigger_Rename = { .markDistributed = false, }; - /* * GetDistributeObjectOps looks up the DistributeObjectOps which handles the node. * @@ -1283,6 +1318,25 @@ GetDistributeObjectOps(Node *node) { switch (nodeTag(node)) { + case T_AlterDatabaseStmt: + { + return &Database_Alter; + } + +#if PG_VERSION_NUM >= PG_VERSION_15 + case T_AlterDatabaseRefreshCollStmt: + { + return &Database_RefreshColl; + } + +#endif + + case T_AlterDatabaseSetStmt: + { + return &Database_Set; + } + + case T_AlterDomainStmt: { return &Domain_Alter; diff --git a/src/backend/distributed/commands/role.c b/src/backend/distributed/commands/role.c index 99ce4fb9f..63ede986d 100644 --- a/src/backend/distributed/commands/role.c +++ b/src/backend/distributed/commands/role.c @@ -78,6 +78,7 @@ static const char * WrapQueryInAlterRoleIfExistsCall(const char *query, RoleSpec static VariableSetStmt * MakeVariableSetStmt(const char *config); static int ConfigGenericNameCompare(const void *lhs, const void *rhs); static List * RoleSpecToObjectAddress(RoleSpec *role, bool missing_ok); +static bool IsGrantRoleWithInheritOrSetOption(GrantRoleStmt *stmt); /* controlled via GUC */ bool EnableCreateRolePropagation = true; @@ -1141,6 +1142,19 @@ PreprocessGrantRoleStmt(Node *node, const char *queryString, return NIL; } + if (IsGrantRoleWithInheritOrSetOption(stmt)) + { + if (EnableUnsupportedFeatureMessages) + { + ereport(NOTICE, (errmsg("not propagating GRANT/REVOKE commands with specified" + " INHERIT/SET options to worker nodes"), + errhint( + "Connect to worker nodes directly to manually run the same" + " GRANT/REVOKE command after disabling DDL propagation."))); + } + return NIL; + } + /* * Postgres don't seem to use the grantor. Even dropping the grantor doesn't * seem to affect the membership. If this changes, we might need to add grantors @@ -1190,6 +1204,27 @@ PostprocessGrantRoleStmt(Node *node, const char *queryString) } +/* + * IsGrantRoleWithInheritOrSetOption returns true if the given + * GrantRoleStmt has inherit or set option specified in its options + */ +static bool +IsGrantRoleWithInheritOrSetOption(GrantRoleStmt *stmt) +{ +#if PG_VERSION_NUM >= PG_VERSION_16 + DefElem *opt = NULL; + foreach_ptr(opt, stmt->opt) + { + if (strcmp(opt->defname, "inherit") == 0 || strcmp(opt->defname, "set") == 0) + { + return true; + } + } +#endif + return false; +} + + /* * ConfigGenericNameCompare compares two config_generic structs based on their * name fields. If the name fields contain the same strings two structs are diff --git a/src/backend/distributed/deparser/citus_setutils.c b/src/backend/distributed/deparser/citus_setutils.c new file mode 100644 index 000000000..481a2860b --- /dev/null +++ b/src/backend/distributed/deparser/citus_setutils.c @@ -0,0 +1,174 @@ +#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" +#include "nodes/print.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", nodeToString(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 f1aaaa63b..d3d3ce633 100644 --- a/src/backend/distributed/deparser/deparse_database_stmts.c +++ b/src/backend/distributed/deparser/deparse_database_stmts.c @@ -20,9 +20,15 @@ #include "distributed/deparser.h" #include "distributed/citus_ruleutils.h" +#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 AppendDefElemConnLimit(StringInfo buf, DefElem *def); char * DeparseAlterDatabaseOwnerStmt(Node *node) @@ -82,6 +88,52 @@ AppendGrantOnDatabaseStmt(StringInfo buf, GrantStmt *stmt) } +static void +AppendDefElemConnLimit(StringInfo buf, DefElem *def) +{ + appendStringInfo(buf, " CONNECTION LIMIT %ld", (long int) defGetNumeric(def)); +} + + +static void +AppendAlterDatabaseStmt(StringInfo buf, AlterDatabaseStmt *stmt) +{ + appendStringInfo(buf, "ALTER DATABASE %s ", quote_identifier(stmt->dbname)); + + if (stmt->options) + { + ListCell *cell = NULL; + appendStringInfo(buf, "WITH "); + foreach(cell, stmt->options) + { + DefElem *def = castNode(DefElem, lfirst(cell)); + if (strcmp(def->defname, "is_template") == 0) + { + appendStringInfo(buf, "IS_TEMPLATE %s", + quote_literal_cstr(strVal(def->arg))); + } + else if (strcmp(def->defname, "connection_limit") == 0) + { + AppendDefElemConnLimit(buf, def); + } + else if (strcmp(def->defname, "allow_connections") == 0) + { + ereport(ERROR, + errmsg("ALLOW_CONNECTIONS is not supported")); + } + else + { + ereport(ERROR, + errmsg("unrecognized ALTER DATABASE option: %s", + def->defname)); + } + } + } + + appendStringInfo(buf, ";"); +} + + char * DeparseGrantOnDatabaseStmt(Node *node) { @@ -95,3 +147,61 @@ DeparseGrantOnDatabaseStmt(Node *node) return str.data; } + + +char * +DeparseAlterDatabaseStmt(Node *node) +{ + AlterDatabaseStmt *stmt = castNode(AlterDatabaseStmt, node); + + StringInfoData str = { 0 }; + initStringInfo(&str); + + AppendAlterDatabaseStmt(&str, stmt); + + return str.data; +} + + +#if PG_VERSION_NUM >= PG_VERSION_15 +char * +DeparseAlterDatabaseRefreshCollStmt(Node *node) +{ + AlterDatabaseRefreshCollStmt *stmt = (AlterDatabaseRefreshCollStmt *) node; + + StringInfoData str; + initStringInfo(&str); + + appendStringInfo(&str, "ALTER DATABASE %s REFRESH COLLATION VERSION;", + quote_identifier( + stmt->dbname)); + + 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..a5bc52e5a 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,164 +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/backend/distributed/deparser/deparse_role_stmts.c b/src/backend/distributed/deparser/deparse_role_stmts.c index c1f1d769f..4d41f8ec4 100644 --- a/src/backend/distributed/deparser/deparse_role_stmts.c +++ b/src/backend/distributed/deparser/deparse_role_stmts.c @@ -15,6 +15,7 @@ #include "pg_version_compat.h" +#include "commands/defrem.h" #include "distributed/citus_ruleutils.h" #include "distributed/deparser.h" #include "distributed/listutils.h" @@ -384,7 +385,10 @@ AppendGrantWithAdminOption(StringInfo buf, GrantRoleStmt *stmt) DefElem *opt = NULL; foreach_ptr(opt, stmt->opt) { - if (strcmp(opt->defname, "admin") == 0) + bool admin_option = false; + char *optval = defGetString(opt); + if (strcmp(opt->defname, "admin") == 0 && + parse_bool(optval, &admin_option) && admin_option) { appendStringInfo(buf, " WITH ADMIN OPTION"); break; diff --git a/src/backend/distributed/sql/citus--12.1-1--12.2-1.sql b/src/backend/distributed/sql/citus--12.1-1--12.2-1.sql new file mode 100644 index 000000000..bb9d22969 --- /dev/null +++ b/src/backend/distributed/sql/citus--12.1-1--12.2-1.sql @@ -0,0 +1,3 @@ +-- citus--12.1-1--12.2-1 + +-- bump version to 12.2-1 diff --git a/src/backend/distributed/sql/downgrades/citus--12.2-1--12.1-1.sql b/src/backend/distributed/sql/downgrades/citus--12.2-1--12.1-1.sql new file mode 100644 index 000000000..b26fc16bc --- /dev/null +++ b/src/backend/distributed/sql/downgrades/citus--12.2-1--12.1-1.sql @@ -0,0 +1,2 @@ +-- citus--12.2-1--12.1-1 +-- this is an empty downgrade path since citus--12.2-1--12.1-1.sql is empty for now diff --git a/src/include/distributed/commands.h b/src/include/distributed/commands.h index c120f9429..429016f9f 100644 --- a/src/include/distributed/commands.h +++ b/src/include/distributed/commands.h @@ -223,6 +223,18 @@ extern List * DatabaseOwnerDDLCommands(const ObjectAddress *address); extern List * PreprocessGrantOnDatabaseStmt(Node *node, const char *queryString, ProcessUtilityContext processUtilityContext); +extern List * PreprocessAlterDatabaseStmt(Node *node, const char *queryString, + ProcessUtilityContext processUtilityContext); + +extern List * PreprocessAlterDatabaseRefreshCollStmt(Node *node, const char *queryString, + ProcessUtilityContext + 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 85e6b9de0..6ffe33cc2 100644 --- a/src/include/distributed/deparser.h +++ b/src/include/distributed/deparser.h @@ -223,6 +223,10 @@ extern char * DeparseAlterExtensionStmt(Node *stmt); /* forward declarations for deparse_database_stmts.c */ 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); diff --git a/src/include/pg_version_compat.h b/src/include/pg_version_compat.h index 2b0320003..1bdbae580 100644 --- a/src/include/pg_version_compat.h +++ b/src/include/pg_version_compat.h @@ -46,6 +46,8 @@ get_guc_variables_compat(int *gucCount) #define object_ownercheck(a, b, c) object_ownercheck(a, b, c) #define object_aclcheck(a, b, c, d) object_aclcheck(a, b, c, d) +#define pgstat_fetch_stat_local_beentry(a) pgstat_get_local_beentry_by_index(a) + #else #include "catalog/pg_class_d.h" diff --git a/src/test/regress/citus_tests/config.py b/src/test/regress/citus_tests/config.py index 97f8a421f..9b81863e2 100644 --- a/src/test/regress/citus_tests/config.py +++ b/src/test/regress/citus_tests/config.py @@ -43,7 +43,7 @@ CITUS_ARBITRARY_TEST_DIR = "./tmp_citus_test" MASTER = "master" # This should be updated when citus version changes -MASTER_VERSION = "12.1" +MASTER_VERSION = "12.2" HOME = expanduser("~") diff --git a/src/test/regress/expected/alter_database_propagation.out b/src/test/regress/expected/alter_database_propagation.out new file mode 100644 index 000000000..0ce217749 --- /dev/null +++ b/src/test/regress/expected/alter_database_propagation.out @@ -0,0 +1,150 @@ +set citus.log_remote_commands = true; +set citus.grep_remote_commands = '%ALTER DATABASE%'; +-- since ALLOW_CONNECTIONS alter option should be executed in a different database +-- and since we don't have a multiple database support for now, +-- this statement will get error +alter database regression ALLOW_CONNECTIONS false; +ERROR: ALLOW_CONNECTIONS is not supported +alter database regression with CONNECTION LIMIT 100; +NOTICE: issuing ALTER DATABASE regression WITH CONNECTION LIMIT 100; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ALTER DATABASE regression WITH CONNECTION LIMIT 100; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +alter database regression with IS_TEMPLATE true CONNECTION LIMIT 50; +NOTICE: issuing ALTER DATABASE regression WITH IS_TEMPLATE 'true' CONNECTION LIMIT 50; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ALTER DATABASE regression WITH IS_TEMPLATE 'true' CONNECTION LIMIT 50; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +alter database regression with CONNECTION LIMIT -1; +NOTICE: issuing ALTER DATABASE regression WITH CONNECTION LIMIT -1; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ALTER DATABASE regression WITH CONNECTION LIMIT -1; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +alter database regression with IS_TEMPLATE true; +NOTICE: issuing ALTER DATABASE regression WITH IS_TEMPLATE 'true'; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ALTER DATABASE regression WITH IS_TEMPLATE 'true'; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +alter database regression with IS_TEMPLATE false; +NOTICE: issuing ALTER DATABASE regression WITH IS_TEMPLATE 'false'; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ALTER DATABASE regression WITH IS_TEMPLATE 'false'; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +-- this statement will get error since we don't have a multiple database support for now +alter database regression rename to regression2; +ERROR: current database cannot be renamed +alter database regression set default_transaction_read_only = true; +NOTICE: issuing ALTER DATABASE regression SET default_transaction_read_only = 'true' +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ALTER DATABASE regression SET default_transaction_read_only = 'true' +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +set default_transaction_read_only = false; +alter database regression set default_transaction_read_only from current; +NOTICE: issuing ALTER DATABASE regression SET default_transaction_read_only FROM CURRENT +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ALTER DATABASE regression SET default_transaction_read_only FROM CURRENT +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +alter database regression set default_transaction_read_only to DEFAULT; +NOTICE: issuing ALTER DATABASE regression SET default_transaction_read_only TO DEFAULT +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ALTER DATABASE regression SET default_transaction_read_only TO DEFAULT +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +alter database regression RESET default_transaction_read_only; +NOTICE: issuing ALTER DATABASE regression RESET default_transaction_read_only +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ALTER DATABASE regression RESET default_transaction_read_only +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +alter database regression SET TIME ZONE '-7'; +NOTICE: issuing ALTER DATABASE regression SET timezone = '-7' +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ALTER DATABASE regression SET timezone = '-7' +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +alter database regression set TIME ZONE LOCAL; +NOTICE: issuing ALTER DATABASE regression SET timezone TO DEFAULT +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ALTER DATABASE regression SET timezone TO DEFAULT +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +alter database regression set TIME ZONE DEFAULT; +NOTICE: issuing ALTER DATABASE regression SET timezone TO DEFAULT +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ALTER DATABASE regression SET timezone TO DEFAULT +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +alter database regression RESET TIME ZONE; +NOTICE: issuing ALTER DATABASE regression RESET timezone +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ALTER DATABASE regression RESET timezone +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +alter database regression SET TIME ZONE INTERVAL '-08:00' HOUR TO MINUTE; +NOTICE: issuing ALTER DATABASE regression SET TIME ZONE INTERVAL '@ 8 hours ago' +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ALTER DATABASE regression SET TIME ZONE INTERVAL '@ 8 hours ago' +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +alter database regression RESET TIME ZONE; +NOTICE: issuing ALTER DATABASE regression RESET timezone +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ALTER DATABASE regression RESET timezone +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +alter database regression set default_transaction_isolation = 'serializable'; +NOTICE: issuing ALTER DATABASE regression SET default_transaction_isolation = 'serializable' +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ALTER DATABASE regression SET default_transaction_isolation = 'serializable' +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +set default_transaction_isolation = 'read committed'; +alter database regression set default_transaction_isolation from current; +NOTICE: issuing ALTER DATABASE regression SET default_transaction_isolation FROM CURRENT +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ALTER DATABASE regression SET default_transaction_isolation FROM CURRENT +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +alter database regression set default_transaction_isolation to DEFAULT; +NOTICE: issuing ALTER DATABASE regression SET default_transaction_isolation TO DEFAULT +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ALTER DATABASE regression SET default_transaction_isolation TO DEFAULT +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +alter database regression RESET default_transaction_isolation; +NOTICE: issuing ALTER DATABASE regression RESET default_transaction_isolation +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ALTER DATABASE regression RESET default_transaction_isolation +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +alter database regression set statement_timeout = 1000; +NOTICE: issuing ALTER DATABASE regression SET statement_timeout = 1000 +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ALTER DATABASE regression SET statement_timeout = 1000 +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +set statement_timeout = 2000; +alter database regression set statement_timeout from current; +NOTICE: issuing ALTER DATABASE regression SET statement_timeout FROM CURRENT +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ALTER DATABASE regression SET statement_timeout FROM CURRENT +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +alter database regression set statement_timeout to DEFAULT; +NOTICE: issuing ALTER DATABASE regression SET statement_timeout TO DEFAULT +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ALTER DATABASE regression SET statement_timeout TO DEFAULT +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +alter database regression RESET statement_timeout; +NOTICE: issuing ALTER DATABASE regression RESET statement_timeout +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ALTER DATABASE regression RESET statement_timeout +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +alter database regression set lock_timeout = 1201.5; +NOTICE: issuing ALTER DATABASE regression SET lock_timeout = 1201.5 +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ALTER DATABASE regression SET lock_timeout = 1201.5 +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +set lock_timeout = 1202.5; +alter database regression set lock_timeout from current; +NOTICE: issuing ALTER DATABASE regression SET lock_timeout FROM CURRENT +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ALTER DATABASE regression SET lock_timeout FROM CURRENT +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +alter database regression set lock_timeout to DEFAULT; +NOTICE: issuing ALTER DATABASE regression SET lock_timeout TO DEFAULT +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ALTER DATABASE regression SET lock_timeout TO DEFAULT +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +alter database regression RESET lock_timeout; +NOTICE: issuing ALTER DATABASE regression RESET lock_timeout +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ALTER DATABASE regression RESET lock_timeout +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +set citus.log_remote_commands = false; diff --git a/src/test/regress/expected/citus_non_blocking_split_shards.out b/src/test/regress/expected/citus_non_blocking_split_shards.out index fe3cade55..1f42eab61 100644 --- a/src/test/regress/expected/citus_non_blocking_split_shards.out +++ b/src/test/regress/expected/citus_non_blocking_split_shards.out @@ -639,11 +639,7 @@ SELECT pg_reload_conf(); t (1 row) +SET client_min_messages TO ERROR; DROP SCHEMA "citus_split_test_schema" CASCADE; -NOTICE: drop cascades to 4 other objects -DETAIL: drop cascades to table citus_split_test_schema.sensors -drop cascades to table citus_split_test_schema.reference_table -drop cascades to table citus_split_test_schema.colocated_dist_table -drop cascades to table citus_split_test_schema.table_with_index_rep_identity DROP ROLE test_shard_split_role; --END : Cleanup diff --git a/src/test/regress/expected/multi_extension.out b/src/test/regress/expected/multi_extension.out index fe203efb5..295b10c76 100644 --- a/src/test/regress/expected/multi_extension.out +++ b/src/test/regress/expected/multi_extension.out @@ -1408,12 +1408,28 @@ SELECT * FROM multi_extension.print_extension_changes(); | function citus_schema_move(regnamespace,text,integer,citus.shard_transfer_mode) void (5 rows) +-- Test downgrade to 12.1-1 from 12.2-1 +ALTER EXTENSION citus UPDATE TO '12.2-1'; +ALTER EXTENSION citus UPDATE TO '12.1-1'; +-- Should be empty result since upgrade+downgrade should be a no-op +SELECT * FROM multi_extension.print_extension_changes(); + previous_object | current_object +--------------------------------------------------------------------- +(0 rows) + +-- Snapshot of state at 12.2-1 +ALTER EXTENSION citus UPDATE TO '12.2-1'; +SELECT * FROM multi_extension.print_extension_changes(); + previous_object | current_object +--------------------------------------------------------------------- +(0 rows) + DROP TABLE multi_extension.prev_objects, multi_extension.extension_diff; -- show running version SHOW citus.version; citus.version --------------------------------------------------------------------- - 12.1devel + 12.2devel (1 row) -- ensure no unexpected objects were created outside pg_catalog @@ -1447,7 +1463,7 @@ DROP EXTENSION citus; DROP EXTENSION citus_columnar; CREATE EXTENSION citus VERSION '8.0-1'; ERROR: specified version incompatible with loaded Citus library -DETAIL: Loaded library requires 12.1, but 8.0-1 was specified. +DETAIL: Loaded library requires 12.2, but 8.0-1 was specified. HINT: If a newer library is present, restart the database and try the command again. -- Test non-distributed queries work even in version mismatch SET citus.enable_version_checks TO 'false'; @@ -1492,7 +1508,7 @@ ORDER BY 1; -- We should not distribute table in version mistmatch SELECT create_distributed_table('version_mismatch_table', 'column1'); ERROR: loaded Citus library version differs from installed extension version -DETAIL: Loaded library requires 12.1, but the installed extension version is 8.1-1. +DETAIL: Loaded library requires 12.2, but the installed extension version is 8.1-1. HINT: Run ALTER EXTENSION citus UPDATE and try again. -- This function will cause fail in next ALTER EXTENSION CREATE OR REPLACE FUNCTION pg_catalog.relation_is_a_known_shard(regclass) diff --git a/src/test/regress/expected/pg15.out b/src/test/regress/expected/pg15.out index 740a87812..fcbb0cd12 100644 --- a/src/test/regress/expected/pg15.out +++ b/src/test/regress/expected/pg15.out @@ -1523,6 +1523,13 @@ ORDER BY is_coordinator DESC, result; f | [{"indexdefs": ["CREATE UNIQUE INDEX referencing__key ON pg15.referencing USING btree (test_2)"], "indexnames": ["referencing__key"]}, {"indexdefs": ["CREATE UNIQUE INDEX referencing__key1 ON pg15.referencing USING btree (test_3) NULLS NOT DISTINCT"], "indexnames": ["referencing__key1"]}] (3 rows) +set citus.log_remote_commands = true; +set citus.grep_remote_commands = '%ALTER DATABASE%'; +alter database regression REFRESH COLLATION VERSION; +NOTICE: version has not changed +NOTICE: issuing ALTER DATABASE regression REFRESH COLLATION VERSION; +NOTICE: issuing ALTER DATABASE regression REFRESH COLLATION VERSION; +set citus.log_remote_commands = false; -- Clean up \set VERBOSITY terse SET client_min_messages TO ERROR; diff --git a/src/test/regress/expected/pg16.out b/src/test/regress/expected/pg16.out index 95a1f9dd6..8d47b6f1b 100644 --- a/src/test/regress/expected/pg16.out +++ b/src/test/regress/expected/pg16.out @@ -971,6 +971,133 @@ LEFT JOIN ref_table ON TRUE; 1.19 (1 row) +-- +-- PG16 added WITH ADMIN FALSE option to GRANT ROLE +-- WITH ADMIN FALSE is the default, make sure we propagate correctly in Citus +-- Relevant PG commit: https://github.com/postgres/postgres/commit/e3ce2de +-- +CREATE ROLE role1; +CREATE ROLE role2; +SET citus.log_remote_commands TO on; +SET citus.grep_remote_commands = '%GRANT%'; +-- default admin option is false +GRANT role1 TO role2; +NOTICE: issuing GRANT role1 TO role2; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing GRANT role1 TO role2; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +REVOKE role1 FROM role2; +-- should behave same as default +GRANT role1 TO role2 WITH ADMIN FALSE; +NOTICE: issuing GRANT role1 TO role2; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing GRANT role1 TO role2; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +REVOKE role1 FROM role2; +-- with admin option and with admin true are the same +GRANT role1 TO role2 WITH ADMIN OPTION; +NOTICE: issuing GRANT role1 TO role2 WITH ADMIN OPTION; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing GRANT role1 TO role2 WITH ADMIN OPTION; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +REVOKE role1 FROM role2; +GRANT role1 TO role2 WITH ADMIN TRUE; +NOTICE: issuing GRANT role1 TO role2 WITH ADMIN OPTION; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing GRANT role1 TO role2 WITH ADMIN OPTION; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +REVOKE role1 FROM role2; +RESET citus.log_remote_commands; +RESET citus.grep_remote_commands; +-- +-- PG16 added new options to GRANT ROLE +-- inherit: https://github.com/postgres/postgres/commit/e3ce2de +-- set: https://github.com/postgres/postgres/commit/3d14e17 +-- We don't propagate for now in Citus +-- +GRANT role1 TO role2 WITH INHERIT FALSE; +NOTICE: not propagating GRANT/REVOKE commands with specified INHERIT/SET options to worker nodes +HINT: Connect to worker nodes directly to manually run the same GRANT/REVOKE command after disabling DDL propagation. +REVOKE role1 FROM role2; +GRANT role1 TO role2 WITH INHERIT TRUE; +NOTICE: not propagating GRANT/REVOKE commands with specified INHERIT/SET options to worker nodes +HINT: Connect to worker nodes directly to manually run the same GRANT/REVOKE command after disabling DDL propagation. +REVOKE role1 FROM role2; +GRANT role1 TO role2 WITH INHERIT OPTION; +NOTICE: not propagating GRANT/REVOKE commands with specified INHERIT/SET options to worker nodes +HINT: Connect to worker nodes directly to manually run the same GRANT/REVOKE command after disabling DDL propagation. +REVOKE role1 FROM role2; +GRANT role1 TO role2 WITH SET FALSE; +NOTICE: not propagating GRANT/REVOKE commands with specified INHERIT/SET options to worker nodes +HINT: Connect to worker nodes directly to manually run the same GRANT/REVOKE command after disabling DDL propagation. +REVOKE role1 FROM role2; +GRANT role1 TO role2 WITH SET TRUE; +NOTICE: not propagating GRANT/REVOKE commands with specified INHERIT/SET options to worker nodes +HINT: Connect to worker nodes directly to manually run the same GRANT/REVOKE command after disabling DDL propagation. +REVOKE role1 FROM role2; +GRANT role1 TO role2 WITH SET OPTION; +NOTICE: not propagating GRANT/REVOKE commands with specified INHERIT/SET options to worker nodes +HINT: Connect to worker nodes directly to manually run the same GRANT/REVOKE command after disabling DDL propagation. +REVOKE role1 FROM role2; +-- connect to worker node +GRANT role1 TO role2 WITH ADMIN OPTION, INHERIT FALSE, SET FALSE; +NOTICE: not propagating GRANT/REVOKE commands with specified INHERIT/SET options to worker nodes +HINT: Connect to worker nodes directly to manually run the same GRANT/REVOKE command after disabling DDL propagation. +SELECT roleid::regrole::text AS role, member::regrole::text, +admin_option, inherit_option, set_option FROM pg_auth_members +WHERE roleid::regrole::text = 'role1' ORDER BY 1, 2; + role | member | admin_option | inherit_option | set_option +--------------------------------------------------------------------- + role1 | role2 | t | f | f +(1 row) + +\c - - - :worker_1_port +SELECT roleid::regrole::text AS role, member::regrole::text, +admin_option, inherit_option, set_option FROM pg_auth_members +WHERE roleid::regrole::text = 'role1' ORDER BY 1, 2; + role | member | admin_option | inherit_option | set_option +--------------------------------------------------------------------- +(0 rows) + +SET citus.enable_ddl_propagation TO off; +GRANT role1 TO role2 WITH ADMIN OPTION, INHERIT FALSE, SET FALSE; +RESET citus.enable_ddl_propagation; +SELECT roleid::regrole::text AS role, member::regrole::text, +admin_option, inherit_option, set_option FROM pg_auth_members +WHERE roleid::regrole::text = 'role1' ORDER BY 1, 2; + role | member | admin_option | inherit_option | set_option +--------------------------------------------------------------------- + role1 | role2 | t | f | f +(1 row) + +\c - - - :master_port +REVOKE role1 FROM role2; +-- test REVOKES as well +GRANT role1 TO role2; +REVOKE SET OPTION FOR role1 FROM role2; +NOTICE: not propagating GRANT/REVOKE commands with specified INHERIT/SET options to worker nodes +HINT: Connect to worker nodes directly to manually run the same GRANT/REVOKE command after disabling DDL propagation. +REVOKE INHERIT OPTION FOR role1 FROM role2; +NOTICE: not propagating GRANT/REVOKE commands with specified INHERIT/SET options to worker nodes +HINT: Connect to worker nodes directly to manually run the same GRANT/REVOKE command after disabling DDL propagation. +DROP ROLE role1, role2; +-- test that everything works fine for roles that are not propagated +SET citus.enable_ddl_propagation TO off; +CREATE ROLE role3; +CREATE ROLE role4; +CREATE ROLE role5; +RESET citus.enable_ddl_propagation; +-- by default, admin option is false, inherit is true, set is true +GRANT role3 TO role4; +GRANT role3 TO role5 WITH ADMIN TRUE, INHERIT FALSE, SET FALSE; +SELECT roleid::regrole::text AS role, member::regrole::text, admin_option, inherit_option, set_option FROM pg_auth_members WHERE roleid::regrole::text = 'role3' ORDER BY 1, 2; + role | member | admin_option | inherit_option | set_option +--------------------------------------------------------------------- + role3 | role4 | f | t | t + role3 | role5 | t | f | f +(2 rows) + +DROP ROLE role3, role4, role5; \set VERBOSITY terse SET client_min_messages TO ERROR; DROP EXTENSION postgres_fdw CASCADE; diff --git a/src/test/regress/multi_1_schedule b/src/test/regress/multi_1_schedule index 145a83df2..4dead5be3 100644 --- a/src/test/regress/multi_1_schedule +++ b/src/test/regress/multi_1_schedule @@ -51,6 +51,7 @@ test: multi_metadata_attributes test: multi_read_from_secondaries test: grant_on_database_propagation +test: alter_database_propagation # ---------- # multi_citus_tools tests utility functions written for citus tools diff --git a/src/test/regress/sql/alter_database_propagation.sql b/src/test/regress/sql/alter_database_propagation.sql new file mode 100644 index 000000000..2b9d3ac33 --- /dev/null +++ b/src/test/regress/sql/alter_database_propagation.sql @@ -0,0 +1,59 @@ +set citus.log_remote_commands = true; +set citus.grep_remote_commands = '%ALTER DATABASE%'; + + +-- since ALLOW_CONNECTIONS alter option should be executed in a different database +-- and since we don't have a multiple database support for now, +-- this statement will get error +alter database regression ALLOW_CONNECTIONS false; + + +alter database regression with CONNECTION LIMIT 100; +alter database regression with IS_TEMPLATE true CONNECTION LIMIT 50; +alter database regression with CONNECTION LIMIT -1; +alter database regression with IS_TEMPLATE true; +alter database regression with IS_TEMPLATE false; +-- this statement will get error since we don't have a multiple database support for now +alter database regression rename to regression2; + +alter database regression set default_transaction_read_only = true; + +set default_transaction_read_only = false; + +alter database regression set default_transaction_read_only from current; +alter database regression set default_transaction_read_only to DEFAULT; +alter database regression RESET default_transaction_read_only; + +alter database regression SET TIME ZONE '-7'; + +alter database regression set TIME ZONE LOCAL; +alter database regression set TIME ZONE DEFAULT; +alter database regression RESET TIME ZONE; + +alter database regression SET TIME ZONE INTERVAL '-08:00' HOUR TO MINUTE; + +alter database regression RESET TIME ZONE; + + +alter database regression set default_transaction_isolation = 'serializable'; +set default_transaction_isolation = 'read committed'; + +alter database regression set default_transaction_isolation from current; +alter database regression set default_transaction_isolation to DEFAULT; +alter database regression RESET default_transaction_isolation; + +alter database regression set statement_timeout = 1000; +set statement_timeout = 2000; + +alter database regression set statement_timeout from current; +alter database regression set statement_timeout to DEFAULT; +alter database regression RESET statement_timeout; + +alter database regression set lock_timeout = 1201.5; +set lock_timeout = 1202.5; + +alter database regression set lock_timeout from current; +alter database regression set lock_timeout to DEFAULT; +alter database regression RESET lock_timeout; + +set citus.log_remote_commands = false; diff --git a/src/test/regress/sql/citus_non_blocking_split_shards.sql b/src/test/regress/sql/citus_non_blocking_split_shards.sql index 909beac02..88eef3ca3 100644 --- a/src/test/regress/sql/citus_non_blocking_split_shards.sql +++ b/src/test/regress/sql/citus_non_blocking_split_shards.sql @@ -317,6 +317,7 @@ SELECT run_command_on_workers($$SELECT count(*) FROM pg_subscription$$); ALTER SYSTEM RESET citus.defer_shard_delete_interval; SELECT pg_reload_conf(); +SET client_min_messages TO ERROR; DROP SCHEMA "citus_split_test_schema" CASCADE; DROP ROLE test_shard_split_role; --END : Cleanup diff --git a/src/test/regress/sql/multi_extension.sql b/src/test/regress/sql/multi_extension.sql index 1726c260f..8cbbbc3ed 100644 --- a/src/test/regress/sql/multi_extension.sql +++ b/src/test/regress/sql/multi_extension.sql @@ -634,6 +634,16 @@ SELECT * FROM multi_extension.print_extension_changes(); ALTER EXTENSION citus UPDATE TO '12.1-1'; SELECT * FROM multi_extension.print_extension_changes(); +-- Test downgrade to 12.1-1 from 12.2-1 +ALTER EXTENSION citus UPDATE TO '12.2-1'; +ALTER EXTENSION citus UPDATE TO '12.1-1'; +-- Should be empty result since upgrade+downgrade should be a no-op +SELECT * FROM multi_extension.print_extension_changes(); + +-- Snapshot of state at 12.2-1 +ALTER EXTENSION citus UPDATE TO '12.2-1'; +SELECT * FROM multi_extension.print_extension_changes(); + DROP TABLE multi_extension.prev_objects, multi_extension.extension_diff; -- show running version diff --git a/src/test/regress/sql/pg15.sql b/src/test/regress/sql/pg15.sql index e7dfbda04..fe60222dd 100644 --- a/src/test/regress/sql/pg15.sql +++ b/src/test/regress/sql/pg15.sql @@ -965,6 +965,11 @@ SELECT (groupid = 0) AS is_coordinator, result FROM run_command_on_all_nodes( JOIN pg_dist_node USING (nodeid) ORDER BY is_coordinator DESC, result; +set citus.log_remote_commands = true; +set citus.grep_remote_commands = '%ALTER DATABASE%'; +alter database regression REFRESH COLLATION VERSION; +set citus.log_remote_commands = false; + -- Clean up \set VERBOSITY terse SET client_min_messages TO ERROR; diff --git a/src/test/regress/sql/pg16.sql b/src/test/regress/sql/pg16.sql index 99732798d..82e9edf1e 100644 --- a/src/test/regress/sql/pg16.sql +++ b/src/test/regress/sql/pg16.sql @@ -565,6 +565,95 @@ SELECT PERCENTILE_DISC((2 > random_normal(stddev => 1, mean => 0))::int::numeric FROM dist_table LEFT JOIN ref_table ON TRUE; +-- +-- PG16 added WITH ADMIN FALSE option to GRANT ROLE +-- WITH ADMIN FALSE is the default, make sure we propagate correctly in Citus +-- Relevant PG commit: https://github.com/postgres/postgres/commit/e3ce2de +-- + +CREATE ROLE role1; +CREATE ROLE role2; + +SET citus.log_remote_commands TO on; +SET citus.grep_remote_commands = '%GRANT%'; +-- default admin option is false +GRANT role1 TO role2; +REVOKE role1 FROM role2; +-- should behave same as default +GRANT role1 TO role2 WITH ADMIN FALSE; +REVOKE role1 FROM role2; +-- with admin option and with admin true are the same +GRANT role1 TO role2 WITH ADMIN OPTION; +REVOKE role1 FROM role2; +GRANT role1 TO role2 WITH ADMIN TRUE; +REVOKE role1 FROM role2; + +RESET citus.log_remote_commands; +RESET citus.grep_remote_commands; + +-- +-- PG16 added new options to GRANT ROLE +-- inherit: https://github.com/postgres/postgres/commit/e3ce2de +-- set: https://github.com/postgres/postgres/commit/3d14e17 +-- We don't propagate for now in Citus +-- +GRANT role1 TO role2 WITH INHERIT FALSE; +REVOKE role1 FROM role2; +GRANT role1 TO role2 WITH INHERIT TRUE; +REVOKE role1 FROM role2; +GRANT role1 TO role2 WITH INHERIT OPTION; +REVOKE role1 FROM role2; +GRANT role1 TO role2 WITH SET FALSE; +REVOKE role1 FROM role2; +GRANT role1 TO role2 WITH SET TRUE; +REVOKE role1 FROM role2; +GRANT role1 TO role2 WITH SET OPTION; +REVOKE role1 FROM role2; + +-- connect to worker node +GRANT role1 TO role2 WITH ADMIN OPTION, INHERIT FALSE, SET FALSE; + +SELECT roleid::regrole::text AS role, member::regrole::text, +admin_option, inherit_option, set_option FROM pg_auth_members +WHERE roleid::regrole::text = 'role1' ORDER BY 1, 2; + +\c - - - :worker_1_port + +SELECT roleid::regrole::text AS role, member::regrole::text, +admin_option, inherit_option, set_option FROM pg_auth_members +WHERE roleid::regrole::text = 'role1' ORDER BY 1, 2; + +SET citus.enable_ddl_propagation TO off; +GRANT role1 TO role2 WITH ADMIN OPTION, INHERIT FALSE, SET FALSE; +RESET citus.enable_ddl_propagation; + +SELECT roleid::regrole::text AS role, member::regrole::text, +admin_option, inherit_option, set_option FROM pg_auth_members +WHERE roleid::regrole::text = 'role1' ORDER BY 1, 2; + +\c - - - :master_port +REVOKE role1 FROM role2; + +-- test REVOKES as well +GRANT role1 TO role2; +REVOKE SET OPTION FOR role1 FROM role2; +REVOKE INHERIT OPTION FOR role1 FROM role2; + +DROP ROLE role1, role2; + +-- test that everything works fine for roles that are not propagated +SET citus.enable_ddl_propagation TO off; +CREATE ROLE role3; +CREATE ROLE role4; +CREATE ROLE role5; +RESET citus.enable_ddl_propagation; +-- by default, admin option is false, inherit is true, set is true +GRANT role3 TO role4; +GRANT role3 TO role5 WITH ADMIN TRUE, INHERIT FALSE, SET FALSE; +SELECT roleid::regrole::text AS role, member::regrole::text, admin_option, inherit_option, set_option FROM pg_auth_members WHERE roleid::regrole::text = 'role3' ORDER BY 1, 2; + +DROP ROLE role3, role4, role5; + \set VERBOSITY terse SET client_min_messages TO ERROR; DROP EXTENSION postgres_fdw CASCADE;