From 2ebeea3ce354a6c283f82537d7de1855bf5ff256 Mon Sep 17 00:00:00 2001 From: gindibay Date: Fri, 10 Nov 2023 06:10:24 +0300 Subject: [PATCH 01/68] Fixes compilation warnings --- src/backend/distributed/commands/utility_hook.c | 1 - src/backend/distributed/deparser/citus_deparseutils.c | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/backend/distributed/commands/utility_hook.c b/src/backend/distributed/commands/utility_hook.c index 5d7fa3947c5..eb08b0539bc 100644 --- a/src/backend/distributed/commands/utility_hook.c +++ b/src/backend/distributed/commands/utility_hook.c @@ -64,7 +64,6 @@ #include "distributed/multi_executor.h" #include "distributed/multi_explain.h" #include "distributed/multi_physical_planner.h" -#include "distributed/pg_version_constants.h" #include "distributed/reference_table_utils.h" #include "distributed/resource_lock.h" #include "distributed/string_utils.h" diff --git a/src/backend/distributed/deparser/citus_deparseutils.c b/src/backend/distributed/deparser/citus_deparseutils.c index 0cfd7dd6fcf..bdc591a0162 100644 --- a/src/backend/distributed/deparser/citus_deparseutils.c +++ b/src/backend/distributed/deparser/citus_deparseutils.c @@ -6,11 +6,13 @@ * to their equivalent SQL representation. * */ +#include "pg_version_constants.h" + #include "postgres.h" + #include "commands/defrem.h" #include "distributed/deparser.h" -#include "distributed/pg_version_constants.h" #include "utils/builtins.h" #include "utils/elog.h" #include "utils/rel.h" From 1b9a8ea2db13cab333ff559fb1bb5305fb817e82 Mon Sep 17 00:00:00 2001 From: gindibay Date: Fri, 10 Nov 2023 06:11:43 +0300 Subject: [PATCH 02/68] Fixes indentation --- src/backend/distributed/commands/database.c | 28 +++++++++++-------- .../deparser/deparse_database_stmts.c | 11 ++++---- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/backend/distributed/commands/database.c b/src/backend/distributed/commands/database.c index baf6cd42448..b9a459cf4bf 100644 --- a/src/backend/distributed/commands/database.c +++ b/src/backend/distributed/commands/database.c @@ -574,55 +574,61 @@ GenerateCreateDatabaseStatementFromPgDatabase(Form_pg_database databaseForm) initStringInfo(&str); appendStringInfo(&str, "CREATE DATABASE %s", - quote_identifier(NameStr(databaseForm->datname))); + quote_identifier(NameStr(databaseForm->datname))); if (databaseForm->datdba != InvalidOid) { appendStringInfo(&str, " OWNER = %s", - quote_literal_cstr(GetUserNameFromId(databaseForm->datdba,false))); + quote_literal_cstr(GetUserNameFromId(databaseForm->datdba, + false))); } if (databaseForm->encoding != -1) { appendStringInfo(&str, " ENCODING = %s", - quote_literal_cstr(pg_encoding_to_char(databaseForm->encoding))); + quote_literal_cstr(pg_encoding_to_char(databaseForm->encoding))); } if (collInfo.collation != NULL) { - appendStringInfo(&str, " LC_COLLATE = %s", quote_literal_cstr(collInfo.collation)); + appendStringInfo(&str, " LC_COLLATE = %s", quote_literal_cstr( + collInfo.collation)); } if (collInfo.ctype != NULL) { - appendStringInfo(&str, " LC_CTYPE = %s", quote_literal_cstr(collInfo.ctype)); + appendStringInfo(&str, " LC_CTYPE = %s", quote_literal_cstr(collInfo.ctype)); } #if PG_VERSION_NUM >= PG_VERSION_15 if (collInfo.icu_locale != NULL) { - appendStringInfo(&str, " ICU_LOCALE = %s", quote_literal_cstr(collInfo.icu_locale)); + appendStringInfo(&str, " ICU_LOCALE = %s", quote_literal_cstr( + collInfo.icu_locale)); } if (databaseForm->datlocprovider != 0) { appendStringInfo(&str, " LOCALE_PROVIDER = %s", - quote_literal_cstr(GetLocaleProviderString(databaseForm->datlocprovider))); + quote_literal_cstr(GetLocaleProviderString( + databaseForm->datlocprovider))); } if (collInfo.collversion != NULL) { - appendStringInfo(&str, " COLLATION_VERSION = %s", quote_literal_cstr(collInfo.collversion)); + appendStringInfo(&str, " COLLATION_VERSION = %s", quote_literal_cstr( + collInfo.collversion)); } #endif if (databaseForm->dattablespace != InvalidOid) { appendStringInfo(&str, " TABLESPACE = %s", - quote_identifier(GetTablespaceName(databaseForm->dattablespace))); + quote_identifier(GetTablespaceName( + databaseForm->dattablespace))); } appendStringInfo(&str, " ALLOW_CONNECTIONS = %s", - quote_literal_cstr(databaseForm->datallowconn ?"true" : "false")); + quote_literal_cstr(databaseForm->datallowconn ? "true" : "false")); if (databaseForm->datconnlimit >= 0) { @@ -630,7 +636,7 @@ GenerateCreateDatabaseStatementFromPgDatabase(Form_pg_database databaseForm) } appendStringInfo(&str, " IS_TEMPLATE = %s", - quote_literal_cstr(databaseForm->datistemplate ? "true" :"false")); + quote_literal_cstr(databaseForm->datistemplate ? "true" : "false")); FreeDatabaseCollationInfo(collInfo); diff --git a/src/backend/distributed/deparser/deparse_database_stmts.c b/src/backend/distributed/deparser/deparse_database_stmts.c index a0626372113..494bfb30cba 100644 --- a/src/backend/distributed/deparser/deparse_database_stmts.c +++ b/src/backend/distributed/deparser/deparse_database_stmts.c @@ -259,20 +259,19 @@ DeparseAlterDatabaseSetStmt(Node *node) static void ValidateCreateDatabaseOptions(DefElem *option) { - if (strcmp(option->defname, "strategy") == 0){ + if (strcmp(option->defname, "strategy") == 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) + if (strcmp(option->defname, "template") == 0 && strcmp(optionValue, "template1") != 0) { - - ereport(ERROR,errmsg("Only template1 is supported as template parameter for CREATE DATABASE")); - + ereport(ERROR, errmsg( + "Only template1 is supported as template parameter for CREATE DATABASE")); } - } From 7a6afb0beb1dd10b8d7e00085c833bce51edfe06 Mon Sep 17 00:00:00 2001 From: gindibay Date: Fri, 10 Nov 2023 07:24:54 +0300 Subject: [PATCH 03/68] Fixes review comments --- .../regress/expected/create_drop_database_propagation.out | 2 ++ src/test/regress/sql/create_drop_database_propagation.sql | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/test/regress/expected/create_drop_database_propagation.out b/src/test/regress/expected/create_drop_database_propagation.out index c5ab0e2dfec..3cd59dc7516 100644 --- a/src/test/regress/expected/create_drop_database_propagation.out +++ b/src/test/regress/expected/create_drop_database_propagation.out @@ -114,6 +114,8 @@ select 1 from citus_add_node('localhost', :worker_2_port); 1 (1 row) +SET citus.log_remote_commands = true; +set citus.grep_remote_commands = '%CREATE DATABASE%'; SELECT result from run_command_on_all_nodes( $$ SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( diff --git a/src/test/regress/sql/create_drop_database_propagation.sql b/src/test/regress/sql/create_drop_database_propagation.sql index 157c70b28e0..09386c9b4bf 100644 --- a/src/test/regress/sql/create_drop_database_propagation.sql +++ b/src/test/regress/sql/create_drop_database_propagation.sql @@ -55,8 +55,6 @@ SELECT result from run_command_on_all_nodes( drop database mydatabase; - - SELECT result from run_command_on_all_nodes( $$ SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( @@ -103,6 +101,9 @@ SELECT result from run_command_on_all_nodes( select 1 from citus_add_node('localhost', :worker_2_port); +SET citus.log_remote_commands = true; +set citus.grep_remote_commands = '%CREATE DATABASE%'; + SELECT result from run_command_on_all_nodes( $$ SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( From 3067d1ef083b79b42f5b8d3115cdb436279eed1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCrkan=20=C4=B0ndibay?= Date: Fri, 10 Nov 2023 17:34:09 +0300 Subject: [PATCH 04/68] Apply suggestions from code review Co-authored-by: Onur Tirtir --- src/backend/distributed/commands/database.c | 6 ++++-- .../distributed/deparser/citus_deparseutils.c | 18 +++++++++++------- src/backend/distributed/metadata/distobject.c | 6 ++++-- .../distributed/metadata/metadata_sync.c | 6 ++++-- src/include/distributed/deparser.h | 6 +++--- 5 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/backend/distributed/commands/database.c b/src/backend/distributed/commands/database.c index b9a459cf4bf..0f4705c21ff 100644 --- a/src/backend/distributed/commands/database.c +++ b/src/backend/distributed/commands/database.c @@ -275,7 +275,7 @@ PreprocessAlterDatabaseSetStmt(Node *node, const char *queryString, * postgres instance. * * In this stage, we can perform validations and prepare the commands that need to - * be run on all workers to grant. + * be run on all workers to create the database. */ List * PreprocessCreateDatabaseStmt(Node *node, const char *queryString, @@ -298,7 +298,9 @@ PreprocessCreateDatabaseStmt(Node *node, const char *queryString, /* * PostprocessCreatedbStmt 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 - * all workers to create the database. + * 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. * */ List * diff --git a/src/backend/distributed/deparser/citus_deparseutils.c b/src/backend/distributed/deparser/citus_deparseutils.c index bdc591a0162..1fae9407f93 100644 --- a/src/backend/distributed/deparser/citus_deparseutils.c +++ b/src/backend/distributed/deparser/citus_deparseutils.c @@ -1,11 +1,15 @@ -/* +/*------------------------------------------------------------------------- + * * citus_deparseutils.c - * --------------------- * - * This file contains common functions used for deparsing PostgreSQL statements - * to their equivalent SQL representation. + * This file contains common functions used for deparsing PostgreSQL + * statements to their equivalent SQL representation. + * + * Copyright (c) Citus Data, Inc. * + *------------------------------------------------------------------------- */ + #include "pg_version_constants.h" #include "postgres.h" @@ -31,9 +35,9 @@ * @param optionFormatsLen The number of option formats in the opt_formats array. */ void -DefElemOptionToStatement(StringInfo buf, DefElem *option, const - DefElemOptionFormat *optionFormats, int - optionFormatsLen) +DefElemOptionToStatement(StringInfo buf, DefElem *option, + const DefElemOptionFormat *optionFormats, + int optionFormatsLen) { const char *name = option->defname; int i; diff --git a/src/backend/distributed/metadata/distobject.c b/src/backend/distributed/metadata/distobject.c index afdaf57c18e..5350a08bc94 100644 --- a/src/backend/distributed/metadata/distobject.c +++ b/src/backend/distributed/metadata/distobject.c @@ -360,8 +360,10 @@ ExecuteCommandAsSuperuser(char *query, int paramCount, Oid *paramTypes, /* - * Deletes all pg_dist_object records for distributed roles in `DROP ROLE` statement a - * and for all databases in `DROP DATABASE` statement + * UnmarkNodeWideObjectsDistributed deletes pg_dist_object records + * for all distributed objects in given Drop stmt node. + * + * Today we only expect DropRoleStmt and DropdbStmt to get here. */ void UnmarkNodeWideObjectsDistributed(Node *node) diff --git a/src/backend/distributed/metadata/metadata_sync.c b/src/backend/distributed/metadata/metadata_sync.c index 6db3ce28e91..dfcf243bf3f 100644 --- a/src/backend/distributed/metadata/metadata_sync.c +++ b/src/backend/distributed/metadata/metadata_sync.c @@ -3902,11 +3902,13 @@ Datum citus_internal_database_command(PG_FUNCTION_ARGS) { CheckCitusVersion(ERROR); + if (!ShouldSkipMetadataChecks()) { EnsureCitusInitiatedOperation(); } - PG_ENSURE_ARGNOTNULL(0, "database command"); + + PG_ENSURE_ARGNOTNULL(0, "command"); text *commandText = PG_GETARG_TEXT_P(0); char *command = text_to_cstring(commandText); @@ -3960,7 +3962,7 @@ citus_internal_database_command(PG_FUNCTION_ARGS) ereport(ERROR, (errmsg("unsupported command type %d", nodeTag(parseTree)))); } - /* Rollbacks GUCs to the state before this session */ + /* rollback GUCs to the state before this session */ AtEOXact_GUC(true, saveNestLevel); PG_RETURN_VOID(); diff --git a/src/include/distributed/deparser.h b/src/include/distributed/deparser.h index 59e6d40c6dd..5159f4c9d1e 100644 --- a/src/include/distributed/deparser.h +++ b/src/include/distributed/deparser.h @@ -140,9 +140,9 @@ typedef enum OptionFormatType } OptionFormatType; -extern void DefElemOptionToStatement(StringInfo buf, DefElem *option, const - DefElemOptionFormat *opt_formats, int - opt_formats_len); +extern void DefElemOptionToStatement(StringInfo buf, DefElem *option, + const DefElemOptionFormat *opt_formats, + int opt_formats_len); /* forward declarations for deparse_statistics_stmts.c */ From f8b3f322aae794128cd808fe8919e5ad490c9fd1 Mon Sep 17 00:00:00 2001 From: gindibay Date: Fri, 10 Nov 2023 08:33:51 +0300 Subject: [PATCH 05/68] Fixed review items --- src/backend/distributed/commands/database.c | 46 ++++--------------- .../distributed/commands/utility_hook.c | 1 - .../distributed/deparser/citus_deparseutils.c | 1 - .../deparser/deparse_database_stmts.c | 6 --- src/backend/distributed/metadata/distobject.c | 5 +- .../distributed/metadata/metadata_sync.c | 1 - 6 files changed, 11 insertions(+), 49 deletions(-) diff --git a/src/backend/distributed/commands/database.c b/src/backend/distributed/commands/database.c index 0f4705c21ff..2d0a2ce1692 100644 --- a/src/backend/distributed/commands/database.c +++ b/src/backend/distributed/commands/database.c @@ -298,7 +298,7 @@ PreprocessCreateDatabaseStmt(Node *node, const char *queryString, /* * PostprocessCreatedbStmt 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 - * all workers to create the database. Since the CREATE DATABASE statement gives error + * 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. * @@ -388,11 +388,11 @@ GetDatabaseAddressFromDatabaseName(char *databaseName, bool missingOk) * object of the DropdbStmt. */ List * -DropDatabaseStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess) +DropDatabaseStmtObjectAddress(Node *node, bool missingOk, bool isPostprocess) { DropdbStmt *stmt = castNode(DropdbStmt, node); ObjectAddress *dbAddress = GetDatabaseAddressFromDatabaseName(stmt->dbname, - missing_ok); + missingOk); return list_make1(dbAddress); } @@ -402,11 +402,11 @@ DropDatabaseStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess) * object of the CreatedbStmt. */ List * -CreateDatabaseStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess) +CreateDatabaseStmtObjectAddress(Node *node, bool missingOk, bool isPostprocess) { CreatedbStmt *stmt = castNode(CreatedbStmt, node); ObjectAddress *dbAddress = GetDatabaseAddressFromDatabaseName(stmt->dbname, - missing_ok); + missingOk); return list_make1(dbAddress); } @@ -424,7 +424,7 @@ GetTablespaceName(Oid tablespaceOid) } Form_pg_tablespace tablespaceForm = (Form_pg_tablespace) GETSTRUCT(tuple); - char *tablespaceName = NameStr(tablespaceForm->spcname); + char *tablespaceName = pstrdup(NameStr(tablespaceForm->spcname)); ReleaseSysCache(tuple); @@ -437,17 +437,17 @@ GetTablespaceName(Oid tablespaceOid) * We need this method since collation related info in Form_pg_database is not accessible */ static DatabaseCollationInfo -GetDatabaseCollation(Oid db_oid) +GetDatabaseCollation(Oid dbOid) { DatabaseCollationInfo info; bool isNull; Snapshot snapshot = RegisterSnapshot(GetLatestSnapshot()); Relation rel = table_open(DatabaseRelationId, AccessShareLock); - HeapTuple tup = get_catalog_object_by_oid(rel, Anum_pg_database_oid, db_oid); + HeapTuple tup = get_catalog_object_by_oid(rel, Anum_pg_database_oid, dbOid); if (!HeapTupleIsValid(tup)) { - elog(ERROR, "cache lookup failed for database %u", db_oid); + elog(ERROR, "cache lookup failed for database %u", dbOid); } TupleDesc tupdesc = RelationGetDescr(rel); @@ -505,29 +505,6 @@ GetDatabaseCollation(Oid db_oid) } -/* - * FreeDatabaseCollationInfo frees the memory allocated for DatabaseCollationInfo - */ -static void -FreeDatabaseCollationInfo(DatabaseCollationInfo collInfo) -{ - if (collInfo.collation != NULL) - { - pfree(collInfo.collation); - } - if (collInfo.ctype != NULL) - { - pfree(collInfo.ctype); - } - #if PG_VERSION_NUM >= PG_VERSION_15 - if (collInfo.icu_locale != NULL) - { - pfree(collInfo.icu_locale); - } - #endif -} - - #if PG_VERSION_NUM >= PG_VERSION_15 /* @@ -640,8 +617,6 @@ GenerateCreateDatabaseStatementFromPgDatabase(Form_pg_database databaseForm) appendStringInfo(&str, " IS_TEMPLATE = %s", quote_literal_cstr(databaseForm->datistemplate ? "true" : "false")); - FreeDatabaseCollationInfo(collInfo); - return str.data; } @@ -669,7 +644,6 @@ GenerateCreateDatabaseCommandList(void) char *createStmt = GenerateCreateDatabaseStatementFromPgDatabase(databaseForm); - StringInfo outerDbStmt = makeStringInfo(); /* Generate the CREATE DATABASE statement */ @@ -678,8 +652,6 @@ GenerateCreateDatabaseCommandList(void) quote_literal_cstr( createStmt)); - elog(LOG, "outerDbStmt: %s", outerDbStmt->data); - /* Add the statement to the list of commands */ commands = lappend(commands, outerDbStmt->data); } diff --git a/src/backend/distributed/commands/utility_hook.c b/src/backend/distributed/commands/utility_hook.c index eb08b0539bc..29d7e08da1f 100644 --- a/src/backend/distributed/commands/utility_hook.c +++ b/src/backend/distributed/commands/utility_hook.c @@ -25,7 +25,6 @@ *------------------------------------------------------------------------- */ - #include "pg_version_constants.h" #include "postgres.h" diff --git a/src/backend/distributed/deparser/citus_deparseutils.c b/src/backend/distributed/deparser/citus_deparseutils.c index 1fae9407f93..3f83f57fdfc 100644 --- a/src/backend/distributed/deparser/citus_deparseutils.c +++ b/src/backend/distributed/deparser/citus_deparseutils.c @@ -14,7 +14,6 @@ #include "postgres.h" - #include "commands/defrem.h" #include "distributed/deparser.h" #include "utils/builtins.h" diff --git a/src/backend/distributed/deparser/deparse_database_stmts.c b/src/backend/distributed/deparser/deparse_database_stmts.c index 494bfb30cba..bba6bedb4bc 100644 --- a/src/backend/distributed/deparser/deparse_database_stmts.c +++ b/src/backend/distributed/deparser/deparse_database_stmts.c @@ -275,9 +275,6 @@ ValidateCreateDatabaseOptions(DefElem *option) } -/* - * Prepares a CREATE DATABASE statement with given empty StringInfo buffer and CreatedbStmt node. - */ static void AppendCreateDatabaseStmt(StringInfo buf, CreatedbStmt *stmt) { @@ -314,9 +311,6 @@ DeparseCreateDatabaseStmt(Node *node) } -/* - * Prepares a DROP DATABASE statement with given empty StringInfo buffer and DropdbStmt node. - */ static void AppendDropDatabaseStmt(StringInfo buf, DropdbStmt *stmt) { diff --git a/src/backend/distributed/metadata/distobject.c b/src/backend/distributed/metadata/distobject.c index 5350a08bc94..94c12d47f5d 100644 --- a/src/backend/distributed/metadata/distobject.c +++ b/src/backend/distributed/metadata/distobject.c @@ -22,11 +22,13 @@ #include "catalog/dependency.h" #include "catalog/namespace.h" #include "catalog/objectaddress.h" +#include "catalog/pg_database.h" #include "catalog/pg_extension_d.h" #include "catalog/pg_namespace.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" #include "citus_version.h" +#include "commands/dbcommands.h" #include "commands/extension.h" #include "distributed/listutils.h" #include "distributed/colocation_utils.h" @@ -48,9 +50,6 @@ #include "utils/lsyscache.h" #include "utils/regproc.h" #include "utils/rel.h" -#include "catalog/pg_database.h" -#include "commands/dbcommands.h" - static char * CreatePgDistObjectEntryCommand(const ObjectAddress *objectAddress); static int ExecuteCommandAsSuperuser(char *query, int paramCount, Oid *paramTypes, diff --git a/src/backend/distributed/metadata/metadata_sync.c b/src/backend/distributed/metadata/metadata_sync.c index dfcf243bf3f..e612a468ad4 100644 --- a/src/backend/distributed/metadata/metadata_sync.c +++ b/src/backend/distributed/metadata/metadata_sync.c @@ -3951,7 +3951,6 @@ citus_internal_database_command(PG_FUNCTION_ARGS) bool missingOk = false; Oid databaseOid = get_database_oid(stmt->dbname, missingOk); - if (OidIsValid(databaseOid)) { DropDatabase(pstate, (DropdbStmt *) parseTree); From fe242276387cc7c9bbb1924c44f2c439a92262f7 Mon Sep 17 00:00:00 2001 From: Onur Tirtir Date: Fri, 10 Nov 2023 22:00:41 +0300 Subject: [PATCH 06/68] Improve tests for PG <= 14 --- .../create_drop_database_propagation.out | 500 +++++++++++++----- .../regress/expected/multi_test_helpers.out | 54 ++ .../sql/create_drop_database_propagation.sql | 272 ++++++---- src/test/regress/sql/multi_test_helpers.sql | 56 ++ 4 files changed, 638 insertions(+), 244 deletions(-) diff --git a/src/test/regress/expected/create_drop_database_propagation.out b/src/test/regress/expected/create_drop_database_propagation.out index 3cd59dc7516..6bc94e7a199 100644 --- a/src/test/regress/expected/create_drop_database_propagation.out +++ b/src/test/regress/expected/create_drop_database_propagation.out @@ -1,6 +1,58 @@ --- test for create/drop database propagation --- This test is only executes for Postgres 14 --- For postgres 15 tests, pg15_create_drop_database_propagation.sql is used +-- Test for create/drop database propagation. +-- This test is only executes for Postgres versions < 15. +-- For versions >= 15, pg15_create_drop_database_propagation.sql is used. +-- For versions >= 16, pg16_create_drop_database_propagation.sql is used. +-- Test the UDF that we use to issue database command during metadata sync. +SELECT pg_catalog.citus_internal_database_command(null); +ERROR: This is an internal Citus function can only be used in a distributed transaction +CREATE ROLE test_db_commands WITH LOGIN; +ALTER SYSTEM SET citus.enable_manual_metadata_changes_for_user TO 'test_db_commands'; +SELECT pg_reload_conf(); + pg_reload_conf +--------------------------------------------------------------------- + t +(1 row) + +SELECT pg_sleep(0.1); + pg_sleep +--------------------------------------------------------------------- + +(1 row) + +SET ROLE test_db_commands; +-- fails on null input +SELECT pg_catalog.citus_internal_database_command(null); +ERROR: command cannot be NULL +-- fails on non create / drop db command +SELECT pg_catalog.citus_internal_database_command('CREATE TABLE foo_bar(a int)'); +ERROR: unsupported command type 255 +SELECT pg_catalog.citus_internal_database_command('SELECT 1'); +ERROR: unsupported command type 242 +SELECT pg_catalog.citus_internal_database_command('asfsfdsg'); +ERROR: syntax error at or near "asfsfdsg" +SELECT pg_catalog.citus_internal_database_command(''); +ERROR: cannot execute multiple utility events +RESET ROLE; +ALTER ROLE test_db_commands nocreatedb; +SET ROLE test_db_commands; +-- make sure that pg_catalog.citus_internal_database_command doesn't cause privilege escalation +SELECT pg_catalog.citus_internal_database_command('CREATE DATABASE no_permissions'); +ERROR: permission denied to create database +RESET ROLE; +DROP USER test_db_commands; +ALTER SYSTEM RESET citus.enable_manual_metadata_changes_for_user; +SELECT pg_reload_conf(); + pg_reload_conf +--------------------------------------------------------------------- + t +(1 row) + +SELECT pg_sleep(0.1); + pg_sleep +--------------------------------------------------------------------- + +(1 row) + \set create_drop_db_tablespace :abs_srcdir '/tmp_check/ts3' CREATE TABLESPACE create_drop_db_tablespace LOCATION :'create_drop_db_tablespace'; \c - - - :worker_1_port @@ -9,6 +61,54 @@ CREATE TABLESPACE create_drop_db_tablespace LOCATION :'create_drop_db_tablespace \c - - - :worker_2_port \set create_drop_db_tablespace :abs_srcdir '/tmp_check/ts5' CREATE TABLESPACE create_drop_db_tablespace LOCATION :'create_drop_db_tablespace'; +\c - - - :master_port +CREATE DATABASE local_database; +NOTICE: Citus partially supports CREATE DATABASE for distributed databases +DETAIL: Citus does not propagate CREATE DATABASE command to workers +HINT: You can manually create a database and its extensions on workers. +-- check that it's only created for coordinator +SELECT * FROM public.check_database_on_all_nodes('local_database') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator (local) | {"database_properties": {"datacl": null, "datname": "local_database", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} + worker node (remote) | {"database_properties": null, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} + worker node (remote) | {"database_properties": null, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} +(3 rows) + +DROP DATABASE local_database; +-- and is dropped +SELECT * FROM public.check_database_on_all_nodes('local_database') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator (local) | {"database_properties": null, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} + worker node (remote) | {"database_properties": null, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} + worker node (remote) | {"database_properties": null, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} +(3 rows) + +\c - - - :worker_1_port +CREATE DATABASE local_database; +NOTICE: Citus partially supports CREATE DATABASE for distributed databases +DETAIL: Citus does not propagate CREATE DATABASE command to workers +HINT: You can manually create a database and its extensions on workers. +-- check that it's only created for coordinator +SELECT * FROM public.check_database_on_all_nodes('local_database') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator (remote) | {"database_properties": null, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} + worker node (local) | {"database_properties": {"datacl": null, "datname": "local_database", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} + worker node (remote) | {"database_properties": null, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} +(3 rows) + +DROP DATABASE local_database; +-- and is dropped +SELECT * FROM public.check_database_on_all_nodes('local_database') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator (remote) | {"database_properties": null, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} + worker node (local) | {"database_properties": null, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} + worker node (remote) | {"database_properties": null, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} +(3 rows) + \c - - - :master_port create user create_drop_db_test_user; set citus.enable_create_database_propagation=on; @@ -22,7 +122,7 @@ CREATE DATABASE mydatabase ALLOW_CONNECTIONS = true IS_TEMPLATE = false; ERROR: Only template1 is supported as template parameter for CREATE DATABASE -CREATE DATABASE mydatabase +CREATE DATABASE mydatabase_1 WITH template=template1 OWNER = create_drop_db_test_user ENCODING = 'UTF8' @@ -30,47 +130,104 @@ CREATE DATABASE mydatabase TABLESPACE = create_drop_db_tablespace ALLOW_CONNECTIONS = true IS_TEMPLATE = false; -SELECT result from run_command_on_all_nodes( +SELECT * FROM public.check_database_on_all_nodes('mydatabase_1') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator (local) | {"database_properties": {"datacl": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} +(3 rows) + +-- Test LC / LOCALE settings that don't match the ones provided in template db. +-- All should throw an error on the coordinator. +CREATE DATABASE lc_collate_test LC_COLLATE = 'en_US.UTF-8'; +ERROR: new collation (en_US.UTF-8) is incompatible with the collation of the template database (C) +HINT: Use the same collation as in the template database, or use template0 as template. +CREATE DATABASE lc_ctype_test LC_CTYPE = 'en_US.UTF-8'; +ERROR: new LC_CTYPE (en_US.UTF-8) is incompatible with the LC_CTYPE of the template database (C) +HINT: Use the same LC_CTYPE as in the template database, or use template0 as template. +CREATE DATABASE locale_test LOCALE = 'en_US.UTF-8'; +ERROR: new collation (en_US.UTF-8) is incompatible with the collation of the template database (C) +HINT: Use the same collation as in the template database, or use template0 as template. +CREATE DATABASE lc_collate_lc_ctype_test LC_COLLATE = 'en_US.UTF-8' LC_CTYPE = 'en_US.UTF-8'; +ERROR: new collation (en_US.UTF-8) is incompatible with the collation of the template database (C) +HINT: Use the same collation as in the template database, or use template0 as template. +-- Test LC / LOCALE settings that match the ones provided in template db. +CREATE DATABASE lc_collate_test LC_COLLATE = 'C'; +CREATE DATABASE lc_ctype_test LC_CTYPE = 'C'; +CREATE DATABASE locale_test LOCALE = 'C'; +CREATE DATABASE lc_collate_lc_ctype_test LC_COLLATE = 'C' LC_CTYPE = 'C'; +SELECT * FROM public.check_database_on_all_nodes('lc_collate_test') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator (local) | {"database_properties": {"datacl": null, "datname": "lc_collate_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "lc_collate_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "lc_collate_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} +(3 rows) + +SELECT * FROM public.check_database_on_all_nodes('lc_ctype_test') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator (local) | {"database_properties": {"datacl": null, "datname": "lc_ctype_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "lc_ctype_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "lc_ctype_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} +(3 rows) + +SELECT * FROM public.check_database_on_all_nodes('locale_test') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator (local) | {"database_properties": {"datacl": null, "datname": "locale_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "locale_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "locale_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} +(3 rows) + +SELECT * FROM public.check_database_on_all_nodes('lc_collate_lc_ctype_test') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator (local) | {"database_properties": {"datacl": null, "datname": "lc_collate_lc_ctype_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "lc_collate_lc_ctype_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "lc_collate_lc_ctype_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} +(3 rows) + +DROP DATABASE lc_collate_test; +DROP DATABASE lc_ctype_test; +DROP DATABASE locale_test; +DROP DATABASE lc_collate_lc_ctype_test; +-- ALTER TABLESPACE .. RENAME TO .. is not supported, so we need to rename it manually. +SELECT result FROM run_command_on_all_nodes( $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'mydatabase' - ) q2 + ALTER TABLESPACE create_drop_db_tablespace RENAME TO "ts-needs\!escape" $$ -) ORDER BY result; - result +); + result --------------------------------------------------------------------- - [{"datacl": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": true, "datconnlimit": 10, "datistemplate": false, "database_owner": "create_drop_db_test_user"}] - [{"datacl": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": true, "datconnlimit": 10, "datistemplate": false, "database_owner": "create_drop_db_test_user"}] - [{"datacl": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": true, "datconnlimit": 10, "datistemplate": false, "database_owner": "create_drop_db_test_user"}] + ALTER TABLESPACE + ALTER TABLESPACE + ALTER TABLESPACE (3 rows) -drop database mydatabase; -SELECT result from run_command_on_all_nodes( +CREATE USER "role-needs\!escape"; +CREATE DATABASE "db-needs\!escape" owner "role-needs\!escape" tablespace "ts-needs\!escape"; +-- Rename it to make check_database_on_all_nodes happy. +-- Today we don't support ALTER DATABASE .. RENAME TO .., so need to propagate it manually. +SELECT result FROM run_command_on_all_nodes( $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'mydatabase' - ) q2 + ALTER DATABASE "db-needs\!escape" RENAME TO db_needs_escape $$ -) ORDER BY result; - result +); + result --------------------------------------------------------------------- + ALTER DATABASE + ALTER DATABASE + ALTER DATABASE +(3 rows) - - +SELECT * FROM public.check_database_on_all_nodes('db_needs_escape') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator (local) | {"database_properties": {"datacl": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "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": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "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": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} (3 rows) -- test database syncing after node addition @@ -85,56 +242,128 @@ CREATE DATABASE mydatabase OWNER = create_drop_db_test_user CONNECTION LIMIT = 10 ENCODING = 'UTF8' - TABLESPACE = create_drop_db_tablespace + TABLESPACE = "ts-needs\!escape" ALLOW_CONNECTIONS = false IS_TEMPLATE = false; -SELECT result from run_command_on_all_nodes( - $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'mydatabase' - ) q2 - $$ -) ORDER BY result; - result +SELECT * FROM public.check_database_on_all_nodes('mydatabase') ORDER BY node_type; + node_type | result --------------------------------------------------------------------- - [{"datacl": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": false, "datconnlimit": 10, "datistemplate": false, "database_owner": "create_drop_db_test_user"}] - [{"datacl": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": false, "datconnlimit": 10, "datistemplate": false, "database_owner": "create_drop_db_test_user"}] + coordinator (local) | {"database_properties": {"datacl": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} (2 rows) +SET citus.metadata_sync_mode to 'transactional'; select 1 from citus_add_node('localhost', :worker_2_port); ?column? --------------------------------------------------------------------- 1 (1 row) -SET citus.log_remote_commands = true; -set citus.grep_remote_commands = '%CREATE DATABASE%'; -SELECT result from run_command_on_all_nodes( - $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'mydatabase' - ) q2 - $$ -) ORDER BY result; - result +SELECT * FROM public.check_database_on_all_nodes('mydatabase') ORDER BY node_type; + node_type | result --------------------------------------------------------------------- - [{"datacl": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": false, "datconnlimit": 10, "datistemplate": false, "database_owner": "create_drop_db_test_user"}] - [{"datacl": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": false, "datconnlimit": 10, "datistemplate": false, "database_owner": "create_drop_db_test_user"}] - [{"datacl": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": false, "datconnlimit": 10, "datistemplate": false, "database_owner": "create_drop_db_test_user"}] + coordinator (local) | {"database_properties": {"datacl": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} +(3 rows) + +SELECT * FROM public.check_database_on_all_nodes('mydatabase_1') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator (local) | {"database_properties": {"datacl": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} +(3 rows) + +SELECT * FROM public.check_database_on_all_nodes('db_needs_escape') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator (local) | {"database_properties": {"datacl": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "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": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "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": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} +(3 rows) + +select 1 from citus_remove_node('localhost', :worker_2_port); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +SET citus.metadata_sync_mode to 'nontransactional'; +select 1 from citus_add_node('localhost', :worker_2_port); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +RESET citus.metadata_sync_mode; +SELECT * FROM public.check_database_on_all_nodes('mydatabase') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator (local) | {"database_properties": {"datacl": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} +(3 rows) + +SELECT * FROM public.check_database_on_all_nodes('mydatabase_1') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator (local) | {"database_properties": {"datacl": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} +(3 rows) + +SELECT * FROM public.check_database_on_all_nodes('db_needs_escape') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator (local) | {"database_properties": {"datacl": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "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": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "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": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} +(3 rows) + +SELECT citus_disable_node_and_wait('localhost', :worker_1_port, true); + citus_disable_node_and_wait +--------------------------------------------------------------------- + +(1 row) + +CREATE DATABASE test_node_activation; +SELECT 1 FROM citus_activate_node('localhost', :worker_1_port); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT * FROM public.check_database_on_all_nodes('mydatabase') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator (local) | {"database_properties": {"datacl": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} +(3 rows) + +SELECT * FROM public.check_database_on_all_nodes('mydatabase_1') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator (local) | {"database_properties": {"datacl": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} +(3 rows) + +SELECT * FROM public.check_database_on_all_nodes('db_needs_escape') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator (local) | {"database_properties": {"datacl": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "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": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "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": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} +(3 rows) + +SELECT * FROM public.check_database_on_all_nodes('test_node_activation') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator (local) | {"database_properties": {"datacl": null, "datname": "test_node_activation", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "test_node_activation", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "test_node_activation", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} (3 rows) SET citus.log_remote_commands = true; @@ -145,54 +374,37 @@ DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing DROP DATABASE mydatabase DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx SET citus.log_remote_commands = false; -SELECT result from run_command_on_all_nodes( - $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'mydatabase' - ) q2 - $$ -) ORDER BY result; - result +-- check that we actually drop the database +drop database mydatabase_1; +SELECT * FROM public.check_database_on_all_nodes('mydatabase_1') ORDER BY node_type; + node_type | result --------------------------------------------------------------------- + coordinator (local) | {"database_properties": null, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} + worker node (remote) | {"database_properties": null, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} + worker node (remote) | {"database_properties": null, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} +(3 rows) - - +SELECT * FROM public.check_database_on_all_nodes('mydatabase') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator (local) | {"database_properties": null, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} + worker node (remote) | {"database_properties": null, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} + worker node (remote) | {"database_properties": null, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} (3 rows) -- create a template database with all options set and allow connections false CREATE DATABASE my_template_database WITH OWNER = create_drop_db_test_user ENCODING = 'UTF8' - TABLESPACE = create_drop_db_tablespace + TABLESPACE = "ts-needs\!escape" ALLOW_CONNECTIONS = false IS_TEMPLATE = true; -SET citus.log_remote_commands = false; -SELECT result from run_command_on_all_nodes( - $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'my_template_database' - ) q2 - $$ -) ORDER BY result; - result +SELECT * FROM public.check_database_on_all_nodes('my_template_database') ORDER BY node_type; + node_type | result --------------------------------------------------------------------- - [{"datacl": null, "datname": "my_template_database", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": false, "datconnlimit": -1, "datistemplate": true, "database_owner": "create_drop_db_test_user"}] - [{"datacl": null, "datname": "my_template_database", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": false, "datconnlimit": -1, "datistemplate": true, "database_owner": "create_drop_db_test_user"}] - [{"datacl": null, "datname": "my_template_database", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": false, "datconnlimit": -1, "datistemplate": true, "database_owner": "create_drop_db_test_user"}] + coordinator (local) | {"database_properties": {"datacl": null, "datname": "my_template_database", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": false, "datconnlimit": -1, "daticulocale": null, "datistemplate": true, "database_owner": "create_drop_db_test_user", "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": null, "datname": "my_template_database", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": false, "datconnlimit": -1, "daticulocale": null, "datistemplate": true, "database_owner": "create_drop_db_test_user", "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": null, "datname": "my_template_database", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": false, "datconnlimit": -1, "daticulocale": null, "datistemplate": true, "database_owner": "create_drop_db_test_user", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} (3 rows) --template databases could not be dropped so we need to change the template flag @@ -216,25 +428,12 @@ DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing DROP DATABASE my_template_database DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx SET citus.log_remote_commands = false; -SELECT result from run_command_on_all_nodes( - $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'my_template_database' - ) q2 - $$ -) ORDER BY result; - result +SELECT * FROM public.check_database_on_all_nodes('my_template_database') ORDER BY node_type; + node_type | result --------------------------------------------------------------------- - - - + coordinator (local) | {"database_properties": null, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} + worker node (remote) | {"database_properties": null, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} + worker node (remote) | {"database_properties": null, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} (3 rows) --tests for special characters in database name @@ -252,11 +451,44 @@ NOTICE: issuing DROP DATABASE IF EXISTS "mydatabase#1'2" DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing DROP DATABASE IF EXISTS "mydatabase#1'2" DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx ---clean up resources created by this test -drop tablespace create_drop_db_tablespace; +reset citus.grep_remote_commands; +reset citus.log_remote_commands; +-- it doesn't fail thanks to "if exists" +drop database if exists "mydatabase#1'2"; +NOTICE: database "mydatabase#1'2" does not exist, skipping +-- recreate it to verify that it's actually dropped +create database "mydatabase#1'2"; +drop database "mydatabase#1'2"; +-- second time we try to drop it, it fails due to lack of "if exists" +drop database "mydatabase#1'2"; +ERROR: database "mydatabase#1'2" does not exist \c - - - :worker_1_port -drop tablespace create_drop_db_tablespace; -\c - - - :worker_2_port -drop tablespace create_drop_db_tablespace; +SET citus.enable_create_database_propagation TO ON; +-- show that dropping the database from workers is not allowed when citus.enable_create_database_propagation is on +DROP DATABASE db_needs_escape; +ERROR: operation is not allowed on this node +HINT: Connect to the coordinator and run it again. +-- and the same applies to create database too +create database error_test; +ERROR: operation is not allowed on this node +HINT: Connect to the coordinator and run it again. \c - - - :master_port +SET citus.enable_create_database_propagation TO ON; +DROP DATABASE test_node_activation; +DROP DATABASE db_needs_escape; +DROP USER "role-needs\!escape"; +--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( + $$ + drop tablespace "ts-needs\!escape" + $$ +); + result +--------------------------------------------------------------------- + DROP TABLESPACE + DROP TABLESPACE + DROP TABLESPACE +(3 rows) + drop user create_drop_db_test_user; diff --git a/src/test/regress/expected/multi_test_helpers.out b/src/test/regress/expected/multi_test_helpers.out index b8758e561bd..da771b4c5eb 100644 --- a/src/test/regress/expected/multi_test_helpers.out +++ b/src/test/regress/expected/multi_test_helpers.out @@ -526,3 +526,57 @@ BEGIN RETURN result; END; $func$ LANGUAGE plpgsql; +-- For all nodes, returns database properties of given database, except +-- oid, datfrozenxid and datminmxid. +-- +-- Also returns whether the node has a pg_dist_object record for the database +-- and whether there are any stale pg_dist_object records for a database. +CREATE OR REPLACE FUNCTION check_database_on_all_nodes(p_database_name text) +RETURNS TABLE (node_type text, result text) +AS $func$ +BEGIN + RETURN QUERY + SELECT + CASE WHEN (groupid = 0 AND groupid = (SELECT groupid FROM pg_dist_local_group)) THEN 'coordinator (local)' + WHEN (groupid = 0) THEN 'coordinator (remote)' + WHEN (groupid = (SELECT groupid FROM pg_dist_local_group)) THEN 'worker node (local)' + ELSE 'worker node (remote)' + END AS node_type, + q2.result + FROM run_command_on_all_nodes( + format( + $$ + SELECT to_jsonb(q.*) + FROM ( + SELECT + ( + SELECT to_jsonb(database_properties.*) + FROM ( + SELECT datname, pa.rolname as database_owner, + pg_encoding_to_char(pd.encoding) as encoding, datlocprovider, + datistemplate, datallowconn, datconnlimit, + pt.spcname AS tablespace, datcollate, datctype, daticulocale, + datcollversion, datacl + FROM pg_database pd + JOIN pg_authid pa ON pd.datdba = pa.oid + JOIN pg_tablespace pt ON pd.dattablespace = pt.oid + WHERE datname = '%s' + ) database_properties + ) AS database_properties, + ( + SELECT COUNT(*)=1 + FROM pg_dist_object WHERE objid = (SELECT oid FROM pg_database WHERE datname = '%s') + ) AS pg_dist_object_record_for_db_exists, + ( + SELECT COUNT(*) > 0 + FROM pg_dist_object + WHERE classid = 1262 AND objid NOT IN (SELECT oid FROM pg_database) + ) AS stale_pg_dist_object_record_for_a_db_exists + ) q + $$, + p_database_name, p_database_name + ) + ) q2 + JOIN pg_dist_node USING (nodeid); +END; +$func$ LANGUAGE plpgsql; diff --git a/src/test/regress/sql/create_drop_database_propagation.sql b/src/test/regress/sql/create_drop_database_propagation.sql index 09386c9b4bf..d75ecdf9fb0 100644 --- a/src/test/regress/sql/create_drop_database_propagation.sql +++ b/src/test/regress/sql/create_drop_database_propagation.sql @@ -1,7 +1,40 @@ --- test for create/drop database propagation --- This test is only executes for Postgres 14 --- For postgres 15 tests, pg15_create_drop_database_propagation.sql is used +-- Test for create/drop database propagation. +-- This test is only executes for Postgres versions < 15. +-- For versions >= 15, pg15_create_drop_database_propagation.sql is used. +-- For versions >= 16, pg16_create_drop_database_propagation.sql is used. + +-- Test the UDF that we use to issue database command during metadata sync. +SELECT pg_catalog.citus_internal_database_command(null); + +CREATE ROLE test_db_commands WITH LOGIN; +ALTER SYSTEM SET citus.enable_manual_metadata_changes_for_user TO 'test_db_commands'; +SELECT pg_reload_conf(); +SELECT pg_sleep(0.1); +SET ROLE test_db_commands; + +-- fails on null input +SELECT pg_catalog.citus_internal_database_command(null); + +-- fails on non create / drop db command +SELECT pg_catalog.citus_internal_database_command('CREATE TABLE foo_bar(a int)'); +SELECT pg_catalog.citus_internal_database_command('SELECT 1'); +SELECT pg_catalog.citus_internal_database_command('asfsfdsg'); +SELECT pg_catalog.citus_internal_database_command(''); + +RESET ROLE; +ALTER ROLE test_db_commands nocreatedb; +SET ROLE test_db_commands; + +-- make sure that pg_catalog.citus_internal_database_command doesn't cause privilege escalation +SELECT pg_catalog.citus_internal_database_command('CREATE DATABASE no_permissions'); + +RESET ROLE; +DROP USER test_db_commands; +ALTER SYSTEM RESET citus.enable_manual_metadata_changes_for_user; +SELECT pg_reload_conf(); +SELECT pg_sleep(0.1); + \set create_drop_db_tablespace :abs_srcdir '/tmp_check/ts3' CREATE TABLESPACE create_drop_db_tablespace LOCATION :'create_drop_db_tablespace'; @@ -13,6 +46,28 @@ CREATE TABLESPACE create_drop_db_tablespace LOCATION :'create_drop_db_tablespace \set create_drop_db_tablespace :abs_srcdir '/tmp_check/ts5' CREATE TABLESPACE create_drop_db_tablespace LOCATION :'create_drop_db_tablespace'; +\c - - - :master_port +CREATE DATABASE local_database; + +-- check that it's only created for coordinator +SELECT * FROM public.check_database_on_all_nodes('local_database') ORDER BY node_type; + +DROP DATABASE local_database; + +-- and is dropped +SELECT * FROM public.check_database_on_all_nodes('local_database') ORDER BY node_type; + +\c - - - :worker_1_port +CREATE DATABASE local_database; + +-- check that it's only created for coordinator +SELECT * FROM public.check_database_on_all_nodes('local_database') ORDER BY node_type; + +DROP DATABASE local_database; + +-- and is dropped +SELECT * FROM public.check_database_on_all_nodes('local_database') ORDER BY node_type; + \c - - - :master_port create user create_drop_db_test_user; @@ -28,7 +83,7 @@ CREATE DATABASE mydatabase ALLOW_CONNECTIONS = true IS_TEMPLATE = false; -CREATE DATABASE mydatabase +CREATE DATABASE mydatabase_1 WITH template=template1 OWNER = create_drop_db_test_user ENCODING = 'UTF8' @@ -37,38 +92,51 @@ CREATE DATABASE mydatabase ALLOW_CONNECTIONS = true IS_TEMPLATE = false; -SELECT result from run_command_on_all_nodes( +SELECT * FROM public.check_database_on_all_nodes('mydatabase_1') ORDER BY node_type; + +-- Test LC / LOCALE settings that don't match the ones provided in template db. +-- All should throw an error on the coordinator. +CREATE DATABASE lc_collate_test LC_COLLATE = 'en_US.UTF-8'; +CREATE DATABASE lc_ctype_test LC_CTYPE = 'en_US.UTF-8'; +CREATE DATABASE locale_test LOCALE = 'en_US.UTF-8'; +CREATE DATABASE lc_collate_lc_ctype_test LC_COLLATE = 'en_US.UTF-8' LC_CTYPE = 'en_US.UTF-8'; + +-- Test LC / LOCALE settings that match the ones provided in template db. +CREATE DATABASE lc_collate_test LC_COLLATE = 'C'; +CREATE DATABASE lc_ctype_test LC_CTYPE = 'C'; +CREATE DATABASE locale_test LOCALE = 'C'; +CREATE DATABASE lc_collate_lc_ctype_test LC_COLLATE = 'C' LC_CTYPE = 'C'; + +SELECT * FROM public.check_database_on_all_nodes('lc_collate_test') ORDER BY node_type; +SELECT * FROM public.check_database_on_all_nodes('lc_ctype_test') ORDER BY node_type; +SELECT * FROM public.check_database_on_all_nodes('locale_test') ORDER BY node_type; +SELECT * FROM public.check_database_on_all_nodes('lc_collate_lc_ctype_test') ORDER BY node_type; + +DROP DATABASE lc_collate_test; +DROP DATABASE lc_ctype_test; +DROP DATABASE locale_test; +DROP DATABASE lc_collate_lc_ctype_test; + +-- ALTER TABLESPACE .. RENAME TO .. is not supported, so we need to rename it manually. +SELECT result FROM run_command_on_all_nodes( $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'mydatabase' - ) q2 + ALTER TABLESPACE create_drop_db_tablespace RENAME TO "ts-needs\!escape" $$ -) ORDER BY result; +); +CREATE USER "role-needs\!escape"; -drop database mydatabase; +CREATE DATABASE "db-needs\!escape" owner "role-needs\!escape" tablespace "ts-needs\!escape"; -SELECT result from run_command_on_all_nodes( +-- Rename it to make check_database_on_all_nodes happy. +-- Today we don't support ALTER DATABASE .. RENAME TO .., so need to propagate it manually. +SELECT result FROM run_command_on_all_nodes( $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'mydatabase' - ) q2 + ALTER DATABASE "db-needs\!escape" RENAME TO db_needs_escape $$ -) ORDER BY result; +); + +SELECT * FROM public.check_database_on_all_nodes('db_needs_escape') ORDER BY node_type; -- test database syncing after node addition @@ -79,45 +147,39 @@ CREATE DATABASE mydatabase OWNER = create_drop_db_test_user CONNECTION LIMIT = 10 ENCODING = 'UTF8' - TABLESPACE = create_drop_db_tablespace + TABLESPACE = "ts-needs\!escape" ALLOW_CONNECTIONS = false IS_TEMPLATE = false; +SELECT * FROM public.check_database_on_all_nodes('mydatabase') ORDER BY node_type; -SELECT result from run_command_on_all_nodes( - $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'mydatabase' - ) q2 - $$ -) ORDER BY result; +SET citus.metadata_sync_mode to 'transactional'; +select 1 from citus_add_node('localhost', :worker_2_port); + +SELECT * FROM public.check_database_on_all_nodes('mydatabase') ORDER BY node_type; +SELECT * FROM public.check_database_on_all_nodes('mydatabase_1') ORDER BY node_type; +SELECT * FROM public.check_database_on_all_nodes('db_needs_escape') ORDER BY node_type; +select 1 from citus_remove_node('localhost', :worker_2_port); + +SET citus.metadata_sync_mode to 'nontransactional'; select 1 from citus_add_node('localhost', :worker_2_port); -SET citus.log_remote_commands = true; -set citus.grep_remote_commands = '%CREATE DATABASE%'; +RESET citus.metadata_sync_mode; -SELECT result from run_command_on_all_nodes( - $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'mydatabase' - ) q2 - $$ -) ORDER BY result; +SELECT * FROM public.check_database_on_all_nodes('mydatabase') ORDER BY node_type; +SELECT * FROM public.check_database_on_all_nodes('mydatabase_1') ORDER BY node_type; +SELECT * FROM public.check_database_on_all_nodes('db_needs_escape') ORDER BY node_type; + +SELECT citus_disable_node_and_wait('localhost', :worker_1_port, true); + +CREATE DATABASE test_node_activation; +SELECT 1 FROM citus_activate_node('localhost', :worker_1_port); + +SELECT * FROM public.check_database_on_all_nodes('mydatabase') ORDER BY node_type; +SELECT * FROM public.check_database_on_all_nodes('mydatabase_1') ORDER BY node_type; +SELECT * FROM public.check_database_on_all_nodes('db_needs_escape') ORDER BY node_type; +SELECT * FROM public.check_database_on_all_nodes('test_node_activation') ORDER BY node_type; SET citus.log_remote_commands = true; set citus.grep_remote_commands = '%DROP DATABASE%'; @@ -125,45 +187,22 @@ drop database mydatabase; SET citus.log_remote_commands = false; -SELECT result from run_command_on_all_nodes( - $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'mydatabase' - ) q2 - $$ -) ORDER BY result; +-- check that we actually drop the database +drop database mydatabase_1; + +SELECT * FROM public.check_database_on_all_nodes('mydatabase_1') ORDER BY node_type; + +SELECT * FROM public.check_database_on_all_nodes('mydatabase') ORDER BY node_type; -- create a template database with all options set and allow connections false CREATE DATABASE my_template_database WITH OWNER = create_drop_db_test_user ENCODING = 'UTF8' - TABLESPACE = create_drop_db_tablespace + TABLESPACE = "ts-needs\!escape" ALLOW_CONNECTIONS = false IS_TEMPLATE = true; -SET citus.log_remote_commands = false; - -SELECT result from run_command_on_all_nodes( - $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'my_template_database' - ) q2 - $$ -) ORDER BY result; +SELECT * FROM public.check_database_on_all_nodes('my_template_database') ORDER BY node_type; --template databases could not be dropped so we need to change the template flag SELECT result from run_command_on_all_nodes( @@ -178,20 +217,8 @@ set citus.grep_remote_commands = '%DROP DATABASE%'; drop database my_template_database; SET citus.log_remote_commands = false; -SELECT result from run_command_on_all_nodes( - $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'my_template_database' - ) q2 - $$ -) ORDER BY result; + +SELECT * FROM public.check_database_on_all_nodes('my_template_database') ORDER BY node_type; --tests for special characters in database name set citus.enable_create_database_propagation=on; @@ -203,19 +230,44 @@ create database "mydatabase#1'2"; set citus.grep_remote_commands = '%DROP DATABASE%'; drop database if exists "mydatabase#1'2"; +reset citus.grep_remote_commands; +reset citus.log_remote_commands; ---clean up resources created by this test +-- it doesn't fail thanks to "if exists" +drop database if exists "mydatabase#1'2"; + +-- recreate it to verify that it's actually dropped +create database "mydatabase#1'2"; +drop database "mydatabase#1'2"; -drop tablespace create_drop_db_tablespace; +-- second time we try to drop it, it fails due to lack of "if exists" +drop database "mydatabase#1'2"; \c - - - :worker_1_port -drop tablespace create_drop_db_tablespace; +SET citus.enable_create_database_propagation TO ON; -\c - - - :worker_2_port +-- show that dropping the database from workers is not allowed when citus.enable_create_database_propagation is on +DROP DATABASE db_needs_escape; -drop tablespace create_drop_db_tablespace; +-- and the same applies to create database too +create database error_test; \c - - - :master_port +SET citus.enable_create_database_propagation TO ON; + +DROP DATABASE test_node_activation; +DROP DATABASE db_needs_escape; +DROP USER "role-needs\!escape"; + +--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( + $$ + drop tablespace "ts-needs\!escape" + $$ +); + drop user create_drop_db_test_user; diff --git a/src/test/regress/sql/multi_test_helpers.sql b/src/test/regress/sql/multi_test_helpers.sql index f7c97f1b2d3..2e8654ce201 100644 --- a/src/test/regress/sql/multi_test_helpers.sql +++ b/src/test/regress/sql/multi_test_helpers.sql @@ -550,3 +550,59 @@ BEGIN RETURN result; END; $func$ LANGUAGE plpgsql; + + +-- For all nodes, returns database properties of given database, except +-- oid, datfrozenxid and datminmxid. +-- +-- Also returns whether the node has a pg_dist_object record for the database +-- and whether there are any stale pg_dist_object records for a database. +CREATE OR REPLACE FUNCTION check_database_on_all_nodes(p_database_name text) +RETURNS TABLE (node_type text, result text) +AS $func$ +BEGIN + RETURN QUERY + SELECT + CASE WHEN (groupid = 0 AND groupid = (SELECT groupid FROM pg_dist_local_group)) THEN 'coordinator (local)' + WHEN (groupid = 0) THEN 'coordinator (remote)' + WHEN (groupid = (SELECT groupid FROM pg_dist_local_group)) THEN 'worker node (local)' + ELSE 'worker node (remote)' + END AS node_type, + q2.result + FROM run_command_on_all_nodes( + format( + $$ + SELECT to_jsonb(q.*) + FROM ( + SELECT + ( + SELECT to_jsonb(database_properties.*) + FROM ( + SELECT datname, pa.rolname as database_owner, + pg_encoding_to_char(pd.encoding) as encoding, datlocprovider, + datistemplate, datallowconn, datconnlimit, + pt.spcname AS tablespace, datcollate, datctype, daticulocale, + datcollversion, datacl + FROM pg_database pd + JOIN pg_authid pa ON pd.datdba = pa.oid + JOIN pg_tablespace pt ON pd.dattablespace = pt.oid + WHERE datname = '%s' + ) database_properties + ) AS database_properties, + ( + SELECT COUNT(*)=1 + FROM pg_dist_object WHERE objid = (SELECT oid FROM pg_database WHERE datname = '%s') + ) AS pg_dist_object_record_for_db_exists, + ( + SELECT COUNT(*) > 0 + FROM pg_dist_object + WHERE classid = 1262 AND objid NOT IN (SELECT oid FROM pg_database) + ) AS stale_pg_dist_object_record_for_a_db_exists + ) q + $$, + p_database_name, p_database_name + ) + ) q2 + JOIN pg_dist_node USING (nodeid); +END; +$func$ LANGUAGE plpgsql; From 5b446b11372043ccb5648b561ef8816dfc0f5635 Mon Sep 17 00:00:00 2001 From: Onur Tirtir Date: Mon, 13 Nov 2023 11:27:29 +0300 Subject: [PATCH 07/68] make tests passing --- .../distributed/metadata/metadata_sync.c | 4 +++- .../create_drop_database_propagation.out | 20 +++++++++---------- .../sql/create_drop_database_propagation.sql | 8 ++++---- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/backend/distributed/metadata/metadata_sync.c b/src/backend/distributed/metadata/metadata_sync.c index e612a468ad4..0c04502e6dc 100644 --- a/src/backend/distributed/metadata/metadata_sync.c +++ b/src/backend/distributed/metadata/metadata_sync.c @@ -3958,7 +3958,9 @@ citus_internal_database_command(PG_FUNCTION_ARGS) } else { - ereport(ERROR, (errmsg("unsupported command type %d", nodeTag(parseTree)))); + ereport(ERROR, (errmsg("citus_internal_database_command() can only be used " + "for CREATE DATABASE and DROP DATABASE commands by " + "Citus."))); } /* rollback GUCs to the state before this session */ diff --git a/src/test/regress/expected/create_drop_database_propagation.out b/src/test/regress/expected/create_drop_database_propagation.out index 6bc94e7a199..00f95c7c306 100644 --- a/src/test/regress/expected/create_drop_database_propagation.out +++ b/src/test/regress/expected/create_drop_database_propagation.out @@ -25,9 +25,9 @@ SELECT pg_catalog.citus_internal_database_command(null); ERROR: command cannot be NULL -- fails on non create / drop db command SELECT pg_catalog.citus_internal_database_command('CREATE TABLE foo_bar(a int)'); -ERROR: unsupported command type 255 +ERROR: citus_internal_database_command() can only be used for CREATE DATABASE and DROP DATABASE commands by Citus. SELECT pg_catalog.citus_internal_database_command('SELECT 1'); -ERROR: unsupported command type 242 +ERROR: citus_internal_database_command() can only be used for CREATE DATABASE and DROP DATABASE commands by Citus. SELECT pg_catalog.citus_internal_database_command('asfsfdsg'); ERROR: syntax error at or near "asfsfdsg" SELECT pg_catalog.citus_internal_database_command(''); @@ -140,17 +140,17 @@ SELECT * FROM public.check_database_on_all_nodes('mydatabase_1') ORDER BY node_t -- Test LC / LOCALE settings that don't match the ones provided in template db. -- All should throw an error on the coordinator. -CREATE DATABASE lc_collate_test LC_COLLATE = 'en_US.UTF-8'; -ERROR: new collation (en_US.UTF-8) is incompatible with the collation of the template database (C) +CREATE DATABASE lc_collate_test LC_COLLATE = 'C.UTF-8'; +ERROR: new collation (C.UTF-8) is incompatible with the collation of the template database (C) HINT: Use the same collation as in the template database, or use template0 as template. -CREATE DATABASE lc_ctype_test LC_CTYPE = 'en_US.UTF-8'; -ERROR: new LC_CTYPE (en_US.UTF-8) is incompatible with the LC_CTYPE of the template database (C) +CREATE DATABASE lc_ctype_test LC_CTYPE = 'C.UTF-8'; +ERROR: new LC_CTYPE (C.UTF-8) is incompatible with the LC_CTYPE of the template database (C) HINT: Use the same LC_CTYPE as in the template database, or use template0 as template. -CREATE DATABASE locale_test LOCALE = 'en_US.UTF-8'; -ERROR: new collation (en_US.UTF-8) is incompatible with the collation of the template database (C) +CREATE DATABASE locale_test LOCALE = 'C.UTF-8'; +ERROR: new collation (C.UTF-8) is incompatible with the collation of the template database (C) HINT: Use the same collation as in the template database, or use template0 as template. -CREATE DATABASE lc_collate_lc_ctype_test LC_COLLATE = 'en_US.UTF-8' LC_CTYPE = 'en_US.UTF-8'; -ERROR: new collation (en_US.UTF-8) is incompatible with the collation of the template database (C) +CREATE DATABASE lc_collate_lc_ctype_test LC_COLLATE = 'C.UTF-8' LC_CTYPE = 'C.UTF-8'; +ERROR: new collation (C.UTF-8) is incompatible with the collation of the template database (C) HINT: Use the same collation as in the template database, or use template0 as template. -- Test LC / LOCALE settings that match the ones provided in template db. CREATE DATABASE lc_collate_test LC_COLLATE = 'C'; diff --git a/src/test/regress/sql/create_drop_database_propagation.sql b/src/test/regress/sql/create_drop_database_propagation.sql index d75ecdf9fb0..6e6ecacb5ba 100644 --- a/src/test/regress/sql/create_drop_database_propagation.sql +++ b/src/test/regress/sql/create_drop_database_propagation.sql @@ -96,10 +96,10 @@ SELECT * FROM public.check_database_on_all_nodes('mydatabase_1') ORDER BY node_t -- Test LC / LOCALE settings that don't match the ones provided in template db. -- All should throw an error on the coordinator. -CREATE DATABASE lc_collate_test LC_COLLATE = 'en_US.UTF-8'; -CREATE DATABASE lc_ctype_test LC_CTYPE = 'en_US.UTF-8'; -CREATE DATABASE locale_test LOCALE = 'en_US.UTF-8'; -CREATE DATABASE lc_collate_lc_ctype_test LC_COLLATE = 'en_US.UTF-8' LC_CTYPE = 'en_US.UTF-8'; +CREATE DATABASE lc_collate_test LC_COLLATE = 'C.UTF-8'; +CREATE DATABASE lc_ctype_test LC_CTYPE = 'C.UTF-8'; +CREATE DATABASE locale_test LOCALE = 'C.UTF-8'; +CREATE DATABASE lc_collate_lc_ctype_test LC_COLLATE = 'C.UTF-8' LC_CTYPE = 'C.UTF-8'; -- Test LC / LOCALE settings that match the ones provided in template db. CREATE DATABASE lc_collate_test LC_COLLATE = 'C'; From 30e1a858d214f5d65ed7ff0ed410327d1421a433 Mon Sep 17 00:00:00 2001 From: gindibay Date: Mon, 13 Nov 2023 11:41:58 +0300 Subject: [PATCH 08/68] Removes oid support --- src/backend/distributed/deparser/deparse_database_stmts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/distributed/deparser/deparse_database_stmts.c b/src/backend/distributed/deparser/deparse_database_stmts.c index bba6bedb4bc..3383d29540e 100644 --- a/src/backend/distributed/deparser/deparse_database_stmts.c +++ b/src/backend/distributed/deparser/deparse_database_stmts.c @@ -259,7 +259,7 @@ DeparseAlterDatabaseSetStmt(Node *node) static void ValidateCreateDatabaseOptions(DefElem *option) { - if (strcmp(option->defname, "strategy") == 0) + if (strcmp(option->defname, "strategy") == 0 || strcmp(option->defname, "oid") == 0) { ereport(ERROR, errmsg("CREATE DATABASE option \"%s\" is not supported", From 2ca6e2c3580db227f86743caac6dbd0eed3d164c Mon Sep 17 00:00:00 2001 From: Onur Tirtir Date: Mon, 13 Nov 2023 11:43:31 +0300 Subject: [PATCH 09/68] indent --- src/backend/distributed/metadata/metadata_sync.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/distributed/metadata/metadata_sync.c b/src/backend/distributed/metadata/metadata_sync.c index 0c04502e6dc..5aa01b8acc2 100644 --- a/src/backend/distributed/metadata/metadata_sync.c +++ b/src/backend/distributed/metadata/metadata_sync.c @@ -3959,8 +3959,8 @@ citus_internal_database_command(PG_FUNCTION_ARGS) else { ereport(ERROR, (errmsg("citus_internal_database_command() can only be used " - "for CREATE DATABASE and DROP DATABASE commands by " - "Citus."))); + "for CREATE DATABASE and DROP DATABASE commands by " + "Citus."))); } /* rollback GUCs to the state before this session */ From 69217678b3ebd3e231531e752208078c877b6064 Mon Sep 17 00:00:00 2001 From: Onur Tirtir Date: Mon, 13 Nov 2023 12:16:08 +0300 Subject: [PATCH 10/68] allow wal_log option only if it's set to default --- .../distributed/deparser/deparse_database_stmts.c | 13 ++++++++++--- src/include/distributed/commands.h | 4 ++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/backend/distributed/deparser/deparse_database_stmts.c b/src/backend/distributed/deparser/deparse_database_stmts.c index 3383d29540e..2c1dc0b66e8 100644 --- a/src/backend/distributed/deparser/deparse_database_stmts.c +++ b/src/backend/distributed/deparser/deparse_database_stmts.c @@ -259,7 +259,7 @@ DeparseAlterDatabaseSetStmt(Node *node) static void ValidateCreateDatabaseOptions(DefElem *option) { - if (strcmp(option->defname, "strategy") == 0 || strcmp(option->defname, "oid") == 0) + if (strcmp(option->defname, "oid") == 0) { ereport(ERROR, errmsg("CREATE DATABASE option \"%s\" is not supported", @@ -267,11 +267,18 @@ ValidateCreateDatabaseOptions(DefElem *option) } 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")); + 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")); + } } diff --git a/src/include/distributed/commands.h b/src/include/distributed/commands.h index 85c55d39a82..4c47d3ecd21 100644 --- a/src/include/distributed/commands.h +++ b/src/include/distributed/commands.h @@ -239,9 +239,9 @@ extern List * PreprocessCreateDatabaseStmt(Node *node, const char *queryString, extern List * PostprocessCreateDatabaseStmt(Node *node, const char *queryString); extern List * PreprocessDropDatabaseStmt(Node *node, const char *queryString, ProcessUtilityContext processUtilityContext); -extern List * DropDatabaseStmtObjectAddress(Node *node, bool missing_ok, +extern List * DropDatabaseStmtObjectAddress(Node *node, bool missingOk, bool isPostprocess); -extern List * CreateDatabaseStmtObjectAddress(Node *node, bool missing_ok, +extern List * CreateDatabaseStmtObjectAddress(Node *node, bool missingOk, bool isPostprocess); extern List * GenerateCreateDatabaseCommandList(void); From ffa1fa09637c576e19ad31df7623d0252c673f0c Mon Sep 17 00:00:00 2001 From: Onur Tirtir Date: Mon, 13 Nov 2023 12:38:43 +0300 Subject: [PATCH 11/68] Improve tests for >= pg15 & pg >= 16 --- .../create_drop_database_propagation.out | 150 ++++---- .../create_drop_database_propagation_pg15.out | 325 +++--------------- .../create_drop_database_propagation_pg16.out | 23 ++ ...reate_drop_database_propagation_pg16_0.out | 9 + .../regress/expected/multi_test_helpers.out | 30 +- src/test/regress/multi_1_schedule | 1 + .../create_drop_database_propagation_pg15.sql | 255 ++------------ .../create_drop_database_propagation_pg16.sql | 22 ++ src/test/regress/sql/multi_test_helpers.sql | 30 +- 9 files changed, 265 insertions(+), 580 deletions(-) create mode 100644 src/test/regress/expected/create_drop_database_propagation_pg16.out create mode 100644 src/test/regress/expected/create_drop_database_propagation_pg16_0.out create mode 100644 src/test/regress/sql/create_drop_database_propagation_pg16.sql diff --git a/src/test/regress/expected/create_drop_database_propagation.out b/src/test/regress/expected/create_drop_database_propagation.out index 00f95c7c306..18bfa0c8ab5 100644 --- a/src/test/regress/expected/create_drop_database_propagation.out +++ b/src/test/regress/expected/create_drop_database_propagation.out @@ -68,9 +68,9 @@ DETAIL: Citus does not propagate CREATE DATABASE command to workers HINT: You can manually create a database and its extensions on workers. -- check that it's only created for coordinator SELECT * FROM public.check_database_on_all_nodes('local_database') ORDER BY node_type; - node_type | result + node_type | result --------------------------------------------------------------------- - coordinator (local) | {"database_properties": {"datacl": null, "datname": "local_database", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} + coordinator (local) | {"database_properties": {"datacl": null, "datname": "local_database", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} worker node (remote) | {"database_properties": null, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} worker node (remote) | {"database_properties": null, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} (3 rows) @@ -92,10 +92,10 @@ DETAIL: Citus does not propagate CREATE DATABASE command to workers HINT: You can manually create a database and its extensions on workers. -- check that it's only created for coordinator SELECT * FROM public.check_database_on_all_nodes('local_database') ORDER BY node_type; - node_type | result + node_type | result --------------------------------------------------------------------- coordinator (remote) | {"database_properties": null, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} - worker node (local) | {"database_properties": {"datacl": null, "datname": "local_database", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} + worker node (local) | {"database_properties": {"datacl": null, "datname": "local_database", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} worker node (remote) | {"database_properties": null, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} (3 rows) @@ -131,11 +131,11 @@ CREATE DATABASE mydatabase_1 ALLOW_CONNECTIONS = true IS_TEMPLATE = false; SELECT * FROM public.check_database_on_all_nodes('mydatabase_1') ORDER BY node_type; - node_type | result + node_type | result --------------------------------------------------------------------- - coordinator (local) | {"database_properties": {"datacl": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} + coordinator (local) | {"database_properties": {"datacl": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "daticurules": null, "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "daticurules": null, "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "daticurules": null, "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} (3 rows) -- Test LC / LOCALE settings that don't match the ones provided in template db. @@ -158,35 +158,35 @@ CREATE DATABASE lc_ctype_test LC_CTYPE = 'C'; CREATE DATABASE locale_test LOCALE = 'C'; CREATE DATABASE lc_collate_lc_ctype_test LC_COLLATE = 'C' LC_CTYPE = 'C'; SELECT * FROM public.check_database_on_all_nodes('lc_collate_test') ORDER BY node_type; - node_type | result + node_type | result --------------------------------------------------------------------- - coordinator (local) | {"database_properties": {"datacl": null, "datname": "lc_collate_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "lc_collate_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "lc_collate_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} + coordinator (local) | {"database_properties": {"datacl": null, "datname": "lc_collate_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "lc_collate_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "lc_collate_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} (3 rows) SELECT * FROM public.check_database_on_all_nodes('lc_ctype_test') ORDER BY node_type; - node_type | result + node_type | result --------------------------------------------------------------------- - coordinator (local) | {"database_properties": {"datacl": null, "datname": "lc_ctype_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "lc_ctype_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "lc_ctype_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} + coordinator (local) | {"database_properties": {"datacl": null, "datname": "lc_ctype_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "lc_ctype_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "lc_ctype_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} (3 rows) SELECT * FROM public.check_database_on_all_nodes('locale_test') ORDER BY node_type; - node_type | result + node_type | result --------------------------------------------------------------------- - coordinator (local) | {"database_properties": {"datacl": null, "datname": "locale_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "locale_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "locale_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} + coordinator (local) | {"database_properties": {"datacl": null, "datname": "locale_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "locale_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "locale_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} (3 rows) SELECT * FROM public.check_database_on_all_nodes('lc_collate_lc_ctype_test') ORDER BY node_type; - node_type | result + node_type | result --------------------------------------------------------------------- - coordinator (local) | {"database_properties": {"datacl": null, "datname": "lc_collate_lc_ctype_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "lc_collate_lc_ctype_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "lc_collate_lc_ctype_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} + coordinator (local) | {"database_properties": {"datacl": null, "datname": "lc_collate_lc_ctype_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "lc_collate_lc_ctype_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "lc_collate_lc_ctype_test", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} (3 rows) DROP DATABASE lc_collate_test; @@ -223,11 +223,11 @@ SELECT result FROM run_command_on_all_nodes( (3 rows) SELECT * FROM public.check_database_on_all_nodes('db_needs_escape') ORDER BY node_type; - node_type | result + node_type | result --------------------------------------------------------------------- - coordinator (local) | {"database_properties": {"datacl": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "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": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "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": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} + coordinator (local) | {"database_properties": {"datacl": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "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": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "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": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} (3 rows) -- test database syncing after node addition @@ -246,10 +246,10 @@ CREATE DATABASE mydatabase ALLOW_CONNECTIONS = false IS_TEMPLATE = false; SELECT * FROM public.check_database_on_all_nodes('mydatabase') ORDER BY node_type; - node_type | result + node_type | result --------------------------------------------------------------------- - coordinator (local) | {"database_properties": {"datacl": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} + coordinator (local) | {"database_properties": {"datacl": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} (2 rows) SET citus.metadata_sync_mode to 'transactional'; @@ -260,27 +260,27 @@ select 1 from citus_add_node('localhost', :worker_2_port); (1 row) SELECT * FROM public.check_database_on_all_nodes('mydatabase') ORDER BY node_type; - node_type | result + node_type | result --------------------------------------------------------------------- - coordinator (local) | {"database_properties": {"datacl": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} + coordinator (local) | {"database_properties": {"datacl": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} (3 rows) SELECT * FROM public.check_database_on_all_nodes('mydatabase_1') ORDER BY node_type; - node_type | result + node_type | result --------------------------------------------------------------------- - coordinator (local) | {"database_properties": {"datacl": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} + coordinator (local) | {"database_properties": {"datacl": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} (3 rows) SELECT * FROM public.check_database_on_all_nodes('db_needs_escape') ORDER BY node_type; - node_type | result + node_type | result --------------------------------------------------------------------- - coordinator (local) | {"database_properties": {"datacl": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "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": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "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": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} + coordinator (local) | {"database_properties": {"datacl": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "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": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "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": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} (3 rows) select 1 from citus_remove_node('localhost', :worker_2_port); @@ -298,27 +298,27 @@ select 1 from citus_add_node('localhost', :worker_2_port); RESET citus.metadata_sync_mode; SELECT * FROM public.check_database_on_all_nodes('mydatabase') ORDER BY node_type; - node_type | result + node_type | result --------------------------------------------------------------------- - coordinator (local) | {"database_properties": {"datacl": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} + coordinator (local) | {"database_properties": {"datacl": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} (3 rows) SELECT * FROM public.check_database_on_all_nodes('mydatabase_1') ORDER BY node_type; - node_type | result + node_type | result --------------------------------------------------------------------- - coordinator (local) | {"database_properties": {"datacl": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} + coordinator (local) | {"database_properties": {"datacl": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} (3 rows) SELECT * FROM public.check_database_on_all_nodes('db_needs_escape') ORDER BY node_type; - node_type | result + node_type | result --------------------------------------------------------------------- - coordinator (local) | {"database_properties": {"datacl": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "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": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "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": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} + coordinator (local) | {"database_properties": {"datacl": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "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": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "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": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} (3 rows) SELECT citus_disable_node_and_wait('localhost', :worker_1_port, true); @@ -335,35 +335,35 @@ SELECT 1 FROM citus_activate_node('localhost', :worker_1_port); (1 row) SELECT * FROM public.check_database_on_all_nodes('mydatabase') ORDER BY node_type; - node_type | result + node_type | result --------------------------------------------------------------------- - coordinator (local) | {"database_properties": {"datacl": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} + coordinator (local) | {"database_properties": {"datacl": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": false, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} (3 rows) SELECT * FROM public.check_database_on_all_nodes('mydatabase_1') ORDER BY node_type; - node_type | result + node_type | result --------------------------------------------------------------------- - coordinator (local) | {"database_properties": {"datacl": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} + coordinator (local) | {"database_properties": {"datacl": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "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": null, "datname": "mydatabase_1", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": true, "datconnlimit": 10, "daticulocale": null, "datistemplate": false, "database_owner": "create_drop_db_test_user", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} (3 rows) SELECT * FROM public.check_database_on_all_nodes('db_needs_escape') ORDER BY node_type; - node_type | result + node_type | result --------------------------------------------------------------------- - coordinator (local) | {"database_properties": {"datacl": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "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": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "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": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} + coordinator (local) | {"database_properties": {"datacl": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "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": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "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": null, "datname": "db_needs_escape", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "role-needs\\!escape", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} (3 rows) SELECT * FROM public.check_database_on_all_nodes('test_node_activation') ORDER BY node_type; - node_type | result + node_type | result --------------------------------------------------------------------- - coordinator (local) | {"database_properties": {"datacl": null, "datname": "test_node_activation", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "test_node_activation", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "test_node_activation", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} + coordinator (local) | {"database_properties": {"datacl": null, "datname": "test_node_activation", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "test_node_activation", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "test_node_activation", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} (3 rows) SET citus.log_remote_commands = true; @@ -400,11 +400,11 @@ CREATE DATABASE my_template_database ALLOW_CONNECTIONS = false IS_TEMPLATE = true; SELECT * FROM public.check_database_on_all_nodes('my_template_database') ORDER BY node_type; - node_type | result + node_type | result --------------------------------------------------------------------- - coordinator (local) | {"database_properties": {"datacl": null, "datname": "my_template_database", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": false, "datconnlimit": -1, "daticulocale": null, "datistemplate": true, "database_owner": "create_drop_db_test_user", "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": null, "datname": "my_template_database", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": false, "datconnlimit": -1, "daticulocale": null, "datistemplate": true, "database_owner": "create_drop_db_test_user", "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": null, "datname": "my_template_database", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "datallowconn": false, "datconnlimit": -1, "daticulocale": null, "datistemplate": true, "database_owner": "create_drop_db_test_user", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} + coordinator (local) | {"database_properties": {"datacl": null, "datname": "my_template_database", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": false, "datconnlimit": -1, "daticulocale": null, "datistemplate": true, "database_owner": "create_drop_db_test_user", "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": null, "datname": "my_template_database", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": false, "datconnlimit": -1, "daticulocale": null, "datistemplate": true, "database_owner": "create_drop_db_test_user", "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": null, "datname": "my_template_database", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "ts-needs\\!escape", "daticurules": null, "datallowconn": false, "datconnlimit": -1, "daticulocale": null, "datistemplate": true, "database_owner": "create_drop_db_test_user", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} (3 rows) --template databases could not be dropped so we need to change the template flag diff --git a/src/test/regress/expected/create_drop_database_propagation_pg15.out b/src/test/regress/expected/create_drop_database_propagation_pg15.out index bc637480350..9a501558a2b 100644 --- a/src/test/regress/expected/create_drop_database_propagation_pg15.out +++ b/src/test/regress/expected/create_drop_database_propagation_pg15.out @@ -8,298 +8,75 @@ SELECT substring(:'server_version', '\d+')::int >= 15 AS server_version_ge_15 \else \q \endif --- create/drop database for pg > 15 -\set create_drop_db_tablespace :abs_srcdir '/tmp_check/ts3' -CREATE TABLESPACE create_drop_db_tablespace LOCATION :'create_drop_db_tablespace'; -\c - - - :worker_1_port -\set create_drop_db_tablespace :abs_srcdir '/tmp_check/ts4' -CREATE TABLESPACE create_drop_db_tablespace LOCATION :'create_drop_db_tablespace'; -\c - - - :worker_2_port -\set create_drop_db_tablespace :abs_srcdir '/tmp_check/ts5' -CREATE TABLESPACE create_drop_db_tablespace LOCATION :'create_drop_db_tablespace'; -\c - - - :master_port -create user create_drop_db_test_user; +-- create/drop database for pg >= 15 set citus.enable_create_database_propagation=on; -SET citus.log_remote_commands = true; -set citus.grep_remote_commands = '%CREATE DATABASE%'; CREATE DATABASE mydatabase - WITH - OWNER = create_drop_db_test_user - CONNECTION LIMIT = 10 - ENCODING = 'UTF8' - TABLESPACE = create_drop_db_tablespace - ALLOW_CONNECTIONS = true - IS_TEMPLATE = false - OID = 966345; -NOTICE: issuing CREATE DATABASE mydatabase OWNER create_drop_db_test_user CONNECTION LIMIT 10 ENCODING 'UTF8' TABLESPACE create_drop_db_tablespace ALLOW_CONNECTIONS true IS_TEMPLATE false OID 966345 -DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing CREATE DATABASE mydatabase OWNER create_drop_db_test_user CONNECTION LIMIT 10 ENCODING 'UTF8' TABLESPACE create_drop_db_tablespace ALLOW_CONNECTIONS true IS_TEMPLATE false OID 966345 -DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -SET citus.log_remote_commands = false; -SELECT result from run_command_on_all_nodes( - $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'mydatabase' - ) q2 - $$ -) ORDER BY result; - result + WITH OID = 966345; +ERROR: CREATE DATABASE option "oid" is not supported +CREATE DATABASE mydatabase + WITH strategy file_copy; +ERROR: Only wal_log is supported as strategy parameter for CREATE DATABASE +CREATE DATABASE st_wal_log + WITH strategy WaL_LoG; +SELECT * FROM public.check_database_on_all_nodes('st_wal_log') ORDER BY node_type; + node_type | result --------------------------------------------------------------------- - [{"datacl": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": true, "datconnlimit": 10, "datistemplate": false, "database_owner": "create_drop_db_test_user"}] - [{"datacl": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": true, "datconnlimit": 10, "datistemplate": false, "database_owner": "create_drop_db_test_user"}] - [{"datacl": null, "datname": "mydatabase", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": true, "datconnlimit": 10, "datistemplate": false, "database_owner": "create_drop_db_test_user"}] + coordinator (local) | {"database_properties": {"datacl": null, "datname": "st_wal_log", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "st_wal_log", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "st_wal_log", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} (3 rows) -SET citus.log_remote_commands = true; -set citus.grep_remote_commands = '%DROP DATABASE%'; -drop database mydatabase; -NOTICE: issuing DROP DATABASE mydatabase -DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing DROP DATABASE mydatabase -DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -SET citus.log_remote_commands = false; -SELECT result from run_command_on_all_nodes( - $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'mydatabase' - ) q2 - $$ -) ORDER BY result; - result ---------------------------------------------------------------------- - - - -(3 rows) - -select citus_remove_node('localhost', :worker_2_port); - citus_remove_node +drop database st_wal_log; +select 1 from citus_remove_node('localhost', :worker_2_port); + ?column? --------------------------------------------------------------------- - + 1 (1 row) -SET citus.log_remote_commands = true; -set citus.grep_remote_commands = '%CREATE DATABASE%'; -CREATE DATABASE mydatabase2 - WITH OWNER = create_drop_db_test_user - ENCODING = 'UTF8' - TABLESPACE = create_drop_db_tablespace - ALLOW_CONNECTIONS = true - IS_TEMPLATE = false - OID = 966345; -NOTICE: issuing CREATE DATABASE mydatabase2 OWNER create_drop_db_test_user ENCODING 'UTF8' TABLESPACE create_drop_db_tablespace ALLOW_CONNECTIONS true IS_TEMPLATE false OID 966345 -DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -SET citus.log_remote_commands = false; -SELECT result from run_command_on_all_nodes( - $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'mydatabase2' - ) q2 - $$ -) ORDER BY result; - result ---------------------------------------------------------------------- - [{"datacl": null, "datname": "mydatabase2", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": true, "datconnlimit": -1, "datistemplate": false, "database_owner": "create_drop_db_test_user"}] - [{"datacl": null, "datname": "mydatabase2", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": true, "datconnlimit": -1, "datistemplate": false, "database_owner": "create_drop_db_test_user"}] -(2 rows) - +-- test COLLATION_VERSION +CREATE DATABASE test_collation_version + WITH ENCODING = 'UTF8' + COLLATION_VERSION = '1.0' + ALLOW_CONNECTIONS = false; select 1 from citus_add_node('localhost', :worker_2_port); ?column? --------------------------------------------------------------------- 1 (1 row) -SELECT result from run_command_on_all_nodes( - $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'mydatabase2' - ) q2 - $$ -) ORDER BY result; - result +SELECT * FROM public.check_database_on_all_nodes('test_collation_version') ORDER BY node_type; + node_type | result --------------------------------------------------------------------- - [{"datacl": null, "datname": "mydatabase2", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": true, "datconnlimit": -1, "datistemplate": false, "database_owner": "create_drop_db_test_user"}] - [{"datacl": null, "datname": "mydatabase2", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": true, "datconnlimit": -1, "datistemplate": false, "database_owner": "create_drop_db_test_user"}] - [{"datacl": null, "datname": "mydatabase2", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": true, "datconnlimit": -1, "datistemplate": false, "database_owner": "create_drop_db_test_user"}] + coordinator (local) | {"database_properties": {"datacl": null, "datname": "test_collation_version", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": false, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "datcollversion": "1.0", "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": null, "datname": "test_collation_version", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": false, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "datcollversion": "1.0", "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": null, "datname": "test_collation_version", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": false, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "datcollversion": "1.0", "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} (3 rows) -SET citus.log_remote_commands = true; -set citus.grep_remote_commands = '%DROP DATABASE%'; -drop database mydatabase2; -NOTICE: issuing DROP DATABASE mydatabase2 -DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing DROP DATABASE mydatabase2 -DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -SET citus.log_remote_commands = false; -SELECT result from run_command_on_all_nodes( - $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'mydatabase' - ) q2 - $$ -) ORDER BY result; - result +drop database test_collation_version; +SET client_min_messages TO WARNING; +-- test LOCALE_PROVIDER & ICU_LOCALE +CREATE DATABASE test_locale_provider + WITH ENCODING = 'UTF8' + LOCALE_PROVIDER = 'icu' + ICU_LOCALE = 'en_US'; +ERROR: new locale provider (icu) does not match locale provider of the template database (libc) +HINT: Use the same locale provider as in the template database, or use template0 as template. +RESET client_min_messages; +CREATE DATABASE test_locale_provider + WITH ENCODING = 'UTF8' + LOCALE_PROVIDER = 'libc' + ICU_LOCALE = 'en_US'; +ERROR: ICU locale cannot be specified unless locale provider is ICU +CREATE DATABASE test_locale_provider + WITH ENCODING = 'UTF8' + LOCALE_PROVIDER = 'libc'; +SELECT * FROM public.check_database_on_all_nodes('test_locale_provider') ORDER BY node_type; + node_type | result --------------------------------------------------------------------- - - - + coordinator (local) | {"database_properties": {"datacl": null, "datname": "test_locale_provider", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "test_locale_provider", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "test_locale_provider", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} (3 rows) -SET citus.log_remote_commands = true; -set citus.grep_remote_commands = '%CREATE DATABASE%'; --- create a template database with all options set and allow connections false -CREATE DATABASE my_template_database - WITH OWNER = create_drop_db_test_user - ENCODING = 'UTF8' - COLLATION_VERSION = '1.0' - TABLESPACE = create_drop_db_tablespace - ALLOW_CONNECTIONS = false - IS_TEMPLATE = true; -NOTICE: issuing CREATE DATABASE my_template_database OWNER create_drop_db_test_user ENCODING 'UTF8' COLLATION_VERSION '1.0' TABLESPACE create_drop_db_tablespace ALLOW_CONNECTIONS false IS_TEMPLATE true -DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing CREATE DATABASE my_template_database OWNER create_drop_db_test_user ENCODING 'UTF8' COLLATION_VERSION '1.0' TABLESPACE create_drop_db_tablespace ALLOW_CONNECTIONS false IS_TEMPLATE true -DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -SET citus.log_remote_commands = false; -SELECT result from run_command_on_all_nodes( - $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'my_template_database' - ) q2 - $$ -) ORDER BY result; - result ---------------------------------------------------------------------- - [{"datacl": null, "datname": "my_template_database", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": false, "datconnlimit": -1, "datistemplate": true, "database_owner": "create_drop_db_test_user"}] - [{"datacl": null, "datname": "my_template_database", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": false, "datconnlimit": -1, "datistemplate": true, "database_owner": "create_drop_db_test_user"}] - [{"datacl": null, "datname": "my_template_database", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": false, "datconnlimit": -1, "datistemplate": true, "database_owner": "create_drop_db_test_user"}] -(3 rows) - -SET citus.log_remote_commands = false; -SELECT result from run_command_on_all_nodes( - $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'my_template_database' - ) q2 - $$ -) ORDER BY result; - result ---------------------------------------------------------------------- - [{"datacl": null, "datname": "my_template_database", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": false, "datconnlimit": -1, "datistemplate": true, "database_owner": "create_drop_db_test_user"}] - [{"datacl": null, "datname": "my_template_database", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": false, "datconnlimit": -1, "datistemplate": true, "database_owner": "create_drop_db_test_user"}] - [{"datacl": null, "datname": "my_template_database", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "create_drop_db_tablespace", "datallowconn": false, "datconnlimit": -1, "datistemplate": true, "database_owner": "create_drop_db_test_user"}] -(3 rows) - -SET citus.log_remote_commands = true; ---template databases could not be dropped so we need to change the template flag -SELECT result from run_command_on_all_nodes( - $$ - UPDATE pg_database SET datistemplate = false WHERE datname = 'my_template_database' - $$ -) ORDER BY result; - result ---------------------------------------------------------------------- - UPDATE 1 - UPDATE 1 - UPDATE 1 -(3 rows) - -set citus.grep_remote_commands = '%DROP DATABASE%'; -drop database my_template_database; -NOTICE: issuing DROP DATABASE my_template_database -DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing DROP DATABASE my_template_database -DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -SET citus.log_remote_commands = false; -SELECT result from run_command_on_all_nodes( - $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'my_template_database' - ) q2 - $$ -) ORDER BY result; - result ---------------------------------------------------------------------- - - - -(3 rows) - ---tests for special characters in database name -set citus.enable_create_database_propagation=on; -SET citus.log_remote_commands = true; -set citus.grep_remote_commands = '%CREATE DATABASE%'; -create database "mydatabase#1'2"; -NOTICE: issuing CREATE DATABASE "mydatabase#1'2" -DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing CREATE DATABASE "mydatabase#1'2" -DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -set citus.grep_remote_commands = '%DROP DATABASE%'; -drop database if exists "mydatabase#1'2"; -NOTICE: issuing DROP DATABASE IF EXISTS "mydatabase#1'2" -DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing DROP DATABASE IF EXISTS "mydatabase#1'2" -DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -\c - - - :master_port -drop tablespace create_drop_db_tablespace; -\c - - - :worker_1_port -drop tablespace create_drop_db_tablespace; -\c - - - :worker_2_port -drop tablespace create_drop_db_tablespace; +drop database test_locale_provider; \c - - - :master_port -drop user create_drop_db_test_user; diff --git a/src/test/regress/expected/create_drop_database_propagation_pg16.out b/src/test/regress/expected/create_drop_database_propagation_pg16.out new file mode 100644 index 00000000000..75cd99e616c --- /dev/null +++ b/src/test/regress/expected/create_drop_database_propagation_pg16.out @@ -0,0 +1,23 @@ +-- +-- PG16 +-- +SHOW server_version \gset +SELECT substring(:'server_version', '\d+')::int >= 16 AS server_version_ge_16 +\gset +\if :server_version_ge_16 +\else +\q +\endif +-- create/drop database for pg >= 16 +set citus.enable_create_database_propagation=on; +-- test icu_rules +-- +-- practically we don't support it but better to test +CREATE DATABASE citus_icu_rules_test WITH icu_rules='de_DE@collation=phonebook'; +ERROR: ICU rules cannot be specified unless locale provider is ICU +CREATE DATABASE citus_icu_rules_test WITH icu_rules='de_DE@collation=phonebook' locale_provider='icu'; +ERROR: LOCALE or ICU_LOCALE must be specified +CREATE DATABASE citus_icu_rules_test WITH icu_rules='de_DE@collation=phonebook' locale_provider='icu' icu_locale = 'de_DE'; +NOTICE: using standard form "de-DE" for ICU locale "de_DE" +ERROR: new locale provider (icu) does not match locale provider of the template database (libc) +HINT: Use the same locale provider as in the template database, or use template0 as template. diff --git a/src/test/regress/expected/create_drop_database_propagation_pg16_0.out b/src/test/regress/expected/create_drop_database_propagation_pg16_0.out new file mode 100644 index 00000000000..730c916cadc --- /dev/null +++ b/src/test/regress/expected/create_drop_database_propagation_pg16_0.out @@ -0,0 +1,9 @@ +-- +-- PG16 +-- +SHOW server_version \gset +SELECT substring(:'server_version', '\d+')::int >= 16 AS server_version_ge_16 +\gset +\if :server_version_ge_16 +\else +\q diff --git a/src/test/regress/expected/multi_test_helpers.out b/src/test/regress/expected/multi_test_helpers.out index da771b4c5eb..1d077bb1170 100644 --- a/src/test/regress/expected/multi_test_helpers.out +++ b/src/test/regress/expected/multi_test_helpers.out @@ -534,7 +534,22 @@ $func$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION check_database_on_all_nodes(p_database_name text) RETURNS TABLE (node_type text, result text) AS $func$ +DECLARE + pg_ge_15_options text := ''; + pg_ge_16_options text := ''; BEGIN + IF EXISTS (SELECT 1 FROM pg_attribute WHERE attrelid = 'pg_database'::regclass AND attname = 'datlocprovider') THEN + pg_ge_15_options := ', daticulocale, datcollversion, datlocprovider'; + ELSE + pg_ge_15_options := $$, null as daticulocale, null as datcollversion, 'c' as datlocprovider$$; + END IF; + + IF EXISTS (SELECT 1 FROM pg_attribute WHERE attrelid = 'pg_database'::regclass AND attname = 'daticurules') THEN + pg_ge_16_options := ', daticurules'; + ELSE + pg_ge_16_options := ', null as daticurules'; + END IF; + RETURN QUERY SELECT CASE WHEN (groupid = 0 AND groupid = (SELECT groupid FROM pg_dist_local_group)) THEN 'coordinator (local)' @@ -553,19 +568,20 @@ BEGIN SELECT to_jsonb(database_properties.*) FROM ( SELECT datname, pa.rolname as database_owner, - pg_encoding_to_char(pd.encoding) as encoding, datlocprovider, - datistemplate, datallowconn, datconnlimit, - pt.spcname AS tablespace, datcollate, datctype, daticulocale, - datcollversion, datacl + pg_encoding_to_char(pd.encoding) as encoding, + datistemplate, datallowconn, datconnlimit, datacl, + pt.spcname AS tablespace, datcollate, datctype + %2$s -- >= pg15 options + %3$s -- >= pg16 options FROM pg_database pd JOIN pg_authid pa ON pd.datdba = pa.oid JOIN pg_tablespace pt ON pd.dattablespace = pt.oid - WHERE datname = '%s' + WHERE datname = '%1$s' ) database_properties ) AS database_properties, ( SELECT COUNT(*)=1 - FROM pg_dist_object WHERE objid = (SELECT oid FROM pg_database WHERE datname = '%s') + FROM pg_dist_object WHERE objid = (SELECT oid FROM pg_database WHERE datname = '%1$s') ) AS pg_dist_object_record_for_db_exists, ( SELECT COUNT(*) > 0 @@ -574,7 +590,7 @@ BEGIN ) AS stale_pg_dist_object_record_for_a_db_exists ) q $$, - p_database_name, p_database_name + p_database_name, pg_ge_15_options, pg_ge_16_options ) ) q2 JOIN pg_dist_node USING (nodeid); diff --git a/src/test/regress/multi_1_schedule b/src/test/regress/multi_1_schedule index 73696bde697..c376d44a82c 100644 --- a/src/test/regress/multi_1_schedule +++ b/src/test/regress/multi_1_schedule @@ -36,6 +36,7 @@ test: distributed_triggers 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 diff --git a/src/test/regress/sql/create_drop_database_propagation_pg15.sql b/src/test/regress/sql/create_drop_database_propagation_pg15.sql index ca3e3b20235..40d1b9e0987 100644 --- a/src/test/regress/sql/create_drop_database_propagation_pg15.sql +++ b/src/test/regress/sql/create_drop_database_propagation_pg15.sql @@ -9,236 +9,57 @@ SELECT substring(:'server_version', '\d+')::int >= 15 AS server_version_ge_15 \q \endif --- create/drop database for pg > 15 +-- create/drop database for pg >= 15 +set citus.enable_create_database_propagation=on; -\set create_drop_db_tablespace :abs_srcdir '/tmp_check/ts3' -CREATE TABLESPACE create_drop_db_tablespace LOCATION :'create_drop_db_tablespace'; +CREATE DATABASE mydatabase + WITH OID = 966345; -\c - - - :worker_1_port -\set create_drop_db_tablespace :abs_srcdir '/tmp_check/ts4' -CREATE TABLESPACE create_drop_db_tablespace LOCATION :'create_drop_db_tablespace'; +CREATE DATABASE mydatabase + WITH strategy file_copy; -\c - - - :worker_2_port -\set create_drop_db_tablespace :abs_srcdir '/tmp_check/ts5' -CREATE TABLESPACE create_drop_db_tablespace LOCATION :'create_drop_db_tablespace'; +CREATE DATABASE st_wal_log + WITH strategy WaL_LoG; -\c - - - :master_port -create user create_drop_db_test_user; -set citus.enable_create_database_propagation=on; -SET citus.log_remote_commands = true; -set citus.grep_remote_commands = '%CREATE DATABASE%'; -CREATE DATABASE mydatabase - WITH - OWNER = create_drop_db_test_user - CONNECTION LIMIT = 10 - ENCODING = 'UTF8' - TABLESPACE = create_drop_db_tablespace - ALLOW_CONNECTIONS = true - IS_TEMPLATE = false - OID = 966345; - -SET citus.log_remote_commands = false; - -SELECT result from run_command_on_all_nodes( - $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'mydatabase' - ) q2 - $$ -) ORDER BY result; - -SET citus.log_remote_commands = true; -set citus.grep_remote_commands = '%DROP DATABASE%'; -drop database mydatabase; - -SET citus.log_remote_commands = false; -SELECT result from run_command_on_all_nodes( - $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'mydatabase' - ) q2 - $$ -) ORDER BY result; - -select citus_remove_node('localhost', :worker_2_port); - - -SET citus.log_remote_commands = true; -set citus.grep_remote_commands = '%CREATE DATABASE%'; - -CREATE DATABASE mydatabase2 - WITH OWNER = create_drop_db_test_user - ENCODING = 'UTF8' - TABLESPACE = create_drop_db_tablespace - ALLOW_CONNECTIONS = true - IS_TEMPLATE = false - OID = 966345; - -SET citus.log_remote_commands = false; -SELECT result from run_command_on_all_nodes( - $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'mydatabase2' - ) q2 - $$ -) ORDER BY result; +SELECT * FROM public.check_database_on_all_nodes('st_wal_log') ORDER BY node_type; +drop database st_wal_log; -select 1 from citus_add_node('localhost', :worker_2_port); +select 1 from citus_remove_node('localhost', :worker_2_port); -SELECT result from run_command_on_all_nodes( - $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'mydatabase2' - ) q2 - $$ -) ORDER BY result; - -SET citus.log_remote_commands = true; -set citus.grep_remote_commands = '%DROP DATABASE%'; -drop database mydatabase2; - -SET citus.log_remote_commands = false; - -SELECT result from run_command_on_all_nodes( - $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'mydatabase' - ) q2 - $$ -) ORDER BY result; - -SET citus.log_remote_commands = true; -set citus.grep_remote_commands = '%CREATE DATABASE%'; - --- create a template database with all options set and allow connections false -CREATE DATABASE my_template_database - WITH OWNER = create_drop_db_test_user - ENCODING = 'UTF8' +-- test COLLATION_VERSION + +CREATE DATABASE test_collation_version + WITH ENCODING = 'UTF8' COLLATION_VERSION = '1.0' - TABLESPACE = create_drop_db_tablespace - ALLOW_CONNECTIONS = false - IS_TEMPLATE = true; - -SET citus.log_remote_commands = false; - -SELECT result from run_command_on_all_nodes( - $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'my_template_database' - ) q2 - $$ -) ORDER BY result; - - -SET citus.log_remote_commands = false; - -SELECT result from run_command_on_all_nodes( - $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'my_template_database' - ) q2 - $$ -) ORDER BY result; - -SET citus.log_remote_commands = true; - ---template databases could not be dropped so we need to change the template flag -SELECT result from run_command_on_all_nodes( - $$ - UPDATE pg_database SET datistemplate = false WHERE datname = 'my_template_database' - $$ -) ORDER BY result; - - -set citus.grep_remote_commands = '%DROP DATABASE%'; -drop database my_template_database; - -SET citus.log_remote_commands = false; -SELECT result from run_command_on_all_nodes( - $$ - SELECT jsonb_agg(to_jsonb(q2.*)) FROM ( - SELECT pd.datname, pg_encoding_to_char(pd.encoding) as encoding, - pd.datistemplate, pd.datallowconn, pd.datconnlimit, - pd.datcollate , pd. datctype , pd.datacl, - pa.rolname AS database_owner, pt.spcname AS tablespace - FROM pg_database pd - JOIN pg_authid pa ON pd.datdba = pa.oid - join pg_tablespace pt on pd.dattablespace = pt.oid - WHERE datname = 'my_template_database' - ) q2 - $$ -) ORDER BY result; - - ---tests for special characters in database name -set citus.enable_create_database_propagation=on; -SET citus.log_remote_commands = true; -set citus.grep_remote_commands = '%CREATE DATABASE%'; + ALLOW_CONNECTIONS = false; -create database "mydatabase#1'2"; +select 1 from citus_add_node('localhost', :worker_2_port); -set citus.grep_remote_commands = '%DROP DATABASE%'; -drop database if exists "mydatabase#1'2"; +SELECT * FROM public.check_database_on_all_nodes('test_collation_version') ORDER BY node_type; -\c - - - :master_port -drop tablespace create_drop_db_tablespace; +drop database test_collation_version; + +SET client_min_messages TO WARNING; +-- test LOCALE_PROVIDER & ICU_LOCALE +CREATE DATABASE test_locale_provider + WITH ENCODING = 'UTF8' + LOCALE_PROVIDER = 'icu' + ICU_LOCALE = 'en_US'; +RESET client_min_messages; + +CREATE DATABASE test_locale_provider + WITH ENCODING = 'UTF8' + LOCALE_PROVIDER = 'libc' + ICU_LOCALE = 'en_US'; + +CREATE DATABASE test_locale_provider + WITH ENCODING = 'UTF8' + LOCALE_PROVIDER = 'libc'; -\c - - - :worker_1_port -drop tablespace create_drop_db_tablespace; +SELECT * FROM public.check_database_on_all_nodes('test_locale_provider') ORDER BY node_type; -\c - - - :worker_2_port -drop tablespace create_drop_db_tablespace; +drop database test_locale_provider; \c - - - :master_port -drop user create_drop_db_test_user; diff --git a/src/test/regress/sql/create_drop_database_propagation_pg16.sql b/src/test/regress/sql/create_drop_database_propagation_pg16.sql new file mode 100644 index 00000000000..cec55381325 --- /dev/null +++ b/src/test/regress/sql/create_drop_database_propagation_pg16.sql @@ -0,0 +1,22 @@ +-- +-- PG16 +-- +SHOW server_version \gset +SELECT substring(:'server_version', '\d+')::int >= 16 AS server_version_ge_16 +\gset +\if :server_version_ge_16 +\else +\q +\endif + +-- create/drop database for pg >= 16 + +set citus.enable_create_database_propagation=on; + +-- test icu_rules +-- +-- practically we don't support it but better to test + +CREATE DATABASE citus_icu_rules_test WITH icu_rules='de_DE@collation=phonebook'; +CREATE DATABASE citus_icu_rules_test WITH icu_rules='de_DE@collation=phonebook' locale_provider='icu'; +CREATE DATABASE citus_icu_rules_test WITH icu_rules='de_DE@collation=phonebook' locale_provider='icu' icu_locale = 'de_DE'; diff --git a/src/test/regress/sql/multi_test_helpers.sql b/src/test/regress/sql/multi_test_helpers.sql index 2e8654ce201..835174169e3 100644 --- a/src/test/regress/sql/multi_test_helpers.sql +++ b/src/test/regress/sql/multi_test_helpers.sql @@ -560,7 +560,22 @@ $func$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION check_database_on_all_nodes(p_database_name text) RETURNS TABLE (node_type text, result text) AS $func$ +DECLARE + pg_ge_15_options text := ''; + pg_ge_16_options text := ''; BEGIN + IF EXISTS (SELECT 1 FROM pg_attribute WHERE attrelid = 'pg_database'::regclass AND attname = 'datlocprovider') THEN + pg_ge_15_options := ', daticulocale, datcollversion, datlocprovider'; + ELSE + pg_ge_15_options := $$, null as daticulocale, null as datcollversion, 'c' as datlocprovider$$; + END IF; + + IF EXISTS (SELECT 1 FROM pg_attribute WHERE attrelid = 'pg_database'::regclass AND attname = 'daticurules') THEN + pg_ge_16_options := ', daticurules'; + ELSE + pg_ge_16_options := ', null as daticurules'; + END IF; + RETURN QUERY SELECT CASE WHEN (groupid = 0 AND groupid = (SELECT groupid FROM pg_dist_local_group)) THEN 'coordinator (local)' @@ -579,19 +594,20 @@ BEGIN SELECT to_jsonb(database_properties.*) FROM ( SELECT datname, pa.rolname as database_owner, - pg_encoding_to_char(pd.encoding) as encoding, datlocprovider, - datistemplate, datallowconn, datconnlimit, - pt.spcname AS tablespace, datcollate, datctype, daticulocale, - datcollversion, datacl + pg_encoding_to_char(pd.encoding) as encoding, + datistemplate, datallowconn, datconnlimit, datacl, + pt.spcname AS tablespace, datcollate, datctype + %2$s -- >= pg15 options + %3$s -- >= pg16 options FROM pg_database pd JOIN pg_authid pa ON pd.datdba = pa.oid JOIN pg_tablespace pt ON pd.dattablespace = pt.oid - WHERE datname = '%s' + WHERE datname = '%1$s' ) database_properties ) AS database_properties, ( SELECT COUNT(*)=1 - FROM pg_dist_object WHERE objid = (SELECT oid FROM pg_database WHERE datname = '%s') + FROM pg_dist_object WHERE objid = (SELECT oid FROM pg_database WHERE datname = '%1$s') ) AS pg_dist_object_record_for_db_exists, ( SELECT COUNT(*) > 0 @@ -600,7 +616,7 @@ BEGIN ) AS stale_pg_dist_object_record_for_a_db_exists ) q $$, - p_database_name, p_database_name + p_database_name, pg_ge_15_options, pg_ge_16_options ) ) q2 JOIN pg_dist_node USING (nodeid); From 1a80181a9c125e9c6a31d26098e44aaf17af9d4d Mon Sep 17 00:00:00 2001 From: Onur Tirtir Date: Mon, 13 Nov 2023 13:42:11 +0300 Subject: [PATCH 12/68] indent --- .../distributed/deparser/deparse_database_stmts.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/backend/distributed/deparser/deparse_database_stmts.c b/src/backend/distributed/deparser/deparse_database_stmts.c index 2c1dc0b66e8..4551ccd1de7 100644 --- a/src/backend/distributed/deparser/deparse_database_stmts.c +++ b/src/backend/distributed/deparser/deparse_database_stmts.c @@ -271,14 +271,14 @@ ValidateCreateDatabaseOptions(DefElem *option) if (strcmp(option->defname, "template") == 0 && strcmp(optionValue, "template1") != 0) { ereport(ERROR, errmsg("Only template1 is supported as template " - "parameter for CREATE DATABASE")); + "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")); - } + 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")); + } } From 8728871cc313f29162892b84caec24c9a94fbe4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCrkan=20=C4=B0ndibay?= Date: Mon, 13 Nov 2023 14:56:24 +0300 Subject: [PATCH 13/68] Apply suggestions from code review Co-authored-by: Onur Tirtir --- src/backend/distributed/commands/database.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/distributed/commands/database.c b/src/backend/distributed/commands/database.c index 2d0a2ce1692..07cfda8056c 100644 --- a/src/backend/distributed/commands/database.c +++ b/src/backend/distributed/commands/database.c @@ -296,7 +296,7 @@ PreprocessCreateDatabaseStmt(Node *node, const char *queryString, /* - * PostprocessCreatedbStmt is executed after the statement is applied to the local + * 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 * 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 @@ -324,7 +324,7 @@ PostprocessCreateDatabaseStmt(Node *node, const char *queryString) /* - * PostprocessAlterDatabaseStmt is executed after the statement is applied to the local + * PreprocessDropDatabaseStmt 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 * all workers to drop the database. Since the DROP DATABASE statement gives error in * transaction context, we need to use NontransactionalNodeDDLTaskList to send the From 52c9e92544341758b1ada76ec311303bc3c096c2 Mon Sep 17 00:00:00 2001 From: gindibay Date: Mon, 13 Nov 2023 13:44:08 +0300 Subject: [PATCH 14/68] Adds IsAnyObjectDistributed check for db --- src/backend/distributed/metadata/distobject.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/backend/distributed/metadata/distobject.c b/src/backend/distributed/metadata/distobject.c index 94c12d47f5d..9c5b6bf6162 100644 --- a/src/backend/distributed/metadata/distobject.c +++ b/src/backend/distributed/metadata/distobject.c @@ -386,7 +386,10 @@ UnmarkNodeWideObjectsDistributed(Node *node) Oid dbOid = get_database_oid(dbName, stmt->missing_ok); ObjectAddress *dbObjectAddress = palloc0(sizeof(ObjectAddress)); ObjectAddressSet(*dbObjectAddress, DatabaseRelationId, dbOid); - UnmarkObjectDistributed(dbObjectAddress); + if(IsAnyObjectDistributed(list_make1(dbObjectAddress))) + { + UnmarkObjectDistributed(dbObjectAddress); + } } } From 3731c45c295c7ec862d9ef4e7eb6b96d485c5e35 Mon Sep 17 00:00:00 2001 From: gindibay Date: Mon, 13 Nov 2023 14:19:19 +0300 Subject: [PATCH 15/68] Fixes drop force option --- .../deparser/deparse_database_stmts.c | 20 ++++++++++++++++++- .../create_drop_database_propagation.out | 18 +++++++++++++++++ .../sql/create_drop_database_propagation.sql | 14 +++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/backend/distributed/deparser/deparse_database_stmts.c b/src/backend/distributed/deparser/deparse_database_stmts.c index 4551ccd1de7..32b89b419bd 100644 --- a/src/backend/distributed/deparser/deparse_database_stmts.c +++ b/src/backend/distributed/deparser/deparse_database_stmts.c @@ -329,11 +329,22 @@ AppendDropDatabaseStmt(StringInfo buf, DropdbStmt *stmt) DefElem *option = NULL; + foreach_ptr(option, stmt->options) { + //if it is the first option then append with "WITH" else append with "," + if (option == linitial(stmt->options)) + { + appendStringInfo(buf, " WITH ( "); + } + else + { + appendStringInfo(buf, ", "); + } + if (strcmp(option->defname, "force") == 0) { - appendStringInfo(buf, " FORCE"); + appendStringInfo(buf, "FORCE"); } else { @@ -341,6 +352,13 @@ AppendDropDatabaseStmt(StringInfo buf, DropdbStmt *stmt) errmsg("unrecognized DROP DATABASE option \"%s\"", option->defname))); } + + //if it is the last option then append with ")" + if (option == llast(stmt->options)) + { + appendStringInfo(buf, " )"); + } + } } diff --git a/src/test/regress/expected/create_drop_database_propagation.out b/src/test/regress/expected/create_drop_database_propagation.out index 18bfa0c8ab5..7bd0cc8fa40 100644 --- a/src/test/regress/expected/create_drop_database_propagation.out +++ b/src/test/regress/expected/create_drop_database_propagation.out @@ -477,6 +477,24 @@ SET citus.enable_create_database_propagation TO ON; DROP DATABASE test_node_activation; DROP DATABASE db_needs_escape; DROP USER "role-needs\!escape"; +-- drop database with force options test +create database db_force_test; +SET citus.log_remote_commands = true; +set citus.grep_remote_commands = '%DROP DATABASE%'; +drop database db_force_test with (force); +NOTICE: issuing DROP DATABASE db_force_test WITH ( FORCE ) +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing DROP DATABASE db_force_test WITH ( FORCE ) +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +reset citus.log_remote_commands; +SELECT * FROM public.check_database_on_all_nodes('db_force_test') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator (local) | {"database_properties": null, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} + worker node (remote) | {"database_properties": null, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} + worker node (remote) | {"database_properties": null, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} +(3 rows) + --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( diff --git a/src/test/regress/sql/create_drop_database_propagation.sql b/src/test/regress/sql/create_drop_database_propagation.sql index 6e6ecacb5ba..1b2f96da2ef 100644 --- a/src/test/regress/sql/create_drop_database_propagation.sql +++ b/src/test/regress/sql/create_drop_database_propagation.sql @@ -261,6 +261,20 @@ DROP DATABASE test_node_activation; DROP DATABASE db_needs_escape; DROP USER "role-needs\!escape"; +-- drop database with force options test + +create database db_force_test; + +SET citus.log_remote_commands = true; +set citus.grep_remote_commands = '%DROP DATABASE%'; + +drop database db_force_test with (force); + +reset citus.log_remote_commands; + +SELECT * FROM public.check_database_on_all_nodes('db_force_test') ORDER BY node_type; + + --clean up resources created by this test -- DROP TABLESPACE is not supported, so we need to drop it manually. From 712fd8ebf358a341b99ce44c58805c1377d6b1d3 Mon Sep 17 00:00:00 2001 From: gindibay Date: Mon, 13 Nov 2023 14:23:37 +0300 Subject: [PATCH 16/68] Fixes comments --- src/backend/distributed/deparser/deparse_database_stmts.c | 5 ++--- src/backend/distributed/metadata/distobject.c | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/backend/distributed/deparser/deparse_database_stmts.c b/src/backend/distributed/deparser/deparse_database_stmts.c index 32b89b419bd..34c4a2dce85 100644 --- a/src/backend/distributed/deparser/deparse_database_stmts.c +++ b/src/backend/distributed/deparser/deparse_database_stmts.c @@ -332,7 +332,7 @@ AppendDropDatabaseStmt(StringInfo buf, DropdbStmt *stmt) foreach_ptr(option, stmt->options) { - //if it is the first option then append with "WITH" else append with "," + /*if it is the first option then append with "WITH" else append with "," */ if (option == linitial(stmt->options)) { appendStringInfo(buf, " WITH ( "); @@ -353,12 +353,11 @@ AppendDropDatabaseStmt(StringInfo buf, DropdbStmt *stmt) option->defname))); } - //if it is the last option then append with ")" + /*if it is the last option then append with ")" */ if (option == llast(stmt->options)) { appendStringInfo(buf, " )"); } - } } diff --git a/src/backend/distributed/metadata/distobject.c b/src/backend/distributed/metadata/distobject.c index 9c5b6bf6162..9f31add60b8 100644 --- a/src/backend/distributed/metadata/distobject.c +++ b/src/backend/distributed/metadata/distobject.c @@ -386,7 +386,7 @@ UnmarkNodeWideObjectsDistributed(Node *node) Oid dbOid = get_database_oid(dbName, stmt->missing_ok); ObjectAddress *dbObjectAddress = palloc0(sizeof(ObjectAddress)); ObjectAddressSet(*dbObjectAddress, DatabaseRelationId, dbOid); - if(IsAnyObjectDistributed(list_make1(dbObjectAddress))) + if (IsAnyObjectDistributed(list_make1(dbObjectAddress))) { UnmarkObjectDistributed(dbObjectAddress); } From e4ac3e6d9a423b88c023a277dea017986bc5a519 Mon Sep 17 00:00:00 2001 From: Naisila Puka <37271756+naisila@users.noreply.github.com> Date: Mon, 13 Nov 2023 15:05:38 +0300 Subject: [PATCH 17/68] Bump PG versions to latest minors 14.10, 15.5, 16.1 (#7336) Postgres got minor updates on Nov9, this starts using the images with the latest version for our tests, namely 14.10, 15.5 and 16.1. These minor updates were compatible with Citus. Sister PR: https://github.com/citusdata/the-process/pull/152 --- .devcontainer/Dockerfile | 8 ++++---- .github/workflows/build_and_test.yml | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 1c1a2f08338..c2cab027294 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -68,7 +68,7 @@ USER citus # build postgres versions separately for effective parrallelism and caching of already built versions when changing only certain versions FROM base AS pg14 -RUN MAKEFLAGS="-j $(nproc)" pgenv build 14.9 +RUN MAKEFLAGS="-j $(nproc)" pgenv build 14.10 RUN rm .pgenv/src/*.tar* RUN make -C .pgenv/src/postgresql-*/ clean RUN make -C .pgenv/src/postgresql-*/src/include install @@ -80,7 +80,7 @@ RUN cp -r .pgenv/src .pgenv/pgsql-* .pgenv/config .pgenv-staging/ RUN rm .pgenv-staging/config/default.conf FROM base AS pg15 -RUN MAKEFLAGS="-j $(nproc)" pgenv build 15.4 +RUN MAKEFLAGS="-j $(nproc)" pgenv build 15.5 RUN rm .pgenv/src/*.tar* RUN make -C .pgenv/src/postgresql-*/ clean RUN make -C .pgenv/src/postgresql-*/src/include install @@ -92,7 +92,7 @@ RUN cp -r .pgenv/src .pgenv/pgsql-* .pgenv/config .pgenv-staging/ RUN rm .pgenv-staging/config/default.conf FROM base AS pg16 -RUN MAKEFLAGS="-j $(nproc)" pgenv build 16.0 +RUN MAKEFLAGS="-j $(nproc)" pgenv build 16.1 RUN rm .pgenv/src/*.tar* RUN make -C .pgenv/src/postgresql-*/ clean RUN make -C .pgenv/src/postgresql-*/src/include install @@ -210,7 +210,7 @@ COPY --chown=citus:citus .psqlrc . RUN sudo chown --from=root:root citus:citus -R ~ # sets default pg version -RUN pgenv switch 16.0 +RUN pgenv switch 16.1 # make connecting to the coordinator easy ENV PGPORT=9700 diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index e938e3904cc..6b33c658f64 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -31,11 +31,11 @@ jobs: pgupgrade_image_name: "citus/pgupgradetester" style_checker_image_name: "citus/stylechecker" style_checker_tools_version: "0.8.18" - image_suffix: "-v9d71045" - pg14_version: '{ "major": "14", "full": "14.9" }' - pg15_version: '{ "major": "15", "full": "15.4" }' - pg16_version: '{ "major": "16", "full": "16.0" }' - upgrade_pg_versions: "14.9-15.4-16.0" + image_suffix: "-vbd8441d" + pg14_version: '{ "major": "14", "full": "14.10" }' + pg15_version: '{ "major": "15", "full": "15.5" }' + pg16_version: '{ "major": "16", "full": "16.1" }' + upgrade_pg_versions: "14.10-15.5-16.1" steps: # Since GHA jobs needs at least one step we use a noop step here. - name: Set up parameters From a9977e884018ba0380185c4aa95513d4ca11369c Mon Sep 17 00:00:00 2001 From: gindibay Date: Mon, 13 Nov 2023 15:49:19 +0300 Subject: [PATCH 18/68] Moves validation to preprocess for createdb --- src/backend/distributed/commands/database.c | 55 ++++++++++++++++++- .../deparser/deparse_database_stmts.c | 33 +---------- 2 files changed, 54 insertions(+), 34 deletions(-) diff --git a/src/backend/distributed/commands/database.c b/src/backend/distributed/commands/database.c index 07cfda8056c..82b7eed972f 100644 --- a/src/backend/distributed/commands/database.c +++ b/src/backend/distributed/commands/database.c @@ -21,6 +21,7 @@ #include "catalog/pg_database_d.h" #include "catalog/pg_tablespace.h" #include "commands/dbcommands.h" +#include "commands/defrem.h" #include "nodes/parsenodes.h" #include "utils/builtins.h" #include "utils/lsyscache.h" @@ -270,6 +271,55 @@ 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. @@ -288,8 +338,9 @@ PreprocessCreateDatabaseStmt(Node *node, const char *queryString, EnsureCoordinator(); - /*Validate the statement */ - DeparseTreeNode(node); + /*validate the statement*/ + CreatedbStmt *stmt = castNode(CreatedbStmt, node); + EnsureSupportedCreateDatabaseCommand(stmt); return NIL; } diff --git a/src/backend/distributed/deparser/deparse_database_stmts.c b/src/backend/distributed/deparser/deparse_database_stmts.c index 34c4a2dce85..e726fa84529 100644 --- a/src/backend/distributed/deparser/deparse_database_stmts.c +++ b/src/backend/distributed/deparser/deparse_database_stmts.c @@ -251,37 +251,6 @@ DeparseAlterDatabaseSetStmt(Node *node) } -/* - * Validates for if option is template, lc_type, locale or lc_collate, propagation will - * not be supported since template and strategy options are not stored in the catalog - * and lc_type, locale and lc_collate options depends on template parameter. - */ -static void -ValidateCreateDatabaseOptions(DefElem *option) -{ - 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")); - } -} - - static void AppendCreateDatabaseStmt(StringInfo buf, CreatedbStmt *stmt) { @@ -293,7 +262,7 @@ AppendCreateDatabaseStmt(StringInfo buf, CreatedbStmt *stmt) foreach_ptr(option, stmt->options) { - ValidateCreateDatabaseOptions(option); + /*ValidateCreateDatabaseOptions(option); */ DefElemOptionToStatement(buf, option, create_database_option_formats, lengthof(create_database_option_formats)); From fcdea98edd228580e15c9fa2e56885fd178b7844 Mon Sep 17 00:00:00 2001 From: gindibay Date: Mon, 13 Nov 2023 15:58:09 +0300 Subject: [PATCH 19/68] Removes drop in citus_internal_db_command udf --- .../distributed/metadata/metadata_sync.c | 18 +++--------------- .../create_drop_database_propagation.out | 4 ++-- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/src/backend/distributed/metadata/metadata_sync.c b/src/backend/distributed/metadata/metadata_sync.c index 5aa01b8acc2..56a0a8566b7 100644 --- a/src/backend/distributed/metadata/metadata_sync.c +++ b/src/backend/distributed/metadata/metadata_sync.c @@ -3895,7 +3895,7 @@ citus_internal_update_none_dist_table_metadata(PG_FUNCTION_ARGS) /* * citus_internal_database_command is an internal UDF to - * create/drop a database in an idempotent maner without + * create a database in an idempotent maner without * transaction block restrictions. */ Datum @@ -3925,7 +3925,7 @@ citus_internal_database_command(PG_FUNCTION_ARGS) GUC_ACTION_LOCAL, true, 0, false); /* - * createdb() / DropDatabase() uses ParseState to report the error position for the + * createdb() uses ParseState to report the error position for the * input command and the position is reported to be 0 when it's provided as NULL. * We're okay with that because we don't expect this UDF to be called with an incorrect * DDL command. @@ -3944,22 +3944,10 @@ citus_internal_database_command(PG_FUNCTION_ARGS) createdb(pstate, (CreatedbStmt *) parseTree); } } - else if (IsA(parseTree, DropdbStmt)) - { - DropdbStmt *stmt = castNode(DropdbStmt, parseTree); - - bool missingOk = false; - Oid databaseOid = get_database_oid(stmt->dbname, missingOk); - - if (OidIsValid(databaseOid)) - { - DropDatabase(pstate, (DropdbStmt *) parseTree); - } - } else { ereport(ERROR, (errmsg("citus_internal_database_command() can only be used " - "for CREATE DATABASE and DROP DATABASE commands by " + "for CREATE DATABASE command by " "Citus."))); } diff --git a/src/test/regress/expected/create_drop_database_propagation.out b/src/test/regress/expected/create_drop_database_propagation.out index 7bd0cc8fa40..5ab27ba34a4 100644 --- a/src/test/regress/expected/create_drop_database_propagation.out +++ b/src/test/regress/expected/create_drop_database_propagation.out @@ -25,9 +25,9 @@ SELECT pg_catalog.citus_internal_database_command(null); ERROR: command cannot be NULL -- fails on non create / drop db command SELECT pg_catalog.citus_internal_database_command('CREATE TABLE foo_bar(a int)'); -ERROR: citus_internal_database_command() can only be used for CREATE DATABASE and DROP DATABASE commands by Citus. +ERROR: citus_internal_database_command() can only be used for CREATE DATABASE command by Citus. SELECT pg_catalog.citus_internal_database_command('SELECT 1'); -ERROR: citus_internal_database_command() can only be used for CREATE DATABASE and DROP DATABASE commands by Citus. +ERROR: citus_internal_database_command() can only be used for CREATE DATABASE command by Citus. SELECT pg_catalog.citus_internal_database_command('asfsfdsg'); ERROR: syntax error at or near "asfsfdsg" SELECT pg_catalog.citus_internal_database_command(''); From b5388ddf929517dacc2c4d5c14986459584675ab Mon Sep 17 00:00:00 2001 From: gindibay Date: Mon, 13 Nov 2023 17:13:56 +0300 Subject: [PATCH 20/68] Adds icu_rules in meta_data sync --- src/backend/distributed/commands/database.c | 30 +++++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/backend/distributed/commands/database.c b/src/backend/distributed/commands/database.c index 82b7eed972f..cf6ee36a5d0 100644 --- a/src/backend/distributed/commands/database.c +++ b/src/backend/distributed/commands/database.c @@ -52,8 +52,9 @@ typedef struct DatabaseCollationInfo char *collation; char *ctype; #if PG_VERSION_NUM >= PG_VERSION_15 - char *icu_locale; + char *icuLocale; char *collversion; + char *icuRules; #endif } DatabaseCollationInfo; @@ -529,11 +530,11 @@ GetDatabaseCollation(Oid dbOid) &isNull); if (isNull) { - info.icu_locale = NULL; + info.icuLocale = NULL; } else { - info.icu_locale = TextDatumGetCString(icuLocaleDatum); + info.icuLocale = TextDatumGetCString(icuLocaleDatum); } Datum collverDatum = heap_getattr(tup, Anum_pg_database_datcollversion, tupdesc, @@ -548,6 +549,20 @@ GetDatabaseCollation(Oid dbOid) } #endif + #if PG_VERSION_NUM >= PG_VERSION_16 + Datum icuRulesDatum = heap_getattr(tup, Anum_pg_database_daticurules, tupdesc, + &isNull); + + if (isNull) + { + info.icuRules = NULL; + } + else + { + info.icuRules = TextDatumGetCString(icuRulesDatum); + } + #endif + table_close(rel, AccessShareLock); UnregisterSnapshot(snapshot); heap_freetuple(tup); @@ -630,10 +645,10 @@ GenerateCreateDatabaseStatementFromPgDatabase(Form_pg_database databaseForm) } #if PG_VERSION_NUM >= PG_VERSION_15 - if (collInfo.icu_locale != NULL) + if (collInfo.icuLocale != NULL) { appendStringInfo(&str, " ICU_LOCALE = %s", quote_literal_cstr( - collInfo.icu_locale)); + collInfo.icuLocale)); } if (databaseForm->datlocprovider != 0) @@ -665,6 +680,11 @@ GenerateCreateDatabaseStatementFromPgDatabase(Form_pg_database databaseForm) appendStringInfo(&str, " CONNECTION LIMIT %d", databaseForm->datconnlimit); } + if(collInfo.icuRules != NULL){ + appendStringInfo(&str, " ICU_RULES = %s", + quote_literal_cstr(collInfo.icuRules)); + } + appendStringInfo(&str, " IS_TEMPLATE = %s", quote_literal_cstr(databaseForm->datistemplate ? "true" : "false")); From 7e15939e500f2f2384b9a4593494169109150113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCrkan=20=C4=B0ndibay?= Date: Mon, 13 Nov 2023 17:15:11 +0300 Subject: [PATCH 21/68] Update src/backend/distributed/commands/database.c Co-authored-by: Onur Tirtir --- src/backend/distributed/commands/database.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/backend/distributed/commands/database.c b/src/backend/distributed/commands/database.c index 82b7eed972f..94477a47b84 100644 --- a/src/backend/distributed/commands/database.c +++ b/src/backend/distributed/commands/database.c @@ -594,6 +594,10 @@ GetLocaleProviderString(char datlocprovider) /* * GenerateCreateDatabaseStatementFromPgDatabase gets the pg_database tuple and returns the * CREATE DATABASE statement that can be used to create given database. + * + * Note that this doesn't deparse OID of the database and this is not a + * problem as we anyway don't allow specifying custom OIDs for databases + * when creating them. */ static char * GenerateCreateDatabaseStatementFromPgDatabase(Form_pg_database databaseForm) From c1dce6fc2bfc2b359b00cd88a14333d24b9c5c2e Mon Sep 17 00:00:00 2001 From: Onur Tirtir Date: Mon, 13 Nov 2023 18:00:42 +0300 Subject: [PATCH 22/68] commit --- src/backend/distributed/commands/database.c | 158 +++++++++----------- 1 file changed, 67 insertions(+), 91 deletions(-) diff --git a/src/backend/distributed/commands/database.c b/src/backend/distributed/commands/database.c index 94477a47b84..0d00bb6018c 100644 --- a/src/backend/distributed/commands/database.c +++ b/src/backend/distributed/commands/database.c @@ -49,12 +49,17 @@ */ typedef struct DatabaseCollationInfo { - char *collation; - char *ctype; - #if PG_VERSION_NUM >= PG_VERSION_15 - char *icu_locale; - char *collversion; - #endif + char *datcollate; + char *datctype; + +#if PG_VERSION_NUM >= PG_VERSION_15 + char *daticulocale; + char *datcollversion; +#endif + +#if PG_VERSION_NUM >= PG_VERSION_16 + char *daticurules; +#endif } DatabaseCollationInfo; static AlterOwnerStmt * RecreateAlterDatabaseOwnerStmt(Oid databaseOid); @@ -485,15 +490,14 @@ GetTablespaceName(Oid tablespaceOid) /* * GetDatabaseCollation gets oid of a database and returns all the collation related information - * We need this method since collation related info in Form_pg_database is not accessible + * We need this method since collation related info in Form_pg_database is not accessible. */ static DatabaseCollationInfo GetDatabaseCollation(Oid dbOid) { DatabaseCollationInfo info; - bool isNull; + memset(&info, 0, sizeof(DatabaseCollationInfo)); - Snapshot snapshot = RegisterSnapshot(GetLatestSnapshot()); Relation rel = table_open(DatabaseRelationId, AccessShareLock); HeapTuple tup = get_catalog_object_by_oid(rel, Anum_pg_database_oid, dbOid); if (!HeapTupleIsValid(tup)) @@ -501,55 +505,44 @@ GetDatabaseCollation(Oid dbOid) elog(ERROR, "cache lookup failed for database %u", dbOid); } + bool isNull = false; + TupleDesc tupdesc = RelationGetDescr(rel); + Datum collationDatum = heap_getattr(tup, Anum_pg_database_datcollate, tupdesc, &isNull); - if (isNull) - { - info.collation = NULL; - } - else - { - info.collation = TextDatumGetCString(collationDatum); - } + info.datcollate = TextDatumGetCString(collationDatum); Datum ctypeDatum = heap_getattr(tup, Anum_pg_database_datctype, tupdesc, &isNull); - if (isNull) - { - info.ctype = NULL; - } - else - { - info.ctype = TextDatumGetCString(ctypeDatum); - } + info.datctype = TextDatumGetCString(ctypeDatum); - #if PG_VERSION_NUM >= PG_VERSION_15 +#if PG_VERSION_NUM >= PG_VERSION_15 Datum icuLocaleDatum = heap_getattr(tup, Anum_pg_database_daticulocale, tupdesc, &isNull); - if (isNull) - { - info.icu_locale = NULL; - } - else + if (!isNull) { - info.icu_locale = TextDatumGetCString(icuLocaleDatum); + info.daticulocale = TextDatumGetCString(icuLocaleDatum); } Datum collverDatum = heap_getattr(tup, Anum_pg_database_datcollversion, tupdesc, &isNull); - if (isNull) + if (!isNull) { - info.collversion = NULL; + info.datcollversion = TextDatumGetCString(collverDatum); } - else +#endif + +#if PG_VERSION_NUM >= PG_VERSION_16 + Datum icurulesDatum = heap_getattr(tup, Anum_pg_database_daticurules, tupdesc, + &isNull); + if (!isNull) { - info.collversion = TextDatumGetCString(collverDatum); + info.daticurules = TextDatumGetCString(icurulesDatum); } - #endif +#endif table_close(rel, AccessShareLock); - UnregisterSnapshot(snapshot); heap_freetuple(tup); return info; @@ -577,13 +570,11 @@ GetLocaleProviderString(char datlocprovider) return "icu"; } - case 'l': + default: { - return "locale"; + ereport(ERROR, (errmsg("unexpected datlocprovider value: %c", + datlocprovider))); } - - default: - return ""; } } @@ -610,68 +601,53 @@ GenerateCreateDatabaseStatementFromPgDatabase(Form_pg_database databaseForm) appendStringInfo(&str, "CREATE DATABASE %s", quote_identifier(NameStr(databaseForm->datname))); - if (databaseForm->datdba != InvalidOid) - { - appendStringInfo(&str, " OWNER = %s", - quote_literal_cstr(GetUserNameFromId(databaseForm->datdba, - false))); - } + appendStringInfo(&str, " CONNECTION LIMIT %d", databaseForm->datconnlimit); - if (databaseForm->encoding != -1) - { - appendStringInfo(&str, " ENCODING = %s", - quote_literal_cstr(pg_encoding_to_char(databaseForm->encoding))); - } + appendStringInfo(&str, " ALLOW_CONNECTIONS = %s", + quote_literal_cstr(databaseForm->datallowconn ? "true" : "false")); - if (collInfo.collation != NULL) - { - appendStringInfo(&str, " LC_COLLATE = %s", quote_literal_cstr( - collInfo.collation)); - } - if (collInfo.ctype != NULL) - { - appendStringInfo(&str, " LC_CTYPE = %s", quote_literal_cstr(collInfo.ctype)); - } + appendStringInfo(&str, " IS_TEMPLATE = %s", + quote_literal_cstr(databaseForm->datistemplate ? "true" : "false")); - #if PG_VERSION_NUM >= PG_VERSION_15 - if (collInfo.icu_locale != NULL) - { - appendStringInfo(&str, " ICU_LOCALE = %s", quote_literal_cstr( - collInfo.icu_locale)); - } + appendStringInfo(&str, " LC_COLLATE = %s", + quote_literal_cstr(collInfo.datcollate)); - if (databaseForm->datlocprovider != 0) - { - appendStringInfo(&str, " LOCALE_PROVIDER = %s", - quote_literal_cstr(GetLocaleProviderString( - databaseForm->datlocprovider))); - } + appendStringInfo(&str, " LC_CTYPE = %s", quote_literal_cstr(collInfo.datctype)); - if (collInfo.collversion != NULL) + appendStringInfo(&str, " OWNER = %s", + quote_literal_cstr(GetUserNameFromId(databaseForm->datdba, false))); + + appendStringInfo(&str, " TABLESPACE = %s", + quote_identifier(GetTablespaceName(databaseForm->dattablespace))); + + appendStringInfo(&str, " ENCODING = %s", + quote_literal_cstr(pg_encoding_to_char(databaseForm->encoding))); + +#if PG_VERSION_NUM >= PG_VERSION_15 + if (collInfo.datcollversion != NULL) { - appendStringInfo(&str, " COLLATION_VERSION = %s", quote_literal_cstr( - collInfo.collversion)); + appendStringInfo(&str, " COLLATION_VERSION = %s", + quote_literal_cstr(collInfo.datcollversion)); } - #endif - if (databaseForm->dattablespace != InvalidOid) + if (collInfo.daticulocale != NULL) { - appendStringInfo(&str, " TABLESPACE = %s", - quote_identifier(GetTablespaceName( - databaseForm->dattablespace))); + appendStringInfo(&str, " ICU_LOCALE = %s", quote_literal_cstr( + collInfo.daticulocale)); } - appendStringInfo(&str, " ALLOW_CONNECTIONS = %s", - quote_literal_cstr(databaseForm->datallowconn ? "true" : "false")); + appendStringInfo(&str, " LOCALE_PROVIDER = %s", + quote_literal_cstr(GetLocaleProviderString( + databaseForm->datlocprovider))); +#endif - if (databaseForm->datconnlimit >= 0) +#if PG_VERSION_NUM >= PG_VERSION_16 + if (collInfo.daticurules != NULL) { - appendStringInfo(&str, " CONNECTION LIMIT %d", databaseForm->datconnlimit); + appendStringInfo(&str, " ICU_RULES = %s", quote_literal_cstr( + collInfo.daticurules)); } - - appendStringInfo(&str, " IS_TEMPLATE = %s", - quote_literal_cstr(databaseForm->datistemplate ? "true" : "false")); - +#endif return str.data; } From 32df3313d449c70d18944bc2cbfe650d5a4d0653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCrkan=20=C4=B0ndibay?= Date: Mon, 13 Nov 2023 18:59:36 +0300 Subject: [PATCH 23/68] Update src/backend/distributed/metadata/metadata_sync.c Co-authored-by: Onur Tirtir --- src/backend/distributed/metadata/metadata_sync.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/backend/distributed/metadata/metadata_sync.c b/src/backend/distributed/metadata/metadata_sync.c index 56a0a8566b7..cb74ebcb5c1 100644 --- a/src/backend/distributed/metadata/metadata_sync.c +++ b/src/backend/distributed/metadata/metadata_sync.c @@ -3947,8 +3947,7 @@ citus_internal_database_command(PG_FUNCTION_ARGS) else { ereport(ERROR, (errmsg("citus_internal_database_command() can only be used " - "for CREATE DATABASE command by " - "Citus."))); + "for CREATE DATABASE command by Citus."))); } /* rollback GUCs to the state before this session */ From a956786bd8fb45e64152a1f2601ee2221bf3164b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCrkan=20=C4=B0ndibay?= Date: Mon, 13 Nov 2023 18:59:48 +0300 Subject: [PATCH 24/68] Update src/backend/distributed/deparser/deparse_database_stmts.c Co-authored-by: Onur Tirtir --- src/backend/distributed/deparser/deparse_database_stmts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/distributed/deparser/deparse_database_stmts.c b/src/backend/distributed/deparser/deparse_database_stmts.c index e726fa84529..5e7fa54169f 100644 --- a/src/backend/distributed/deparser/deparse_database_stmts.c +++ b/src/backend/distributed/deparser/deparse_database_stmts.c @@ -322,7 +322,7 @@ AppendDropDatabaseStmt(StringInfo buf, DropdbStmt *stmt) option->defname))); } - /*if it is the last option then append with ")" */ + /* if it is the last option then append with ")" */ if (option == llast(stmt->options)) { appendStringInfo(buf, " )"); From 1e044a217d9fb5ed6e2aa57f773dfdbc0f77b88b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCrkan=20=C4=B0ndibay?= Date: Mon, 13 Nov 2023 19:00:01 +0300 Subject: [PATCH 25/68] Update src/backend/distributed/deparser/deparse_database_stmts.c Co-authored-by: Onur Tirtir --- src/backend/distributed/deparser/deparse_database_stmts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/distributed/deparser/deparse_database_stmts.c b/src/backend/distributed/deparser/deparse_database_stmts.c index 5e7fa54169f..5878d7ea8e2 100644 --- a/src/backend/distributed/deparser/deparse_database_stmts.c +++ b/src/backend/distributed/deparser/deparse_database_stmts.c @@ -301,7 +301,7 @@ AppendDropDatabaseStmt(StringInfo buf, DropdbStmt *stmt) foreach_ptr(option, stmt->options) { - /*if it is the first option then append with "WITH" else append with "," */ + /* if it is the first option then append with "WITH" else append with "," */ if (option == linitial(stmt->options)) { appendStringInfo(buf, " WITH ( "); From 77801016192efbb57b78077082109680dc4b6e11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCrkan=20=C4=B0ndibay?= Date: Mon, 13 Nov 2023 19:00:40 +0300 Subject: [PATCH 26/68] Update src/backend/distributed/commands/database.c Co-authored-by: Onur Tirtir --- src/backend/distributed/commands/database.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/distributed/commands/database.c b/src/backend/distributed/commands/database.c index 0d00bb6018c..f846cee70a9 100644 --- a/src/backend/distributed/commands/database.c +++ b/src/backend/distributed/commands/database.c @@ -654,8 +654,8 @@ GenerateCreateDatabaseStatementFromPgDatabase(Form_pg_database databaseForm) /* - * GenerateCreateDatabaseCommandList gets a list of pg_database tuples and returns - * a list of CREATE DATABASE statements for all the databases. + * GenerateCreateDatabaseCommandList returns a list of CREATE DATABASE statements + * for all the databases. * * Commands in the list are wrapped by citus_internal_database_command() UDF * to avoid from transaction block restrictions that apply to database commands From c1e9335fb74ed3d6096c8deac73d6ccd2fe076fc Mon Sep 17 00:00:00 2001 From: gindibay Date: Tue, 14 Nov 2023 09:01:00 +0300 Subject: [PATCH 27/68] Adds distributed check in metadata syncing --- src/backend/distributed/commands/database.c | 9 ++++ .../create_drop_database_propagation.out | 42 +++++++++++++++++++ .../sql/create_drop_database_propagation.sql | 21 ++++++++++ 3 files changed, 72 insertions(+) diff --git a/src/backend/distributed/commands/database.c b/src/backend/distributed/commands/database.c index f846cee70a9..0454122c5a7 100644 --- a/src/backend/distributed/commands/database.c +++ b/src/backend/distributed/commands/database.c @@ -673,6 +673,15 @@ GenerateCreateDatabaseCommandList(void) { 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; + } + char *createStmt = GenerateCreateDatabaseStatementFromPgDatabase(databaseForm); StringInfo outerDbStmt = makeStringInfo(); diff --git a/src/test/regress/expected/create_drop_database_propagation.out b/src/test/regress/expected/create_drop_database_propagation.out index 5ab27ba34a4..4eff2e19b8f 100644 --- a/src/test/regress/expected/create_drop_database_propagation.out +++ b/src/test/regress/expected/create_drop_database_propagation.out @@ -495,6 +495,48 @@ SELECT * FROM public.check_database_on_all_nodes('db_force_test') ORDER BY node_ worker node (remote) | {"database_properties": null, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} (3 rows) +-- test that we won't propagate non-distributed databases in citus_add_node +select 1 from citus_remove_node('localhost', :worker_2_port); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +SET citus.enable_create_database_propagation TO off; +CREATE DATABASE non_distributed_db; +NOTICE: Citus partially supports CREATE DATABASE for distributed databases +DETAIL: Citus does not propagate CREATE DATABASE command to workers +HINT: You can manually create a database and its extensions on workers. +SET citus.enable_create_database_propagation TO on; +create database distributed_db; +select 1 from citus_add_node('localhost', :worker_2_port); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +--non_distributed_db should not be propagated to worker_2 +SELECT * FROM public.check_database_on_all_nodes('non_distributed_db') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator (local) | {"database_properties": {"datacl": null, "datname": "non_distributed_db", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} + worker node (remote) | {"database_properties": null, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} + worker node (remote) | {"database_properties": null, "pg_dist_object_record_for_db_exists": false, "stale_pg_dist_object_record_for_a_db_exists": false} +(3 rows) + +--distributed_db should be propagated to worker_2 +SELECT * FROM public.check_database_on_all_nodes('distributed_db') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator (local) | {"database_properties": {"datacl": null, "datname": "distributed_db", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "distributed_db", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "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": null, "datname": "distributed_db", "datctype": "C", "encoding": "UTF8", "datcollate": "C", "tablespace": "pg_default", "daticurules": null, "datallowconn": true, "datconnlimit": -1, "daticulocale": null, "datistemplate": false, "database_owner": "postgres", "datcollversion": null, "datlocprovider": "c"}, "pg_dist_object_record_for_db_exists": true, "stale_pg_dist_object_record_for_a_db_exists": false} +(3 rows) + +--clean up resources created by this test +drop database distributed_db; +set citus.enable_create_database_propagation TO off; +drop database non_distributed_db; --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( diff --git a/src/test/regress/sql/create_drop_database_propagation.sql b/src/test/regress/sql/create_drop_database_propagation.sql index 1b2f96da2ef..1fdd4ea772a 100644 --- a/src/test/regress/sql/create_drop_database_propagation.sql +++ b/src/test/regress/sql/create_drop_database_propagation.sql @@ -274,6 +274,27 @@ reset citus.log_remote_commands; SELECT * FROM public.check_database_on_all_nodes('db_force_test') ORDER BY node_type; +-- test that we won't propagate non-distributed databases in citus_add_node + +select 1 from citus_remove_node('localhost', :worker_2_port); +SET citus.enable_create_database_propagation TO off; +CREATE DATABASE non_distributed_db; +SET citus.enable_create_database_propagation TO on; +create database distributed_db; + +select 1 from citus_add_node('localhost', :worker_2_port); + +--non_distributed_db should not be propagated to worker_2 +SELECT * FROM public.check_database_on_all_nodes('non_distributed_db') ORDER BY node_type; +--distributed_db should be propagated to worker_2 +SELECT * FROM public.check_database_on_all_nodes('distributed_db') ORDER BY node_type; + +--clean up resources created by this test +drop database distributed_db; + +set citus.enable_create_database_propagation TO off; +drop database non_distributed_db; + --clean up resources created by this test From cf019b858cd0c2f31c907c1eed9cc505b6bb59be Mon Sep 17 00:00:00 2001 From: gindibay Date: Tue, 14 Nov 2023 10:21:23 +0300 Subject: [PATCH 28/68] Adds static declarations --- src/backend/distributed/commands/database.c | 10 ++++++++-- .../distributed/deparser/deparse_database_stmts.c | 4 ++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/backend/distributed/commands/database.c b/src/backend/distributed/commands/database.c index 0454122c5a7..a0972eda1bf 100644 --- a/src/backend/distributed/commands/database.c +++ b/src/backend/distributed/commands/database.c @@ -62,10 +62,16 @@ typedef struct DatabaseCollationInfo #endif } DatabaseCollationInfo; +static void EnsureSupportedCreateDatabaseCommand(CreatedbStmt *stmt); +static char * GenerateCreateDatabaseStatementFromPgDatabase(Form_pg_database databaseForm); +static DatabaseCollationInfo GetDatabaseCollation(Oid dbOid); static AlterOwnerStmt * RecreateAlterDatabaseOwnerStmt(Oid databaseOid); +static char * GetLocaleProviderString(char datlocprovider); +static char * GetTablespaceName(Oid tablespaceOid); +static ObjectAddress * GetDatabaseAddressFromDatabaseName(char *databaseName,bool missingOk); + static Oid get_database_owner(Oid db_oid); -List * PreprocessGrantOnDatabaseStmt(Node *node, const char *queryString, - ProcessUtilityContext processUtilityContext); + /* controlled via GUC */ bool EnableCreateDatabasePropagation = false; diff --git a/src/backend/distributed/deparser/deparse_database_stmts.c b/src/backend/distributed/deparser/deparse_database_stmts.c index 5878d7ea8e2..cd86eedf1a2 100644 --- a/src/backend/distributed/deparser/deparse_database_stmts.c +++ b/src/backend/distributed/deparser/deparse_database_stmts.c @@ -27,8 +27,12 @@ static void AppendAlterDatabaseOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt); +static void AppendAlterDatabaseSetStmt(StringInfo buf, AlterDatabaseSetStmt *stmt); static void AppendAlterDatabaseStmt(StringInfo buf, AlterDatabaseStmt *stmt); static void AppendDefElemConnLimit(StringInfo buf, DefElem *def); +static void AppendCreateDatabaseStmt(StringInfo buf, CreatedbStmt *stmt); +static void AppendDropDatabaseStmt(StringInfo buf, DropdbStmt *stmt); +static void AppendGrantOnDatabaseStmt(StringInfo buf, GrantStmt *stmt); const DefElemOptionFormat create_database_option_formats[] = { { "owner", " OWNER %s", OPTION_FORMAT_STRING }, From cdef2d522413a87d055dabfa2f4e46d570a08900 Mon Sep 17 00:00:00 2001 From: Naisila Puka <37271756+naisila@users.noreply.github.com> Date: Tue, 14 Nov 2023 12:49:15 +0300 Subject: [PATCH 29/68] Random tests refactoring (#7342) While investigating replication slots leftovers in PR https://github.com/citusdata/citus/pull/7338, I ran into the following refactoring/cleanup that can be done in our test suite: - Add separate test to remove non default nodes - Remove coordinator removal from `add_coordinator` test Use `remove_coordinator_from_metadata` test where needed - Don't print nodeids in `multi_multiuser_auth` and `multi_poolinfo_usage` tests - Use `startswith` when checking for isolation or failure tests - Add some dependencies accordingly in `run_test.py` for running flaky test schedules --- src/test/regress/citus_tests/run_test.py | 70 ++++++++----------- src/test/regress/expected/add_coordinator.out | 7 -- .../regress/expected/multi_multiuser_auth.out | 14 +--- .../regress/expected/multi_poolinfo_usage.out | 14 +--- .../multi_tenant_isolation_nonblocking.out | 6 -- .../expected/remove_non_default_nodes.out | 13 ++++ .../worker_split_binary_copy_test.out | 37 ---------- src/test/regress/multi_1_schedule | 1 + src/test/regress/split_schedule | 1 + src/test/regress/sql/add_coordinator.sql | 2 - src/test/regress/sql/multi_multiuser_auth.sql | 4 +- src/test/regress/sql/multi_poolinfo_usage.sql | 4 +- .../multi_tenant_isolation_nonblocking.sql | 3 - .../regress/sql/remove_non_default_nodes.sql | 8 +++ .../sql/worker_split_binary_copy_test.sql | 8 --- 15 files changed, 60 insertions(+), 132 deletions(-) create mode 100644 src/test/regress/expected/remove_non_default_nodes.out create mode 100644 src/test/regress/sql/remove_non_default_nodes.sql diff --git a/src/test/regress/citus_tests/run_test.py b/src/test/regress/citus_tests/run_test.py index b902a799835..be3529c19cb 100755 --- a/src/test/regress/citus_tests/run_test.py +++ b/src/test/regress/citus_tests/run_test.py @@ -135,20 +135,10 @@ def extra_tests(self): ), "alter_role_propagation": TestDeps("minimal_schedule"), "background_rebalance": TestDeps( - None, - [ - "multi_test_helpers", - "multi_cluster_management", - ], - worker_count=3, + None, ["multi_test_helpers", "multi_cluster_management"], worker_count=3 ), "background_rebalance_parallel": TestDeps( - None, - [ - "multi_test_helpers", - "multi_cluster_management", - ], - worker_count=6, + None, ["multi_test_helpers", "multi_cluster_management"], worker_count=6 ), "function_propagation": TestDeps("minimal_schedule"), "citus_shards": TestDeps("minimal_schedule"), @@ -165,30 +155,17 @@ def extra_tests(self): ), "schema_based_sharding": TestDeps("minimal_schedule"), "multi_sequence_default": TestDeps( - None, - [ - "multi_test_helpers", - "multi_cluster_management", - "multi_table_ddl", - ], + None, ["multi_test_helpers", "multi_cluster_management", "multi_table_ddl"] ), "grant_on_schema_propagation": TestDeps("minimal_schedule"), "propagate_extension_commands": TestDeps("minimal_schedule"), "multi_size_queries": TestDeps("base_schedule", ["multi_copy"]), "multi_mx_node_metadata": TestDeps( - None, - [ - "multi_extension", - "multi_test_helpers", - "multi_test_helpers_superuser", - ], + None, ["multi_extension", "multi_test_helpers", "multi_test_helpers_superuser"] ), "multi_mx_function_table_reference": TestDeps( None, - [ - "multi_cluster_management", - "remove_coordinator_from_metadata", - ], + ["multi_cluster_management", "remove_coordinator_from_metadata"], # because it queries node group id and it changes as we add / remove nodes repeatable=False, ), @@ -201,15 +178,25 @@ def extra_tests(self): ], ), "metadata_sync_helpers": TestDeps( - None, - [ - "multi_mx_node_metadata", - "multi_cluster_management", - ], + None, ["multi_mx_node_metadata", "multi_cluster_management"] ), - "multi_utilities": TestDeps( + "multi_utilities": TestDeps("minimal_schedule", ["multi_data_types"]), + "multi_tenant_isolation_nonblocking": TestDeps( + "minimal_schedule", ["multi_data_types", "remove_coordinator_from_metadata"] + ), + "remove_non_default_nodes": TestDeps( + None, ["multi_mx_node_metadata", "multi_cluster_management"], repeatable=False + ), + "citus_split_shard_columnar_partitioned": TestDeps( + "minimal_schedule", ["remove_coordinator_from_metadata"] + ), + "add_coordinator": TestDeps( + "minimal_schedule", ["remove_coordinator_from_metadata"], repeatable=False + ), + "multi_multiuser_auth": TestDeps( "minimal_schedule", - ["multi_data_types"], + ["multi_create_table", "multi_create_users", "multi_multiuser_load_data"], + repeatable=False, ), } @@ -303,9 +290,13 @@ def run_schedule_with_multiregress(test_name, schedule, dependencies, args): worker_count = needed_worker_count(test_name, dependencies) # find suitable make recipe - if dependencies.schedule == "base_isolation_schedule" or "isolation" in test_name: + if dependencies.schedule == "base_isolation_schedule" or test_name.startswith( + "isolation" + ): make_recipe = "check-isolation-custom-schedule" - elif dependencies.schedule == "failure_base_schedule" or "failure" in test_name: + elif dependencies.schedule == "failure_base_schedule" or test_name.startswith( + "failure" + ): make_recipe = "check-failure-custom-schedule" else: make_recipe = "check-custom-schedule" @@ -418,10 +409,7 @@ def test_dependencies(test_name, test_schedule, schedule_line, args): if "upgrade_columnar_before" not in before_tests: before_tests.append("upgrade_columnar_before") - return TestDeps( - default_base_schedule(test_schedule, args), - before_tests, - ) + return TestDeps(default_base_schedule(test_schedule, args), before_tests) # before_ tests leave stuff around on purpose for the after tests. So they # are not repeatable by definition. diff --git a/src/test/regress/expected/add_coordinator.out b/src/test/regress/expected/add_coordinator.out index 49966938536..01f3a682d5f 100644 --- a/src/test/regress/expected/add_coordinator.out +++ b/src/test/regress/expected/add_coordinator.out @@ -2,13 +2,6 @@ -- ADD_COORDINATOR -- -- node trying to add itself without specifying groupid => 0 should error out --- first remove the coordinator to for testing master_add_node for coordinator -SELECT master_remove_node('localhost', :master_port); - master_remove_node ---------------------------------------------------------------------- - -(1 row) - SELECT master_add_node('localhost', :master_port); ERROR: Node cannot add itself as a worker. HINT: Add the node as a coordinator by using: SELECT citus_set_coordinator_host('localhost', 57636); diff --git a/src/test/regress/expected/multi_multiuser_auth.out b/src/test/regress/expected/multi_multiuser_auth.out index 8dd9b8ba720..7a72eeba1e9 100644 --- a/src/test/regress/expected/multi_multiuser_auth.out +++ b/src/test/regress/expected/multi_multiuser_auth.out @@ -12,19 +12,9 @@ \set bob_worker_1_pw triplex-royalty-warranty-stand-cheek \set bob_worker_2_pw omnibus-plectrum-comet-sneezy-ensile \set bob_fallback_pw :bob_worker_1_pw -SELECT nodeid AS worker_1_id FROM pg_dist_node WHERE nodename = 'localhost' AND nodeport = :worker_1_port; - worker_1_id ---------------------------------------------------------------------- - 17 -(1 row) - +SELECT nodeid AS worker_1_id FROM pg_dist_node WHERE nodename = 'localhost' AND nodeport = :worker_1_port \gset -SELECT nodeid AS worker_2_id FROM pg_dist_node WHERE nodename = 'localhost' AND nodeport = :worker_2_port; - worker_2_id ---------------------------------------------------------------------- - 35 -(1 row) - +SELECT nodeid AS worker_2_id FROM pg_dist_node WHERE nodename = 'localhost' AND nodeport = :worker_2_port \gset -- alice is a superuser so she can update own password CREATE USER alice PASSWORD :'alice_master_pw' SUPERUSER; diff --git a/src/test/regress/expected/multi_poolinfo_usage.out b/src/test/regress/expected/multi_poolinfo_usage.out index ee98f0df79c..53dfca24ef8 100644 --- a/src/test/regress/expected/multi_poolinfo_usage.out +++ b/src/test/regress/expected/multi_poolinfo_usage.out @@ -6,19 +6,9 @@ -- Test of ability to override host/port for a node SET citus.shard_replication_factor TO 1; SET citus.next_shard_id TO 20000000; -SELECT nodeid AS worker_1_id FROM pg_dist_node WHERE nodename = 'localhost' AND nodeport = :worker_1_port; - worker_1_id ---------------------------------------------------------------------- - 17 -(1 row) - +SELECT nodeid AS worker_1_id FROM pg_dist_node WHERE nodename = 'localhost' AND nodeport = :worker_1_port \gset -SELECT nodeid AS worker_2_id FROM pg_dist_node WHERE nodename = 'localhost' AND nodeport = :worker_2_port; - worker_2_id ---------------------------------------------------------------------- - 35 -(1 row) - +SELECT nodeid AS worker_2_id FROM pg_dist_node WHERE nodename = 'localhost' AND nodeport = :worker_2_port \gset CREATE TABLE lotsa_connections (id integer, name text); SELECT create_distributed_table('lotsa_connections', 'id'); diff --git a/src/test/regress/expected/multi_tenant_isolation_nonblocking.out b/src/test/regress/expected/multi_tenant_isolation_nonblocking.out index 3ec16e6eeb5..dbd15b0564c 100644 --- a/src/test/regress/expected/multi_tenant_isolation_nonblocking.out +++ b/src/test/regress/expected/multi_tenant_isolation_nonblocking.out @@ -1275,9 +1275,3 @@ SELECT count(*) FROM pg_catalog.pg_dist_partition WHERE colocationid > 0; TRUNCATE TABLE pg_catalog.pg_dist_colocation; ALTER SEQUENCE pg_catalog.pg_dist_colocationid_seq RESTART 100; ALTER SEQUENCE pg_catalog.pg_dist_placement_placementid_seq RESTART :last_placement_id; -SELECT citus_set_coordinator_host('localhost'); - citus_set_coordinator_host ---------------------------------------------------------------------- - -(1 row) - diff --git a/src/test/regress/expected/remove_non_default_nodes.out b/src/test/regress/expected/remove_non_default_nodes.out new file mode 100644 index 00000000000..7645af708db --- /dev/null +++ b/src/test/regress/expected/remove_non_default_nodes.out @@ -0,0 +1,13 @@ +-- The default nodes for the citus test suite are coordinator and 2 worker nodes +-- Which we identify with master_port, worker_1_port, worker_2_port. +-- When needed in some tests, GetLocalNodeId() does not behave correctly, +-- So we remove the non default nodes. This tests expects the non default nodes +-- to not have any active placements. +SELECT any_value(citus_remove_node('localhost', nodeport)) +FROM pg_dist_node +WHERE nodeport NOT IN (:master_port, :worker_1_port, :worker_2_port); + any_value +--------------------------------------------------------------------- + +(1 row) + diff --git a/src/test/regress/expected/worker_split_binary_copy_test.out b/src/test/regress/expected/worker_split_binary_copy_test.out index f23dc2043f0..e161b7f67b0 100644 --- a/src/test/regress/expected/worker_split_binary_copy_test.out +++ b/src/test/regress/expected/worker_split_binary_copy_test.out @@ -3,43 +3,6 @@ SET search_path TO worker_split_binary_copy_test; SET citus.shard_count TO 1; SET citus.shard_replication_factor TO 1; SET citus.next_shard_id TO 81060000; --- Remove extra nodes added, otherwise GetLocalNodeId() does not bahave correctly. -SELECT citus_remove_node('localhost', 8887); - citus_remove_node ---------------------------------------------------------------------- - -(1 row) - -SELECT citus_remove_node('localhost', 9995); - citus_remove_node ---------------------------------------------------------------------- - -(1 row) - -SELECT citus_remove_node('localhost', 9992); - citus_remove_node ---------------------------------------------------------------------- - -(1 row) - -SELECT citus_remove_node('localhost', 9998); - citus_remove_node ---------------------------------------------------------------------- - -(1 row) - -SELECT citus_remove_node('localhost', 9997); - citus_remove_node ---------------------------------------------------------------------- - -(1 row) - -SELECT citus_remove_node('localhost', 8888); - citus_remove_node ---------------------------------------------------------------------- - -(1 row) - -- BEGIN: Create distributed table and insert data. CREATE TABLE worker_split_binary_copy_test.shard_to_split_copy ( l_orderkey bigint not null, diff --git a/src/test/regress/multi_1_schedule b/src/test/regress/multi_1_schedule index 5b93c9e8b2e..287f4557ad8 100644 --- a/src/test/regress/multi_1_schedule +++ b/src/test/regress/multi_1_schedule @@ -295,6 +295,7 @@ test: multi_foreign_key_relation_graph # Replicating reference tables to coordinator. Add coordinator to pg_dist_node # and rerun some of the tests. # -------- +test: remove_coordinator_from_metadata test: add_coordinator test: replicate_reference_tables_to_coordinator test: citus_local_tables diff --git a/src/test/regress/split_schedule b/src/test/regress/split_schedule index b47acd8282f..53c422eab9c 100644 --- a/src/test/regress/split_schedule +++ b/src/test/regress/split_schedule @@ -10,6 +10,7 @@ test: foreign_key_to_reference_table # Split tests go here. test: split_shard test: worker_split_copy_test +test: remove_non_default_nodes test: worker_split_binary_copy_test test: worker_split_text_copy_test test: citus_split_shard_by_split_points_negative diff --git a/src/test/regress/sql/add_coordinator.sql b/src/test/regress/sql/add_coordinator.sql index 81b77bfcd8b..2dba7806405 100644 --- a/src/test/regress/sql/add_coordinator.sql +++ b/src/test/regress/sql/add_coordinator.sql @@ -3,8 +3,6 @@ -- -- node trying to add itself without specifying groupid => 0 should error out --- first remove the coordinator to for testing master_add_node for coordinator -SELECT master_remove_node('localhost', :master_port); SELECT master_add_node('localhost', :master_port); SELECT master_add_node('localhost', :master_port, groupid => 0) AS master_nodeid \gset diff --git a/src/test/regress/sql/multi_multiuser_auth.sql b/src/test/regress/sql/multi_multiuser_auth.sql index 43cb3c11f2f..1cd566b50bf 100644 --- a/src/test/regress/sql/multi_multiuser_auth.sql +++ b/src/test/regress/sql/multi_multiuser_auth.sql @@ -16,9 +16,9 @@ \set bob_worker_2_pw omnibus-plectrum-comet-sneezy-ensile \set bob_fallback_pw :bob_worker_1_pw -SELECT nodeid AS worker_1_id FROM pg_dist_node WHERE nodename = 'localhost' AND nodeport = :worker_1_port; +SELECT nodeid AS worker_1_id FROM pg_dist_node WHERE nodename = 'localhost' AND nodeport = :worker_1_port \gset -SELECT nodeid AS worker_2_id FROM pg_dist_node WHERE nodename = 'localhost' AND nodeport = :worker_2_port; +SELECT nodeid AS worker_2_id FROM pg_dist_node WHERE nodename = 'localhost' AND nodeport = :worker_2_port \gset -- alice is a superuser so she can update own password diff --git a/src/test/regress/sql/multi_poolinfo_usage.sql b/src/test/regress/sql/multi_poolinfo_usage.sql index da039cfcafd..2fbaed2ed14 100644 --- a/src/test/regress/sql/multi_poolinfo_usage.sql +++ b/src/test/regress/sql/multi_poolinfo_usage.sql @@ -7,9 +7,9 @@ SET citus.shard_replication_factor TO 1; SET citus.next_shard_id TO 20000000; -SELECT nodeid AS worker_1_id FROM pg_dist_node WHERE nodename = 'localhost' AND nodeport = :worker_1_port; +SELECT nodeid AS worker_1_id FROM pg_dist_node WHERE nodename = 'localhost' AND nodeport = :worker_1_port \gset -SELECT nodeid AS worker_2_id FROM pg_dist_node WHERE nodename = 'localhost' AND nodeport = :worker_2_port; +SELECT nodeid AS worker_2_id FROM pg_dist_node WHERE nodename = 'localhost' AND nodeport = :worker_2_port \gset CREATE TABLE lotsa_connections (id integer, name text); diff --git a/src/test/regress/sql/multi_tenant_isolation_nonblocking.sql b/src/test/regress/sql/multi_tenant_isolation_nonblocking.sql index 1299c928262..f7483510832 100644 --- a/src/test/regress/sql/multi_tenant_isolation_nonblocking.sql +++ b/src/test/regress/sql/multi_tenant_isolation_nonblocking.sql @@ -607,6 +607,3 @@ TRUNCATE TABLE pg_catalog.pg_dist_colocation; ALTER SEQUENCE pg_catalog.pg_dist_colocationid_seq RESTART 100; ALTER SEQUENCE pg_catalog.pg_dist_placement_placementid_seq RESTART :last_placement_id; - -SELECT citus_set_coordinator_host('localhost'); - diff --git a/src/test/regress/sql/remove_non_default_nodes.sql b/src/test/regress/sql/remove_non_default_nodes.sql new file mode 100644 index 00000000000..4175e87dc29 --- /dev/null +++ b/src/test/regress/sql/remove_non_default_nodes.sql @@ -0,0 +1,8 @@ +-- The default nodes for the citus test suite are coordinator and 2 worker nodes +-- Which we identify with master_port, worker_1_port, worker_2_port. +-- When needed in some tests, GetLocalNodeId() does not behave correctly, +-- So we remove the non default nodes. This tests expects the non default nodes +-- to not have any active placements. +SELECT any_value(citus_remove_node('localhost', nodeport)) +FROM pg_dist_node +WHERE nodeport NOT IN (:master_port, :worker_1_port, :worker_2_port); diff --git a/src/test/regress/sql/worker_split_binary_copy_test.sql b/src/test/regress/sql/worker_split_binary_copy_test.sql index 489ff9dc4d9..d6ca3c9dfc8 100644 --- a/src/test/regress/sql/worker_split_binary_copy_test.sql +++ b/src/test/regress/sql/worker_split_binary_copy_test.sql @@ -4,14 +4,6 @@ SET citus.shard_count TO 1; SET citus.shard_replication_factor TO 1; SET citus.next_shard_id TO 81060000; --- Remove extra nodes added, otherwise GetLocalNodeId() does not bahave correctly. -SELECT citus_remove_node('localhost', 8887); -SELECT citus_remove_node('localhost', 9995); -SELECT citus_remove_node('localhost', 9992); -SELECT citus_remove_node('localhost', 9998); -SELECT citus_remove_node('localhost', 9997); -SELECT citus_remove_node('localhost', 8888); - -- BEGIN: Create distributed table and insert data. CREATE TABLE worker_split_binary_copy_test.shard_to_split_copy ( l_orderkey bigint not null, From a960799dfbdbbb4d66686bfada1997ea0cfc4e88 Mon Sep 17 00:00:00 2001 From: Naisila Puka <37271756+naisila@users.noreply.github.com> Date: Tue, 14 Nov 2023 18:50:54 +0300 Subject: [PATCH 30/68] Clean up leftover replication slots in tests (#7338) This commit fixes the flakiness in `logical_replication` and `citus_non_blocking_split_shard_cleanup` tests. The flakiness was related to leftover replication slots. Below is a flaky example for each test: logical_replication https://github.com/citusdata/citus/actions/runs/6721324131/attempts/1#summary-18267030604 citus_non_blocking_split_shard_cleanup https://github.com/citusdata/citus/actions/runs/6721324131/attempts/1#summary-18267006967 ```diff -- Replication slots should be cleaned up SELECT slot_name FROM pg_replication_slots; slot_name --------------------------------- -(0 rows) + citus_shard_split_slot_19_10_17 +(1 row) ``` The tests by themselves are not flaky: 32 flaky test schedules each with 20 runs run successfully. https://github.com/citusdata/citus/actions/runs/6822020127?pr=7338 The conclusion is that: 1. `multi_tenant_isolation_nonblocking` is the problematic test running before `logical_replication` in the `enterprise_schedule`, so I added a cleanup at the end of `multi_tenant_isolation_nonblocking`. https://github.com/citusdata/citus/actions/runs/6824334614/attempts/1#summary-18560127461 2. `citus_split_shard_by_split_points_negative` is the problematic test running before `citus_non_blocking_split_shards_cleanup` in the split schedule. Also added cleanup line. For details on the investigation of leftover replication slots, please check the PR https://github.com/citusdata/citus/pull/7338 --- .../citus_split_shard_by_split_points_negative.out | 6 ++++++ .../expected/multi_tenant_isolation_nonblocking.out | 7 +++++++ .../sql/citus_split_shard_by_split_points_negative.sql | 1 + .../regress/sql/multi_tenant_isolation_nonblocking.sql | 3 +++ 4 files changed, 17 insertions(+) diff --git a/src/test/regress/expected/citus_split_shard_by_split_points_negative.out b/src/test/regress/expected/citus_split_shard_by_split_points_negative.out index 85b1fc3eed7..6a4265f8182 100644 --- a/src/test/regress/expected/citus_split_shard_by_split_points_negative.out +++ b/src/test/regress/expected/citus_split_shard_by_split_points_negative.out @@ -135,4 +135,10 @@ NOTICE: drop cascades to 3 other objects DETAIL: drop cascades to table citus_split_shard_by_split_points_negative.range_paritioned_table_to_split drop cascades to table citus_split_shard_by_split_points_negative.table_to_split drop cascades to table citus_split_shard_by_split_points_negative.table_to_split_replication_factor_2 +SELECT public.wait_for_resource_cleanup(); + wait_for_resource_cleanup +--------------------------------------------------------------------- + +(1 row) + --END : Cleanup diff --git a/src/test/regress/expected/multi_tenant_isolation_nonblocking.out b/src/test/regress/expected/multi_tenant_isolation_nonblocking.out index dbd15b0564c..3daac7dacd9 100644 --- a/src/test/regress/expected/multi_tenant_isolation_nonblocking.out +++ b/src/test/regress/expected/multi_tenant_isolation_nonblocking.out @@ -1275,3 +1275,10 @@ SELECT count(*) FROM pg_catalog.pg_dist_partition WHERE colocationid > 0; TRUNCATE TABLE pg_catalog.pg_dist_colocation; ALTER SEQUENCE pg_catalog.pg_dist_colocationid_seq RESTART 100; ALTER SEQUENCE pg_catalog.pg_dist_placement_placementid_seq RESTART :last_placement_id; +-- make sure we don't have any replication objects leftover on the nodes +SELECT public.wait_for_resource_cleanup(); + wait_for_resource_cleanup +--------------------------------------------------------------------- + +(1 row) + diff --git a/src/test/regress/sql/citus_split_shard_by_split_points_negative.sql b/src/test/regress/sql/citus_split_shard_by_split_points_negative.sql index fe37777c792..4c180052f60 100644 --- a/src/test/regress/sql/citus_split_shard_by_split_points_negative.sql +++ b/src/test/regress/sql/citus_split_shard_by_split_points_negative.sql @@ -113,4 +113,5 @@ SELECT citus_split_shard_by_split_points( --BEGIN : Cleanup \c - postgres - :master_port DROP SCHEMA "citus_split_shard_by_split_points_negative" CASCADE; +SELECT public.wait_for_resource_cleanup(); --END : Cleanup diff --git a/src/test/regress/sql/multi_tenant_isolation_nonblocking.sql b/src/test/regress/sql/multi_tenant_isolation_nonblocking.sql index f7483510832..994f29f0a98 100644 --- a/src/test/regress/sql/multi_tenant_isolation_nonblocking.sql +++ b/src/test/regress/sql/multi_tenant_isolation_nonblocking.sql @@ -607,3 +607,6 @@ TRUNCATE TABLE pg_catalog.pg_dist_colocation; ALTER SEQUENCE pg_catalog.pg_dist_colocationid_seq RESTART 100; ALTER SEQUENCE pg_catalog.pg_dist_placement_placementid_seq RESTART :last_placement_id; + +-- make sure we don't have any replication objects leftover on the nodes +SELECT public.wait_for_resource_cleanup(); From c6fbb72c0269f9debf65e36b9aafbcfaff3fe23a Mon Sep 17 00:00:00 2001 From: Naisila Puka <37271756+naisila@users.noreply.github.com> Date: Wed, 15 Nov 2023 13:28:43 +0300 Subject: [PATCH 31/68] Fix flaky multi_prepare_plsql (#7346) Simple need of an `ORDER BY` clause Ran into this twice this week already! https://github.com/citusdata/citus/actions/runs/6849701315/attempts/1#summary-18622563506 https://github.com/citusdata/citus/actions/runs/6875051160/attempts/1#summary-18698009952 ```diff SELECT nspname, typname FROM pg_type JOIN pg_namespace ON pg_namespace.oid = pg_type.typnamespace WHERE typname = 'prepare_ddl_type_backup'; nspname | typname -------------+------------------------- - public | prepare_ddl_type_backup otherschema | prepare_ddl_type_backup + public | prepare_ddl_type_backup (2 rows) ``` --- src/test/regress/citus_tests/run_test.py | 1 + src/test/regress/expected/multi_prepare_plsql.out | 5 +++-- src/test/regress/multi_schedule | 3 ++- src/test/regress/sql/multi_prepare_plsql.sql | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/test/regress/citus_tests/run_test.py b/src/test/regress/citus_tests/run_test.py index be3529c19cb..158a44ef65a 100755 --- a/src/test/regress/citus_tests/run_test.py +++ b/src/test/regress/citus_tests/run_test.py @@ -198,6 +198,7 @@ def extra_tests(self): ["multi_create_table", "multi_create_users", "multi_multiuser_load_data"], repeatable=False, ), + "multi_prepare_plsql": TestDeps("base_schedule"), } diff --git a/src/test/regress/expected/multi_prepare_plsql.out b/src/test/regress/expected/multi_prepare_plsql.out index 74c9835ffad..a87b47a34d3 100644 --- a/src/test/regress/expected/multi_prepare_plsql.out +++ b/src/test/regress/expected/multi_prepare_plsql.out @@ -1317,11 +1317,11 @@ SELECT type_ddl_plpgsql(); (1 row) -- find all renamed types to verify the schema name didn't leak, nor a crash happened -SELECT nspname, typname FROM pg_type JOIN pg_namespace ON pg_namespace.oid = pg_type.typnamespace WHERE typname = 'prepare_ddl_type_backup'; +SELECT nspname, typname FROM pg_type JOIN pg_namespace ON pg_namespace.oid = pg_type.typnamespace WHERE typname = 'prepare_ddl_type_backup' ORDER BY 1; nspname | typname --------------------------------------------------------------------- - public | prepare_ddl_type_backup otherschema | prepare_ddl_type_backup + public | prepare_ddl_type_backup (2 rows) DROP TYPE prepare_ddl_type_backup; @@ -1332,6 +1332,7 @@ DROP FUNCTION ddl_in_plpgsql(); DROP FUNCTION copy_in_plpgsql(); DROP TABLE prepare_ddl; DROP TABLE local_ddl; +DROP TABLE plpgsql_table; DROP SCHEMA otherschema; -- clean-up functions DROP FUNCTION plpgsql_test_1(); diff --git a/src/test/regress/multi_schedule b/src/test/regress/multi_schedule index 65a27256687..2c8f7b085b1 100644 --- a/src/test/regress/multi_schedule +++ b/src/test/regress/multi_schedule @@ -83,7 +83,8 @@ test: forcedelegation_functions # this should be run alone as it gets too many clients test: join_pushdown test: multi_subquery_union multi_subquery_in_where_clause multi_subquery_misc statement_cancel_error_message -test: multi_agg_distinct multi_limit_clause_approximate multi_outer_join_reference multi_single_relation_subquery multi_prepare_plsql set_role_in_transaction +test: multi_agg_distinct +test: multi_limit_clause_approximate multi_outer_join_reference multi_single_relation_subquery multi_prepare_plsql set_role_in_transaction test: multi_reference_table multi_select_for_update relation_access_tracking pg13_with_ties test: custom_aggregate_support aggregate_support tdigest_aggregate_support test: multi_average_expression multi_working_columns multi_having_pushdown having_subquery diff --git a/src/test/regress/sql/multi_prepare_plsql.sql b/src/test/regress/sql/multi_prepare_plsql.sql index 8589e5b5af1..e71e2818e71 100644 --- a/src/test/regress/sql/multi_prepare_plsql.sql +++ b/src/test/regress/sql/multi_prepare_plsql.sql @@ -624,7 +624,7 @@ CREATE TYPE prepare_ddl_type AS (x int, y int); SELECT type_ddl_plpgsql(); -- find all renamed types to verify the schema name didn't leak, nor a crash happened -SELECT nspname, typname FROM pg_type JOIN pg_namespace ON pg_namespace.oid = pg_type.typnamespace WHERE typname = 'prepare_ddl_type_backup'; +SELECT nspname, typname FROM pg_type JOIN pg_namespace ON pg_namespace.oid = pg_type.typnamespace WHERE typname = 'prepare_ddl_type_backup' ORDER BY 1; DROP TYPE prepare_ddl_type_backup; RESET search_path; @@ -635,6 +635,7 @@ DROP FUNCTION ddl_in_plpgsql(); DROP FUNCTION copy_in_plpgsql(); DROP TABLE prepare_ddl; DROP TABLE local_ddl; +DROP TABLE plpgsql_table; DROP SCHEMA otherschema; -- clean-up functions From 9a558bdece3dd285bc63d353148c63d75db67e39 Mon Sep 17 00:00:00 2001 From: gindibay Date: Wed, 15 Nov 2023 16:04:26 +0300 Subject: [PATCH 32/68] Adds datacl propagation --- src/backend/distributed/commands/common.c | 2 - src/backend/distributed/commands/database.c | 47 ++++- .../distributed/commands/dependencies.c | 7 + src/backend/distributed/commands/role.c | 2 + .../distributed/metadata/metadata_sync.c | 126 +++++++++++- src/include/distributed/commands.h | 1 + src/include/distributed/metadata_sync.h | 1 + .../create_drop_database_propagation.out | 181 ++++++++++++++++++ .../sql/create_drop_database_propagation.sql | 116 +++++++++++ 9 files changed, 471 insertions(+), 12 deletions(-) diff --git a/src/backend/distributed/commands/common.c b/src/backend/distributed/commands/common.c index 957e2616128..797981d47d3 100644 --- a/src/backend/distributed/commands/common.c +++ b/src/backend/distributed/commands/common.c @@ -14,10 +14,8 @@ #include "postgres.h" #include "catalog/objectaddress.h" -#include "catalog/pg_database.h" #include "catalog/pg_ts_config.h" #include "catalog/pg_ts_dict.h" -#include "commands/dbcommands.h" #include "nodes/parsenodes.h" #include "tcop/utility.h" diff --git a/src/backend/distributed/commands/database.c b/src/backend/distributed/commands/database.c index a0972eda1bf..233997dc938 100644 --- a/src/backend/distributed/commands/database.c +++ b/src/backend/distributed/commands/database.c @@ -658,6 +658,50 @@ GenerateCreateDatabaseStatementFromPgDatabase(Form_pg_database databaseForm) return str.data; } +/* + * 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); + } + + char *grantCommand = NULL; + + foreach_ptr(grantCommand, grantCommands) + { + elog(DEBUG1, "grantCommand: %s", grantCommand); + } + + heap_endscan(scan); + table_close(pgDatabaseRel, AccessShareLock); + + return grantCommands; +} + /* * GenerateCreateDatabaseCommandList returns a list of CREATE DATABASE statements @@ -666,8 +710,7 @@ GenerateCreateDatabaseStatementFromPgDatabase(Form_pg_database databaseForm) * Commands in the list are wrapped by citus_internal_database_command() UDF * to avoid from transaction block restrictions that apply to database commands */ -List * -GenerateCreateDatabaseCommandList(void) +List * GenerateCreateDatabaseCommandList(void) { List *commands = NIL; diff --git a/src/backend/distributed/commands/dependencies.c b/src/backend/distributed/commands/dependencies.c index e309ee86c7d..9957bcdfcb0 100644 --- a/src/backend/distributed/commands/dependencies.c +++ b/src/backend/distributed/commands/dependencies.c @@ -465,6 +465,13 @@ GetDependencyCreateDDLCommands(const ObjectAddress *dependency) List *ownerDDLCommands = DatabaseOwnerDDLCommands(dependency); databaseDDLCommands = list_concat(databaseDDLCommands, ownerDDLCommands); } + //TODO: To reviewer: Having a code block for dependency makes sense + // However dependency tree is based on pg metadata; which does not reflect + // actual database dependencies. I added this block just to point out the issue. + // if(EnableCreateDatabasePropagation){ + // List *dbGrants = GrantOnDatabaseDDLCommands(dependency->objectId); + // databaseDDLCommands = list_concat(databaseDDLCommands, dbGrants); + // } return databaseDDLCommands; } diff --git a/src/backend/distributed/commands/role.c b/src/backend/distributed/commands/role.c index f3ac7b4fff0..632920f7003 100644 --- a/src/backend/distributed/commands/role.c +++ b/src/backend/distributed/commands/role.c @@ -513,6 +513,8 @@ GenerateRoleOptionsList(HeapTuple tuple) List * GenerateCreateOrAlterRoleCommand(Oid roleOid) { + elog(LOG,"GenerateCreateOrAlterRoleCommand execution"); + HeapTuple roleTuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleOid)); Form_pg_authid role = ((Form_pg_authid) GETSTRUCT(roleTuple)); diff --git a/src/backend/distributed/metadata/metadata_sync.c b/src/backend/distributed/metadata/metadata_sync.c index cb74ebcb5c1..d917b57a288 100644 --- a/src/backend/distributed/metadata/metadata_sync.c +++ b/src/backend/distributed/metadata/metadata_sync.c @@ -123,6 +123,7 @@ static List * GetObjectsForGrantStmt(ObjectType objectType, Oid objectId); static AccessPriv * GetAccessPrivObjectForGrantStmt(char *permission); static List * GenerateGrantOnSchemaQueriesFromAclItem(Oid schemaOid, AclItem *aclItem); +static List * GenerateGrantOnDatabaseFromAclItem(Oid databaseOid, AclItem *aclItem); static List * GenerateGrantOnFunctionQueriesFromAclItem(Oid schemaOid, AclItem *aclItem); static List * GrantOnSequenceDDLCommands(Oid sequenceOid); @@ -154,6 +155,7 @@ 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); @@ -2046,6 +2048,80 @@ GenerateGrantOnSchemaQueriesFromAclItem(Oid schemaOid, AclItem *aclItem) return queries; } +List * +GrantOnDatabaseDDLCommands(Oid databaseOid) +{ + HeapTuple databaseTuple = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(databaseOid)); + bool isNull = true; + Datum aclDatum = SysCacheGetAttr(DATABASEOID, databaseTuple, Anum_pg_database_datacl, + &isNull); + if (isNull) + { + ReleaseSysCache(databaseTuple); + return NIL; + } + Acl *acl = DatumGetAclPCopy(aclDatum); + AclItem *aclDat = ACL_DAT(acl); + int aclNum = ACL_NUM(acl); + List *commands = NIL; + + ReleaseSysCache(databaseTuple); + + for (int i = 0; i < aclNum; i++) + { + commands = list_concat(commands, + GenerateGrantOnDatabaseFromAclItem( + databaseOid,&aclDat[i])); + } + + return commands; +} + + +List * +GenerateGrantOnDatabaseFromAclItem(Oid databaseOid, AclItem *aclItem) +{ + AclMode permissions = ACLITEM_GET_PRIVS(*aclItem) & ACL_ALL_RIGHTS_DATABASE; + AclMode grants = ACLITEM_GET_GOPTIONS(*aclItem) & ACL_ALL_RIGHTS_DATABASE; + + /* + * seems unlikely but we check if there is a grant option in the list without the actual permission + */ + Assert(!(grants & ACL_CONNECT) || (permissions & ACL_CONNECT)); + Assert(!(grants & ACL_CREATE) || (permissions & ACL_CREATE)); + Assert(!(grants & ACL_CREATE_TEMP) || (permissions & ACL_CREATE_TEMP)); + Oid granteeOid = aclItem->ai_grantee; + List *queries = NIL; + + queries = lappend(queries, GenerateSetRoleQuery(aclItem->ai_grantor)); + + if (permissions & ACL_CONNECT) + { + char *query = DeparseTreeNode((Node *) GenerateGrantStmtForRights( + OBJECT_DATABASE ,granteeOid, databaseOid, "CONNECT", + grants & ACL_CONNECT)); + queries = lappend(queries, query); + } + if (permissions & ACL_CREATE) + { + char *query = DeparseTreeNode((Node *) GenerateGrantStmtForRights( + OBJECT_DATABASE, granteeOid, databaseOid, "CREATE", + grants & ACL_CREATE)); + queries = lappend(queries, query); + } + if (permissions & ACL_CREATE_TEMP) + { + char *query = DeparseTreeNode((Node *) GenerateGrantStmtForRights( + OBJECT_DATABASE, granteeOid, databaseOid, "TEMPORARY", + grants & ACL_CREATE_TEMP)); + queries = lappend(queries, query); + } + + queries = lappend(queries, "RESET ROLE"); + + return queries; +} + /* * GenerateGrantStmtForRights is the function for creating GrantStmt's for all @@ -2120,6 +2196,11 @@ GetObjectsForGrantStmt(ObjectType objectType, Oid objectId) return list_make1(sequence); } + case OBJECT_DATABASE: + { + return list_make1(makeString(get_database_name(objectId))); + } + default: { elog(ERROR, "unsupported object type for GRANT"); @@ -4563,13 +4644,6 @@ PropagateNodeWideObjectsCommandList(void) /* collect all commands */ List *ddlCommands = NIL; - if (EnableCreateDatabasePropagation) - { - /* get commands for database creation */ - List *createDatabaseCommands = GenerateCreateDatabaseCommandList(); - ddlCommands = list_concat(ddlCommands, createDatabaseCommands); - } - if (EnableAlterRoleSetPropagation) { /* @@ -4580,6 +4654,13 @@ PropagateNodeWideObjectsCommandList(void) ddlCommands = list_concat(ddlCommands, alterRoleSetCommands); } + if (EnableCreateDatabasePropagation) + { + /* get commands for database creation */ + List *createDatabaseCommands = GenerateCreateDatabaseCommandList(); + ddlCommands = list_concat(ddlCommands, createDatabaseCommands); + } + return ddlCommands; } @@ -4611,7 +4692,7 @@ SyncDistributedObjects(MetadataSyncContext *context) Assert(ShouldPropagate()); - /* Send systemwide objects, only roles for now */ + /* send systemwide objects; i.e. roles and databases for now */ SendNodeWideObjectsSyncCommands(context); /* @@ -4651,6 +4732,12 @@ SyncDistributedObjects(MetadataSyncContext *context) * those tables. */ SendInterTableRelationshipCommands(context); + + /* + * After creation of databases and roles, send the grant database commands + * to the workers. + */ + SendDatabaseGrantSyncCommands(context); } @@ -4675,6 +4762,29 @@ SendNodeWideObjectsSyncCommands(MetadataSyncContext *context) SendOrCollectCommandListToActivatedNodes(context, commandList); } +/* + * SendDatabaseGrantSyncCommands sends database grants to roles to workers with + * transactional or nontransactional mode according to transactionMode inside + * metadataSyncContext. + * 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) +{ + /* 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 diff --git a/src/include/distributed/commands.h b/src/include/distributed/commands.h index 4c47d3ecd21..aaee98d1c2e 100644 --- a/src/include/distributed/commands.h +++ b/src/include/distributed/commands.h @@ -244,6 +244,7 @@ extern List * DropDatabaseStmtObjectAddress(Node *node, bool missingOk, extern List * CreateDatabaseStmtObjectAddress(Node *node, bool missingOk, bool isPostprocess); extern List * GenerateCreateDatabaseCommandList(void); +extern List * GenerateGrantDatabaseCommandList(void); /* domain.c - forward declarations */ diff --git a/src/include/distributed/metadata_sync.h b/src/include/distributed/metadata_sync.h index 237df363a13..7b993ec31d4 100644 --- a/src/include/distributed/metadata_sync.h +++ b/src/include/distributed/metadata_sync.h @@ -107,6 +107,7 @@ extern char * ColocationIdUpdateCommand(Oid relationId, uint32 colocationId); extern char * CreateSchemaDDLCommand(Oid schemaId); extern List * GrantOnSchemaDDLCommands(Oid schemaId); extern List * GrantOnFunctionDDLCommands(Oid functionOid); +extern List * GrantOnDatabaseDDLCommands(Oid databaseOid); extern List * GrantOnForeignServerDDLCommands(Oid serverId); extern List * GenerateGrantOnForeignServerQueriesFromAclItem(Oid serverId, AclItem *aclItem); diff --git a/src/test/regress/expected/create_drop_database_propagation.out b/src/test/regress/expected/create_drop_database_propagation.out index 4eff2e19b8f..23184ebca8d 100644 --- a/src/test/regress/expected/create_drop_database_propagation.out +++ b/src/test/regress/expected/create_drop_database_propagation.out @@ -487,6 +487,7 @@ DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing DROP DATABASE db_force_test WITH ( FORCE ) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx reset citus.log_remote_commands; +reset citus.grep_remote_commands; SELECT * FROM public.check_database_on_all_nodes('db_force_test') ORDER BY node_type; node_type | result --------------------------------------------------------------------- @@ -537,6 +538,186 @@ SELECT * FROM public.check_database_on_all_nodes('distributed_db') ORDER BY node 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 +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); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +CREATE DATABASE db_role_grants_test; +revoke connect,temp,temporary on database db_role_grants_test from public; +SET citus.log_remote_commands = true; +set citus.grep_remote_commands = '%CREATE ROLE%'; +CREATE ROLE db_role_grants_test_role_missing_on_node_2; +NOTICE: issuing SELECT worker_create_or_alter_role('db_role_grants_test_role_missing_on_node_2', 'CREATE ROLE db_role_grants_test_role_missing_on_node_2', 'ALTER ROLE db_role_grants_test_role_missing_on_node_2') +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +RESET citus.log_remote_commands ; +RESET citus.grep_remote_commands; +-- check the privileges before grant +SELECT result from run_command_on_all_nodes( + $$ + select has_database_privilege('db_role_grants_test_role_exists_on_node_2','db_role_grants_test', 'CREATE') + $$ +) ORDER BY result; + result +--------------------------------------------------------------------- + f + f +(2 rows) + +SELECT result from run_command_on_all_nodes( + $$ + select has_database_privilege('db_role_grants_test_role_exists_on_node_2','db_role_grants_test', 'TEMPORARY') + $$ +) ORDER BY result; + result +--------------------------------------------------------------------- + f + f +(2 rows) + +SELECT result from run_command_on_all_nodes( + $$ + select has_database_privilege('db_role_grants_test_role_exists_on_node_2','db_role_grants_test', 'CONNECT') + $$ +) ORDER BY result; + result +--------------------------------------------------------------------- + f + f +(2 rows) + +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', 'CREATE') + $$ +) ORDER BY result; + result +--------------------------------------------------------------------- + f + f +(2 rows) + +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') + $$ +) ORDER BY result; + result +--------------------------------------------------------------------- + f + f +(2 rows) + +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', 'CONNECT') + $$ +) ORDER BY result; + result +--------------------------------------------------------------------- + f + f +(2 rows) + +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; +NOTICE: issuing GRANT connect, temporary, create ON DATABASE db_role_grants_test TO db_role_grants_test_role_exists_on_node_2; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +grant CONNECT,TEMPORARY,CREATE on DATABASE db_role_grants_test to db_role_grants_test_role_missing_on_node_2; +NOTICE: issuing GRANT connect, temporary, create ON DATABASE db_role_grants_test TO db_role_grants_test_role_missing_on_node_2; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +select 1 from citus_add_node('localhost', :worker_2_port); +NOTICE: issuing CREATE SCHEMA IF NOT EXISTS public AUTHORIZATION pg_database_owner;SET ROLE pg_database_owner;GRANT USAGE ON SCHEMA public TO pg_database_owner;;GRANT CREATE ON SCHEMA public TO pg_database_owner;;RESET ROLE;SET ROLE pg_database_owner;GRANT USAGE ON SCHEMA public TO PUBLIC;;RESET ROLE +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing CREATE SCHEMA IF NOT EXISTS information_schema AUTHORIZATION postgres;SET ROLE postgres;GRANT USAGE ON SCHEMA information_schema TO postgres;;GRANT CREATE ON SCHEMA information_schema TO postgres;;RESET ROLE;SET ROLE postgres;GRANT USAGE ON SCHEMA information_schema TO PUBLIC;;RESET ROLE +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing SET citus.enable_ddl_propagation TO 'off';SET ROLE postgres;GRANT CONNECT ON DATABASE db_role_grants_test TO postgres;;GRANT CREATE ON DATABASE db_role_grants_test TO postgres;;GRANT TEMPORARY ON DATABASE db_role_grants_test TO postgres;;RESET ROLE;SET ROLE postgres;GRANT CONNECT ON DATABASE db_role_grants_test TO db_role_grants_test_role_exists_on_node_2;;GRANT CREATE ON DATABASE db_role_grants_test TO db_role_grants_test_role_exists_on_node_2;;GRANT TEMPORARY ON DATABASE db_role_grants_test TO db_role_grants_test_role_exists_on_node_2;;RESET ROLE;SET ROLE postgres;GRANT CONNECT ON DATABASE db_role_grants_test TO db_role_grants_test_role_missing_on_node_2;;GRANT CREATE ON DATABASE db_role_grants_test TO db_role_grants_test_role_missing_on_node_2;;GRANT TEMPORARY ON DATABASE db_role_grants_test TO db_role_grants_test_role_missing_on_node_2;;RESET ROLE;SET citus.enable_ddl_propagation TO 'on' +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT result from run_command_on_all_nodes( + $$ + select has_database_privilege('db_role_grants_test_role_exists_on_node_2','db_role_grants_test', 'CREATE') + $$ +) ORDER BY result; + result +--------------------------------------------------------------------- + t + t + t +(3 rows) + +SELECT result from run_command_on_all_nodes( + $$ + select has_database_privilege('db_role_grants_test_role_exists_on_node_2','db_role_grants_test', 'TEMPORARY') + $$ +) ORDER BY result; + result +--------------------------------------------------------------------- + t + t + t +(3 rows) + +SELECT result from run_command_on_all_nodes( + $$ + select has_database_privilege('db_role_grants_test_role_exists_on_node_2','db_role_grants_test', 'CONNECT') + $$ +) ORDER BY result; + result +--------------------------------------------------------------------- + t + t + t +(3 rows) + +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', 'CREATE') + $$ +) ORDER BY result; + result +--------------------------------------------------------------------- + t + t + t +(3 rows) + +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') + $$ +) ORDER BY result; + result +--------------------------------------------------------------------- + t + t + t +(3 rows) + +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', 'CONNECT') + $$ +) ORDER BY result; + result +--------------------------------------------------------------------- + t + t + t +(3 rows) + +DROP DATABASE db_role_grants_test; +DROP ROLE db_role_grants_test_role_exists_on_node_2; +DROP ROLE db_role_grants_test_role_missing_on_node_2; --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( diff --git a/src/test/regress/sql/create_drop_database_propagation.sql b/src/test/regress/sql/create_drop_database_propagation.sql index 1fdd4ea772a..bc53f87df84 100644 --- a/src/test/regress/sql/create_drop_database_propagation.sql +++ b/src/test/regress/sql/create_drop_database_propagation.sql @@ -271,6 +271,7 @@ set citus.grep_remote_commands = '%DROP DATABASE%'; drop database db_force_test with (force); reset citus.log_remote_commands; +reset citus.grep_remote_commands; SELECT * FROM public.check_database_on_all_nodes('db_force_test') ORDER BY node_type; @@ -296,6 +297,121 @@ set citus.enable_create_database_propagation TO off; drop database non_distributed_db; + +-- test role grants on DATABASE in metadata sync + + + +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; + +revoke connect,temp,temporary on database db_role_grants_test from public; + +SET citus.log_remote_commands = true; +set citus.grep_remote_commands = '%CREATE ROLE%'; +CREATE ROLE db_role_grants_test_role_missing_on_node_2; + +RESET citus.log_remote_commands ; +RESET citus.grep_remote_commands; + +-- check the privileges before grant + +SELECT result from run_command_on_all_nodes( + $$ + select has_database_privilege('db_role_grants_test_role_exists_on_node_2','db_role_grants_test', 'CREATE') + $$ +) ORDER BY result; + +SELECT result from run_command_on_all_nodes( + $$ + select has_database_privilege('db_role_grants_test_role_exists_on_node_2','db_role_grants_test', 'TEMPORARY') + $$ +) ORDER BY result; + +SELECT result from run_command_on_all_nodes( + $$ + select has_database_privilege('db_role_grants_test_role_exists_on_node_2','db_role_grants_test', 'CONNECT') + $$ +) 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', 'CREATE') + $$ +) 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') + $$ +) 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', 'CONNECT') + $$ +) ORDER BY result; + +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; + +select 1 from citus_add_node('localhost', :worker_2_port); + + +SELECT result from run_command_on_all_nodes( + $$ + select has_database_privilege('db_role_grants_test_role_exists_on_node_2','db_role_grants_test', 'CREATE') + $$ +) ORDER BY result; + +SELECT result from run_command_on_all_nodes( + $$ + select has_database_privilege('db_role_grants_test_role_exists_on_node_2','db_role_grants_test', 'TEMPORARY') + $$ +) ORDER BY result; + +SELECT result from run_command_on_all_nodes( + $$ + select has_database_privilege('db_role_grants_test_role_exists_on_node_2','db_role_grants_test', 'CONNECT') + $$ +) 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', 'CREATE') + $$ +) 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') + $$ +) 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', 'CONNECT') + $$ +) ORDER BY result; + + +DROP DATABASE db_role_grants_test; +DROP ROLE db_role_grants_test_role_exists_on_node_2; +DROP ROLE db_role_grants_test_role_missing_on_node_2; + + --clean up resources created by this test -- DROP TABLESPACE is not supported, so we need to drop it manually. From eb46d9399bce062e6cd01bc6a1cad9c8cd42bde3 Mon Sep 17 00:00:00 2001 From: gindibay Date: Wed, 15 Nov 2023 16:26:14 +0300 Subject: [PATCH 33/68] Fixes warning on compile --- src/backend/distributed/commands/database.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/backend/distributed/commands/database.c b/src/backend/distributed/commands/database.c index 233997dc938..3570966a204 100644 --- a/src/backend/distributed/commands/database.c +++ b/src/backend/distributed/commands/database.c @@ -66,7 +66,9 @@ static void EnsureSupportedCreateDatabaseCommand(CreatedbStmt *stmt); static char * GenerateCreateDatabaseStatementFromPgDatabase(Form_pg_database databaseForm); static DatabaseCollationInfo GetDatabaseCollation(Oid dbOid); static AlterOwnerStmt * RecreateAlterDatabaseOwnerStmt(Oid databaseOid); +#if PG_VERSION_NUM >= PG_VERSION_15 static char * GetLocaleProviderString(char datlocprovider); +#endif static char * GetTablespaceName(Oid tablespaceOid); static ObjectAddress * GetDatabaseAddressFromDatabaseName(char *databaseName,bool missingOk); From 6f838e6e8849d11d6e59962678050771a3ce443a Mon Sep 17 00:00:00 2001 From: gindibay Date: Wed, 15 Nov 2023 16:34:02 +0300 Subject: [PATCH 34/68] Fixes indentation --- src/backend/distributed/commands/database.c | 17 +++++++++++------ src/backend/distributed/commands/dependencies.c | 14 +++++++------- src/backend/distributed/commands/role.c | 2 +- .../distributed/metadata/metadata_sync.c | 15 ++++++++++----- 4 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/backend/distributed/commands/database.c b/src/backend/distributed/commands/database.c index 3570966a204..71e33b096df 100644 --- a/src/backend/distributed/commands/database.c +++ b/src/backend/distributed/commands/database.c @@ -63,14 +63,16 @@ typedef struct DatabaseCollationInfo } DatabaseCollationInfo; static void EnsureSupportedCreateDatabaseCommand(CreatedbStmt *stmt); -static char * GenerateCreateDatabaseStatementFromPgDatabase(Form_pg_database databaseForm); +static char * GenerateCreateDatabaseStatementFromPgDatabase(Form_pg_database + databaseForm); static DatabaseCollationInfo GetDatabaseCollation(Oid dbOid); static AlterOwnerStmt * RecreateAlterDatabaseOwnerStmt(Oid databaseOid); #if PG_VERSION_NUM >= PG_VERSION_15 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); @@ -660,12 +662,14 @@ GenerateCreateDatabaseStatementFromPgDatabase(Form_pg_database databaseForm) return str.data; } + /* * GrantOnDatabaseDDLCommands returns a list of sql statements to idempotently apply a * GRANT on distributed databases. */ - -List * GenerateGrantDatabaseCommandList(void){ +List * +GenerateGrantDatabaseCommandList(void) +{ List *grantCommands = NIL; Relation pgDatabaseRel = table_open(DatabaseRelationId, AccessShareLock); @@ -685,7 +689,7 @@ List * GenerateGrantDatabaseCommandList(void){ continue; } - List *dbGrants = GrantOnDatabaseDDLCommands(databaseForm->oid); + List *dbGrants = GrantOnDatabaseDDLCommands(databaseForm->oid); /* append dbGrants into grantCommands*/ grantCommands = list_concat(grantCommands, dbGrants); @@ -712,7 +716,8 @@ List * GenerateGrantDatabaseCommandList(void){ * Commands in the list are wrapped by citus_internal_database_command() UDF * to avoid from transaction block restrictions that apply to database commands */ -List * GenerateCreateDatabaseCommandList(void) +List * +GenerateCreateDatabaseCommandList(void) { List *commands = NIL; diff --git a/src/backend/distributed/commands/dependencies.c b/src/backend/distributed/commands/dependencies.c index 9957bcdfcb0..3c279f1e6fd 100644 --- a/src/backend/distributed/commands/dependencies.c +++ b/src/backend/distributed/commands/dependencies.c @@ -465,13 +465,13 @@ GetDependencyCreateDDLCommands(const ObjectAddress *dependency) List *ownerDDLCommands = DatabaseOwnerDDLCommands(dependency); databaseDDLCommands = list_concat(databaseDDLCommands, ownerDDLCommands); } - //TODO: To reviewer: Having a code block for dependency makes sense - // However dependency tree is based on pg metadata; which does not reflect - // actual database dependencies. I added this block just to point out the issue. - // if(EnableCreateDatabasePropagation){ - // List *dbGrants = GrantOnDatabaseDDLCommands(dependency->objectId); - // databaseDDLCommands = list_concat(databaseDDLCommands, dbGrants); - // } + /*TODO: To reviewer: Having a code block for dependency makes sense */ + /* However dependency tree is based on pg metadata; which does not reflect */ + /* actual database dependencies. I added this block just to point out the issue. */ + /* if(EnableCreateDatabasePropagation){ */ + /* List *dbGrants = GrantOnDatabaseDDLCommands(dependency->objectId); */ + /* databaseDDLCommands = list_concat(databaseDDLCommands, dbGrants); */ + /* } */ return databaseDDLCommands; } diff --git a/src/backend/distributed/commands/role.c b/src/backend/distributed/commands/role.c index 632920f7003..0293d6a7b63 100644 --- a/src/backend/distributed/commands/role.c +++ b/src/backend/distributed/commands/role.c @@ -513,7 +513,7 @@ GenerateRoleOptionsList(HeapTuple tuple) List * GenerateCreateOrAlterRoleCommand(Oid roleOid) { - elog(LOG,"GenerateCreateOrAlterRoleCommand execution"); + elog(LOG, "GenerateCreateOrAlterRoleCommand execution"); HeapTuple roleTuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleOid)); Form_pg_authid role = ((Form_pg_authid) GETSTRUCT(roleTuple)); diff --git a/src/backend/distributed/metadata/metadata_sync.c b/src/backend/distributed/metadata/metadata_sync.c index d917b57a288..089094fac8a 100644 --- a/src/backend/distributed/metadata/metadata_sync.c +++ b/src/backend/distributed/metadata/metadata_sync.c @@ -2048,6 +2048,7 @@ GenerateGrantOnSchemaQueriesFromAclItem(Oid schemaOid, AclItem *aclItem) return queries; } + List * GrantOnDatabaseDDLCommands(Oid databaseOid) { @@ -2071,7 +2072,7 @@ GrantOnDatabaseDDLCommands(Oid databaseOid) { commands = list_concat(commands, GenerateGrantOnDatabaseFromAclItem( - databaseOid,&aclDat[i])); + databaseOid, &aclDat[i])); } return commands; @@ -2098,21 +2099,24 @@ GenerateGrantOnDatabaseFromAclItem(Oid databaseOid, AclItem *aclItem) if (permissions & ACL_CONNECT) { char *query = DeparseTreeNode((Node *) GenerateGrantStmtForRights( - OBJECT_DATABASE ,granteeOid, databaseOid, "CONNECT", + OBJECT_DATABASE, granteeOid, databaseOid, + "CONNECT", grants & ACL_CONNECT)); queries = lappend(queries, query); } if (permissions & ACL_CREATE) { char *query = DeparseTreeNode((Node *) GenerateGrantStmtForRights( - OBJECT_DATABASE, granteeOid, databaseOid, "CREATE", + OBJECT_DATABASE, granteeOid, databaseOid, + "CREATE", grants & ACL_CREATE)); queries = lappend(queries, query); } if (permissions & ACL_CREATE_TEMP) { char *query = DeparseTreeNode((Node *) GenerateGrantStmtForRights( - OBJECT_DATABASE, granteeOid, databaseOid, "TEMPORARY", + OBJECT_DATABASE, granteeOid, databaseOid, + "TEMPORARY", grants & ACL_CREATE_TEMP)); queries = lappend(queries, query); } @@ -4736,7 +4740,7 @@ SyncDistributedObjects(MetadataSyncContext *context) /* * After creation of databases and roles, send the grant database commands * to the workers. - */ + */ SendDatabaseGrantSyncCommands(context); } @@ -4762,6 +4766,7 @@ SendNodeWideObjectsSyncCommands(MetadataSyncContext *context) SendOrCollectCommandListToActivatedNodes(context, commandList); } + /* * SendDatabaseGrantSyncCommands sends database grants to roles to workers with * transactional or nontransactional mode according to transactionMode inside From 931514b0f7631a3b86538efe11ad7b513cee08be Mon Sep 17 00:00:00 2001 From: gindibay Date: Wed, 15 Nov 2023 16:41:38 +0300 Subject: [PATCH 35/68] Fixes indentation --- src/backend/distributed/commands/dependencies.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/backend/distributed/commands/dependencies.c b/src/backend/distributed/commands/dependencies.c index 3c279f1e6fd..732d1040974 100644 --- a/src/backend/distributed/commands/dependencies.c +++ b/src/backend/distributed/commands/dependencies.c @@ -465,6 +465,7 @@ GetDependencyCreateDDLCommands(const ObjectAddress *dependency) List *ownerDDLCommands = DatabaseOwnerDDLCommands(dependency); databaseDDLCommands = list_concat(databaseDDLCommands, ownerDDLCommands); } + /*TODO: To reviewer: Having a code block for dependency makes sense */ /* However dependency tree is based on pg metadata; which does not reflect */ /* actual database dependencies. I added this block just to point out the issue. */ From 37681eaed074a2bc26280aa644b600e05eed1f36 Mon Sep 17 00:00:00 2001 From: gindibay Date: Wed, 15 Nov 2023 17:01:42 +0300 Subject: [PATCH 36/68] Fixes comment on code --- src/backend/distributed/commands/dependencies.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/backend/distributed/commands/dependencies.c b/src/backend/distributed/commands/dependencies.c index 732d1040974..c15f9fba4cb 100644 --- a/src/backend/distributed/commands/dependencies.c +++ b/src/backend/distributed/commands/dependencies.c @@ -468,11 +468,17 @@ GetDependencyCreateDDLCommands(const ObjectAddress *dependency) /*TODO: To reviewer: Having a code block for dependency makes sense */ /* However dependency tree is based on pg metadata; which does not reflect */ - /* actual database dependencies. I added this block just to point out the issue. */ - /* if(EnableCreateDatabasePropagation){ */ - /* List *dbGrants = GrantOnDatabaseDDLCommands(dependency->objectId); */ - /* databaseDDLCommands = list_concat(databaseDDLCommands, dbGrants); */ - /* } */ + + /* actual database dependencies. I added this block just to point out the issue. + */ + + /* + * if(EnableCreateDatabasePropagation){ + * List *dbGrants = GrantOnDatabaseDDLCommands(dependency->objectId); + * databaseDDLCommands = list_concat(databaseDDLCommands, dbGrants); + * + * } + */ return databaseDDLCommands; } From 79392673267b3a11ff34d19b39d8e56b3c9ed312 Mon Sep 17 00:00:00 2001 From: gindibay Date: Wed, 15 Nov 2023 17:28:50 +0300 Subject: [PATCH 37/68] Fixes flakiness of test --- .../regress/expected/create_drop_database_propagation.out | 8 ++------ src/test/regress/sql/create_drop_database_propagation.sql | 5 +++++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/test/regress/expected/create_drop_database_propagation.out b/src/test/regress/expected/create_drop_database_propagation.out index 23184ebca8d..993abee1da6 100644 --- a/src/test/regress/expected/create_drop_database_propagation.out +++ b/src/test/regress/expected/create_drop_database_propagation.out @@ -631,13 +631,9 @@ DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx grant CONNECT,TEMPORARY,CREATE on DATABASE db_role_grants_test to db_role_grants_test_role_missing_on_node_2; NOTICE: issuing GRANT connect, temporary, create ON DATABASE db_role_grants_test TO db_role_grants_test_role_missing_on_node_2; DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +RESET citus.log_remote_commands; +RESET citus.grep_remote_commands; select 1 from citus_add_node('localhost', :worker_2_port); -NOTICE: issuing CREATE SCHEMA IF NOT EXISTS public AUTHORIZATION pg_database_owner;SET ROLE pg_database_owner;GRANT USAGE ON SCHEMA public TO pg_database_owner;;GRANT CREATE ON SCHEMA public TO pg_database_owner;;RESET ROLE;SET ROLE pg_database_owner;GRANT USAGE ON SCHEMA public TO PUBLIC;;RESET ROLE -DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing CREATE SCHEMA IF NOT EXISTS information_schema AUTHORIZATION postgres;SET ROLE postgres;GRANT USAGE ON SCHEMA information_schema TO postgres;;GRANT CREATE ON SCHEMA information_schema TO postgres;;RESET ROLE;SET ROLE postgres;GRANT USAGE ON SCHEMA information_schema TO PUBLIC;;RESET ROLE -DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing SET citus.enable_ddl_propagation TO 'off';SET ROLE postgres;GRANT CONNECT ON DATABASE db_role_grants_test TO postgres;;GRANT CREATE ON DATABASE db_role_grants_test TO postgres;;GRANT TEMPORARY ON DATABASE db_role_grants_test TO postgres;;RESET ROLE;SET ROLE postgres;GRANT CONNECT ON DATABASE db_role_grants_test TO db_role_grants_test_role_exists_on_node_2;;GRANT CREATE ON DATABASE db_role_grants_test TO db_role_grants_test_role_exists_on_node_2;;GRANT TEMPORARY ON DATABASE db_role_grants_test TO db_role_grants_test_role_exists_on_node_2;;RESET ROLE;SET ROLE postgres;GRANT CONNECT ON DATABASE db_role_grants_test TO db_role_grants_test_role_missing_on_node_2;;GRANT CREATE ON DATABASE db_role_grants_test TO db_role_grants_test_role_missing_on_node_2;;GRANT TEMPORARY ON DATABASE db_role_grants_test TO db_role_grants_test_role_missing_on_node_2;;RESET ROLE;SET citus.enable_ddl_propagation TO 'on' -DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx ?column? --------------------------------------------------------------------- 1 diff --git a/src/test/regress/sql/create_drop_database_propagation.sql b/src/test/regress/sql/create_drop_database_propagation.sql index bc53f87df84..c151f463882 100644 --- a/src/test/regress/sql/create_drop_database_propagation.sql +++ b/src/test/regress/sql/create_drop_database_propagation.sql @@ -366,9 +366,14 @@ 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; + +RESET citus.log_remote_commands; +RESET citus.grep_remote_commands; + select 1 from citus_add_node('localhost', :worker_2_port); + SELECT result from run_command_on_all_nodes( $$ select has_database_privilege('db_role_grants_test_role_exists_on_node_2','db_role_grants_test', 'CREATE') From deee8e53dd3cd4ad864007f64d56fd6c41c92b61 Mon Sep 17 00:00:00 2001 From: gindibay Date: Wed, 15 Nov 2023 17:33:14 +0300 Subject: [PATCH 38/68] Adds grant to public --- .../regress/expected/create_drop_database_propagation.out | 3 ++- src/test/regress/sql/create_drop_database_propagation.sql | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/test/regress/expected/create_drop_database_propagation.out b/src/test/regress/expected/create_drop_database_propagation.out index 993abee1da6..ac3b9a1c208 100644 --- a/src/test/regress/expected/create_drop_database_propagation.out +++ b/src/test/regress/expected/create_drop_database_propagation.out @@ -548,7 +548,7 @@ select 1 from citus_remove_node('localhost', :worker_2_port); (1 row) CREATE DATABASE db_role_grants_test; -revoke connect,temp,temporary on database db_role_grants_test from public; +revoke connect,temp,temporary,create on database db_role_grants_test from public; SET citus.log_remote_commands = true; set citus.grep_remote_commands = '%CREATE ROLE%'; CREATE ROLE db_role_grants_test_role_missing_on_node_2; @@ -711,6 +711,7 @@ SELECT result from run_command_on_all_nodes( t (3 rows) +grant connect,temp,temporary,create on database db_role_grants_test to public; DROP DATABASE db_role_grants_test; DROP ROLE db_role_grants_test_role_exists_on_node_2; DROP ROLE db_role_grants_test_role_missing_on_node_2; diff --git a/src/test/regress/sql/create_drop_database_propagation.sql b/src/test/regress/sql/create_drop_database_propagation.sql index c151f463882..5eed4ad2449 100644 --- a/src/test/regress/sql/create_drop_database_propagation.sql +++ b/src/test/regress/sql/create_drop_database_propagation.sql @@ -313,7 +313,7 @@ select 1 from citus_remove_node('localhost', :worker_2_port); CREATE DATABASE db_role_grants_test; -revoke connect,temp,temporary on database db_role_grants_test from public; +revoke connect,temp,temporary,create on database db_role_grants_test from public; SET citus.log_remote_commands = true; set citus.grep_remote_commands = '%CREATE ROLE%'; @@ -411,12 +411,15 @@ SELECT result from run_command_on_all_nodes( $$ ) ORDER BY result; +grant connect,temp,temporary,create on database db_role_grants_test to public; DROP DATABASE db_role_grants_test; DROP ROLE db_role_grants_test_role_exists_on_node_2; DROP ROLE db_role_grants_test_role_missing_on_node_2; + + --clean up resources created by this test -- DROP TABLESPACE is not supported, so we need to drop it manually. From dc48833679004076524baa5949413464907d2060 Mon Sep 17 00:00:00 2001 From: gindibay Date: Wed, 15 Nov 2023 18:48:46 +0300 Subject: [PATCH 39/68] Adds tests for non-distributed database --- .../create_drop_database_propagation.out | 229 ++++++++++++++++-- .../sql/create_drop_database_propagation.sql | 126 +++++++++- 2 files changed, 329 insertions(+), 26 deletions(-) diff --git a/src/test/regress/expected/create_drop_database_propagation.out b/src/test/regress/expected/create_drop_database_propagation.out index ac3b9a1c208..fa38a1a6958 100644 --- a/src/test/regress/expected/create_drop_database_propagation.out +++ b/src/test/regress/expected/create_drop_database_propagation.out @@ -539,6 +539,30 @@ 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( + $$ + create database db_role_grants_test_non_distributed + $$ +) ORDER BY result; + result +--------------------------------------------------------------------- + CREATE DATABASE + CREATE DATABASE + CREATE DATABASE +(3 rows) + +SELECT result from run_command_on_all_nodes( + $$ + revoke connect,temp,temporary,create on database db_role_grants_test_non_distributed from public + $$ +) ORDER BY result; + result +--------------------------------------------------------------------- + ERROR: operation is not allowed on this node + ERROR: operation is not allowed on this node + REVOKE +(3 rows) + 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); @@ -556,7 +580,22 @@ NOTICE: issuing SELECT worker_create_or_alter_role('db_role_grants_test_role_mi DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx RESET citus.log_remote_commands ; RESET citus.grep_remote_commands; --- check the privileges before grant +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; +NOTICE: issuing GRANT connect, temporary, create ON DATABASE db_role_grants_test TO db_role_grants_test_role_exists_on_node_2; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +grant CONNECT,TEMPORARY,CREATE on DATABASE db_role_grants_test to db_role_grants_test_role_missing_on_node_2; +NOTICE: issuing GRANT connect, temporary, create ON DATABASE db_role_grants_test TO db_role_grants_test_role_missing_on_node_2; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +grant CONNECT,TEMPORARY,CREATE on DATABASE db_role_grants_test_non_distributed to db_role_grants_test_role_exists_on_node_2; +NOTICE: issuing GRANT connect, temporary, create ON DATABASE db_role_grants_test_non_distributed TO db_role_grants_test_role_exists_on_node_2; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +grant CONNECT,TEMPORARY,CREATE on DATABASE db_role_grants_test_non_distributed to db_role_grants_test_role_missing_on_node_2; +NOTICE: issuing GRANT connect, temporary, create ON DATABASE db_role_grants_test_non_distributed TO db_role_grants_test_role_missing_on_node_2; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +-- check the privileges before add_node for database db_role_grants_test, +-- role db_role_grants_test_role_exists_on_node_2 SELECT result from run_command_on_all_nodes( $$ select has_database_privilege('db_role_grants_test_role_exists_on_node_2','db_role_grants_test', 'CREATE') @@ -564,8 +603,8 @@ SELECT result from run_command_on_all_nodes( ) ORDER BY result; result --------------------------------------------------------------------- - f - f + t + t (2 rows) SELECT result from run_command_on_all_nodes( @@ -575,8 +614,8 @@ SELECT result from run_command_on_all_nodes( ) ORDER BY result; result --------------------------------------------------------------------- - f - f + t + t (2 rows) SELECT result from run_command_on_all_nodes( @@ -586,10 +625,12 @@ SELECT result from run_command_on_all_nodes( ) ORDER BY result; result --------------------------------------------------------------------- - f - f + t + t (2 rows) +-- check the privileges before add_node for database db_role_grants_test, +-- role db_role_grants_test_role_missing_on_node_2 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', 'CREATE') @@ -597,8 +638,8 @@ SELECT result from run_command_on_all_nodes( ) ORDER BY result; result --------------------------------------------------------------------- - f - f + t + t (2 rows) SELECT result from run_command_on_all_nodes( @@ -608,8 +649,8 @@ SELECT result from run_command_on_all_nodes( ) ORDER BY result; result --------------------------------------------------------------------- - f - f + t + t (2 rows) SELECT result from run_command_on_all_nodes( @@ -619,18 +660,80 @@ SELECT result from run_command_on_all_nodes( ) ORDER BY result; result --------------------------------------------------------------------- - f - f + t + t +(2 rows) + +-- check the privileges before add_node for database db_role_grants_test_non_distributed, +-- role db_role_grants_test_role_exists_on_node_2 +SELECT result from run_command_on_all_nodes( + $$ + select has_database_privilege('db_role_grants_test_role_exists_on_node_2','db_role_grants_test_non_distributed', 'CREATE') + $$ +) ORDER BY result; + result +--------------------------------------------------------------------- + t + t +(2 rows) + +SELECT result from run_command_on_all_nodes( + $$ + select has_database_privilege('db_role_grants_test_role_exists_on_node_2','db_role_grants_test_non_distributed', 'TEMPORARY') + $$ +) ORDER BY result; + result +--------------------------------------------------------------------- + t + t +(2 rows) + +SELECT result from run_command_on_all_nodes( + $$ + select has_database_privilege('db_role_grants_test_role_exists_on_node_2','db_role_grants_test_non_distributed', 'CONNECT') + $$ +) ORDER BY result; + result +--------------------------------------------------------------------- + t + t +(2 rows) + +-- check the privileges before add_node for database db_role_grants_test_non_distributed, +-- role db_role_grants_test_role_missing_on_node_2 +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', 'CREATE') + $$ +) ORDER BY result; + result +--------------------------------------------------------------------- + t + t +(2 rows) + +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') + $$ +) ORDER BY result; + result +--------------------------------------------------------------------- + t + t +(2 rows) + +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', 'CONNECT') + $$ +) ORDER BY result; + result +--------------------------------------------------------------------- + t + t (2 rows) -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; -NOTICE: issuing GRANT connect, temporary, create ON DATABASE db_role_grants_test TO db_role_grants_test_role_exists_on_node_2; -DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -grant CONNECT,TEMPORARY,CREATE on DATABASE db_role_grants_test to db_role_grants_test_role_missing_on_node_2; -NOTICE: issuing GRANT connect, temporary, create ON DATABASE db_role_grants_test TO db_role_grants_test_role_missing_on_node_2; -DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx RESET citus.log_remote_commands; RESET citus.grep_remote_commands; select 1 from citus_add_node('localhost', :worker_2_port); @@ -639,6 +742,8 @@ select 1 from citus_add_node('localhost', :worker_2_port); 1 (1 row) +-- check the privileges after add_node for database db_role_grants_test, +-- role db_role_grants_test_role_exists_on_node_2 SELECT result from run_command_on_all_nodes( $$ select has_database_privilege('db_role_grants_test_role_exists_on_node_2','db_role_grants_test', 'CREATE') @@ -675,6 +780,8 @@ SELECT result from run_command_on_all_nodes( t (3 rows) +-- check the privileges after add_node for database db_role_grants_test, +-- role db_role_grants_test_role_missing_on_node_2 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', 'CREATE') @@ -711,10 +818,90 @@ SELECT result from run_command_on_all_nodes( t (3 rows) +-- check the privileges after add_node for database db_role_grants_test_non_distributed, +-- role db_role_grants_test_role_exists_on_node_2 +SELECT result from run_command_on_all_nodes( + $$ + select has_database_privilege('db_role_grants_test_role_exists_on_node_2','db_role_grants_test_non_distributed', 'CREATE') + $$ +) ORDER BY result; + result +--------------------------------------------------------------------- + f + t + t +(3 rows) + +SELECT result from run_command_on_all_nodes( + $$ + select has_database_privilege('db_role_grants_test_role_exists_on_node_2','db_role_grants_test_non_distributed', 'TEMPORARY') + $$ +) ORDER BY result; + result +--------------------------------------------------------------------- + f + t + t +(3 rows) + +SELECT result from run_command_on_all_nodes( + $$ + select has_database_privilege('db_role_grants_test_role_exists_on_node_2','db_role_grants_test_non_distributed', 'CONNECT') + $$ +) ORDER BY result; + result +--------------------------------------------------------------------- + f + t + t +(3 rows) + +-- check the privileges after add_node for database db_role_grants_test_non_distributed, +-- role db_role_grants_test_role_missing_on_node_2 +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', 'CREATE') + $$ +) ORDER BY result; + result +--------------------------------------------------------------------- + f + t + t +(3 rows) + +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') + $$ +) ORDER BY result; + result +--------------------------------------------------------------------- + f + t + t +(3 rows) + +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', 'CONNECT') + $$ +) ORDER BY result; + result +--------------------------------------------------------------------- + f + t + t +(3 rows) + grant connect,temp,temporary,create on database db_role_grants_test to public; DROP DATABASE db_role_grants_test; DROP ROLE db_role_grants_test_role_exists_on_node_2; +ERROR: role "db_role_grants_test_role_exists_on_node_2" cannot be dropped because some objects depend on it +DETAIL: privileges for database db_role_grants_test_non_distributed DROP ROLE db_role_grants_test_role_missing_on_node_2; +ERROR: role "db_role_grants_test_role_missing_on_node_2" cannot be dropped because some objects depend on it +DETAIL: privileges for database db_role_grants_test_non_distributed --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( diff --git a/src/test/regress/sql/create_drop_database_propagation.sql b/src/test/regress/sql/create_drop_database_propagation.sql index 5eed4ad2449..727ae02982a 100644 --- a/src/test/regress/sql/create_drop_database_propagation.sql +++ b/src/test/regress/sql/create_drop_database_propagation.sql @@ -300,7 +300,17 @@ drop database non_distributed_db; -- test role grants on DATABASE in metadata sync +SELECT result from run_command_on_all_nodes( + $$ + create database db_role_grants_test_non_distributed + $$ +) ORDER BY result; +SELECT result from run_command_on_all_nodes( + $$ + revoke connect,temp,temporary,create on database db_role_grants_test_non_distributed from public + $$ +) ORDER BY result; SET citus.enable_create_database_propagation TO on; @@ -322,7 +332,20 @@ CREATE ROLE db_role_grants_test_role_missing_on_node_2; RESET citus.log_remote_commands ; RESET citus.grep_remote_commands; --- check the privileges before grant + + +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; + +-- check the privileges before add_node for database db_role_grants_test, +-- role db_role_grants_test_role_exists_on_node_2 SELECT result from run_command_on_all_nodes( $$ @@ -342,6 +365,9 @@ SELECT result from run_command_on_all_nodes( $$ ) ORDER BY result; +-- check the privileges before add_node for database db_role_grants_test, +-- role db_role_grants_test_role_missing_on_node_2 + 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', 'CREATE') @@ -361,10 +387,47 @@ SELECT result from run_command_on_all_nodes( $$ ) ORDER BY result; -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; +-- check the privileges before add_node for database db_role_grants_test_non_distributed, +-- role db_role_grants_test_role_exists_on_node_2 +SELECT result from run_command_on_all_nodes( + $$ + select has_database_privilege('db_role_grants_test_role_exists_on_node_2','db_role_grants_test_non_distributed', 'CREATE') + $$ +) ORDER BY result; + +SELECT result from run_command_on_all_nodes( + $$ + select has_database_privilege('db_role_grants_test_role_exists_on_node_2','db_role_grants_test_non_distributed', 'TEMPORARY') + $$ +) ORDER BY result; + +SELECT result from run_command_on_all_nodes( + $$ + select has_database_privilege('db_role_grants_test_role_exists_on_node_2','db_role_grants_test_non_distributed', 'CONNECT') + $$ +) ORDER BY result; + +-- check the privileges before add_node for database db_role_grants_test_non_distributed, +-- role db_role_grants_test_role_missing_on_node_2 + +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', 'CREATE') + $$ +) 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') + $$ +) 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', 'CONNECT') + $$ +) ORDER BY result; RESET citus.log_remote_commands; @@ -373,6 +436,8 @@ 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 SELECT result from run_command_on_all_nodes( $$ @@ -392,6 +457,9 @@ SELECT result from run_command_on_all_nodes( $$ ) ORDER BY result; +-- check the privileges after add_node for database db_role_grants_test, +-- role db_role_grants_test_role_missing_on_node_2 + 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', 'CREATE') @@ -411,9 +479,57 @@ SELECT result from run_command_on_all_nodes( $$ ) ORDER BY result; +-- check the privileges after add_node for database db_role_grants_test_non_distributed, +-- role db_role_grants_test_role_exists_on_node_2 +SELECT result from run_command_on_all_nodes( + $$ + select has_database_privilege('db_role_grants_test_role_exists_on_node_2','db_role_grants_test_non_distributed', 'CREATE') + $$ +) ORDER BY result; + +SELECT result from run_command_on_all_nodes( + $$ + select has_database_privilege('db_role_grants_test_role_exists_on_node_2','db_role_grants_test_non_distributed', 'TEMPORARY') + $$ +) ORDER BY result; + +SELECT result from run_command_on_all_nodes( + $$ + select has_database_privilege('db_role_grants_test_role_exists_on_node_2','db_role_grants_test_non_distributed', 'CONNECT') + $$ +) ORDER BY result; + +-- check the privileges after add_node for database db_role_grants_test_non_distributed, +-- role db_role_grants_test_role_missing_on_node_2 + +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', 'CREATE') + $$ +) 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') + $$ +) 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', 'CONNECT') + $$ +) ORDER BY result; + grant connect,temp,temporary,create on database db_role_grants_test to public; DROP DATABASE db_role_grants_test; + +SELECT result from run_command_on_all_nodes( + $$ + drop database db_role_grants_test_non_distributed + $$ +) ORDER BY result; DROP ROLE db_role_grants_test_role_exists_on_node_2; DROP ROLE db_role_grants_test_role_missing_on_node_2; From 0f1273f6ad737aab5b1d117cfbcd45f2e2442a58 Mon Sep 17 00:00:00 2001 From: gindibay Date: Wed, 15 Nov 2023 19:03:16 +0300 Subject: [PATCH 40/68] Updates test output --- .../create_drop_database_propagation.out | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/test/regress/expected/create_drop_database_propagation.out b/src/test/regress/expected/create_drop_database_propagation.out index fa38a1a6958..84ad9f7b5db 100644 --- a/src/test/regress/expected/create_drop_database_propagation.out +++ b/src/test/regress/expected/create_drop_database_propagation.out @@ -896,12 +896,20 @@ SELECT result from run_command_on_all_nodes( grant connect,temp,temporary,create on database db_role_grants_test to public; DROP DATABASE db_role_grants_test; +SELECT result from run_command_on_all_nodes( + $$ + drop database db_role_grants_test_non_distributed + $$ +) ORDER BY result; + result +--------------------------------------------------------------------- + DROP DATABASE + DROP DATABASE + DROP DATABASE +(3 rows) + DROP ROLE db_role_grants_test_role_exists_on_node_2; -ERROR: role "db_role_grants_test_role_exists_on_node_2" cannot be dropped because some objects depend on it -DETAIL: privileges for database db_role_grants_test_non_distributed DROP ROLE db_role_grants_test_role_missing_on_node_2; -ERROR: role "db_role_grants_test_role_missing_on_node_2" cannot be dropped because some objects depend on it -DETAIL: privileges for database db_role_grants_test_non_distributed --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( @@ -917,3 +925,4 @@ SELECT result FROM run_command_on_all_nodes( (3 rows) drop user create_drop_db_test_user; + From 11e7c94e2f6f62c08f6476e4d45b9d5a64ce10a1 Mon Sep 17 00:00:00 2001 From: gindibay Date: Wed, 15 Nov 2023 19:32:00 +0300 Subject: [PATCH 41/68] Removes extra line in test --- src/test/regress/expected/create_drop_database_propagation.out | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/regress/expected/create_drop_database_propagation.out b/src/test/regress/expected/create_drop_database_propagation.out index 84ad9f7b5db..b0831aa5540 100644 --- a/src/test/regress/expected/create_drop_database_propagation.out +++ b/src/test/regress/expected/create_drop_database_propagation.out @@ -925,4 +925,3 @@ SELECT result FROM run_command_on_all_nodes( (3 rows) drop user create_drop_db_test_user; - From 144ede3bf0fa6b730590742da8497ff5860632ee Mon Sep 17 00:00:00 2001 From: gindibay Date: Wed, 15 Nov 2023 20:45:13 +0300 Subject: [PATCH 42/68] Adds GUC check for db grant sync --- src/backend/distributed/metadata/metadata_sync.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/backend/distributed/metadata/metadata_sync.c b/src/backend/distributed/metadata/metadata_sync.c index 089094fac8a..af352088823 100644 --- a/src/backend/distributed/metadata/metadata_sync.c +++ b/src/backend/distributed/metadata/metadata_sync.c @@ -4741,7 +4741,9 @@ SyncDistributedObjects(MetadataSyncContext *context) * After creation of databases and roles, send the grant database commands * to the workers. */ - SendDatabaseGrantSyncCommands(context); + if (EnableCreateDatabasePropagation){ + SendDatabaseGrantSyncCommands(context); + } } From e18c4400dcccc48200d823df461a6733fb7e9d10 Mon Sep 17 00:00:00 2001 From: gindibay Date: Wed, 15 Nov 2023 20:48:17 +0300 Subject: [PATCH 43/68] Removes unnecessary logs --- src/backend/distributed/commands/database.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/backend/distributed/commands/database.c b/src/backend/distributed/commands/database.c index 71e33b096df..b988122ef3c 100644 --- a/src/backend/distributed/commands/database.c +++ b/src/backend/distributed/commands/database.c @@ -695,13 +695,6 @@ GenerateGrantDatabaseCommandList(void) grantCommands = list_concat(grantCommands, dbGrants); } - char *grantCommand = NULL; - - foreach_ptr(grantCommand, grantCommands) - { - elog(DEBUG1, "grantCommand: %s", grantCommand); - } - heap_endscan(scan); table_close(pgDatabaseRel, AccessShareLock); From cd40380d80807d0f0ddec8cfcd3be6665e13b639 Mon Sep 17 00:00:00 2001 From: gindibay Date: Wed, 15 Nov 2023 20:51:40 +0300 Subject: [PATCH 44/68] resets enable_create_database_propagation --- src/test/regress/sql/create_drop_database_propagation.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/regress/sql/create_drop_database_propagation.sql b/src/test/regress/sql/create_drop_database_propagation.sql index 727ae02982a..cdd7cb3e0cf 100644 --- a/src/test/regress/sql/create_drop_database_propagation.sql +++ b/src/test/regress/sql/create_drop_database_propagation.sql @@ -546,3 +546,4 @@ SELECT result FROM run_command_on_all_nodes( ); drop user create_drop_db_test_user; +reset enable_create_database_propagation; From 9b6511ad10d9fea81c0a707fc50715582e05fddc Mon Sep 17 00:00:00 2001 From: gindibay Date: Wed, 15 Nov 2023 20:57:27 +0300 Subject: [PATCH 45/68] Fixes indentation --- src/backend/distributed/metadata/metadata_sync.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/backend/distributed/metadata/metadata_sync.c b/src/backend/distributed/metadata/metadata_sync.c index af352088823..d31ccde3d8a 100644 --- a/src/backend/distributed/metadata/metadata_sync.c +++ b/src/backend/distributed/metadata/metadata_sync.c @@ -4741,7 +4741,8 @@ SyncDistributedObjects(MetadataSyncContext *context) * After creation of databases and roles, send the grant database commands * to the workers. */ - if (EnableCreateDatabasePropagation){ + if (EnableCreateDatabasePropagation) + { SendDatabaseGrantSyncCommands(context); } } From feb609868e2648ac638b86e5742f00adfded91f9 Mon Sep 17 00:00:00 2001 From: gindibay Date: Wed, 15 Nov 2023 21:12:45 +0300 Subject: [PATCH 46/68] Fixes flag name --- src/test/regress/expected/create_drop_database_propagation.out | 1 + src/test/regress/sql/create_drop_database_propagation.sql | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/regress/expected/create_drop_database_propagation.out b/src/test/regress/expected/create_drop_database_propagation.out index b0831aa5540..bc3d5314f05 100644 --- a/src/test/regress/expected/create_drop_database_propagation.out +++ b/src/test/regress/expected/create_drop_database_propagation.out @@ -925,3 +925,4 @@ SELECT result FROM run_command_on_all_nodes( (3 rows) drop user create_drop_db_test_user; +reset citus.enable_create_database_propagation; diff --git a/src/test/regress/sql/create_drop_database_propagation.sql b/src/test/regress/sql/create_drop_database_propagation.sql index cdd7cb3e0cf..82e179591db 100644 --- a/src/test/regress/sql/create_drop_database_propagation.sql +++ b/src/test/regress/sql/create_drop_database_propagation.sql @@ -546,4 +546,4 @@ SELECT result FROM run_command_on_all_nodes( ); drop user create_drop_db_test_user; -reset enable_create_database_propagation; +reset citus.enable_create_database_propagation; From 772a13dc7c13983cb9b7f11d0a6518ea46fc26a4 Mon Sep 17 00:00:00 2001 From: gindibay Date: Wed, 15 Nov 2023 21:32:01 +0300 Subject: [PATCH 47/68] Removes SendDatabaseGrantSyncCommands --- src/backend/distributed/metadata/metadata_sync.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/backend/distributed/metadata/metadata_sync.c b/src/backend/distributed/metadata/metadata_sync.c index d31ccde3d8a..2a1f826f424 100644 --- a/src/backend/distributed/metadata/metadata_sync.c +++ b/src/backend/distributed/metadata/metadata_sync.c @@ -4741,10 +4741,9 @@ SyncDistributedObjects(MetadataSyncContext *context) * After creation of databases and roles, send the grant database commands * to the workers. */ - if (EnableCreateDatabasePropagation) - { - SendDatabaseGrantSyncCommands(context); - } + + SendDatabaseGrantSyncCommands(context); + } From a68587391c766788c95984d543f349e16bd6e6fc Mon Sep 17 00:00:00 2001 From: gindibay Date: Wed, 15 Nov 2023 21:32:28 +0300 Subject: [PATCH 48/68] Fixes indentation --- src/backend/distributed/metadata/metadata_sync.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/backend/distributed/metadata/metadata_sync.c b/src/backend/distributed/metadata/metadata_sync.c index 2a1f826f424..82267ac0290 100644 --- a/src/backend/distributed/metadata/metadata_sync.c +++ b/src/backend/distributed/metadata/metadata_sync.c @@ -4743,7 +4743,6 @@ SyncDistributedObjects(MetadataSyncContext *context) */ SendDatabaseGrantSyncCommands(context); - } From bb2b7ae9da0af87297f49b8b5b93ea970116e2a6 Mon Sep 17 00:00:00 2001 From: gindibay Date: Wed, 15 Nov 2023 23:02:21 +0300 Subject: [PATCH 49/68] Adds logs for test --- src/backend/distributed/commands/database.c | 1 + src/test/regress/sql/multi_metadata_sync.sql | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/src/backend/distributed/commands/database.c b/src/backend/distributed/commands/database.c index b988122ef3c..4a1cb642872 100644 --- a/src/backend/distributed/commands/database.c +++ b/src/backend/distributed/commands/database.c @@ -688,6 +688,7 @@ GenerateGrantDatabaseCommandList(void) { continue; } + elog(NOTICE, "Granting on database %s", NameStr(databaseForm->datname)); List *dbGrants = GrantOnDatabaseDDLCommands(databaseForm->oid); diff --git a/src/test/regress/sql/multi_metadata_sync.sql b/src/test/regress/sql/multi_metadata_sync.sql index 1b8043cdd6d..4c72de7adfb 100644 --- a/src/test/regress/sql/multi_metadata_sync.sql +++ b/src/test/regress/sql/multi_metadata_sync.sql @@ -47,8 +47,15 @@ ALTER ROLE CURRENT_USER WITH PASSWORD 'dummypassword'; -- Show that, with no MX tables, activate node snapshot contains only the delete commands, -- pg_dist_node entries, pg_dist_object entries and roles. + +select pdo.*, pd.datname +from pg_dist_object pdo + left outer join pg_database pd on pdo.objid = pd.oid; + SELECT unnest(activate_node_snapshot()) order by 1; + + -- Create a test table with constraints and SERIAL and default from user defined sequence CREATE SEQUENCE user_defined_seq; CREATE TABLE mx_test_table (col_1 int UNIQUE, col_2 text NOT NULL, col_3 BIGSERIAL, col_4 BIGINT DEFAULT nextval('user_defined_seq')); From b474d98e5c670d09f10596a121661952b6fcfd3f Mon Sep 17 00:00:00 2001 From: gindibay Date: Wed, 15 Nov 2023 23:09:35 +0300 Subject: [PATCH 50/68] Removes log --- src/backend/distributed/commands/database.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/backend/distributed/commands/database.c b/src/backend/distributed/commands/database.c index 4a1cb642872..b988122ef3c 100644 --- a/src/backend/distributed/commands/database.c +++ b/src/backend/distributed/commands/database.c @@ -688,7 +688,6 @@ GenerateGrantDatabaseCommandList(void) { continue; } - elog(NOTICE, "Granting on database %s", NameStr(databaseForm->datname)); List *dbGrants = GrantOnDatabaseDDLCommands(databaseForm->oid); From 95572c5adaf437156e1580a82477fe3c1633a189 Mon Sep 17 00:00:00 2001 From: gindibay Date: Thu, 16 Nov 2023 02:58:56 +0300 Subject: [PATCH 51/68] Adds EnableCreateDatabasePropagation flag check --- src/backend/distributed/commands/role.c | 1 - .../distributed/metadata/metadata_sync.c | 23 +++++++++++-------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/backend/distributed/commands/role.c b/src/backend/distributed/commands/role.c index 0293d6a7b63..3c56e3fd725 100644 --- a/src/backend/distributed/commands/role.c +++ b/src/backend/distributed/commands/role.c @@ -513,7 +513,6 @@ GenerateRoleOptionsList(HeapTuple tuple) List * GenerateCreateOrAlterRoleCommand(Oid roleOid) { - elog(LOG, "GenerateCreateOrAlterRoleCommand execution"); HeapTuple roleTuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleOid)); Form_pg_authid role = ((Form_pg_authid) GETSTRUCT(roleTuple)); diff --git a/src/backend/distributed/metadata/metadata_sync.c b/src/backend/distributed/metadata/metadata_sync.c index 82267ac0290..77e0edd1176 100644 --- a/src/backend/distributed/metadata/metadata_sync.c +++ b/src/backend/distributed/metadata/metadata_sync.c @@ -4771,24 +4771,27 @@ SendNodeWideObjectsSyncCommands(MetadataSyncContext *context) /* * SendDatabaseGrantSyncCommands sends database grants to roles to workers with * transactional or nontransactional mode according to transactionMode inside - * metadataSyncContext. + * 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) { - /* propagate node wide objects. It includes only roles for now. */ - List *commandList = GenerateGrantDatabaseCommandList(); + if(EnableCreateDatabasePropagation){ + /* propagate node wide objects. It includes only roles for now. */ + List *commandList = GenerateGrantDatabaseCommandList(); - if (commandList == NIL) - { - return; - } + if (commandList == NIL) + { + return; + } - commandList = lcons(DISABLE_DDL_PROPAGATION, commandList); - commandList = lappend(commandList, ENABLE_DDL_PROPAGATION); - SendOrCollectCommandListToActivatedNodes(context, commandList); + commandList = lcons(DISABLE_DDL_PROPAGATION, commandList); + commandList = lappend(commandList, ENABLE_DDL_PROPAGATION); + SendOrCollectCommandListToActivatedNodes(context, commandList); + } } From 8443ae36e70db11bf978e2baec960bea8ff23070 Mon Sep 17 00:00:00 2001 From: gindibay Date: Thu, 16 Nov 2023 02:59:32 +0300 Subject: [PATCH 52/68] Fixes indentation --- src/backend/distributed/commands/role.c | 1 - src/backend/distributed/metadata/metadata_sync.c | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/distributed/commands/role.c b/src/backend/distributed/commands/role.c index 3c56e3fd725..f3ac7b4fff0 100644 --- a/src/backend/distributed/commands/role.c +++ b/src/backend/distributed/commands/role.c @@ -513,7 +513,6 @@ GenerateRoleOptionsList(HeapTuple tuple) List * GenerateCreateOrAlterRoleCommand(Oid roleOid) { - HeapTuple roleTuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleOid)); Form_pg_authid role = ((Form_pg_authid) GETSTRUCT(roleTuple)); diff --git a/src/backend/distributed/metadata/metadata_sync.c b/src/backend/distributed/metadata/metadata_sync.c index 77e0edd1176..ce2201903fb 100644 --- a/src/backend/distributed/metadata/metadata_sync.c +++ b/src/backend/distributed/metadata/metadata_sync.c @@ -4779,7 +4779,8 @@ SendNodeWideObjectsSyncCommands(MetadataSyncContext *context) static void SendDatabaseGrantSyncCommands(MetadataSyncContext *context) { - if(EnableCreateDatabasePropagation){ + if (EnableCreateDatabasePropagation) + { /* propagate node wide objects. It includes only roles for now. */ List *commandList = GenerateGrantDatabaseCommandList(); From 56bc813bd0471f75e6111d40cb7d36d57c8cf891 Mon Sep 17 00:00:00 2001 From: gindibay Date: Thu, 16 Nov 2023 05:13:10 +0300 Subject: [PATCH 53/68] Adds check_database_on_all_nodes --- .../regress/expected/multi_test_helpers.out | 55 +++++++++++++++++++ src/test/regress/sql/multi_metadata_sync.sql | 7 --- src/test/regress/sql/multi_test_helpers.sql | 55 +++++++++++++++++++ 3 files changed, 110 insertions(+), 7 deletions(-) diff --git a/src/test/regress/expected/multi_test_helpers.out b/src/test/regress/expected/multi_test_helpers.out index 4b621b968d5..9822a093106 100644 --- a/src/test/regress/expected/multi_test_helpers.out +++ b/src/test/regress/expected/multi_test_helpers.out @@ -556,3 +556,58 @@ BEGIN ORDER BY node_type; END; $func$ LANGUAGE plpgsql; +$func$ LANGUAGE plpgsql; +-- For all nodes, returns database properties of given database, except +-- oid, datfrozenxid and datminmxid. +-- +-- Also returns whether the node has a pg_dist_object record for the database +-- and whether there are any stale pg_dist_object records for a database. +CREATE OR REPLACE FUNCTION check_database_on_all_nodes(p_database_name text) +RETURNS TABLE (node_type text, result text) +AS $func$ +BEGIN + RETURN QUERY + SELECT + CASE WHEN (groupid = 0 AND groupid = (SELECT groupid FROM pg_dist_local_group)) THEN 'coordinator (local)' + WHEN (groupid = 0) THEN 'coordinator (remote)' + WHEN (groupid = (SELECT groupid FROM pg_dist_local_group)) THEN 'worker node (local)' + ELSE 'worker node (remote)' + END AS node_type, + q2.result + FROM run_command_on_all_nodes( + format( + $$ + SELECT to_jsonb(q.*) + FROM ( + SELECT + ( + SELECT to_jsonb(database_properties.*) + FROM ( + SELECT datname, pa.rolname as database_owner, + pg_encoding_to_char(pd.encoding) as encoding, datlocprovider, + datistemplate, datallowconn, datconnlimit, + pt.spcname AS tablespace, datcollate, datctype, daticulocale, + datcollversion, datacl + FROM pg_database pd + JOIN pg_authid pa ON pd.datdba = pa.oid + JOIN pg_tablespace pt ON pd.dattablespace = pt.oid + WHERE datname = '%s' + ) database_properties + ) AS database_properties, + ( + SELECT COUNT(*)=1 + FROM pg_dist_object WHERE objid = (SELECT oid FROM pg_database WHERE datname = '%s') + ) AS pg_dist_object_record_for_db_exists, + ( + SELECT COUNT(*) > 0 + FROM pg_dist_object + WHERE classid = 1262 AND objid NOT IN (SELECT oid FROM pg_database) + ) AS stale_pg_dist_object_record_for_a_db_exists + ) q + $$, + p_database_name, p_database_name + ) + ) q2 + JOIN pg_dist_node USING (nodeid); +END; +$func$ LANGUAGE plpgsql; \ No newline at end of file diff --git a/src/test/regress/sql/multi_metadata_sync.sql b/src/test/regress/sql/multi_metadata_sync.sql index 4c72de7adfb..1b8043cdd6d 100644 --- a/src/test/regress/sql/multi_metadata_sync.sql +++ b/src/test/regress/sql/multi_metadata_sync.sql @@ -47,15 +47,8 @@ ALTER ROLE CURRENT_USER WITH PASSWORD 'dummypassword'; -- Show that, with no MX tables, activate node snapshot contains only the delete commands, -- pg_dist_node entries, pg_dist_object entries and roles. - -select pdo.*, pd.datname -from pg_dist_object pdo - left outer join pg_database pd on pdo.objid = pd.oid; - SELECT unnest(activate_node_snapshot()) order by 1; - - -- Create a test table with constraints and SERIAL and default from user defined sequence CREATE SEQUENCE user_defined_seq; CREATE TABLE mx_test_table (col_1 int UNIQUE, col_2 text NOT NULL, col_3 BIGSERIAL, col_4 BIGINT DEFAULT nextval('user_defined_seq')); diff --git a/src/test/regress/sql/multi_test_helpers.sql b/src/test/regress/sql/multi_test_helpers.sql index 7f0346d1421..3aa86b86c05 100644 --- a/src/test/regress/sql/multi_test_helpers.sql +++ b/src/test/regress/sql/multi_test_helpers.sql @@ -581,3 +581,58 @@ BEGIN ORDER BY node_type; END; $func$ LANGUAGE plpgsql; + +-- For all nodes, returns database properties of given database, except +-- oid, datfrozenxid and datminmxid. +-- +-- Also returns whether the node has a pg_dist_object record for the database +-- and whether there are any stale pg_dist_object records for a database. +CREATE OR REPLACE FUNCTION check_database_on_all_nodes(p_database_name text) +RETURNS TABLE (node_type text, result text) +AS $func$ +BEGIN + RETURN QUERY + SELECT + CASE WHEN (groupid = 0 AND groupid = (SELECT groupid FROM pg_dist_local_group)) THEN 'coordinator (local)' + WHEN (groupid = 0) THEN 'coordinator (remote)' + WHEN (groupid = (SELECT groupid FROM pg_dist_local_group)) THEN 'worker node (local)' + ELSE 'worker node (remote)' + END AS node_type, + q2.result + FROM run_command_on_all_nodes( + format( + $$ + SELECT to_jsonb(q.*) + FROM ( + SELECT + ( + SELECT to_jsonb(database_properties.*) + FROM ( + SELECT datname, pa.rolname as database_owner, + pg_encoding_to_char(pd.encoding) as encoding, datlocprovider, + datistemplate, datallowconn, datconnlimit, + pt.spcname AS tablespace, datcollate, datctype, daticulocale, + datcollversion, datacl + FROM pg_database pd + JOIN pg_authid pa ON pd.datdba = pa.oid + JOIN pg_tablespace pt ON pd.dattablespace = pt.oid + WHERE datname = '%s' + ) database_properties + ) AS database_properties, + ( + SELECT COUNT(*)=1 + FROM pg_dist_object WHERE objid = (SELECT oid FROM pg_database WHERE datname = '%s') + ) AS pg_dist_object_record_for_db_exists, + ( + SELECT COUNT(*) > 0 + FROM pg_dist_object + WHERE classid = 1262 AND objid NOT IN (SELECT oid FROM pg_database) + ) AS stale_pg_dist_object_record_for_a_db_exists + ) q + $$, + p_database_name, p_database_name + ) + ) q2 + JOIN pg_dist_node USING (nodeid); +END; +$func$ LANGUAGE plpgsql; From a6e16252d1631b967ff5f216657197ac488dfc6e Mon Sep 17 00:00:00 2001 From: gindibay Date: Thu, 16 Nov 2023 05:34:41 +0300 Subject: [PATCH 54/68] Removes extra line --- src/test/regress/expected/multi_test_helpers.out | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/regress/expected/multi_test_helpers.out b/src/test/regress/expected/multi_test_helpers.out index 9822a093106..4f21258cf38 100644 --- a/src/test/regress/expected/multi_test_helpers.out +++ b/src/test/regress/expected/multi_test_helpers.out @@ -556,7 +556,6 @@ BEGIN ORDER BY node_type; END; $func$ LANGUAGE plpgsql; -$func$ LANGUAGE plpgsql; -- For all nodes, returns database properties of given database, except -- oid, datfrozenxid and datminmxid. -- From ef2a47e882666622a42cc81cc2622fa4f7fef85e Mon Sep 17 00:00:00 2001 From: gindibay Date: Thu, 16 Nov 2023 07:20:59 +0300 Subject: [PATCH 55/68] Fixes function after merge --- src/test/regress/sql/multi_test_helpers.sql | 30 ++++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/test/regress/sql/multi_test_helpers.sql b/src/test/regress/sql/multi_test_helpers.sql index 3aa86b86c05..e67b782a524 100644 --- a/src/test/regress/sql/multi_test_helpers.sql +++ b/src/test/regress/sql/multi_test_helpers.sql @@ -590,7 +590,22 @@ $func$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION check_database_on_all_nodes(p_database_name text) RETURNS TABLE (node_type text, result text) AS $func$ +DECLARE + pg_ge_15_options text := ''; + pg_ge_16_options text := ''; BEGIN + IF EXISTS (SELECT 1 FROM pg_attribute WHERE attrelid = 'pg_database'::regclass AND attname = 'datlocprovider') THEN + pg_ge_15_options := ', daticulocale, datcollversion, datlocprovider'; + ELSE + pg_ge_15_options := $$, null as daticulocale, null as datcollversion, 'c' as datlocprovider$$; + END IF; + + IF EXISTS (SELECT 1 FROM pg_attribute WHERE attrelid = 'pg_database'::regclass AND attname = 'daticurules') THEN + pg_ge_16_options := ', daticurules'; + ELSE + pg_ge_16_options := ', null as daticurules'; + END IF; + RETURN QUERY SELECT CASE WHEN (groupid = 0 AND groupid = (SELECT groupid FROM pg_dist_local_group)) THEN 'coordinator (local)' @@ -609,19 +624,20 @@ BEGIN SELECT to_jsonb(database_properties.*) FROM ( SELECT datname, pa.rolname as database_owner, - pg_encoding_to_char(pd.encoding) as encoding, datlocprovider, - datistemplate, datallowconn, datconnlimit, - pt.spcname AS tablespace, datcollate, datctype, daticulocale, - datcollversion, datacl + pg_encoding_to_char(pd.encoding) as encoding, + datistemplate, datallowconn, datconnlimit, datacl, + pt.spcname AS tablespace, datcollate, datctype + %2$s -- >= pg15 options + %3$s -- >= pg16 options FROM pg_database pd JOIN pg_authid pa ON pd.datdba = pa.oid JOIN pg_tablespace pt ON pd.dattablespace = pt.oid - WHERE datname = '%s' + WHERE datname = '%1$s' ) database_properties ) AS database_properties, ( SELECT COUNT(*)=1 - FROM pg_dist_object WHERE objid = (SELECT oid FROM pg_database WHERE datname = '%s') + FROM pg_dist_object WHERE objid = (SELECT oid FROM pg_database WHERE datname = '%1$s') ) AS pg_dist_object_record_for_db_exists, ( SELECT COUNT(*) > 0 @@ -630,7 +646,7 @@ BEGIN ) AS stale_pg_dist_object_record_for_a_db_exists ) q $$, - p_database_name, p_database_name + p_database_name, pg_ge_15_options, pg_ge_16_options ) ) q2 JOIN pg_dist_node USING (nodeid); From a948358d630dc715005f432d03be0d0ada8c8982 Mon Sep 17 00:00:00 2001 From: gindibay Date: Thu, 16 Nov 2023 08:01:44 +0300 Subject: [PATCH 56/68] Fixes multi_test_helpers --- .../regress/expected/multi_test_helpers.out | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/test/regress/expected/multi_test_helpers.out b/src/test/regress/expected/multi_test_helpers.out index 4f21258cf38..ea02a1cf830 100644 --- a/src/test/regress/expected/multi_test_helpers.out +++ b/src/test/regress/expected/multi_test_helpers.out @@ -555,7 +555,6 @@ BEGIN JOIN pg_dist_node USING (nodeid) ORDER BY node_type; END; -$func$ LANGUAGE plpgsql; -- For all nodes, returns database properties of given database, except -- oid, datfrozenxid and datminmxid. -- @@ -564,7 +563,22 @@ $func$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION check_database_on_all_nodes(p_database_name text) RETURNS TABLE (node_type text, result text) AS $func$ +DECLARE + pg_ge_15_options text := ''; + pg_ge_16_options text := ''; BEGIN + IF EXISTS (SELECT 1 FROM pg_attribute WHERE attrelid = 'pg_database'::regclass AND attname = 'datlocprovider') THEN + pg_ge_15_options := ', daticulocale, datcollversion, datlocprovider'; + ELSE + pg_ge_15_options := $$, null as daticulocale, null as datcollversion, 'c' as datlocprovider$$; + END IF; + + IF EXISTS (SELECT 1 FROM pg_attribute WHERE attrelid = 'pg_database'::regclass AND attname = 'daticurules') THEN + pg_ge_16_options := ', daticurules'; + ELSE + pg_ge_16_options := ', null as daticurules'; + END IF; + RETURN QUERY SELECT CASE WHEN (groupid = 0 AND groupid = (SELECT groupid FROM pg_dist_local_group)) THEN 'coordinator (local)' @@ -583,19 +597,20 @@ BEGIN SELECT to_jsonb(database_properties.*) FROM ( SELECT datname, pa.rolname as database_owner, - pg_encoding_to_char(pd.encoding) as encoding, datlocprovider, - datistemplate, datallowconn, datconnlimit, - pt.spcname AS tablespace, datcollate, datctype, daticulocale, - datcollversion, datacl + pg_encoding_to_char(pd.encoding) as encoding, + datistemplate, datallowconn, datconnlimit, datacl, + pt.spcname AS tablespace, datcollate, datctype + %2$s -- >= pg15 options + %3$s -- >= pg16 options FROM pg_database pd JOIN pg_authid pa ON pd.datdba = pa.oid JOIN pg_tablespace pt ON pd.dattablespace = pt.oid - WHERE datname = '%s' + WHERE datname = '%1$s' ) database_properties ) AS database_properties, ( SELECT COUNT(*)=1 - FROM pg_dist_object WHERE objid = (SELECT oid FROM pg_database WHERE datname = '%s') + FROM pg_dist_object WHERE objid = (SELECT oid FROM pg_database WHERE datname = '%1$s') ) AS pg_dist_object_record_for_db_exists, ( SELECT COUNT(*) > 0 @@ -604,7 +619,7 @@ BEGIN ) AS stale_pg_dist_object_record_for_a_db_exists ) q $$, - p_database_name, p_database_name + p_database_name, pg_ge_15_options, pg_ge_16_options ) ) q2 JOIN pg_dist_node USING (nodeid); From e56a1135726534ffa4a6038319fc2a1c77dd5b6b Mon Sep 17 00:00:00 2001 From: gindibay Date: Thu, 16 Nov 2023 08:09:09 +0300 Subject: [PATCH 57/68] Adds missing line --- src/test/regress/expected/multi_test_helpers.out | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/regress/expected/multi_test_helpers.out b/src/test/regress/expected/multi_test_helpers.out index ea02a1cf830..70a541d2acd 100644 --- a/src/test/regress/expected/multi_test_helpers.out +++ b/src/test/regress/expected/multi_test_helpers.out @@ -555,6 +555,7 @@ BEGIN JOIN pg_dist_node USING (nodeid) ORDER BY node_type; END; +$func$ LANGUAGE plpgsql; -- For all nodes, returns database properties of given database, except -- oid, datfrozenxid and datminmxid. -- From 1cdae74a4cbf9a36c7fa2a8c3d76d583f0556b22 Mon Sep 17 00:00:00 2001 From: gindibay Date: Thu, 16 Nov 2023 08:45:20 +0300 Subject: [PATCH 58/68] Fixes review notes --- src/backend/distributed/commands/database.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/backend/distributed/commands/database.c b/src/backend/distributed/commands/database.c index b988122ef3c..7a3c29b418c 100644 --- a/src/backend/distributed/commands/database.c +++ b/src/backend/distributed/commands/database.c @@ -614,47 +614,47 @@ GenerateCreateDatabaseStatementFromPgDatabase(Form_pg_database databaseForm) appendStringInfo(&str, " CONNECTION LIMIT %d", databaseForm->datconnlimit); appendStringInfo(&str, " ALLOW_CONNECTIONS = %s", - quote_literal_cstr(databaseForm->datallowconn ? "true" : "false")); + quote_identifier(databaseForm->datallowconn ? "true" : "false")); appendStringInfo(&str, " IS_TEMPLATE = %s", - quote_literal_cstr(databaseForm->datistemplate ? "true" : "false")); + quote_identifier(databaseForm->datistemplate ? "true" : "false")); appendStringInfo(&str, " LC_COLLATE = %s", - quote_literal_cstr(collInfo.datcollate)); + quote_identifier(collInfo.datcollate)); - appendStringInfo(&str, " LC_CTYPE = %s", quote_literal_cstr(collInfo.datctype)); + appendStringInfo(&str, " LC_CTYPE = %s", quote_identifier(collInfo.datctype)); appendStringInfo(&str, " OWNER = %s", - quote_literal_cstr(GetUserNameFromId(databaseForm->datdba, false))); + quote_identifier(GetUserNameFromId(databaseForm->datdba, false))); appendStringInfo(&str, " TABLESPACE = %s", quote_identifier(GetTablespaceName(databaseForm->dattablespace))); appendStringInfo(&str, " ENCODING = %s", - quote_literal_cstr(pg_encoding_to_char(databaseForm->encoding))); + quote_identifier(pg_encoding_to_char(databaseForm->encoding))); #if PG_VERSION_NUM >= PG_VERSION_15 if (collInfo.datcollversion != NULL) { appendStringInfo(&str, " COLLATION_VERSION = %s", - quote_literal_cstr(collInfo.datcollversion)); + quote_identifier(collInfo.datcollversion)); } if (collInfo.daticulocale != NULL) { - appendStringInfo(&str, " ICU_LOCALE = %s", quote_literal_cstr( + appendStringInfo(&str, " ICU_LOCALE = %s", quote_identifier( collInfo.daticulocale)); } appendStringInfo(&str, " LOCALE_PROVIDER = %s", - quote_literal_cstr(GetLocaleProviderString( + quote_identifier(GetLocaleProviderString( databaseForm->datlocprovider))); #endif #if PG_VERSION_NUM >= PG_VERSION_16 if (collInfo.daticurules != NULL) { - appendStringInfo(&str, " ICU_RULES = %s", quote_literal_cstr( + appendStringInfo(&str, " ICU_RULES = %s", quote_identifier( collInfo.daticurules)); } #endif From c63f124c936b6a8257bfb708fbb54f74e174ebe8 Mon Sep 17 00:00:00 2001 From: gindibay Date: Thu, 16 Nov 2023 09:08:45 +0300 Subject: [PATCH 59/68] Fixes indentation --- src/backend/distributed/commands/database.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/distributed/commands/database.c b/src/backend/distributed/commands/database.c index 7a3c29b418c..a515bc2a8b6 100644 --- a/src/backend/distributed/commands/database.c +++ b/src/backend/distributed/commands/database.c @@ -648,7 +648,7 @@ GenerateCreateDatabaseStatementFromPgDatabase(Form_pg_database databaseForm) appendStringInfo(&str, " LOCALE_PROVIDER = %s", quote_identifier(GetLocaleProviderString( - databaseForm->datlocprovider))); + databaseForm->datlocprovider))); #endif #if PG_VERSION_NUM >= PG_VERSION_16 From da72cd3cf17beb99671342d0d8a84f54b1afc522 Mon Sep 17 00:00:00 2001 From: gindibay Date: Thu, 16 Nov 2023 10:03:02 +0300 Subject: [PATCH 60/68] Fixes quotes for db columns --- src/backend/distributed/commands/database.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/backend/distributed/commands/database.c b/src/backend/distributed/commands/database.c index a515bc2a8b6..566ad7f983c 100644 --- a/src/backend/distributed/commands/database.c +++ b/src/backend/distributed/commands/database.c @@ -614,15 +614,15 @@ GenerateCreateDatabaseStatementFromPgDatabase(Form_pg_database databaseForm) appendStringInfo(&str, " CONNECTION LIMIT %d", databaseForm->datconnlimit); appendStringInfo(&str, " ALLOW_CONNECTIONS = %s", - quote_identifier(databaseForm->datallowconn ? "true" : "false")); + quote_literal_cstr(databaseForm->datallowconn ? "true" : "false")); appendStringInfo(&str, " IS_TEMPLATE = %s", - quote_identifier(databaseForm->datistemplate ? "true" : "false")); + quote_literal_cstr(databaseForm->datistemplate ? "true" : "false")); appendStringInfo(&str, " LC_COLLATE = %s", - quote_identifier(collInfo.datcollate)); + quote_literal_cstr(collInfo.datcollate)); - appendStringInfo(&str, " LC_CTYPE = %s", quote_identifier(collInfo.datctype)); + appendStringInfo(&str, " LC_CTYPE = %s", quote_literal_cstr(collInfo.datctype)); appendStringInfo(&str, " OWNER = %s", quote_identifier(GetUserNameFromId(databaseForm->datdba, false))); @@ -631,7 +631,7 @@ GenerateCreateDatabaseStatementFromPgDatabase(Form_pg_database databaseForm) quote_identifier(GetTablespaceName(databaseForm->dattablespace))); appendStringInfo(&str, " ENCODING = %s", - quote_identifier(pg_encoding_to_char(databaseForm->encoding))); + quote_literal_cstr(pg_encoding_to_char(databaseForm->encoding))); #if PG_VERSION_NUM >= PG_VERSION_15 if (collInfo.datcollversion != NULL) From 58bb165145fbe83a925b5a863432bd8a7a326203 Mon Sep 17 00:00:00 2001 From: gindibay Date: Thu, 16 Nov 2023 10:19:00 +0300 Subject: [PATCH 61/68] Removes unnecessary comments --- src/backend/distributed/commands/dependencies.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/backend/distributed/commands/dependencies.c b/src/backend/distributed/commands/dependencies.c index c15f9fba4cb..e309ee86c7d 100644 --- a/src/backend/distributed/commands/dependencies.c +++ b/src/backend/distributed/commands/dependencies.c @@ -466,20 +466,6 @@ GetDependencyCreateDDLCommands(const ObjectAddress *dependency) databaseDDLCommands = list_concat(databaseDDLCommands, ownerDDLCommands); } - /*TODO: To reviewer: Having a code block for dependency makes sense */ - /* However dependency tree is based on pg metadata; which does not reflect */ - - /* actual database dependencies. I added this block just to point out the issue. - */ - - /* - * if(EnableCreateDatabasePropagation){ - * List *dbGrants = GrantOnDatabaseDDLCommands(dependency->objectId); - * databaseDDLCommands = list_concat(databaseDDLCommands, dbGrants); - * - * } - */ - return databaseDDLCommands; } From 0d1f18862be68350a2f784bcbe98344ddcad8a6d Mon Sep 17 00:00:00 2001 From: Naisila Puka <37271756+naisila@users.noreply.github.com> Date: Thu, 16 Nov 2023 13:12:30 +0300 Subject: [PATCH 62/68] Propagates SECURITY LABEL ON ROLE stmt (#7304) We propagate `SECURITY LABEL [for provider] ON ROLE rolename IS labelname` to the worker nodes. We also make sure to run the relevant `SecLabelStmt` commands on a newly added node by looking at roles found in `pg_shseclabel`. See official docs for explanation on how this command works: https://www.postgresql.org/docs/current/sql-security-label.html This command stores the role label in the `pg_shseclabel` catalog table. This commit also fixes the regex string in `check_gucs_are_alphabetically_sorted.sh` script such that it escapes the dot. Previously it was looking for all strings starting with "citus" instead of "citus." as it should. To test this feature, I currently make use of a special GUC to control label provider registration in PG_init when creating the Citus extension. --- ci/check_gucs_are_alphabetically_sorted.sh | 2 +- gucs.out | 133 ++++++++++++++ .../commands/distribute_object_ops.c | 14 ++ src/backend/distributed/commands/role.c | 71 ++++++- src/backend/distributed/commands/seclabel.c | 125 +++++++++++++ .../deparser/deparse_seclabel_stmts.c | 78 ++++++++ .../distributed/operations/shard_rebalancer.c | 2 +- .../replication/multi_logical_replication.c | 4 +- src/backend/distributed/shared_library_init.c | 18 +- src/include/distributed/commands.h | 5 + src/include/distributed/deparser.h | 3 + src/include/distributed/shard_rebalancer.h | 2 +- .../regress/expected/multi_test_helpers.out | 30 +++ src/test/regress/expected/seclabel.out | 173 ++++++++++++++++++ src/test/regress/multi_1_schedule | 1 + src/test/regress/pg_regress_multi.pl | 7 +- src/test/regress/sql/multi_test_helpers.sql | 31 ++++ src/test/regress/sql/seclabel.sql | 87 +++++++++ 18 files changed, 774 insertions(+), 12 deletions(-) create mode 100644 gucs.out create mode 100644 src/backend/distributed/commands/seclabel.c create mode 100644 src/backend/distributed/deparser/deparse_seclabel_stmts.c create mode 100644 src/test/regress/expected/seclabel.out create mode 100644 src/test/regress/sql/seclabel.sql diff --git a/ci/check_gucs_are_alphabetically_sorted.sh b/ci/check_gucs_are_alphabetically_sorted.sh index a769ae4fb16..763b5305f81 100755 --- a/ci/check_gucs_are_alphabetically_sorted.sh +++ b/ci/check_gucs_are_alphabetically_sorted.sh @@ -5,6 +5,6 @@ set -euo pipefail source ci/ci_helpers.sh # extract citus gucs in the form of "citus.X" -grep -o -E "(\.*\"citus.\w+\")," src/backend/distributed/shared_library_init.c > gucs.out +grep -o -E "(\.*\"citus\.\w+\")," src/backend/distributed/shared_library_init.c > gucs.out sort -c gucs.out rm gucs.out diff --git a/gucs.out b/gucs.out new file mode 100644 index 00000000000..8501e6c1f75 --- /dev/null +++ b/gucs.out @@ -0,0 +1,133 @@ +"citus.all_modifications_commutative", +"citus.allow_modifications_from_workers_to_replicated_tables", +"citus.allow_nested_distributed_execution", +"citus.allow_unsafe_constraints", +"citus.allow_unsafe_locks_from_workers", +"citus.background_task_queue_interval", +"citus.check_available_space_before_move", +"citus.cluster_name", +"citus.coordinator_aggregation_strategy", +"citus.copy_switchover_threshold", +"citus.count_distinct_error_rate", +"citus.cpu_priority", +"citus.cpu_priority_for_logical_replication_senders", +"citus.create_object_propagation", +"citus.defer_drop_after_shard_move", +"citus.defer_drop_after_shard_split", +"citus.defer_shard_delete_interval", +"citus.desired_percent_disk_available_after_move", +"citus.distributed_deadlock_detection_factor", +"citus.enable_alter_database_owner", +"citus.enable_alter_role_propagation", +"citus.enable_alter_role_set_propagation", +"citus.enable_binary_protocol", +"citus.enable_change_data_capture", +"citus.enable_cluster_clock", +"citus.enable_cost_based_connection_establishment", +"citus.enable_create_role_propagation", +"citus.enable_create_type_propagation", +"citus.enable_ddl_propagation", +"citus.enable_deadlock_prevention", +"citus.enable_fast_path_router_planner", +"citus.enable_local_execution", +"citus.enable_local_reference_table_foreign_keys", +"citus.enable_manual_changes_to_shards", +"citus.enable_manual_metadata_changes_for_user", +"citus.enable_metadata_sync", +"citus.enable_non_colocated_router_query_pushdown", +"citus.enable_repartition_joins", +"citus.enable_repartitioned_insert_select", +"citus.enable_router_execution", +"citus.enable_schema_based_sharding", +"citus.enable_single_hash_repartition_joins", +"citus.enable_statistics_collection", +"citus.enable_unique_job_ids", +"citus.enable_unsafe_triggers", +"citus.enable_unsupported_feature_messages", +"citus.enable_version_checks", +"citus.enforce_foreign_key_restrictions", +"citus.enforce_object_restrictions_for_local_objects", +"citus.executor_slow_start_interval", +"citus.explain_all_tasks", +"citus.explain_analyze_sort_method", +"citus.explain_distributed_queries", +"citus.force_max_query_parallelization", +"citus.function_opens_transaction_block", +"citus.grep_remote_commands", +"citus.hide_citus_dependent_objects", +"citus.hide_shards_from_app_name_prefixes", +"citus.isolation_test_session_process_id", +"citus.isolation_test_session_remote_process_id", +"citus.limit_clause_row_fetch_count", +"citus.local_copy_flush_threshold", +"citus.local_hostname", +"citus.local_shared_pool_size", +"citus.local_table_join_policy", +"citus.log_distributed_deadlock_detection", +"citus.log_intermediate_results", +"citus.log_local_commands", +"citus.log_multi_join_order", +"citus.log_remote_commands", +"citus.logical_replication_timeout", +"citus.main_db", +"citus.max_adaptive_executor_pool_size", +"citus.max_background_task_executors", +"citus.max_background_task_executors_per_node", +"citus.max_cached_connection_lifetime", +"citus.max_cached_conns_per_worker", +"citus.max_client_connections", +"citus.max_high_priority_background_processes", +"citus.max_intermediate_result_size", +"citus.max_matview_size_to_auto_recreate", +"citus.max_rebalancer_logged_ignored_moves", +"citus.max_shared_pool_size", +"citus.max_worker_nodes_tracked", +"citus.metadata_sync_interval", +"citus.metadata_sync_mode", +"citus.metadata_sync_retry_interval", +"citus.mitmfifo", +"citus.multi_shard_modify_mode", +"citus.multi_task_query_log_level", +"citus.next_cleanup_record_id", +"citus.next_operation_id", +"citus.next_placement_id", +"citus.next_shard_id", +"citus.node_connection_timeout", +"citus.node_conninfo", +"citus.override_table_visibility", +"citus.prevent_incomplete_connection_establishment", +"citus.propagate_session_settings_for_loopback_connection", +"citus.propagate_set_commands", +"citus.rebalancer_by_disk_size_base_cost", +"citus.recover_2pc_interval", +"citus.remote_copy_flush_threshold", +"citus.remote_task_check_interval", +"citus.repartition_join_bucket_count_per_node", +"citus.replicate_reference_tables_on_activate", +"citus.replication_model", +"citus.running_under_citus_test_suite", +"citus.select_opens_transaction_block", +"citus.shard_count", +"citus.shard_replication_factor", +"citus.show_shards_for_app_name_prefixes", +"citus.skip_advisory_lock_permission_checks", +"citus.skip_constraint_validation", +"citus.skip_jsonb_validation_in_copy", +"citus.sort_returning", +"citus.stat_statements_max", +"citus.stat_statements_purge_interval", +"citus.stat_statements_track", +"citus.stat_tenants_limit", +"citus.stat_tenants_log_level", +"citus.stat_tenants_period", +"citus.stat_tenants_track", +"citus.stat_tenants_untracked_sample_rate", +"citus.subquery_pushdown", +"citus.task_assignment_policy", +"citus.task_executor_type", +"citus.use_citus_managed_tables", +"citus.use_secondary_nodes", +"citus.values_materialization_threshold", +"citus.version", +"citus.worker_min_messages", +"citus.writable_standby_coordinator", diff --git a/src/backend/distributed/commands/distribute_object_ops.c b/src/backend/distributed/commands/distribute_object_ops.c index f27e4cca6d8..72ea5beb42b 100644 --- a/src/backend/distributed/commands/distribute_object_ops.c +++ b/src/backend/distributed/commands/distribute_object_ops.c @@ -374,6 +374,15 @@ static DistributeObjectOps Any_Rename = { .address = NULL, .markDistributed = false, }; +static DistributeObjectOps Any_SecLabel = { + .deparse = DeparseSecLabelStmt, + .qualify = NULL, + .preprocess = NULL, + .postprocess = PostprocessSecLabelStmt, + .operationType = DIST_OPS_ALTER, + .address = SecLabelStmtObjectAddress, + .markDistributed = false, +}; static DistributeObjectOps Attribute_Rename = { .deparse = DeparseRenameAttributeStmt, .qualify = QualifyRenameAttributeStmt, @@ -2020,6 +2029,11 @@ GetDistributeObjectOps(Node *node) return &Vacuum_Analyze; } + case T_SecLabelStmt: + { + return &Any_SecLabel; + } + case T_RenameStmt: { RenameStmt *stmt = castNode(RenameStmt, node); diff --git a/src/backend/distributed/commands/role.c b/src/backend/distributed/commands/role.c index a2da3bf81b4..3177c73a0f4 100644 --- a/src/backend/distributed/commands/role.c +++ b/src/backend/distributed/commands/role.c @@ -23,6 +23,7 @@ #include "catalog/pg_auth_members.h" #include "catalog/pg_authid.h" #include "catalog/pg_db_role_setting.h" +#include "catalog/pg_shseclabel.h" #include "catalog/pg_type.h" #include "catalog/objectaddress.h" #include "commands/dbcommands.h" @@ -65,6 +66,7 @@ static DefElem * makeDefElemBool(char *name, bool value); static List * GenerateRoleOptionsList(HeapTuple tuple); static List * GenerateGrantRoleStmtsFromOptions(RoleSpec *roleSpec, List *options); static List * GenerateGrantRoleStmtsOfRole(Oid roleid); +static List * GenerateSecLabelOnRoleStmts(Oid roleid, char *rolename); static void EnsureSequentialModeForRoleDDL(void); static char * GetRoleNameFromDbRoleSetting(HeapTuple tuple, @@ -515,13 +517,14 @@ GenerateCreateOrAlterRoleCommand(Oid roleOid) { HeapTuple roleTuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleOid)); Form_pg_authid role = ((Form_pg_authid) GETSTRUCT(roleTuple)); + char *rolename = pstrdup(NameStr(role->rolname)); CreateRoleStmt *createRoleStmt = NULL; if (EnableCreateRolePropagation) { createRoleStmt = makeNode(CreateRoleStmt); createRoleStmt->stmt_type = ROLESTMT_ROLE; - createRoleStmt->role = pstrdup(NameStr(role->rolname)); + createRoleStmt->role = rolename; createRoleStmt->options = GenerateRoleOptionsList(roleTuple); } @@ -532,7 +535,7 @@ GenerateCreateOrAlterRoleCommand(Oid roleOid) alterRoleStmt->role = makeNode(RoleSpec); alterRoleStmt->role->roletype = ROLESPEC_CSTRING; alterRoleStmt->role->location = -1; - alterRoleStmt->role->rolename = pstrdup(NameStr(role->rolname)); + alterRoleStmt->role->rolename = rolename; alterRoleStmt->action = 1; alterRoleStmt->options = GenerateRoleOptionsList(roleTuple); } @@ -544,7 +547,7 @@ GenerateCreateOrAlterRoleCommand(Oid roleOid) { /* add a worker_create_or_alter_role command if any of them are set */ char *createOrAlterRoleQuery = CreateCreateOrAlterRoleCommand( - pstrdup(NameStr(role->rolname)), + rolename, createRoleStmt, alterRoleStmt); @@ -566,6 +569,20 @@ GenerateCreateOrAlterRoleCommand(Oid roleOid) { completeRoleList = lappend(completeRoleList, DeparseTreeNode(stmt)); } + + /* + * append SECURITY LABEL ON ROLE commands for this specific user + * When we propagate user creation, we also want to make sure that we propagate + * all the security labels it has been given. For this, we check pg_shseclabel + * for the ROLE entry corresponding to roleOid, and generate the relevant + * SecLabel stmts to be run in the new node. + */ + List *secLabelOnRoleStmts = GenerateSecLabelOnRoleStmts(roleOid, rolename); + stmt = NULL; + foreach_ptr(stmt, secLabelOnRoleStmts) + { + completeRoleList = lappend(completeRoleList, DeparseTreeNode(stmt)); + } } return completeRoleList; @@ -895,6 +912,54 @@ GenerateGrantRoleStmtsOfRole(Oid roleid) } +/* + * GenerateSecLabelOnRoleStmts generates the SecLabelStmts for the role + * whose oid is roleid. + */ +static List * +GenerateSecLabelOnRoleStmts(Oid roleid, char *rolename) +{ + List *secLabelStmts = NIL; + + /* + * Note that roles are shared database objects, therefore their + * security labels are stored in pg_shseclabel instead of pg_seclabel. + */ + Relation pg_shseclabel = table_open(SharedSecLabelRelationId, AccessShareLock); + ScanKeyData skey[1]; + ScanKeyInit(&skey[0], Anum_pg_shseclabel_objoid, BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(roleid)); + SysScanDesc scan = systable_beginscan(pg_shseclabel, SharedSecLabelObjectIndexId, + true, NULL, 1, &skey[0]); + + HeapTuple tuple = NULL; + while (HeapTupleIsValid(tuple = systable_getnext(scan))) + { + SecLabelStmt *secLabelStmt = makeNode(SecLabelStmt); + secLabelStmt->objtype = OBJECT_ROLE; + secLabelStmt->object = (Node *) makeString(pstrdup(rolename)); + + Datum datumArray[Natts_pg_shseclabel]; + bool isNullArray[Natts_pg_shseclabel]; + + heap_deform_tuple(tuple, RelationGetDescr(pg_shseclabel), datumArray, + isNullArray); + + secLabelStmt->provider = TextDatumGetCString( + datumArray[Anum_pg_shseclabel_provider - 1]); + secLabelStmt->label = TextDatumGetCString( + datumArray[Anum_pg_shseclabel_label - 1]); + + secLabelStmts = lappend(secLabelStmts, secLabelStmt); + } + + systable_endscan(scan); + table_close(pg_shseclabel, AccessShareLock); + + return secLabelStmts; +} + + /* * PreprocessCreateRoleStmt creates a worker_create_or_alter_role query for the * role that is being created. With that query we can create the role in the diff --git a/src/backend/distributed/commands/seclabel.c b/src/backend/distributed/commands/seclabel.c new file mode 100644 index 00000000000..208d186c77c --- /dev/null +++ b/src/backend/distributed/commands/seclabel.c @@ -0,0 +1,125 @@ +/*------------------------------------------------------------------------- + * + * seclabel.c + * + * This file contains the logic of SECURITY LABEL statement propagation. + * + * Copyright (c) Citus Data, Inc. + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "distributed/commands.h" +#include "distributed/commands/utility_hook.h" +#include "distributed/coordinator_protocol.h" +#include "distributed/deparser.h" +#include "distributed/log_utils.h" +#include "distributed/metadata_sync.h" +#include "distributed/metadata/distobject.h" + + +/* + * PostprocessSecLabelStmt prepares the commands that need to be run on all workers to assign + * security labels on distributed objects, currently supporting just Role objects. + * It also ensures that all object dependencies exist on all + * nodes for the object in the SecLabelStmt. + */ +List * +PostprocessSecLabelStmt(Node *node, const char *queryString) +{ + if (!ShouldPropagate()) + { + return NIL; + } + + SecLabelStmt *secLabelStmt = castNode(SecLabelStmt, node); + + List *objectAddresses = GetObjectAddressListFromParseTree(node, false, true); + if (!IsAnyObjectDistributed(objectAddresses)) + { + return NIL; + } + + if (secLabelStmt->objtype != OBJECT_ROLE) + { + /* + * If we are not in the coordinator, we don't want to interrupt the security + * label command with notices, the user expects that from the worker node + * the command will not be propagated + */ + if (EnableUnsupportedFeatureMessages && IsCoordinator()) + { + ereport(NOTICE, (errmsg("not propagating SECURITY LABEL commands whose " + "object type is not role"), + errhint("Connect to worker nodes directly to manually " + "run the same SECURITY LABEL command."))); + } + return NIL; + } + + if (!EnableCreateRolePropagation) + { + return NIL; + } + + EnsureCoordinator(); + EnsureAllObjectDependenciesExistOnAllNodes(objectAddresses); + + const char *sql = DeparseTreeNode((Node *) secLabelStmt); + + List *commandList = list_make3(DISABLE_DDL_PROPAGATION, + (void *) sql, + ENABLE_DDL_PROPAGATION); + + return NodeDDLTaskList(NON_COORDINATOR_NODES, commandList); +} + + +/* + * SecLabelStmtObjectAddress returns the object address of the object on + * which this statement operates (secLabelStmt->object). Note that it has no limitation + * on the object type being OBJECT_ROLE. This is intentionally implemented like this + * since it is fairly simple to implement and we might extend SECURITY LABEL propagation + * in the future to include more object types. + */ +List * +SecLabelStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess) +{ + SecLabelStmt *secLabelStmt = castNode(SecLabelStmt, node); + + Relation rel = NULL; + ObjectAddress address = get_object_address(secLabelStmt->objtype, + secLabelStmt->object, &rel, + AccessShareLock, missing_ok); + if (rel != NULL) + { + relation_close(rel, AccessShareLock); + } + + ObjectAddress *addressPtr = palloc0(sizeof(ObjectAddress)); + *addressPtr = address; + return list_make1(addressPtr); +} + + +/* + * citus_test_object_relabel is a dummy function for check_object_relabel_type hook. + * It is meant to be used in tests combined with citus_test_register_label_provider + */ +void +citus_test_object_relabel(const ObjectAddress *object, const char *seclabel) +{ + if (seclabel == NULL || + strcmp(seclabel, "citus_unclassified") == 0 || + strcmp(seclabel, "citus_classified") == 0 || + strcmp(seclabel, "citus '!unclassified") == 0) + { + return; + } + + ereport(ERROR, + (errcode(ERRCODE_INVALID_NAME), + errmsg("'%s' is not a valid security label for Citus tests.", seclabel))); +} diff --git a/src/backend/distributed/deparser/deparse_seclabel_stmts.c b/src/backend/distributed/deparser/deparse_seclabel_stmts.c new file mode 100644 index 00000000000..a1aa047ccb9 --- /dev/null +++ b/src/backend/distributed/deparser/deparse_seclabel_stmts.c @@ -0,0 +1,78 @@ +/*------------------------------------------------------------------------- + * + * deparse_seclabel_stmts.c + * All routines to deparse SECURITY LABEL statements. + * + * Copyright (c), Citus Data, Inc. + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "distributed/deparser.h" +#include "nodes/parsenodes.h" +#include "utils/builtins.h" + +static void AppendSecLabelStmt(StringInfo buf, SecLabelStmt *stmt); + +/* + * DeparseSecLabelStmt builds and returns a string representing of the + * SecLabelStmt for application on a remote server. + */ +char * +DeparseSecLabelStmt(Node *node) +{ + SecLabelStmt *secLabelStmt = castNode(SecLabelStmt, node); + StringInfoData buf = { 0 }; + initStringInfo(&buf); + + AppendSecLabelStmt(&buf, secLabelStmt); + + return buf.data; +} + + +/* + * AppendSecLabelStmt generates the string representation of the + * SecLabelStmt and appends it to the buffer. + */ +static void +AppendSecLabelStmt(StringInfo buf, SecLabelStmt *stmt) +{ + appendStringInfoString(buf, "SECURITY LABEL "); + + if (stmt->provider != NULL) + { + appendStringInfo(buf, "FOR %s ", quote_identifier(stmt->provider)); + } + + appendStringInfoString(buf, "ON "); + + switch (stmt->objtype) + { + case OBJECT_ROLE: + { + appendStringInfo(buf, "ROLE %s ", quote_identifier(strVal(stmt->object))); + break; + } + + /* normally, we shouldn't reach this */ + default: + { + ereport(ERROR, (errmsg("unsupported security label statement for" + " deparsing"))); + } + } + + appendStringInfoString(buf, "IS "); + + if (stmt->label != NULL) + { + appendStringInfo(buf, "%s", quote_literal_cstr(stmt->label)); + } + else + { + appendStringInfoString(buf, "NULL"); + } +} diff --git a/src/backend/distributed/operations/shard_rebalancer.c b/src/backend/distributed/operations/shard_rebalancer.c index d339ac56a5b..213d135e6ca 100644 --- a/src/backend/distributed/operations/shard_rebalancer.c +++ b/src/backend/distributed/operations/shard_rebalancer.c @@ -317,7 +317,7 @@ PG_FUNCTION_INFO_V1(citus_rebalance_start); PG_FUNCTION_INFO_V1(citus_rebalance_stop); PG_FUNCTION_INFO_V1(citus_rebalance_wait); -bool RunningUnderIsolationTest = false; +bool RunningUnderCitusTestSuite = false; int MaxRebalancerLoggedIgnoredMoves = 5; int RebalancerByDiskSizeBaseCost = 100 * 1024 * 1024; bool PropagateSessionSettingsForLoopbackConnection = false; diff --git a/src/backend/distributed/replication/multi_logical_replication.c b/src/backend/distributed/replication/multi_logical_replication.c index 97f6fdb3d35..48571d7c414 100644 --- a/src/backend/distributed/replication/multi_logical_replication.c +++ b/src/backend/distributed/replication/multi_logical_replication.c @@ -1143,7 +1143,7 @@ ConflictWithIsolationTestingBeforeCopy(void) const bool sessionLock = false; const bool dontWait = false; - if (RunningUnderIsolationTest) + if (RunningUnderCitusTestSuite) { SET_LOCKTAG_ADVISORY(tag, MyDatabaseId, SHARD_MOVE_ADVISORY_LOCK_SECOND_KEY, @@ -1177,7 +1177,7 @@ ConflictWithIsolationTestingAfterCopy(void) const bool sessionLock = false; const bool dontWait = false; - if (RunningUnderIsolationTest) + if (RunningUnderCitusTestSuite) { SET_LOCKTAG_ADVISORY(tag, MyDatabaseId, SHARD_MOVE_ADVISORY_LOCK_FIRST_KEY, diff --git a/src/backend/distributed/shared_library_init.c b/src/backend/distributed/shared_library_init.c index 9b5768ee7ab..22037c82bd4 100644 --- a/src/backend/distributed/shared_library_init.c +++ b/src/backend/distributed/shared_library_init.c @@ -29,6 +29,7 @@ #include "citus_version.h" #include "commands/explain.h" #include "commands/extension.h" +#include "commands/seclabel.h" #include "common/string.h" #include "executor/executor.h" #include "distributed/backend_data.h" @@ -574,6 +575,16 @@ _PG_init(void) INIT_COLUMNAR_SYMBOL(PGFunction, columnar_storage_info); INIT_COLUMNAR_SYMBOL(PGFunction, columnar_store_memory_stats); INIT_COLUMNAR_SYMBOL(PGFunction, test_columnar_storage_write_new_page); + + /* + * This part is only for SECURITY LABEL tests + * mimicking what an actual security label provider would do + */ + if (RunningUnderCitusTestSuite) + { + register_label_provider("citus '!tests_label_provider", + citus_test_object_relabel); + } } @@ -2305,13 +2316,14 @@ RegisterCitusConfigVariables(void) WarnIfReplicationModelIsSet, NULL, NULL); DefineCustomBoolVariable( - "citus.running_under_isolation_test", + "citus.running_under_citus_test_suite", gettext_noop( "Only useful for testing purposes, when set to true, Citus does some " - "tricks to implement useful isolation tests with rebalancing. Should " + "tricks to implement useful isolation tests with rebalancing. It also " + "registers a dummy label provider for SECURITY LABEL tests. Should " "never be set to true on production systems "), gettext_noop("for details of the tricks implemented, refer to the source code"), - &RunningUnderIsolationTest, + &RunningUnderCitusTestSuite, false, PGC_SUSET, GUC_SUPERUSER_ONLY | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, diff --git a/src/include/distributed/commands.h b/src/include/distributed/commands.h index 43429278f1a..df98e66a5e2 100644 --- a/src/include/distributed/commands.h +++ b/src/include/distributed/commands.h @@ -521,6 +521,11 @@ extern List * AlterSchemaOwnerStmtObjectAddress(Node *node, bool missing_ok, extern List * AlterSchemaRenameStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess); +/* seclabel.c - forward declarations*/ +extern List * PostprocessSecLabelStmt(Node *node, const char *queryString); +extern List * SecLabelStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess); +extern void citus_test_object_relabel(const ObjectAddress *object, const char *seclabel); + /* sequence.c - forward declarations */ extern List * PreprocessAlterSequenceStmt(Node *node, const char *queryString, ProcessUtilityContext processUtilityContext); diff --git a/src/include/distributed/deparser.h b/src/include/distributed/deparser.h index 95d948bc9e9..aae526df405 100644 --- a/src/include/distributed/deparser.h +++ b/src/include/distributed/deparser.h @@ -260,6 +260,9 @@ extern void QualifyRenameTextSearchDictionaryStmt(Node *node); extern void QualifyTextSearchConfigurationCommentStmt(Node *node); extern void QualifyTextSearchDictionaryCommentStmt(Node *node); +/* forward declarations for deparse_seclabel_stmts.c */ +extern char * DeparseSecLabelStmt(Node *node); + /* forward declarations for deparse_sequence_stmts.c */ extern char * DeparseDropSequenceStmt(Node *node); extern char * DeparseRenameSequenceStmt(Node *node); diff --git a/src/include/distributed/shard_rebalancer.h b/src/include/distributed/shard_rebalancer.h index 38ce4f48562..345748cedba 100644 --- a/src/include/distributed/shard_rebalancer.h +++ b/src/include/distributed/shard_rebalancer.h @@ -189,7 +189,7 @@ typedef struct RebalancePlanFunctions extern char *VariablesToBePassedToNewConnections; extern int MaxRebalancerLoggedIgnoredMoves; extern int RebalancerByDiskSizeBaseCost; -extern bool RunningUnderIsolationTest; +extern bool RunningUnderCitusTestSuite; extern bool PropagateSessionSettingsForLoopbackConnection; extern int MaxBackgroundTaskExecutorsPerNode; diff --git a/src/test/regress/expected/multi_test_helpers.out b/src/test/regress/expected/multi_test_helpers.out index b8758e561bd..4b621b968d5 100644 --- a/src/test/regress/expected/multi_test_helpers.out +++ b/src/test/regress/expected/multi_test_helpers.out @@ -526,3 +526,33 @@ BEGIN RETURN result; END; $func$ LANGUAGE plpgsql; +-- Returns pg_seclabels entries from all nodes in the cluster for which +-- the object name is the input. +CREATE OR REPLACE FUNCTION get_citus_tests_label_provider_labels(object_name text, + master_port INTEGER DEFAULT 57636, + worker_1_port INTEGER DEFAULT 57637, + worker_2_port INTEGER DEFAULT 57638) +RETURNS TABLE ( + node_type text, + result text +) +AS $func$ +DECLARE + pg_seclabels_cmd TEXT := 'SELECT to_jsonb(q.*) FROM (' || + 'SELECT provider, objtype, label FROM pg_seclabels ' || + 'WHERE objname = ''' || object_name || ''') q'; +BEGIN + RETURN QUERY + SELECT + CASE + WHEN nodeport = master_port THEN 'coordinator' + WHEN nodeport = worker_1_port THEN 'worker_1' + WHEN nodeport = worker_2_port THEN 'worker_2' + ELSE 'unexpected_node' + END AS node_type, + a.result + FROM run_command_on_all_nodes(pg_seclabels_cmd) a + JOIN pg_dist_node USING (nodeid) + ORDER BY node_type; +END; +$func$ LANGUAGE plpgsql; diff --git a/src/test/regress/expected/seclabel.out b/src/test/regress/expected/seclabel.out new file mode 100644 index 00000000000..f826de44b66 --- /dev/null +++ b/src/test/regress/expected/seclabel.out @@ -0,0 +1,173 @@ +-- +-- SECLABEL +-- +-- Test suite for SECURITY LABEL ON ROLE statements +-- +-- first we remove one of the worker nodes to be able to test +-- citus_add_node later +SELECT citus_remove_node('localhost', :worker_2_port); + citus_remove_node +--------------------------------------------------------------------- + +(1 row) + +-- create two roles, one with characters that need escaping +CREATE ROLE user1; +CREATE ROLE "user 2"; +-- check an invalid label for our current dummy hook citus_test_object_relabel +SECURITY LABEL FOR "citus '!tests_label_provider" ON ROLE user1 IS 'invalid_label'; +ERROR: 'invalid_label' is not a valid security label for Citus tests. +-- if we disable metadata_sync, the command will not be propagated +SET citus.enable_metadata_sync TO off; +SECURITY LABEL FOR "citus '!tests_label_provider" ON ROLE user1 IS 'citus_unclassified'; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('user1') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_unclassified", "objtype": "role", "provider": "citus '!tests_label_provider"} + worker_1 | +(2 rows) + +RESET citus.enable_metadata_sync; +-- check that we only support propagating for roles +SET citus.shard_replication_factor to 1; +-- distributed table +CREATE TABLE a (a int); +SELECT create_distributed_table('a', 'a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- distributed view +CREATE VIEW v_dist AS SELECT * FROM a; +-- distributed function +CREATE FUNCTION notice(text) RETURNS void LANGUAGE plpgsql AS $$ + BEGIN RAISE NOTICE '%', $1; END; $$; +SECURITY LABEL ON TABLE a IS 'citus_classified'; +NOTICE: not propagating SECURITY LABEL commands whose object type is not role +HINT: Connect to worker nodes directly to manually run the same SECURITY LABEL command. +SECURITY LABEL ON FUNCTION notice IS 'citus_unclassified'; +NOTICE: not propagating SECURITY LABEL commands whose object type is not role +HINT: Connect to worker nodes directly to manually run the same SECURITY LABEL command. +SECURITY LABEL ON VIEW v_dist IS 'citus_classified'; +NOTICE: not propagating SECURITY LABEL commands whose object type is not role +HINT: Connect to worker nodes directly to manually run the same SECURITY LABEL command. +SELECT node_type, result FROM get_citus_tests_label_provider_labels('a') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} + worker_1 | +(2 rows) + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('notice(text)') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_unclassified", "objtype": "function", "provider": "citus '!tests_label_provider"} + worker_1 | +(2 rows) + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('v_dist') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_classified", "objtype": "view", "provider": "citus '!tests_label_provider"} + worker_1 | +(2 rows) + +\c - - - :worker_1_port +SECURITY LABEL ON TABLE a IS 'citus_classified'; +SECURITY LABEL ON FUNCTION notice IS 'citus_unclassified'; +SECURITY LABEL ON VIEW v_dist IS 'citus_classified'; +\c - - - :master_port +SELECT node_type, result FROM get_citus_tests_label_provider_labels('a') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_classified", "objtype": "table", "provider": "citus '!tests_label_provider"} +(2 rows) + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('notice(text)') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_unclassified", "objtype": "function", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_unclassified", "objtype": "function", "provider": "citus '!tests_label_provider"} +(2 rows) + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('v_dist') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_classified", "objtype": "view", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_classified", "objtype": "view", "provider": "citus '!tests_label_provider"} +(2 rows) + +DROP TABLE a CASCADE; +NOTICE: drop cascades to view v_dist +DROP FUNCTION notice; +-- test that SECURITY LABEL statement is actually propagated for ROLES +SET citus.log_remote_commands TO on; +SET citus.grep_remote_commands = '%SECURITY LABEL%'; +-- we have exactly one provider loaded, so we may not include the provider in the command +SECURITY LABEL for "citus '!tests_label_provider" ON ROLE user1 IS 'citus_classified'; +NOTICE: issuing SECURITY LABEL FOR "citus '!tests_label_provider" ON ROLE user1 IS 'citus_classified' +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +SECURITY LABEL ON ROLE user1 IS NULL; +NOTICE: issuing SECURITY LABEL ON ROLE user1 IS NULL +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +SECURITY LABEL ON ROLE user1 IS 'citus_unclassified'; +NOTICE: issuing SECURITY LABEL ON ROLE user1 IS 'citus_unclassified' +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +SECURITY LABEL for "citus '!tests_label_provider" ON ROLE "user 2" IS 'citus ''!unclassified'; +NOTICE: issuing SECURITY LABEL FOR "citus '!tests_label_provider" ON ROLE "user 2" IS 'citus ''!unclassified' +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +\c - - - :worker_1_port +-- command not allowed from worker node +SECURITY LABEL for "citus '!tests_label_provider" ON ROLE user1 IS 'citus ''!unclassified'; +ERROR: operation is not allowed on this node +HINT: Connect to the coordinator and run it again. +\c - - - :master_port +RESET citus.log_remote_commands; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('user1') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_unclassified", "objtype": "role", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_unclassified", "objtype": "role", "provider": "citus '!tests_label_provider"} +(2 rows) + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('"user 2"') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus '!unclassified", "objtype": "role", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus '!unclassified", "objtype": "role", "provider": "citus '!tests_label_provider"} +(2 rows) + +-- add a new node and check that it also propagates the SECURITY LABEL statement to the new node +SET citus.log_remote_commands TO on; +SET citus.grep_remote_commands = '%SECURITY LABEL%'; +SELECT 1 FROM citus_add_node('localhost', :worker_2_port); +NOTICE: issuing SELECT worker_create_or_alter_role('user1', 'CREATE ROLE user1 NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT NOLOGIN NOREPLICATION NOBYPASSRLS CONNECTION LIMIT -1 PASSWORD NULL VALID UNTIL ''infinity''', 'ALTER ROLE user1 NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT NOLOGIN NOREPLICATION NOBYPASSRLS CONNECTION LIMIT -1 PASSWORD NULL VALID UNTIL ''infinity''');SECURITY LABEL FOR "citus '!tests_label_provider" ON ROLE user1 IS 'citus_unclassified' +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing SELECT worker_create_or_alter_role('user 2', 'CREATE ROLE "user 2" NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT NOLOGIN NOREPLICATION NOBYPASSRLS CONNECTION LIMIT -1 PASSWORD NULL VALID UNTIL ''infinity''', 'ALTER ROLE "user 2" NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT NOLOGIN NOREPLICATION NOBYPASSRLS CONNECTION LIMIT -1 PASSWORD NULL VALID UNTIL ''infinity''');SECURITY LABEL FOR "citus '!tests_label_provider" ON ROLE "user 2" IS 'citus ''!unclassified' +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('user1') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus_unclassified", "objtype": "role", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus_unclassified", "objtype": "role", "provider": "citus '!tests_label_provider"} + worker_2 | {"label": "citus_unclassified", "objtype": "role", "provider": "citus '!tests_label_provider"} +(3 rows) + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('"user 2"') ORDER BY node_type; + node_type | result +--------------------------------------------------------------------- + coordinator | {"label": "citus '!unclassified", "objtype": "role", "provider": "citus '!tests_label_provider"} + worker_1 | {"label": "citus '!unclassified", "objtype": "role", "provider": "citus '!tests_label_provider"} + worker_2 | {"label": "citus '!unclassified", "objtype": "role", "provider": "citus '!tests_label_provider"} +(3 rows) + +-- cleanup +RESET citus.log_remote_commands; +DROP ROLE user1, "user 2"; diff --git a/src/test/regress/multi_1_schedule b/src/test/regress/multi_1_schedule index 287f4557ad8..aed751aa014 100644 --- a/src/test/regress/multi_1_schedule +++ b/src/test/regress/multi_1_schedule @@ -32,6 +32,7 @@ test: propagate_extension_commands test: escape_extension_name test: ref_citus_local_fkeys test: alter_database_owner +test: seclabel test: distributed_triggers test: create_single_shard_table # don't parallelize single_shard_table_udfs to make sure colocation ids are sequential diff --git a/src/test/regress/pg_regress_multi.pl b/src/test/regress/pg_regress_multi.pl index 4cc022198c4..fbadcfc6251 100755 --- a/src/test/regress/pg_regress_multi.pl +++ b/src/test/regress/pg_regress_multi.pl @@ -510,6 +510,12 @@ sub generate_hba # we disable some restrictions for local objects like local views to not break postgres vanilla test behaviour. push(@pgOptions, "citus.enforce_object_restrictions_for_local_objects=false"); } +else +{ + # We currently need this config for isolation tests and security label tests + # this option loads a security label provider, which we don't want in vanilla tests + push(@pgOptions, "citus.running_under_citus_test_suite=true"); +} if ($useMitmproxy) { @@ -560,7 +566,6 @@ sub generate_hba push(@pgOptions, "citus.metadata_sync_interval=1000"); push(@pgOptions, "citus.metadata_sync_retry_interval=100"); push(@pgOptions, "client_min_messages='warning'"); # pg12 introduced notice showing during isolation tests - push(@pgOptions, "citus.running_under_isolation_test=true"); # Disable all features of the maintenance daemon. Otherwise queries might # randomly show temporarily as "waiting..." because they are waiting for the diff --git a/src/test/regress/sql/multi_test_helpers.sql b/src/test/regress/sql/multi_test_helpers.sql index f7c97f1b2d3..7f0346d1421 100644 --- a/src/test/regress/sql/multi_test_helpers.sql +++ b/src/test/regress/sql/multi_test_helpers.sql @@ -550,3 +550,34 @@ BEGIN RETURN result; END; $func$ LANGUAGE plpgsql; + +-- Returns pg_seclabels entries from all nodes in the cluster for which +-- the object name is the input. +CREATE OR REPLACE FUNCTION get_citus_tests_label_provider_labels(object_name text, + master_port INTEGER DEFAULT 57636, + worker_1_port INTEGER DEFAULT 57637, + worker_2_port INTEGER DEFAULT 57638) +RETURNS TABLE ( + node_type text, + result text +) +AS $func$ +DECLARE + pg_seclabels_cmd TEXT := 'SELECT to_jsonb(q.*) FROM (' || + 'SELECT provider, objtype, label FROM pg_seclabels ' || + 'WHERE objname = ''' || object_name || ''') q'; +BEGIN + RETURN QUERY + SELECT + CASE + WHEN nodeport = master_port THEN 'coordinator' + WHEN nodeport = worker_1_port THEN 'worker_1' + WHEN nodeport = worker_2_port THEN 'worker_2' + ELSE 'unexpected_node' + END AS node_type, + a.result + FROM run_command_on_all_nodes(pg_seclabels_cmd) a + JOIN pg_dist_node USING (nodeid) + ORDER BY node_type; +END; +$func$ LANGUAGE plpgsql; diff --git a/src/test/regress/sql/seclabel.sql b/src/test/regress/sql/seclabel.sql new file mode 100644 index 00000000000..e523fc1dacd --- /dev/null +++ b/src/test/regress/sql/seclabel.sql @@ -0,0 +1,87 @@ +-- +-- SECLABEL +-- +-- Test suite for SECURITY LABEL ON ROLE statements +-- + +-- first we remove one of the worker nodes to be able to test +-- citus_add_node later +SELECT citus_remove_node('localhost', :worker_2_port); + +-- create two roles, one with characters that need escaping +CREATE ROLE user1; +CREATE ROLE "user 2"; + +-- check an invalid label for our current dummy hook citus_test_object_relabel +SECURITY LABEL FOR "citus '!tests_label_provider" ON ROLE user1 IS 'invalid_label'; + +-- if we disable metadata_sync, the command will not be propagated +SET citus.enable_metadata_sync TO off; +SECURITY LABEL FOR "citus '!tests_label_provider" ON ROLE user1 IS 'citus_unclassified'; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('user1') ORDER BY node_type; + +RESET citus.enable_metadata_sync; + +-- check that we only support propagating for roles +SET citus.shard_replication_factor to 1; +-- distributed table +CREATE TABLE a (a int); +SELECT create_distributed_table('a', 'a'); +-- distributed view +CREATE VIEW v_dist AS SELECT * FROM a; +-- distributed function +CREATE FUNCTION notice(text) RETURNS void LANGUAGE plpgsql AS $$ + BEGIN RAISE NOTICE '%', $1; END; $$; + +SECURITY LABEL ON TABLE a IS 'citus_classified'; +SECURITY LABEL ON FUNCTION notice IS 'citus_unclassified'; +SECURITY LABEL ON VIEW v_dist IS 'citus_classified'; + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('a') ORDER BY node_type; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('notice(text)') ORDER BY node_type; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('v_dist') ORDER BY node_type; + +\c - - - :worker_1_port +SECURITY LABEL ON TABLE a IS 'citus_classified'; +SECURITY LABEL ON FUNCTION notice IS 'citus_unclassified'; +SECURITY LABEL ON VIEW v_dist IS 'citus_classified'; + +\c - - - :master_port +SELECT node_type, result FROM get_citus_tests_label_provider_labels('a') ORDER BY node_type; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('notice(text)') ORDER BY node_type; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('v_dist') ORDER BY node_type; + +DROP TABLE a CASCADE; +DROP FUNCTION notice; + +-- test that SECURITY LABEL statement is actually propagated for ROLES +SET citus.log_remote_commands TO on; +SET citus.grep_remote_commands = '%SECURITY LABEL%'; + +-- we have exactly one provider loaded, so we may not include the provider in the command +SECURITY LABEL for "citus '!tests_label_provider" ON ROLE user1 IS 'citus_classified'; +SECURITY LABEL ON ROLE user1 IS NULL; +SECURITY LABEL ON ROLE user1 IS 'citus_unclassified'; +SECURITY LABEL for "citus '!tests_label_provider" ON ROLE "user 2" IS 'citus ''!unclassified'; + +\c - - - :worker_1_port +-- command not allowed from worker node +SECURITY LABEL for "citus '!tests_label_provider" ON ROLE user1 IS 'citus ''!unclassified'; + +\c - - - :master_port +RESET citus.log_remote_commands; + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('user1') ORDER BY node_type; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('"user 2"') ORDER BY node_type; + +-- add a new node and check that it also propagates the SECURITY LABEL statement to the new node +SET citus.log_remote_commands TO on; +SET citus.grep_remote_commands = '%SECURITY LABEL%'; +SELECT 1 FROM citus_add_node('localhost', :worker_2_port); + +SELECT node_type, result FROM get_citus_tests_label_provider_labels('user1') ORDER BY node_type; +SELECT node_type, result FROM get_citus_tests_label_provider_labels('"user 2"') ORDER BY node_type; + +-- cleanup +RESET citus.log_remote_commands; +DROP ROLE user1, "user 2"; From 5efd3f181aa9a88de6d29d4bc74f4adedfe93d0e Mon Sep 17 00:00:00 2001 From: Hanefi Onaldi Date: Thu, 16 Nov 2023 14:12:17 +0300 Subject: [PATCH 63/68] Fix wrong PR links in changelog (#7350) When preparing changelog for 12.1.1 release, I accidentally swapped the PR numbers for the two commits. This commit fixes the changelog to point to the correct PRs. --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 686e78dd198..8d979c10420 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,10 @@ ### citus v12.1.1 (November 9, 2023) ### * Fixes leaking of memory and memory contexts in Citus foreign key cache - (#7219) + (#7236) * Makes sure to disallow creating a replicated distributed table concurrently - (#7236) + (#7219) ### citus v12.1.0 (September 12, 2023) ### From 55d500de8d43b1e2bb9086f3ea1f424cacf3fd1c Mon Sep 17 00:00:00 2001 From: Naisila Puka <37271756+naisila@users.noreply.github.com> Date: Thu, 16 Nov 2023 14:51:31 +0300 Subject: [PATCH 64/68] Remove accidentally added gucs.out (#7349) --- .gitignore | 3 ++ gucs.out | 133 ----------------------------------------------------- 2 files changed, 3 insertions(+), 133 deletions(-) delete mode 100644 gucs.out diff --git a/.gitignore b/.gitignore index df447746a9d..e636392ac36 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,6 @@ lib*.pc # style related temporary outputs *.uncrustify .venv + +# added output when modifying check_gucs_are_alphabetically_sorted.sh +guc.out diff --git a/gucs.out b/gucs.out deleted file mode 100644 index 8501e6c1f75..00000000000 --- a/gucs.out +++ /dev/null @@ -1,133 +0,0 @@ -"citus.all_modifications_commutative", -"citus.allow_modifications_from_workers_to_replicated_tables", -"citus.allow_nested_distributed_execution", -"citus.allow_unsafe_constraints", -"citus.allow_unsafe_locks_from_workers", -"citus.background_task_queue_interval", -"citus.check_available_space_before_move", -"citus.cluster_name", -"citus.coordinator_aggregation_strategy", -"citus.copy_switchover_threshold", -"citus.count_distinct_error_rate", -"citus.cpu_priority", -"citus.cpu_priority_for_logical_replication_senders", -"citus.create_object_propagation", -"citus.defer_drop_after_shard_move", -"citus.defer_drop_after_shard_split", -"citus.defer_shard_delete_interval", -"citus.desired_percent_disk_available_after_move", -"citus.distributed_deadlock_detection_factor", -"citus.enable_alter_database_owner", -"citus.enable_alter_role_propagation", -"citus.enable_alter_role_set_propagation", -"citus.enable_binary_protocol", -"citus.enable_change_data_capture", -"citus.enable_cluster_clock", -"citus.enable_cost_based_connection_establishment", -"citus.enable_create_role_propagation", -"citus.enable_create_type_propagation", -"citus.enable_ddl_propagation", -"citus.enable_deadlock_prevention", -"citus.enable_fast_path_router_planner", -"citus.enable_local_execution", -"citus.enable_local_reference_table_foreign_keys", -"citus.enable_manual_changes_to_shards", -"citus.enable_manual_metadata_changes_for_user", -"citus.enable_metadata_sync", -"citus.enable_non_colocated_router_query_pushdown", -"citus.enable_repartition_joins", -"citus.enable_repartitioned_insert_select", -"citus.enable_router_execution", -"citus.enable_schema_based_sharding", -"citus.enable_single_hash_repartition_joins", -"citus.enable_statistics_collection", -"citus.enable_unique_job_ids", -"citus.enable_unsafe_triggers", -"citus.enable_unsupported_feature_messages", -"citus.enable_version_checks", -"citus.enforce_foreign_key_restrictions", -"citus.enforce_object_restrictions_for_local_objects", -"citus.executor_slow_start_interval", -"citus.explain_all_tasks", -"citus.explain_analyze_sort_method", -"citus.explain_distributed_queries", -"citus.force_max_query_parallelization", -"citus.function_opens_transaction_block", -"citus.grep_remote_commands", -"citus.hide_citus_dependent_objects", -"citus.hide_shards_from_app_name_prefixes", -"citus.isolation_test_session_process_id", -"citus.isolation_test_session_remote_process_id", -"citus.limit_clause_row_fetch_count", -"citus.local_copy_flush_threshold", -"citus.local_hostname", -"citus.local_shared_pool_size", -"citus.local_table_join_policy", -"citus.log_distributed_deadlock_detection", -"citus.log_intermediate_results", -"citus.log_local_commands", -"citus.log_multi_join_order", -"citus.log_remote_commands", -"citus.logical_replication_timeout", -"citus.main_db", -"citus.max_adaptive_executor_pool_size", -"citus.max_background_task_executors", -"citus.max_background_task_executors_per_node", -"citus.max_cached_connection_lifetime", -"citus.max_cached_conns_per_worker", -"citus.max_client_connections", -"citus.max_high_priority_background_processes", -"citus.max_intermediate_result_size", -"citus.max_matview_size_to_auto_recreate", -"citus.max_rebalancer_logged_ignored_moves", -"citus.max_shared_pool_size", -"citus.max_worker_nodes_tracked", -"citus.metadata_sync_interval", -"citus.metadata_sync_mode", -"citus.metadata_sync_retry_interval", -"citus.mitmfifo", -"citus.multi_shard_modify_mode", -"citus.multi_task_query_log_level", -"citus.next_cleanup_record_id", -"citus.next_operation_id", -"citus.next_placement_id", -"citus.next_shard_id", -"citus.node_connection_timeout", -"citus.node_conninfo", -"citus.override_table_visibility", -"citus.prevent_incomplete_connection_establishment", -"citus.propagate_session_settings_for_loopback_connection", -"citus.propagate_set_commands", -"citus.rebalancer_by_disk_size_base_cost", -"citus.recover_2pc_interval", -"citus.remote_copy_flush_threshold", -"citus.remote_task_check_interval", -"citus.repartition_join_bucket_count_per_node", -"citus.replicate_reference_tables_on_activate", -"citus.replication_model", -"citus.running_under_citus_test_suite", -"citus.select_opens_transaction_block", -"citus.shard_count", -"citus.shard_replication_factor", -"citus.show_shards_for_app_name_prefixes", -"citus.skip_advisory_lock_permission_checks", -"citus.skip_constraint_validation", -"citus.skip_jsonb_validation_in_copy", -"citus.sort_returning", -"citus.stat_statements_max", -"citus.stat_statements_purge_interval", -"citus.stat_statements_track", -"citus.stat_tenants_limit", -"citus.stat_tenants_log_level", -"citus.stat_tenants_period", -"citus.stat_tenants_track", -"citus.stat_tenants_untracked_sample_rate", -"citus.subquery_pushdown", -"citus.task_assignment_policy", -"citus.task_executor_type", -"citus.use_citus_managed_tables", -"citus.use_secondary_nodes", -"citus.values_materialization_threshold", -"citus.version", -"citus.worker_min_messages", -"citus.writable_standby_coordinator", From 32b0fc23f5b5f439f62defbd80cb09b5294e97ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCrkan=20=C4=B0ndibay?= Date: Fri, 17 Nov 2023 08:51:56 +0300 Subject: [PATCH 65/68] Removes unnecessary package installations in packaging pipelines (#7341) With the recent changes in packaging images, linux package installations to execute validate_output is unnecessary now. In this PR, I removed them to make the pipeline more effective. - [x] Remove the test warning before merge --- .github/packaging/validate_build_output.sh | 5 ++++- .github/workflows/packaging-test-pipelines.yml | 10 ---------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/.github/packaging/validate_build_output.sh b/.github/packaging/validate_build_output.sh index 64098811ec4..dab301aa5ee 100755 --- a/.github/packaging/validate_build_output.sh +++ b/.github/packaging/validate_build_output.sh @@ -32,7 +32,10 @@ python3 -m pip install -r tools/packaging_automation/requirements.txt echo "Package type: ${package_type}" echo "OS version: $(get_rpm_os_version)" - # if os version is centos 7 or oracle linux 7, then remove urllib3 with pip uninstall and install urllib3<2.0.0 with pip install + # For RHEL 7, we need to install urllib3<2 due to below execution error + # ImportError: urllib3 v2.0 only supports OpenSSL 1.1.1+, currently the 'ssl' + # module is compiled with 'OpenSSL 1.0.2k-fips 26 Jan 2017'. + # See: https://github.com/urllib3/urllib3/issues/2168 if [[ ${package_type} == "rpm" && $(get_rpm_os_version) == 7* ]]; then python3 -m pip uninstall -y urllib3 python3 -m pip install 'urllib3<2' diff --git a/.github/workflows/packaging-test-pipelines.yml b/.github/workflows/packaging-test-pipelines.yml index 51bd8250350..4ae741a911a 100644 --- a/.github/workflows/packaging-test-pipelines.yml +++ b/.github/workflows/packaging-test-pipelines.yml @@ -112,11 +112,6 @@ jobs: PACKAGING_DOCKER_IMAGE: ${{ matrix.packaging_docker_image }} run: | echo "Postgres version: ${POSTGRES_VERSION}" - - ## Install required packages to execute packaging tools for rpm based distros - yum install python3-pip python3-devel postgresql-devel -y - python3 -m pip install wheel - ./.github/packaging/validate_build_output.sh "rpm" deb_build_tests: @@ -192,9 +187,4 @@ jobs: PACKAGING_DOCKER_IMAGE: ${{ matrix.packaging_docker_image }} run: | echo "Postgres version: ${POSTGRES_VERSION}" - - apt-get update -y - ## Install required packages to execute packaging tools for deb based distros - apt-get install python3-dev python3-pip -y - apt-get purge -y python3-yaml ./.github/packaging/validate_build_output.sh "deb" From e14e8667ccecc5d9e1e5a349f8acad7644b56703 Mon Sep 17 00:00:00 2001 From: Japin Li Date: Fri, 17 Nov 2023 18:01:23 +0800 Subject: [PATCH 66/68] Fix redundant variable declaration (#7353) The `$workerCount` declare twice in src/test/regress/pg_regress_multi.pl. --- src/test/regress/pg_regress_multi.pl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/regress/pg_regress_multi.pl b/src/test/regress/pg_regress_multi.pl index fbadcfc6251..3acde4c3c62 100755 --- a/src/test/regress/pg_regress_multi.pl +++ b/src/test/regress/pg_regress_multi.pl @@ -90,7 +90,6 @@ () my $serversAreShutdown = "TRUE"; my $usingWindows = 0; my $mitmPid = 0; -my $workerCount = 2; if ($Config{osname} eq "MSWin32") { From c88bf5ff1ce175c24664eabae33fc49f0aac5969 Mon Sep 17 00:00:00 2001 From: Naisila Puka <37271756+naisila@users.noreply.github.com> Date: Fri, 17 Nov 2023 15:11:38 +0300 Subject: [PATCH 67/68] Cleanup leftover replication slots in publication test (#7354) --- src/test/regress/expected/publication.out | 7 +++++++ src/test/regress/expected/publication_0.out | 6 ++++++ src/test/regress/sql/publication.sql | 3 +++ 3 files changed, 16 insertions(+) diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out index c761efb3e95..2df4e59d30b 100644 --- a/src/test/regress/expected/publication.out +++ b/src/test/regress/expected/publication.out @@ -267,6 +267,7 @@ SET client_min_messages TO ERROR; DROP SCHEMA publication CASCADE; DROP SCHEMA "publication-1" CASCADE; DROP SCHEMA citus_schema_1 CASCADE; +SELECT public.wait_for_resource_cleanup(); \q \endif -- recreate a mixed publication @@ -544,3 +545,9 @@ DROP SCHEMA publication CASCADE; DROP SCHEMA "publication-1" CASCADE; DROP SCHEMA citus_schema_1 CASCADE; DROP SCHEMA publication2 CASCADE; +SELECT public.wait_for_resource_cleanup(); + wait_for_resource_cleanup +--------------------------------------------------------------------- + +(1 row) + diff --git a/src/test/regress/expected/publication_0.out b/src/test/regress/expected/publication_0.out index 14fa94d17d6..e768a1d412e 100644 --- a/src/test/regress/expected/publication_0.out +++ b/src/test/regress/expected/publication_0.out @@ -267,4 +267,10 @@ SET client_min_messages TO ERROR; DROP SCHEMA publication CASCADE; DROP SCHEMA "publication-1" CASCADE; DROP SCHEMA citus_schema_1 CASCADE; +SELECT public.wait_for_resource_cleanup(); + wait_for_resource_cleanup +--------------------------------------------------------------------- + +(1 row) + \q diff --git a/src/test/regress/sql/publication.sql b/src/test/regress/sql/publication.sql index 06bdc39fe52..70baf67267d 100644 --- a/src/test/regress/sql/publication.sql +++ b/src/test/regress/sql/publication.sql @@ -195,6 +195,7 @@ SET client_min_messages TO ERROR; DROP SCHEMA publication CASCADE; DROP SCHEMA "publication-1" CASCADE; DROP SCHEMA citus_schema_1 CASCADE; +SELECT public.wait_for_resource_cleanup(); \q \endif @@ -391,3 +392,5 @@ DROP SCHEMA publication CASCADE; DROP SCHEMA "publication-1" CASCADE; DROP SCHEMA citus_schema_1 CASCADE; DROP SCHEMA publication2 CASCADE; + +SELECT public.wait_for_resource_cleanup(); From cedcc220bff9fce22e27142b95ece754b54b1009 Mon Sep 17 00:00:00 2001 From: Naisila Puka <37271756+naisila@users.noreply.github.com> Date: Fri, 17 Nov 2023 18:58:06 +0300 Subject: [PATCH 68/68] Fixes flaky VACUUM (freeze, process toast true) result (#7348) https://app.circleci.com/pipelines/github/citusdata/citus/34550/workflows/5b802f66-2666-4623-a209-6d7799f7ee5f/jobs/1229153 ```diff VACUUM (FREEZE, PROCESS_TOAST true) local_vacuum_table; SELECT relfrozenxid::text::integer > :frozenxid AS frozen_performed FROM pg_class WHERE oid=:reltoastrelid::regclass; frozen_performed ------------------ - t + f (1 row) ``` Process toast option in vacuum was introduced in PG14. The failing test was supposed to be a part of `multi_utilities.sql`, but it was included in `pg14.sql` to avoid alternative output for PG13. See https://github.com/citusdata/citus/commit/ba62c0a1488b0dd160b4c64a45f01fe9bfd76cce#diff-ed03478f693155e2fe092e9ad356bf884dc097f554e8d75eff562d52bbcf7a75L255-L272 for reference. However, now that we don't support PG13 anymore, we can move this test to `multi_utilities.sql`. Moving the test, plus inserting data before running vacuum freeze such that the freeze is more meaningful and not flaky, fixes the flakiness problem of the test. --- src/test/regress/expected/multi_utilities.out | 28 +++++++++++++++++++ src/test/regress/expected/pg14.out | 27 +----------------- src/test/regress/sql/multi_utilities.sql | 21 ++++++++++++++ src/test/regress/sql/pg14.sql | 20 +------------ 4 files changed, 51 insertions(+), 45 deletions(-) diff --git a/src/test/regress/expected/multi_utilities.out b/src/test/regress/expected/multi_utilities.out index d2b0940ed27..5faab87d7c5 100644 --- a/src/test/regress/expected/multi_utilities.out +++ b/src/test/regress/expected/multi_utilities.out @@ -424,6 +424,34 @@ FROM pg_total_relation_size('local_vacuum_table') s ; 35000000 (1 row) +-- vacuum (process_toast true) should be vacuuming toast tables (default is true) +select reltoastrelid from pg_class where relname='local_vacuum_table' +\gset +SELECT relfrozenxid AS frozenxid FROM pg_class WHERE oid=:reltoastrelid::regclass +\gset +insert into local_vacuum_table select i from generate_series(1,10000) i; +VACUUM (FREEZE, PROCESS_TOAST true) local_vacuum_table; +SELECT relfrozenxid::text::integer > :frozenxid AS frozen_performed FROM pg_class +WHERE oid=:reltoastrelid::regclass; + frozen_performed +--------------------------------------------------------------------- + t +(1 row) + +delete from local_vacuum_table; +-- vacuum (process_toast false) should not be vacuuming toast tables (default is true) +SELECT relfrozenxid AS frozenxid FROM pg_class WHERE oid=:reltoastrelid::regclass +\gset +insert into local_vacuum_table select i from generate_series(1,10000) i; +VACUUM (FREEZE, PROCESS_TOAST false) local_vacuum_table; +SELECT relfrozenxid::text::integer = :frozenxid AS frozen_not_performed FROM pg_class +WHERE oid=:reltoastrelid::regclass; + frozen_not_performed +--------------------------------------------------------------------- + t +(1 row) + +delete from local_vacuum_table; -- vacuum (truncate false) should not attempt to truncate off any empty pages at the end of the table (default is true) insert into local_vacuum_table select i from generate_series(1,1000000) i; delete from local_vacuum_table; diff --git a/src/test/regress/expected/pg14.out b/src/test/regress/expected/pg14.out index 1b1d80df215..badd232407e 100644 --- a/src/test/regress/expected/pg14.out +++ b/src/test/regress/expected/pg14.out @@ -71,32 +71,6 @@ NOTICE: issuing VACUUM (FULL,TRUNCATE false,INDEX_CLEANUP auto) pg14.t1_980000 DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing VACUUM (FULL,TRUNCATE false,INDEX_CLEANUP auto) pg14.t1_980001 DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx --- vacuum (process_toast true) should be vacuuming toast tables (default is true) -CREATE TABLE local_vacuum_table(name text); -select reltoastrelid from pg_class where relname='local_vacuum_table' -\gset -SELECT relfrozenxid AS frozenxid FROM pg_class WHERE oid=:reltoastrelid::regclass -\gset -VACUUM (FREEZE, PROCESS_TOAST true) local_vacuum_table; -SELECT relfrozenxid::text::integer > :frozenxid AS frozen_performed FROM pg_class -WHERE oid=:reltoastrelid::regclass; - frozen_performed ---------------------------------------------------------------------- - t -(1 row) - --- vacuum (process_toast false) should not be vacuuming toast tables (default is true) -SELECT relfrozenxid AS frozenxid FROM pg_class WHERE oid=:reltoastrelid::regclass -\gset -VACUUM (FREEZE, PROCESS_TOAST false) local_vacuum_table; -SELECT relfrozenxid::text::integer = :frozenxid AS frozen_not_performed FROM pg_class -WHERE oid=:reltoastrelid::regclass; - frozen_not_performed ---------------------------------------------------------------------- - t -(1 row) - -DROP TABLE local_vacuum_table; SET citus.log_remote_commands TO OFF; create table dist(a int, b int); select create_distributed_table('dist','a'); @@ -1492,4 +1466,5 @@ DROP TABLE compression_and_defaults, compression_and_generated_col; set client_min_messages to error; drop extension postgres_fdw cascade; drop schema pg14 cascade; +DROP ROLE role_1, r1; reset client_min_messages; diff --git a/src/test/regress/sql/multi_utilities.sql b/src/test/regress/sql/multi_utilities.sql index 1124b989027..668e1f32fbf 100644 --- a/src/test/regress/sql/multi_utilities.sql +++ b/src/test/regress/sql/multi_utilities.sql @@ -272,6 +272,27 @@ VACUUM (INDEX_CLEANUP ON, PARALLEL 1) local_vacuum_table; SELECT CASE WHEN s BETWEEN 20000000 AND 49999999 THEN 35000000 ELSE s END size FROM pg_total_relation_size('local_vacuum_table') s ; +-- vacuum (process_toast true) should be vacuuming toast tables (default is true) +select reltoastrelid from pg_class where relname='local_vacuum_table' +\gset + +SELECT relfrozenxid AS frozenxid FROM pg_class WHERE oid=:reltoastrelid::regclass +\gset +insert into local_vacuum_table select i from generate_series(1,10000) i; +VACUUM (FREEZE, PROCESS_TOAST true) local_vacuum_table; +SELECT relfrozenxid::text::integer > :frozenxid AS frozen_performed FROM pg_class +WHERE oid=:reltoastrelid::regclass; +delete from local_vacuum_table; + +-- vacuum (process_toast false) should not be vacuuming toast tables (default is true) +SELECT relfrozenxid AS frozenxid FROM pg_class WHERE oid=:reltoastrelid::regclass +\gset +insert into local_vacuum_table select i from generate_series(1,10000) i; +VACUUM (FREEZE, PROCESS_TOAST false) local_vacuum_table; +SELECT relfrozenxid::text::integer = :frozenxid AS frozen_not_performed FROM pg_class +WHERE oid=:reltoastrelid::regclass; +delete from local_vacuum_table; + -- vacuum (truncate false) should not attempt to truncate off any empty pages at the end of the table (default is true) insert into local_vacuum_table select i from generate_series(1,1000000) i; delete from local_vacuum_table; diff --git a/src/test/regress/sql/pg14.sql b/src/test/regress/sql/pg14.sql index 8d3f430ce9e..47eb6793029 100644 --- a/src/test/regress/sql/pg14.sql +++ b/src/test/regress/sql/pg14.sql @@ -22,25 +22,6 @@ VACUUM (INDEX_CLEANUP "AUTOX") t1; VACUUM (FULL, FREEZE, VERBOSE false, ANALYZE, SKIP_LOCKED, INDEX_CLEANUP, PROCESS_TOAST, TRUNCATE) t1; VACUUM (FULL, FREEZE false, VERBOSE false, ANALYZE false, SKIP_LOCKED false, INDEX_CLEANUP "Auto", PROCESS_TOAST true, TRUNCATE false) t1; --- vacuum (process_toast true) should be vacuuming toast tables (default is true) -CREATE TABLE local_vacuum_table(name text); -select reltoastrelid from pg_class where relname='local_vacuum_table' -\gset - -SELECT relfrozenxid AS frozenxid FROM pg_class WHERE oid=:reltoastrelid::regclass -\gset -VACUUM (FREEZE, PROCESS_TOAST true) local_vacuum_table; -SELECT relfrozenxid::text::integer > :frozenxid AS frozen_performed FROM pg_class -WHERE oid=:reltoastrelid::regclass; - --- vacuum (process_toast false) should not be vacuuming toast tables (default is true) -SELECT relfrozenxid AS frozenxid FROM pg_class WHERE oid=:reltoastrelid::regclass -\gset -VACUUM (FREEZE, PROCESS_TOAST false) local_vacuum_table; -SELECT relfrozenxid::text::integer = :frozenxid AS frozen_not_performed FROM pg_class -WHERE oid=:reltoastrelid::regclass; - -DROP TABLE local_vacuum_table; SET citus.log_remote_commands TO OFF; create table dist(a int, b int); @@ -777,4 +758,5 @@ DROP TABLE compression_and_defaults, compression_and_generated_col; set client_min_messages to error; drop extension postgres_fdw cascade; drop schema pg14 cascade; +DROP ROLE role_1, r1; reset client_min_messages;