From 371f094b681fd879a4230f24c0a53c48ab3ead59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCrkan=20=C4=B0ndibay?= Date: Mon, 21 Aug 2023 17:29:44 +0300 Subject: [PATCH 01/10] Removes pg_send_cancellation (#7135) DESCRIPTION: Removes pg_send_cancellation and all references --- Makefile | 16 +- .../distributed/test/pg_send_cancellation.c | 70 ----- src/bin/pg_send_cancellation/.gitignore | 1 - src/bin/pg_send_cancellation/Makefile | 24 -- src/bin/pg_send_cancellation/README.md | 47 ---- .../pg_send_cancellation.c | 261 ------------------ .../regress/enterprise_isolation_schedule | 1 - .../isolation_pg_send_cancellation.out | 42 --- .../spec/isolation_pg_send_cancellation.spec | 65 ----- 9 files changed, 4 insertions(+), 523 deletions(-) delete mode 100644 src/backend/distributed/test/pg_send_cancellation.c delete mode 100644 src/bin/pg_send_cancellation/.gitignore delete mode 100644 src/bin/pg_send_cancellation/Makefile delete mode 100644 src/bin/pg_send_cancellation/README.md delete mode 100644 src/bin/pg_send_cancellation/pg_send_cancellation.c delete mode 100644 src/test/regress/expected/isolation_pg_send_cancellation.out delete mode 100644 src/test/regress/spec/isolation_pg_send_cancellation.spec diff --git a/Makefile b/Makefile index 098b7c20788..e42d0ffd31a 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ endif include Makefile.global -all: extension pg_send_cancellation +all: extension # build columnar only @@ -40,22 +40,14 @@ clean-full: install-downgrades: $(MAKE) -C src/backend/distributed/ install-downgrades -install-all: install-headers install-pg_send_cancellation +install-all: install-headers $(MAKE) -C src/backend/columnar/ install-all $(MAKE) -C src/backend/distributed/ install-all -# build citus_send_cancellation binary -pg_send_cancellation: - $(MAKE) -C src/bin/pg_send_cancellation/ all -install-pg_send_cancellation: pg_send_cancellation - $(MAKE) -C src/bin/pg_send_cancellation/ install -clean-pg_send_cancellation: - $(MAKE) -C src/bin/pg_send_cancellation/ clean -.PHONY: pg_send_cancellation install-pg_send_cancellation clean-pg_send_cancellation # Add to generic targets -install: install-extension install-headers install-pg_send_cancellation -clean: clean-extension clean-pg_send_cancellation +install: install-extension install-headers +clean: clean-extension # apply or check style reindent: diff --git a/src/backend/distributed/test/pg_send_cancellation.c b/src/backend/distributed/test/pg_send_cancellation.c deleted file mode 100644 index 576d915a68a..00000000000 --- a/src/backend/distributed/test/pg_send_cancellation.c +++ /dev/null @@ -1,70 +0,0 @@ -/*------------------------------------------------------------------------- - * - * pg_send_cancellation.c - * - * This file contains functions to test setting pg_send_cancellation. - * - * Copyright (c) Citus Data, Inc. - * - *------------------------------------------------------------------------- - */ - -#include "postgres.h" -#include "miscadmin.h" -#include "fmgr.h" -#include "port.h" - -#include "postmaster/postmaster.h" - - -#define PG_SEND_CANCELLATION_VERSION \ - "pg_send_cancellation (PostgreSQL) " PG_VERSION "\n" - - -/* exports for SQL callable functions */ -PG_FUNCTION_INFO_V1(get_cancellation_key); -PG_FUNCTION_INFO_V1(run_pg_send_cancellation); - - -/* - * get_cancellation_key returns the cancellation key of the current process - * as an integer. - */ -Datum -get_cancellation_key(PG_FUNCTION_ARGS) -{ - PG_RETURN_INT32(MyCancelKey); -} - - -/* - * run_pg_send_cancellation runs the pg_send_cancellation program with - * the specified arguments - */ -Datum -run_pg_send_cancellation(PG_FUNCTION_ARGS) -{ - int pid = PG_GETARG_INT32(0); - int cancelKey = PG_GETARG_INT32(1); - - char sendCancellationPath[MAXPGPATH]; - char command[1024]; - - /* Locate executable backend before we change working directory */ - if (find_other_exec(my_exec_path, "pg_send_cancellation", - PG_SEND_CANCELLATION_VERSION, - sendCancellationPath) < 0) - { - ereport(ERROR, (errmsg("could not locate pg_send_cancellation"))); - } - - pg_snprintf(command, sizeof(command), "%s %d %d %s %d", - sendCancellationPath, pid, cancelKey, "localhost", PostPortNumber); - - if (system(command) != 0) - { - ereport(ERROR, (errmsg("failed to run command: %s", command))); - } - - PG_RETURN_VOID(); -} diff --git a/src/bin/pg_send_cancellation/.gitignore b/src/bin/pg_send_cancellation/.gitignore deleted file mode 100644 index 8088a2e9821..00000000000 --- a/src/bin/pg_send_cancellation/.gitignore +++ /dev/null @@ -1 +0,0 @@ -pg_send_cancellation diff --git a/src/bin/pg_send_cancellation/Makefile b/src/bin/pg_send_cancellation/Makefile deleted file mode 100644 index 4515c5019ae..00000000000 --- a/src/bin/pg_send_cancellation/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -citus_top_builddir = ../../.. - -PROGRAM = pg_send_cancellation -PGFILEDESC = "pg_send_cancellation sends a custom cancellation message" -OBJS = $(citus_abs_srcdir)/src/bin/pg_send_cancellation/pg_send_cancellation.o -PG_CPPFLAGS = -I$(libpq_srcdir) -PG_LIBS_INTERNAL = $(libpq_pgport) -PG_LDFLAGS += $(LDFLAGS) - -include $(citus_top_builddir)/Makefile.global - -# We reuse all the Citus flags (incl. security flags), but we are building a program not a shared library -# We sometimes build Citus with a newer version of gcc than Postgres was built -# with and this breaks LTO (link-time optimization). Even if disabling it can -# have some perf impact this is ok because pg_send_cancellation is only used -# for tests anyway. -override CFLAGS := $(filter-out -shared, $(CFLAGS)) -fno-lto - -# Filter out unneeded dependencies -override LIBS := $(filter-out -lz -lreadline -ledit -ltermcap -lncurses -lcurses -lpam, $(LIBS)) - -clean: clean-pg_send_cancellation -clean-pg_send_cancellation: - rm -f $(PROGRAM) $(OBJS) diff --git a/src/bin/pg_send_cancellation/README.md b/src/bin/pg_send_cancellation/README.md deleted file mode 100644 index c83316419c3..00000000000 --- a/src/bin/pg_send_cancellation/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# pg_send_cancellation - -pg_send_cancellation is a program for manually sending a cancellation -to a Postgres endpoint. It is effectively a command-line version of -PQcancel in libpq, but it can use any PID or cancellation key. - -We use pg_send_cancellation primarily to propagate cancellations between pgbouncers -behind a load balancer. Since the cancellation protocol involves -opening a new connection, the new connection may go to a different -node that does not recognize the cancellation key. To handle that -scenario, we modified pgbouncer to pass unrecognized cancellation -keys to a shell command. - -Users can configure the cancellation_command, which will be run with: -``` - -``` - -Note that pgbouncer does not use actual PIDs. Instead, it generates PID and cancellation key together a random 8-byte number. This makes the chance of collisions exceedingly small. - -By providing pg_send_cancellation as part of Citus, we can use a shell script that pgbouncer invokes to propagate the cancellation to all *other* worker nodes in the same cluster, for example: - -```bash -#!/bin/sh -remote_ip=$1 -remote_port=$2 -pid=$3 -cancel_key=$4 - -postgres_path=/usr/pgsql-14/bin -pgbouncer_port=6432 - -nodes_query="select nodename from pg_dist_node where groupid > 0 and groupid not in (select groupid from pg_dist_local_group) and nodecluster = current_setting('citus.cluster_name')" - -# Get hostnames of other worker nodes in the cluster, and send cancellation to their pgbouncers -$postgres_path/psql -c "$nodes_query" -tAX | xargs -n 1 sh -c "$postgres_path/pg_send_cancellation $pid $cancel_key \$0 $pgbouncer_port" -``` - -One thing we need to be careful about is that the cancellations do not get forwarded -back-and-forth. This is handled in pgbouncer by setting the last bit of all generated -cancellation keys (sent to clients) to 1, and setting the last bit of all forwarded bits to 0. -That way, when a pgbouncer receives a cancellation key with the last bit set to 0, -it knows it is from another pgbouncer and should not forward further, and should set -the last bit to 1 when comparing to stored cancellation keys. - -Another thing we need to be careful about is that the integers should be encoded -as big endian on the wire. diff --git a/src/bin/pg_send_cancellation/pg_send_cancellation.c b/src/bin/pg_send_cancellation/pg_send_cancellation.c deleted file mode 100644 index 0ab2be95a71..00000000000 --- a/src/bin/pg_send_cancellation/pg_send_cancellation.c +++ /dev/null @@ -1,261 +0,0 @@ -/* - * pg_send_cancellation is a program for manually sending a cancellation - * to a Postgres endpoint. It is effectively a command-line version of - * PQcancel in libpq, but it can use any PID or cancellation key. - * - * Portions Copyright (c) Citus Data, Inc. - * - * For the internal_cancel function: - * - * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * Permission to use, copy, modify, and distribute this software and its - * documentation for any purpose, without fee, and without a written agreement - * is hereby granted, provided that the above copyright notice and this - * paragraph and the following two paragraphs appear in all copies. - * - * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR - * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING - * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS - * DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS - * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO - * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - * - */ -#include "postgres_fe.h" - -#include -#include -#include -#include -#include - -#include "common/ip.h" -#include "common/link-canary.h" -#include "common/scram-common.h" -#include "common/string.h" -#include "libpq-fe.h" -#include "libpq-int.h" -#include "mb/pg_wchar.h" -#include "port/pg_bswap.h" - - -#define ERROR_BUFFER_SIZE 256 - - -static int internal_cancel(SockAddr *raddr, int be_pid, int be_key, - char *errbuf, int errbufsize); - - -/* - * main entry point into the pg_send_cancellation program. - */ -int -main(int argc, char *argv[]) -{ - if (argc == 2 && strcmp(argv[1], "-V") == 0) - { - pg_fprintf(stdout, "pg_send_cancellation (PostgreSQL) " PG_VERSION "\n"); - return 0; - } - - if (argc < 4 || argc > 5) - { - char *program = argv[0]; - pg_fprintf(stderr, "%s requires 4 arguments\n\n", program); - pg_fprintf(stderr, "Usage: %s [port]\n", program); - return 1; - } - - char *pidString = argv[1]; - char *cancelKeyString = argv[2]; - char *host = argv[3]; - char *portString = "5432"; - - if (argc >= 5) - { - portString = argv[4]; - } - - /* parse the PID and cancellation key */ - int pid = strtol(pidString, NULL, 10); - int cancelAuthCode = strtol(cancelKeyString, NULL, 10); - - char errorBuffer[ERROR_BUFFER_SIZE] = { 0 }; - - struct addrinfo *ipAddressList; - struct addrinfo hint; - int ipAddressListFamily = AF_UNSPEC; - SockAddr socketAddress; - - memset(&hint, 0, sizeof(hint)); - hint.ai_socktype = SOCK_STREAM; - hint.ai_family = ipAddressListFamily; - - /* resolve the hostname to an IP */ - int ret = pg_getaddrinfo_all(host, portString, &hint, &ipAddressList); - if (ret || !ipAddressList) - { - pg_fprintf(stderr, "could not translate host name \"%s\" to address: %s\n", - host, gai_strerror(ret)); - return 1; - } - - if (ipAddressList->ai_addrlen > sizeof(socketAddress.addr)) - { - pg_fprintf(stderr, "invalid address length"); - return 1; - } - - /* - * Explanation of IGNORE-BANNED: - * This is a common pattern when using getaddrinfo. The system guarantees - * that ai_addrlen < sizeof(socketAddress.addr). Out of an abundance of - * caution. We also check it above. - */ - memcpy(&socketAddress.addr, ipAddressList->ai_addr, ipAddressList->ai_addrlen); /* IGNORE-BANNED */ - socketAddress.salen = ipAddressList->ai_addrlen; - - /* send the cancellation */ - bool cancelSucceeded = internal_cancel(&socketAddress, pid, cancelAuthCode, - errorBuffer, sizeof(errorBuffer)); - if (!cancelSucceeded) - { - pg_fprintf(stderr, "sending cancellation to %s:%s failed: %s", - host, portString, errorBuffer); - return 1; - } - - pg_freeaddrinfo_all(ipAddressListFamily, ipAddressList); - - return 0; -} - - -/* *INDENT-OFF* */ - -/* - * internal_cancel is copied from fe-connect.c - * - * The return value is true if the cancel request was successfully - * dispatched, false if not (in which case an error message is available). - * Note: successful dispatch is no guarantee that there will be any effect at - * the backend. The application must read the operation result as usual. - * - * CAUTION: we want this routine to be safely callable from a signal handler - * (for example, an application might want to call it in a SIGINT handler). - * This means we cannot use any C library routine that might be non-reentrant. - * malloc/free are often non-reentrant, and anything that might call them is - * just as dangerous. We avoid sprintf here for that reason. Building up - * error messages with strcpy/strcat is tedious but should be quite safe. - * We also save/restore errno in case the signal handler support doesn't. - * - * internal_cancel() is an internal helper function to make code-sharing - * between the two versions of the cancel function possible. - */ -static int -internal_cancel(SockAddr *raddr, int be_pid, int be_key, - char *errbuf, int errbufsize) -{ - int save_errno = SOCK_ERRNO; - pgsocket tmpsock = PGINVALID_SOCKET; - char sebuf[PG_STRERROR_R_BUFLEN]; - int maxlen; - struct - { - uint32 packetlen; - CancelRequestPacket cp; - } crp; - - /* - * We need to open a temporary connection to the postmaster. Do this with - * only kernel calls. - */ - if ((tmpsock = socket(raddr->addr.ss_family, SOCK_STREAM, 0)) == PGINVALID_SOCKET) - { - strlcpy(errbuf, "PQcancel() -- socket() failed: ", errbufsize); - goto cancel_errReturn; - } -retry3: - if (connect(tmpsock, (struct sockaddr *) &raddr->addr, raddr->salen) < 0) - { - if (SOCK_ERRNO == EINTR) - /* Interrupted system call - we'll just try again */ - goto retry3; - strlcpy(errbuf, "PQcancel() -- connect() failed: ", errbufsize); - goto cancel_errReturn; - } - - /* - * We needn't set nonblocking I/O or NODELAY options here. - */ - - /* Create and send the cancel request packet. */ - - crp.packetlen = pg_hton32((uint32) sizeof(crp)); - crp.cp.cancelRequestCode = (MsgType) pg_hton32(CANCEL_REQUEST_CODE); - crp.cp.backendPID = pg_hton32(be_pid); - crp.cp.cancelAuthCode = pg_hton32(be_key); - -retry4: - if (send(tmpsock, (char *) &crp, sizeof(crp), 0) != (int) sizeof(crp)) - { - if (SOCK_ERRNO == EINTR) - /* Interrupted system call - we'll just try again */ - goto retry4; - strlcpy(errbuf, "PQcancel() -- send() failed: ", errbufsize); - goto cancel_errReturn; - } - - /* - * Wait for the postmaster to close the connection, which indicates that - * it's processed the request. Without this delay, we might issue another - * command only to find that our cancel zaps that command instead of the - * one we thought we were canceling. Note we don't actually expect this - * read to obtain any data, we are just waiting for EOF to be signaled. - */ -retry5: - if (recv(tmpsock, (char *) &crp, 1, 0) < 0) - { - if (SOCK_ERRNO == EINTR) - /* Interrupted system call - we'll just try again */ - goto retry5; - /* we ignore other error conditions */ - } - - /* All done */ - closesocket(tmpsock); - SOCK_ERRNO_SET(save_errno); - return true; - -cancel_errReturn: - - /* - * Make sure we don't overflow the error buffer. Leave space for the \n at - * the end, and for the terminating zero. - */ - maxlen = errbufsize - strlen(errbuf) - 2; - if (maxlen >= 0) - { - /* - * Explanation of IGNORE-BANNED: - * This is well-tested libpq code that we would like to preserve in its - * original form. The appropriate length calculation is done above. - */ - strncat(errbuf, SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)), /* IGNORE-BANNED */ - maxlen); - strcat(errbuf, "\n"); /* IGNORE-BANNED */ - } - if (tmpsock != PGINVALID_SOCKET) - closesocket(tmpsock); - SOCK_ERRNO_SET(save_errno); - return false; -} - -/* *INDENT-ON* */ diff --git a/src/test/regress/enterprise_isolation_schedule b/src/test/regress/enterprise_isolation_schedule index 3e6655f88b2..689a7db7542 100644 --- a/src/test/regress/enterprise_isolation_schedule +++ b/src/test/regress/enterprise_isolation_schedule @@ -10,7 +10,6 @@ test: isolation_move_placement_vs_modification test: isolation_move_placement_vs_modification_fk test: isolation_tenant_isolation_with_fkey_to_reference test: isolation_ref2ref_foreign_keys_enterprise -test: isolation_pg_send_cancellation test: isolation_shard_move_vs_start_metadata_sync test: isolation_tenant_isolation test: isolation_tenant_isolation_nonblocking diff --git a/src/test/regress/expected/isolation_pg_send_cancellation.out b/src/test/regress/expected/isolation_pg_send_cancellation.out deleted file mode 100644 index 4b1475352df..00000000000 --- a/src/test/regress/expected/isolation_pg_send_cancellation.out +++ /dev/null @@ -1,42 +0,0 @@ -Parsed test spec with 2 sessions - -starting permutation: s1-register s2-lock s1-lock s2-wrong-cancel-1 s2-wrong-cancel-2 s2-cancel -step s1-register: - INSERT INTO cancel_table VALUES (pg_backend_pid(), get_cancellation_key()); - -step s2-lock: - BEGIN; - LOCK TABLE cancel_table IN ACCESS EXCLUSIVE MODE; - -step s1-lock: - BEGIN; - LOCK TABLE cancel_table IN ACCESS EXCLUSIVE MODE; - END; - -step s2-wrong-cancel-1: - SELECT run_pg_send_cancellation(pid + 1, cancel_key) FROM cancel_table; - -run_pg_send_cancellation ---------------------------------------------------------------------- - -(1 row) - -step s2-wrong-cancel-2: - SELECT run_pg_send_cancellation(pid, cancel_key + 1) FROM cancel_table; - -run_pg_send_cancellation ---------------------------------------------------------------------- - -(1 row) - -step s2-cancel: - SELECT run_pg_send_cancellation(pid, cancel_key) FROM cancel_table; - END; - -run_pg_send_cancellation ---------------------------------------------------------------------- - -(1 row) - -step s1-lock: <... completed> -ERROR: canceling statement due to user request diff --git a/src/test/regress/spec/isolation_pg_send_cancellation.spec b/src/test/regress/spec/isolation_pg_send_cancellation.spec deleted file mode 100644 index 46c6a0539dd..00000000000 --- a/src/test/regress/spec/isolation_pg_send_cancellation.spec +++ /dev/null @@ -1,65 +0,0 @@ -setup -{ - CREATE FUNCTION run_pg_send_cancellation(int,int) - RETURNS void - AS 'citus' - LANGUAGE C STRICT; - - CREATE FUNCTION get_cancellation_key() - RETURNS int - AS 'citus' - LANGUAGE C STRICT; - - CREATE TABLE cancel_table (pid int, cancel_key int); -} - -teardown -{ - DROP TABLE IF EXISTS cancel_table; -} - -session "s1" - -/* store the PID and cancellation key of session 1 */ -step "s1-register" -{ - INSERT INTO cancel_table VALUES (pg_backend_pid(), get_cancellation_key()); -} - -/* lock the table from session 1, will block and get cancelled */ -step "s1-lock" -{ - BEGIN; - LOCK TABLE cancel_table IN ACCESS EXCLUSIVE MODE; - END; -} - -session "s2" - -/* lock the table from session 2 to block session 1 */ -step "s2-lock" -{ - BEGIN; - LOCK TABLE cancel_table IN ACCESS EXCLUSIVE MODE; -} - -/* PID mismatch */ -step "s2-wrong-cancel-1" -{ - SELECT run_pg_send_cancellation(pid + 1, cancel_key) FROM cancel_table; -} - -/* cancellation key mismatch */ -step "s2-wrong-cancel-2" -{ - SELECT run_pg_send_cancellation(pid, cancel_key + 1) FROM cancel_table; -} - -/* cancel the LOCK statement in session 1 */ -step "s2-cancel" -{ - SELECT run_pg_send_cancellation(pid, cancel_key) FROM cancel_table; - END; -} - -permutation "s1-register" "s2-lock" "s1-lock" "s2-wrong-cancel-1" "s2-wrong-cancel-2" "s2-cancel" From 36b51d617c196d4882b784a381514fa28e6b30ee Mon Sep 17 00:00:00 2001 From: Naisila Puka <37271756+naisila@users.noreply.github.com> Date: Wed, 23 Aug 2023 10:25:01 +0300 Subject: [PATCH 02/10] PG16 - Throw meaningful error for stats without a name on Citus tables (#7136) Relevant PG commit: https://github.com/postgres/postgres/commit/624aa2a13bd02dd584bb0995c883b5b93b2152df 624aa2a13bd02dd584bb0995c883b5b93b2152df --- src/backend/distributed/commands/statistics.c | 8 +++++++ src/test/regress/expected/pg16.out | 22 +++++++++++++++++++ src/test/regress/sql/pg16.sql | 15 +++++++++++++ 3 files changed, 45 insertions(+) diff --git a/src/backend/distributed/commands/statistics.c b/src/backend/distributed/commands/statistics.c index a65d6c0fe77..e5d5ac8ce47 100644 --- a/src/backend/distributed/commands/statistics.c +++ b/src/backend/distributed/commands/statistics.c @@ -77,6 +77,14 @@ PreprocessCreateStatisticsStmt(Node *node, const char *queryString, EnsureCoordinator(); + if (!(stmt->defnames)) + { + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot create statistics without a name on a " + "Citus table"), + errhint("Consider specifying a name for the statistics"))); + } + QualifyTreeNode((Node *) stmt); Oid statsOid = get_statistics_object_oid(stmt->defnames, true); diff --git a/src/test/regress/expected/pg16.out b/src/test/regress/expected/pg16.out index 37580d8a77b..ae93b8eb81b 100644 --- a/src/test/regress/expected/pg16.out +++ b/src/test/regress/expected/pg16.out @@ -65,6 +65,28 @@ SET citus.log_remote_commands TO OFF; -- only verifying it works and not printing log -- remote commands because it can be flaky VACUUM (ONLY_DATABASE_STATS); +-- Proper error when creating statistics without a name on a Citus table +-- Relevant PG commit: +-- https://github.com/postgres/postgres/commit/624aa2a13bd02dd584bb0995c883b5b93b2152df +CREATE TABLE test_stats ( + a int, + b int +); +SELECT create_distributed_table('test_stats', 'a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE STATISTICS (dependencies) ON a, b FROM test_stats; +ERROR: cannot create statistics without a name on a Citus table +HINT: Consider specifying a name for the statistics +CREATE STATISTICS (ndistinct, dependencies) on a, b from test_stats; +ERROR: cannot create statistics without a name on a Citus table +HINT: Consider specifying a name for the statistics +CREATE STATISTICS (ndistinct, dependencies, mcv) on a, b from test_stats; +ERROR: cannot create statistics without a name on a Citus table +HINT: Consider specifying a name for the statistics \set VERBOSITY terse SET client_min_messages TO ERROR; DROP SCHEMA pg16 CASCADE; diff --git a/src/test/regress/sql/pg16.sql b/src/test/regress/sql/pg16.sql index 29638ac1c0d..e9806c838c9 100644 --- a/src/test/regress/sql/pg16.sql +++ b/src/test/regress/sql/pg16.sql @@ -45,6 +45,21 @@ SET citus.log_remote_commands TO OFF; -- remote commands because it can be flaky VACUUM (ONLY_DATABASE_STATS); +-- Proper error when creating statistics without a name on a Citus table +-- Relevant PG commit: +-- https://github.com/postgres/postgres/commit/624aa2a13bd02dd584bb0995c883b5b93b2152df + +CREATE TABLE test_stats ( + a int, + b int +); + +SELECT create_distributed_table('test_stats', 'a'); + +CREATE STATISTICS (dependencies) ON a, b FROM test_stats; +CREATE STATISTICS (ndistinct, dependencies) on a, b from test_stats; +CREATE STATISTICS (ndistinct, dependencies, mcv) on a, b from test_stats; + \set VERBOSITY terse SET client_min_messages TO ERROR; DROP SCHEMA pg16 CASCADE; From ba55fd67d7971955717caa4e93882768e7fd22b8 Mon Sep 17 00:00:00 2001 From: Marco Slot Date: Wed, 23 Aug 2023 12:47:18 +0200 Subject: [PATCH 03/10] Rename planner_readme.md to README.md (#7139) --- src/backend/distributed/planner/{planner_readme.md => README.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/backend/distributed/planner/{planner_readme.md => README.md} (100%) diff --git a/src/backend/distributed/planner/planner_readme.md b/src/backend/distributed/planner/README.md similarity index 100% rename from src/backend/distributed/planner/planner_readme.md rename to src/backend/distributed/planner/README.md From c73ef405f5bf79ddfaa2a0cabeca9fdf2bf0c050 Mon Sep 17 00:00:00 2001 From: Naisila Puka <37271756+naisila@users.noreply.github.com> Date: Wed, 23 Aug 2023 14:13:56 +0300 Subject: [PATCH 04/10] PG16 - IS JSON predicate and SYSTEM_USER tests (#7137) Support the IS JSON predicate Relevant PG commit: https://github.com/postgres/postgres/commit/6ee30209 SYSTEM_USER Relevant PG commit: https://github.com/postgres/postgres/commit/0823d061 --- src/test/regress/expected/pg16.out | 208 +++++++++++++++++++++++++++++ src/test/regress/sql/pg16.sql | 113 ++++++++++++++++ 2 files changed, 321 insertions(+) diff --git a/src/test/regress/expected/pg16.out b/src/test/regress/expected/pg16.out index ae93b8eb81b..304d7563559 100644 --- a/src/test/regress/expected/pg16.out +++ b/src/test/regress/expected/pg16.out @@ -87,6 +87,214 @@ HINT: Consider specifying a name for the statistics CREATE STATISTICS (ndistinct, dependencies, mcv) on a, b from test_stats; ERROR: cannot create statistics without a name on a Citus table HINT: Consider specifying a name for the statistics +-- Tests for SQL/JSON: support the IS JSON predicate +-- Relevant PG commit: +-- https://github.com/postgres/postgres/commit/6ee30209 +CREATE TABLE test_is_json (id bigserial, js text); +SELECT create_distributed_table('test_is_json', 'id'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO test_is_json(js) VALUES + (NULL), + (''), + ('123'), + ('"aaa "'), + ('true'), + ('null'), + ('[]'), + ('[1, "2", {}]'), + ('{}'), + ('{ "a": 1, "b": null }'), + ('{ "a": 1, "a": null }'), + ('{ "a": 1, "b": [{ "a": 1 }, { "a": 2 }] }'), + ('{ "a": 1, "b": [{ "a": 1, "b": 0, "a": 2 }] }'), + ('aaa'), + ('{a:1}'), + ('["a",]'); +-- run IS JSON predicate in the worker nodes +SELECT + js, + js IS JSON "JSON", + js IS NOT JSON "NOT JSON", + js IS JSON VALUE "VALUE", + js IS JSON OBJECT "OBJECT", + js IS JSON ARRAY "ARRAY", + js IS JSON SCALAR "SCALAR", + js IS JSON WITHOUT UNIQUE KEYS "WITHOUT UNIQUE", + js IS JSON WITH UNIQUE KEYS "WITH UNIQUE" +FROM + test_is_json ORDER BY js; + js | JSON | NOT JSON | VALUE | OBJECT | ARRAY | SCALAR | WITHOUT UNIQUE | WITH UNIQUE +--------------------------------------------------------------------- + | f | t | f | f | f | f | f | f + "aaa " | t | f | t | f | f | t | t | t + 123 | t | f | t | f | f | t | t | t + ["a",] | f | t | f | f | f | f | f | f + [1, "2", {}] | t | f | t | f | t | f | t | t + [] | t | f | t | f | t | f | t | t + aaa | f | t | f | f | f | f | f | f + null | t | f | t | f | f | t | t | t + true | t | f | t | f | f | t | t | t + { "a": 1, "a": null } | t | f | t | t | f | f | t | f + { "a": 1, "b": [{ "a": 1 }, { "a": 2 }] } | t | f | t | t | f | f | t | t + { "a": 1, "b": [{ "a": 1, "b": 0, "a": 2 }] } | t | f | t | t | f | f | t | f + { "a": 1, "b": null } | t | f | t | t | f | f | t | t + {a:1} | f | t | f | f | f | f | f | f + {} | t | f | t | t | f | f | t | t + | | | | | | | | +(16 rows) + +-- pull the data, and run IS JSON predicate in the coordinator +WITH pulled_data as (SELECT js FROM test_is_json OFFSET 0) +SELECT + js, + js IS JSON "IS JSON", + js IS NOT JSON "IS NOT JSON", + js IS JSON VALUE "IS VALUE", + js IS JSON OBJECT "IS OBJECT", + js IS JSON ARRAY "IS ARRAY", + js IS JSON SCALAR "IS SCALAR", + js IS JSON WITHOUT UNIQUE KEYS "WITHOUT UNIQUE", + js IS JSON WITH UNIQUE KEYS "WITH UNIQUE" +FROM + pulled_data ORDER BY js; + js | IS JSON | IS NOT JSON | IS VALUE | IS OBJECT | IS ARRAY | IS SCALAR | WITHOUT UNIQUE | WITH UNIQUE +--------------------------------------------------------------------- + | f | t | f | f | f | f | f | f + "aaa " | t | f | t | f | f | t | t | t + 123 | t | f | t | f | f | t | t | t + ["a",] | f | t | f | f | f | f | f | f + [1, "2", {}] | t | f | t | f | t | f | t | t + [] | t | f | t | f | t | f | t | t + aaa | f | t | f | f | f | f | f | f + null | t | f | t | f | f | t | t | t + true | t | f | t | f | f | t | t | t + { "a": 1, "a": null } | t | f | t | t | f | f | t | f + { "a": 1, "b": [{ "a": 1 }, { "a": 2 }] } | t | f | t | t | f | f | t | t + { "a": 1, "b": [{ "a": 1, "b": 0, "a": 2 }] } | t | f | t | t | f | f | t | f + { "a": 1, "b": null } | t | f | t | t | f | f | t | t + {a:1} | f | t | f | f | f | f | f | f + {} | t | f | t | t | f | f | t | t + | | | | | | | | +(16 rows) + +SELECT + js, + js IS JSON "IS JSON", + js IS NOT JSON "IS NOT JSON", + js IS JSON VALUE "IS VALUE", + js IS JSON OBJECT "IS OBJECT", + js IS JSON ARRAY "IS ARRAY", + js IS JSON SCALAR "IS SCALAR", + js IS JSON WITHOUT UNIQUE KEYS "WITHOUT UNIQUE", + js IS JSON WITH UNIQUE KEYS "WITH UNIQUE" +FROM + (SELECT js::json FROM test_is_json WHERE js IS JSON) foo(js); + js | IS JSON | IS NOT JSON | IS VALUE | IS OBJECT | IS ARRAY | IS SCALAR | WITHOUT UNIQUE | WITH UNIQUE +--------------------------------------------------------------------- + 123 | t | f | t | f | f | t | t | t + "aaa " | t | f | t | f | f | t | t | t + true | t | f | t | f | f | t | t | t + null | t | f | t | f | f | t | t | t + [] | t | f | t | f | t | f | t | t + [1, "2", {}] | t | f | t | f | t | f | t | t + {} | t | f | t | t | f | f | t | t + { "a": 1, "b": null } | t | f | t | t | f | f | t | t + { "a": 1, "a": null } | t | f | t | t | f | f | t | f + { "a": 1, "b": [{ "a": 1 }, { "a": 2 }] } | t | f | t | t | f | f | t | t + { "a": 1, "b": [{ "a": 1, "b": 0, "a": 2 }] } | t | f | t | t | f | f | t | f +(11 rows) + +SELECT + js0, + js IS JSON "IS JSON", + js IS NOT JSON "IS NOT JSON", + js IS JSON VALUE "IS VALUE", + js IS JSON OBJECT "IS OBJECT", + js IS JSON ARRAY "IS ARRAY", + js IS JSON SCALAR "IS SCALAR", + js IS JSON WITHOUT UNIQUE KEYS "WITHOUT UNIQUE", + js IS JSON WITH UNIQUE KEYS "WITH UNIQUE" +FROM + (SELECT js, js::bytea FROM test_is_json WHERE js IS JSON) foo(js0, js); + js0 | IS JSON | IS NOT JSON | IS VALUE | IS OBJECT | IS ARRAY | IS SCALAR | WITHOUT UNIQUE | WITH UNIQUE +--------------------------------------------------------------------- + 123 | t | f | t | f | f | t | t | t + "aaa " | t | f | t | f | f | t | t | t + true | t | f | t | f | f | t | t | t + null | t | f | t | f | f | t | t | t + [] | t | f | t | f | t | f | t | t + [1, "2", {}] | t | f | t | f | t | f | t | t + {} | t | f | t | t | f | f | t | t + { "a": 1, "b": null } | t | f | t | t | f | f | t | t + { "a": 1, "a": null } | t | f | t | t | f | f | t | f + { "a": 1, "b": [{ "a": 1 }, { "a": 2 }] } | t | f | t | t | f | f | t | t + { "a": 1, "b": [{ "a": 1, "b": 0, "a": 2 }] } | t | f | t | t | f | f | t | f +(11 rows) + +SELECT + js, + js IS JSON "IS JSON", + js IS NOT JSON "IS NOT JSON", + js IS JSON VALUE "IS VALUE", + js IS JSON OBJECT "IS OBJECT", + js IS JSON ARRAY "IS ARRAY", + js IS JSON SCALAR "IS SCALAR", + js IS JSON WITHOUT UNIQUE KEYS "WITHOUT UNIQUE", + js IS JSON WITH UNIQUE KEYS "WITH UNIQUE" +FROM + (SELECT js::jsonb FROM test_is_json WHERE js IS JSON) foo(js); + js | IS JSON | IS NOT JSON | IS VALUE | IS OBJECT | IS ARRAY | IS SCALAR | WITHOUT UNIQUE | WITH UNIQUE +--------------------------------------------------------------------- + 123 | t | f | t | f | f | t | t | t + "aaa " | t | f | t | f | f | t | t | t + true | t | f | t | f | f | t | t | t + null | t | f | t | f | f | t | t | t + [] | t | f | t | f | t | f | t | t + [1, "2", {}] | t | f | t | f | t | f | t | t + {} | t | f | t | t | f | f | t | t + {"a": 1, "b": null} | t | f | t | t | f | f | t | t + {"a": null} | t | f | t | t | f | f | t | t + {"a": 1, "b": [{"a": 1}, {"a": 2}]} | t | f | t | t | f | f | t | t + {"a": 1, "b": [{"a": 2, "b": 0}]} | t | f | t | t | f | f | t | t +(11 rows) + +-- SYSTEM_USER +-- Relevant PG commit: +-- https://github.com/postgres/postgres/commit/0823d061 +CREATE TABLE table_name_for_view(id int, val_1 text); +SELECT create_distributed_table('table_name_for_view', 'id'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO table_name_for_view VALUES (1, 'test'); +-- define a view that uses SYSTEM_USER keyword +CREATE VIEW prop_view_1 AS + SELECT *, SYSTEM_USER AS su FROM table_name_for_view; +SELECT * FROM prop_view_1; + id | val_1 | su +--------------------------------------------------------------------- + 1 | test | +(1 row) + +-- check definition with SYSTEM_USER is correctly propagated to workers +\c - - - :worker_1_port +SELECT pg_get_viewdef('pg16.prop_view_1', true); + pg_get_viewdef +--------------------------------------------------------------------- + SELECT id, + + val_1, + + SYSTEM_USER AS su + + FROM pg16.table_name_for_view; +(1 row) + +\c - - - :master_port +SET search_path TO pg16; \set VERBOSITY terse SET client_min_messages TO ERROR; DROP SCHEMA pg16 CASCADE; diff --git a/src/test/regress/sql/pg16.sql b/src/test/regress/sql/pg16.sql index e9806c838c9..abf29d9329b 100644 --- a/src/test/regress/sql/pg16.sql +++ b/src/test/regress/sql/pg16.sql @@ -60,6 +60,119 @@ CREATE STATISTICS (dependencies) ON a, b FROM test_stats; CREATE STATISTICS (ndistinct, dependencies) on a, b from test_stats; CREATE STATISTICS (ndistinct, dependencies, mcv) on a, b from test_stats; +-- Tests for SQL/JSON: support the IS JSON predicate +-- Relevant PG commit: +-- https://github.com/postgres/postgres/commit/6ee30209 + +CREATE TABLE test_is_json (id bigserial, js text); +SELECT create_distributed_table('test_is_json', 'id'); + +INSERT INTO test_is_json(js) VALUES + (NULL), + (''), + ('123'), + ('"aaa "'), + ('true'), + ('null'), + ('[]'), + ('[1, "2", {}]'), + ('{}'), + ('{ "a": 1, "b": null }'), + ('{ "a": 1, "a": null }'), + ('{ "a": 1, "b": [{ "a": 1 }, { "a": 2 }] }'), + ('{ "a": 1, "b": [{ "a": 1, "b": 0, "a": 2 }] }'), + ('aaa'), + ('{a:1}'), + ('["a",]'); + +-- run IS JSON predicate in the worker nodes +SELECT + js, + js IS JSON "JSON", + js IS NOT JSON "NOT JSON", + js IS JSON VALUE "VALUE", + js IS JSON OBJECT "OBJECT", + js IS JSON ARRAY "ARRAY", + js IS JSON SCALAR "SCALAR", + js IS JSON WITHOUT UNIQUE KEYS "WITHOUT UNIQUE", + js IS JSON WITH UNIQUE KEYS "WITH UNIQUE" +FROM + test_is_json ORDER BY js; + +-- pull the data, and run IS JSON predicate in the coordinator +WITH pulled_data as (SELECT js FROM test_is_json OFFSET 0) +SELECT + js, + js IS JSON "IS JSON", + js IS NOT JSON "IS NOT JSON", + js IS JSON VALUE "IS VALUE", + js IS JSON OBJECT "IS OBJECT", + js IS JSON ARRAY "IS ARRAY", + js IS JSON SCALAR "IS SCALAR", + js IS JSON WITHOUT UNIQUE KEYS "WITHOUT UNIQUE", + js IS JSON WITH UNIQUE KEYS "WITH UNIQUE" +FROM + pulled_data ORDER BY js; + +SELECT + js, + js IS JSON "IS JSON", + js IS NOT JSON "IS NOT JSON", + js IS JSON VALUE "IS VALUE", + js IS JSON OBJECT "IS OBJECT", + js IS JSON ARRAY "IS ARRAY", + js IS JSON SCALAR "IS SCALAR", + js IS JSON WITHOUT UNIQUE KEYS "WITHOUT UNIQUE", + js IS JSON WITH UNIQUE KEYS "WITH UNIQUE" +FROM + (SELECT js::json FROM test_is_json WHERE js IS JSON) foo(js); + +SELECT + js0, + js IS JSON "IS JSON", + js IS NOT JSON "IS NOT JSON", + js IS JSON VALUE "IS VALUE", + js IS JSON OBJECT "IS OBJECT", + js IS JSON ARRAY "IS ARRAY", + js IS JSON SCALAR "IS SCALAR", + js IS JSON WITHOUT UNIQUE KEYS "WITHOUT UNIQUE", + js IS JSON WITH UNIQUE KEYS "WITH UNIQUE" +FROM + (SELECT js, js::bytea FROM test_is_json WHERE js IS JSON) foo(js0, js); + +SELECT + js, + js IS JSON "IS JSON", + js IS NOT JSON "IS NOT JSON", + js IS JSON VALUE "IS VALUE", + js IS JSON OBJECT "IS OBJECT", + js IS JSON ARRAY "IS ARRAY", + js IS JSON SCALAR "IS SCALAR", + js IS JSON WITHOUT UNIQUE KEYS "WITHOUT UNIQUE", + js IS JSON WITH UNIQUE KEYS "WITH UNIQUE" +FROM + (SELECT js::jsonb FROM test_is_json WHERE js IS JSON) foo(js); + +-- SYSTEM_USER +-- Relevant PG commit: +-- https://github.com/postgres/postgres/commit/0823d061 + +CREATE TABLE table_name_for_view(id int, val_1 text); +SELECT create_distributed_table('table_name_for_view', 'id'); +INSERT INTO table_name_for_view VALUES (1, 'test'); + +-- define a view that uses SYSTEM_USER keyword +CREATE VIEW prop_view_1 AS + SELECT *, SYSTEM_USER AS su FROM table_name_for_view; +SELECT * FROM prop_view_1; + +-- check definition with SYSTEM_USER is correctly propagated to workers +\c - - - :worker_1_port +SELECT pg_get_viewdef('pg16.prop_view_1', true); + +\c - - - :master_port +SET search_path TO pg16; + \set VERBOSITY terse SET client_min_messages TO ERROR; DROP SCHEMA pg16 CASCADE; From b8c493f2c44efc1a19895fcadf5291b8285add7c Mon Sep 17 00:00:00 2001 From: Naisila Puka <37271756+naisila@users.noreply.github.com> Date: Wed, 23 Aug 2023 20:15:54 +0300 Subject: [PATCH 05/10] PG16 - Add GENERIC_PLAN option to EXPLAIN (#7141) --- .../distributed/planner/multi_explain.c | 6 +++ src/test/regress/expected/pg16.out | 40 +++++++++++++++++++ src/test/regress/sql/pg16.sql | 16 ++++++++ 3 files changed, 62 insertions(+) diff --git a/src/backend/distributed/planner/multi_explain.c b/src/backend/distributed/planner/multi_explain.c index ec0282840e0..94d125f41af 100644 --- a/src/backend/distributed/planner/multi_explain.c +++ b/src/backend/distributed/planner/multi_explain.c @@ -992,12 +992,18 @@ BuildRemoteExplainQuery(char *queryString, ExplainState *es) appendStringInfo(explainQuery, "EXPLAIN (ANALYZE %s, VERBOSE %s, " "COSTS %s, BUFFERS %s, WAL %s, " +#if PG_VERSION_NUM >= PG_VERSION_16 + "GENERIC_PLAN %s, " +#endif "TIMING %s, SUMMARY %s, FORMAT %s) %s", es->analyze ? "TRUE" : "FALSE", es->verbose ? "TRUE" : "FALSE", es->costs ? "TRUE" : "FALSE", es->buffers ? "TRUE" : "FALSE", es->wal ? "TRUE" : "FALSE", +#if PG_VERSION_NUM >= PG_VERSION_16 + es->generic ? "TRUE" : "FALSE", +#endif es->timing ? "TRUE" : "FALSE", es->summary ? "TRUE" : "FALSE", formatStr, diff --git a/src/test/regress/expected/pg16.out b/src/test/regress/expected/pg16.out index 304d7563559..4a8703ad70b 100644 --- a/src/test/regress/expected/pg16.out +++ b/src/test/regress/expected/pg16.out @@ -65,6 +65,46 @@ SET citus.log_remote_commands TO OFF; -- only verifying it works and not printing log -- remote commands because it can be flaky VACUUM (ONLY_DATABASE_STATS); +-- New GENERIC_PLAN option in EXPLAIN +-- Relevant PG commit: +-- https://github.com/postgres/postgres/commit/3c05284 +CREATE TABLE tenk1 ( + unique1 int4, + unique2 int4, + thousand int4 +); +SELECT create_distributed_table('tenk1', 'unique1'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SET citus.log_remote_commands TO on; +EXPLAIN (GENERIC_PLAN) SELECT unique1 FROM tenk1 WHERE thousand = 1000; +NOTICE: issuing BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;SELECT assign_distributed_transaction_id(xx, xx, 'xxxxxxx'); +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing SAVEPOINT citus_explain_savepoint +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing EXPLAIN (ANALYZE FALSE, VERBOSE FALSE, COSTS TRUE, BUFFERS FALSE, WAL FALSE, GENERIC_PLAN TRUE, TIMING FALSE, SUMMARY FALSE, FORMAT TEXT) SELECT unique1 FROM pg16.tenk1_950001 tenk1 WHERE (thousand OPERATOR(pg_catalog.=) 1000) +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ROLLBACK TO SAVEPOINT citus_explain_savepoint +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing COMMIT +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx + QUERY PLAN +--------------------------------------------------------------------- + Custom Scan (Citus Adaptive) (cost=0.00..0.00 rows=0 width=0) + Task Count: 1 + Tasks Shown: All + -> Task + Node: host=localhost port=xxxxx dbname=regression + -> Seq Scan on tenk1_950001 tenk1 (cost=0.00..35.50 rows=10 width=4) + Filter: (thousand = 1000) +(7 rows) + +EXPLAIN (GENERIC_PLAN, ANALYZE) SELECT unique1 FROM tenk1 WHERE thousand = 1000; +ERROR: EXPLAIN options ANALYZE and GENERIC_PLAN cannot be used together +SET citus.log_remote_commands TO off; -- Proper error when creating statistics without a name on a Citus table -- Relevant PG commit: -- https://github.com/postgres/postgres/commit/624aa2a13bd02dd584bb0995c883b5b93b2152df diff --git a/src/test/regress/sql/pg16.sql b/src/test/regress/sql/pg16.sql index abf29d9329b..85df9eabb0a 100644 --- a/src/test/regress/sql/pg16.sql +++ b/src/test/regress/sql/pg16.sql @@ -45,6 +45,22 @@ SET citus.log_remote_commands TO OFF; -- remote commands because it can be flaky VACUUM (ONLY_DATABASE_STATS); +-- New GENERIC_PLAN option in EXPLAIN +-- Relevant PG commit: +-- https://github.com/postgres/postgres/commit/3c05284 + +CREATE TABLE tenk1 ( + unique1 int4, + unique2 int4, + thousand int4 +); +SELECT create_distributed_table('tenk1', 'unique1'); + +SET citus.log_remote_commands TO on; +EXPLAIN (GENERIC_PLAN) SELECT unique1 FROM tenk1 WHERE thousand = 1000; +EXPLAIN (GENERIC_PLAN, ANALYZE) SELECT unique1 FROM tenk1 WHERE thousand = 1000; +SET citus.log_remote_commands TO off; + -- Proper error when creating statistics without a name on a Citus table -- Relevant PG commit: -- https://github.com/postgres/postgres/commit/624aa2a13bd02dd584bb0995c883b5b93b2152df From 553780e3f17e0dcd4a7194f5d77da02bb64241ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCrkan=20=C4=B0ndibay?= Date: Thu, 24 Aug 2023 10:30:33 +0300 Subject: [PATCH 06/10] Removes ubuntu/bionic from packaging pipelines (#7142) DESCRIPTION: Removes ubuntu/bionic from packaging pipelines Since pg16 beta is not available for ubuntu/bionic and ubuntu/bionic support is EOL, I need to remove this os from pipeline https://ubuntu.com/blog/ubuntu-18-04-eol-for-devices Additionally, added concurrency support for GH Actions Packaging pipeline --- .github/workflows/packaging-test-pipelines.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/packaging-test-pipelines.yml b/.github/workflows/packaging-test-pipelines.yml index f2d568e4cbf..c66c5b4f700 100644 --- a/.github/workflows/packaging-test-pipelines.yml +++ b/.github/workflows/packaging-test-pipelines.yml @@ -6,6 +6,10 @@ on: workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: get_postgres_versions_from_file: @@ -119,7 +123,6 @@ jobs: - debian-buster-all - debian-bookworm-all - debian-bullseye-all - - ubuntu-bionic-all - ubuntu-focal-all - ubuntu-jammy-all - ubuntu-kinetic-all From 8d3a06c1c742002ab02fc53ea3e2e8322bc2f149 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCrkan=20=C4=B0ndibay?= Date: Thu, 24 Aug 2023 14:43:19 +0300 Subject: [PATCH 07/10] Adds grant/revoke privileges on database propagation (#7109) DESCRIPTION: Adds grant/revoke propagation support for database privileges Following the implementation of support for granting and revoking database privileges, certain tests that issued grants for worker nodes experienced failures. These ones are fixed in this PR as well. --- src/backend/distributed/commands/database.c | 40 + .../commands/distribute_object_ops.c | 17 + .../deparser/deparse_database_stmts.c | 76 ++ src/include/distributed/commands.h | 4 + src/include/distributed/deparser.h | 1 + .../grant_on_database_propagation.out | 967 ++++++++++++++++++ .../regress/expected/intermediate_results.out | 7 - .../expected/multi_deparse_shard_query.out | 48 +- .../expected/multi_deparse_shard_query_0.out | 50 +- .../expected/schema_based_sharding.out | 14 - src/test/regress/multi_1_schedule | 2 + .../sql/grant_on_database_propagation.sql | 378 +++++++ src/test/regress/sql/intermediate_results.sql | 1 - .../regress/sql/multi_deparse_shard_query.sql | 6 + .../regress/sql/schema_based_sharding.sql | 2 - 15 files changed, 1544 insertions(+), 69 deletions(-) create mode 100644 src/test/regress/expected/grant_on_database_propagation.out create mode 100644 src/test/regress/sql/grant_on_database_propagation.sql diff --git a/src/backend/distributed/commands/database.c b/src/backend/distributed/commands/database.c index 208d570eb07..ce46b299574 100644 --- a/src/backend/distributed/commands/database.c +++ b/src/backend/distributed/commands/database.c @@ -31,6 +31,8 @@ static AlterOwnerStmt * RecreateAlterDatabaseOwnerStmt(Oid databaseOid); static Oid get_database_owner(Oid db_oid); +List * PreprocessGrantOnDatabaseStmt(Node *node, const char *queryString, + ProcessUtilityContext processUtilityContext); /* controlled via GUC */ bool EnableAlterDatabaseOwner = true; @@ -107,3 +109,41 @@ get_database_owner(Oid db_oid) return dba; } + + +/* + * PreprocessGrantOnDatabaseStmt is executed before the statement is applied to the local + * postgres instance. + * + * In this stage we can prepare the commands that need to be run on all workers to grant + * on databases. + */ +List * +PreprocessGrantOnDatabaseStmt(Node *node, const char *queryString, + ProcessUtilityContext processUtilityContext) +{ + if (!ShouldPropagate()) + { + return NIL; + } + + GrantStmt *stmt = castNode(GrantStmt, node); + Assert(stmt->objtype == OBJECT_DATABASE); + + List *databaseList = stmt->objects; + + if (list_length(databaseList) == 0) + { + return NIL; + } + + EnsureCoordinator(); + + char *sql = DeparseTreeNode((Node *) stmt); + + List *commands = list_make3(DISABLE_DDL_PROPAGATION, + (void *) sql, + ENABLE_DDL_PROPAGATION); + + return NodeDDLTaskList(NON_COORDINATOR_NODES, commands); +} diff --git a/src/backend/distributed/commands/distribute_object_ops.c b/src/backend/distributed/commands/distribute_object_ops.c index 3442b07f246..607a6db314c 100644 --- a/src/backend/distributed/commands/distribute_object_ops.c +++ b/src/backend/distributed/commands/distribute_object_ops.c @@ -432,6 +432,18 @@ static DistributeObjectOps Database_AlterOwner = { .address = AlterDatabaseOwnerObjectAddress, .markDistributed = false, }; + +static DistributeObjectOps Database_Grant = { + .deparse = DeparseGrantOnDatabaseStmt, + .qualify = NULL, + .preprocess = PreprocessGrantOnDatabaseStmt, + .postprocess = NULL, + .objectType = OBJECT_DATABASE, + .operationType = DIST_OPS_ALTER, + .address = NULL, + .markDistributed = false, +}; + static DistributeObjectOps Domain_Alter = { .deparse = DeparseAlterDomainStmt, .qualify = QualifyAlterDomainStmt, @@ -1911,6 +1923,11 @@ GetDistributeObjectOps(Node *node) return &Routine_Grant; } + case OBJECT_DATABASE: + { + return &Database_Grant; + } + default: { return &Any_Grant; diff --git a/src/backend/distributed/deparser/deparse_database_stmts.c b/src/backend/distributed/deparser/deparse_database_stmts.c index b72787993c2..24f61887711 100644 --- a/src/backend/distributed/deparser/deparse_database_stmts.c +++ b/src/backend/distributed/deparser/deparse_database_stmts.c @@ -49,3 +49,79 @@ AppendAlterDatabaseOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt) quote_identifier(strVal((String *) stmt->object)), RoleSpecString(stmt->newowner, true)); } + + +static void +AppendGrantDatabases(StringInfo buf, GrantStmt *stmt) +{ + ListCell *cell = NULL; + appendStringInfo(buf, " ON DATABASE "); + + foreach(cell, stmt->objects) + { + char *database = strVal(lfirst(cell)); + appendStringInfoString(buf, quote_identifier(database)); + if (cell != list_tail(stmt->objects)) + { + appendStringInfo(buf, ", "); + } + } +} + + +static void +AppendGrantOnDatabaseStmt(StringInfo buf, GrantStmt *stmt) +{ + Assert(stmt->objtype == OBJECT_DATABASE); + + appendStringInfo(buf, "%s ", stmt->is_grant ? "GRANT" : "REVOKE"); + + if (!stmt->is_grant && stmt->grant_option) + { + appendStringInfo(buf, "GRANT OPTION FOR "); + } + + AppendGrantPrivileges(buf, stmt); + + AppendGrantDatabases(buf, stmt); + + AppendGrantGrantees(buf, stmt); + + if (stmt->is_grant && stmt->grant_option) + { + appendStringInfo(buf, " WITH GRANT OPTION"); + } + if (!stmt->is_grant) + { + if (stmt->behavior == DROP_RESTRICT) + { + appendStringInfo(buf, " RESTRICT"); + } + else if (stmt->behavior == DROP_CASCADE) + { + appendStringInfo(buf, " CASCADE"); + } + } + + if (stmt->grantor) + { + appendStringInfo(buf, " GRANTED BY %s", RoleSpecString(stmt->grantor, true)); + } + + appendStringInfo(buf, ";"); +} + + +char * +DeparseGrantOnDatabaseStmt(Node *node) +{ + GrantStmt *stmt = castNode(GrantStmt, node); + Assert(stmt->objtype == OBJECT_DATABASE); + + StringInfoData str = { 0 }; + initStringInfo(&str); + + AppendGrantOnDatabaseStmt(&str, stmt); + + return str.data; +} diff --git a/src/include/distributed/commands.h b/src/include/distributed/commands.h index 5c2bf2f4252..57df90708f7 100644 --- a/src/include/distributed/commands.h +++ b/src/include/distributed/commands.h @@ -220,6 +220,9 @@ extern List * AlterDatabaseOwnerObjectAddress(Node *node, bool missing_ok, bool isPostprocess); extern List * DatabaseOwnerDDLCommands(const ObjectAddress *address); +extern List * PreprocessGrantOnDatabaseStmt(Node *node, const char *queryString, + ProcessUtilityContext processUtilityContext); + /* domain.c - forward declarations */ extern List * CreateDomainStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess); @@ -235,6 +238,7 @@ extern List * RenameDomainStmtObjectAddress(Node *node, bool missing_ok, bool extern CreateDomainStmt * RecreateDomainStmt(Oid domainOid); extern Oid get_constraint_typid(Oid conoid); + /* extension.c - forward declarations */ extern bool IsDropCitusExtensionStmt(Node *parsetree); extern List * GetDependentFDWsToExtension(Oid extensionId); diff --git a/src/include/distributed/deparser.h b/src/include/distributed/deparser.h index 7390b486c68..5eb751d23d3 100644 --- a/src/include/distributed/deparser.h +++ b/src/include/distributed/deparser.h @@ -210,6 +210,7 @@ extern char * DeparseAlterExtensionStmt(Node *stmt); /* forward declarations for deparse_database_stmts.c */ extern char * DeparseAlterDatabaseOwnerStmt(Node *node); +extern char * DeparseGrantOnDatabaseStmt(Node *node); /* forward declaration for deparse_publication_stmts.c */ extern char * DeparseCreatePublicationStmt(Node *stmt); diff --git a/src/test/regress/expected/grant_on_database_propagation.out b/src/test/regress/expected/grant_on_database_propagation.out new file mode 100644 index 00000000000..2fd135314bf --- /dev/null +++ b/src/test/regress/expected/grant_on_database_propagation.out @@ -0,0 +1,967 @@ +-- Public role has connect,temp,temporary privileges on database +-- To test these scenarios, we need to revoke these privileges from public role +-- since public role privileges are inherited by new roles/users +revoke connect,temp,temporary on database regression from public; +CREATE SCHEMA grant_on_database_propagation; +SET search_path TO grant_on_database_propagation; +-- test grant/revoke CREATE privilege propagation on database +create user myuser; +grant create on database regression to myuser; +select has_database_privilege('myuser','regression', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +\c - - - :worker_1_port; +select has_database_privilege('myuser','regression', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +\c - - - :master_port +revoke create on database regression from myuser; +select has_database_privilege('myuser','regression', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +\c - - - :worker_1_port +select has_database_privilege('myuser','regression', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +\c - - - :master_port +drop user myuser; +--------------------------------------------------------------------- +-- test grant/revoke CONNECT privilege propagation on database +create user myuser; +grant CONNECT on database regression to myuser; +select has_database_privilege('myuser','regression', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +\c - - - :worker_1_port; +select has_database_privilege('myuser','regression', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +\c - - - :master_port +revoke connect on database regression from myuser; +select has_database_privilege('myuser','regression', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +\c - - - :worker_1_port +select has_database_privilege('myuser','regression', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +\c - - - :master_port +drop user myuser; +--------------------------------------------------------------------- +-- test grant/revoke TEMP privilege propagation on database +create user myuser; +-- test grant/revoke temp on database +grant TEMP on database regression to myuser; +select has_database_privilege('myuser','regression', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +\c - - - :worker_1_port; +select has_database_privilege('myuser','regression', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +\c - - - :master_port +revoke TEMP on database regression from myuser; +select has_database_privilege('myuser','regression', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +\c - - - :worker_1_port +select has_database_privilege('myuser','regression', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +\c - - - :master_port +drop user myuser; +--------------------------------------------------------------------- +-- test temporary privilege on database +create user myuser; +-- test grant/revoke temporary on database +grant TEMPORARY on database regression to myuser; +select has_database_privilege('myuser','regression', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +\c - - - :worker_1_port; +select has_database_privilege('myuser','regression', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +\c - - - :master_port +revoke TEMPORARY on database regression from myuser; +select has_database_privilege('myuser','regression', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +\c - - - :worker_1_port +select has_database_privilege('myuser','regression', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +\c - - - :master_port +drop user myuser; +--------------------------------------------------------------------- +-- test ALL privileges with ALL statement on database +create user myuser; +grant ALL on database regression to myuser; +select has_database_privilege('myuser','regression', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','regression', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','regression', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','regression', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +\c - - - :worker_1_port; +select has_database_privilege('myuser','regression', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','regression', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','regression', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','regression', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +\c - - - :master_port +revoke ALL on database regression from myuser; +select has_database_privilege('myuser','regression', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser','regression', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser','regression', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser','regression', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +\c - - - :worker_1_port +select has_database_privilege('myuser','regression', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser','regression', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser','regression', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser','regression', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +\c - - - :master_port +drop user myuser; +--------------------------------------------------------------------- +-- test CREATE,CONNECT,TEMP,TEMPORARY privileges one by one on database +create user myuser; +grant CREATE,CONNECT,TEMP,TEMPORARY on database regression to myuser; +select has_database_privilege('myuser','regression', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','regression', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','regression', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','regression', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +\c - - - :worker_1_port; +select has_database_privilege('myuser','regression', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','regression', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','regression', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','regression', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +\c - - - :master_port +RESET ROLE; +revoke CREATE,CONNECT,TEMP,TEMPORARY on database regression from myuser; +select has_database_privilege('myuser','regression', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser','regression', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser','regression', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser','regression', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +\c - - - :worker_1_port +select has_database_privilege('myuser','regression', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser','regression', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser','regression', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser','regression', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +\c - - - :master_port +drop user myuser; +--------------------------------------------------------------------- +-- test CREATE,CONNECT,TEMP,TEMPORARY privileges one by one on database with grant option +create user myuser; +create user myuser_1; +grant CREATE,CONNECT,TEMP,TEMPORARY on database regression to myuser; +set role myuser; +--here since myuser does not have grant option, it should fail +grant CREATE,CONNECT,TEMP,TEMPORARY on database regression to myuser_1; +WARNING: no privileges were granted for "regression" +select has_database_privilege('myuser_1','regression', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser_1','regression', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser_1','regression', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser_1','regression', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +\c - - - :worker_1_port +select has_database_privilege('myuser_1','regression', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser_1','regression', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser_1','regression', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser_1','regression', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +\c - - - :master_port +RESET ROLE; +grant CREATE,CONNECT,TEMP,TEMPORARY on database regression to myuser with grant option; +set role myuser; +--here since myuser have grant option, it should succeed +grant CREATE,CONNECT,TEMP,TEMPORARY on database regression to myuser_1 granted by myuser; +select has_database_privilege('myuser_1','regression', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser_1','regression', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser_1','regression', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser_1','regression', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +\c - - - :worker_1_port +select has_database_privilege('myuser_1','regression', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser_1','regression', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser_1','regression', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser_1','regression', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +\c - - - :master_port +RESET ROLE; +--below test should fail and should throw an error since myuser_1 still have the dependent privileges +revoke CREATE,CONNECT,TEMP,TEMPORARY on database regression from myuser restrict; +ERROR: dependent privileges exist +HINT: Use CASCADE to revoke them too. +--below test should fail and should throw an error since myuser_1 still have the dependent privileges +revoke grant option for CREATE,CONNECT,TEMP,TEMPORARY on database regression from myuser restrict ; +ERROR: dependent privileges exist +HINT: Use CASCADE to revoke them too. +--below test should succeed and should not throw any error since myuser_1 privileges are revoked with cascade +revoke grant option for CREATE,CONNECT,TEMP,TEMPORARY on database regression from myuser cascade ; +--here we test if myuser still have the privileges after revoke grant option for +select has_database_privilege('myuser','regression', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','regression', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','regression', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','regression', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +\c - - - :worker_1_port +select has_database_privilege('myuser','regression', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','regression', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','regression', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','regression', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +\c - - - :master_port +reset role; +revoke CREATE,CONNECT,TEMP,TEMPORARY on database regression from myuser; +revoke CREATE,CONNECT,TEMP,TEMPORARY on database regression from myuser_1; +drop user myuser_1; +drop user myuser; +--------------------------------------------------------------------- +-- test CREATE,CONNECT,TEMP,TEMPORARY privileges one by one on database multi database +-- and multi user +create user myuser; +create user myuser_1; +create database test_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. +SELECT result FROM run_command_on_workers($$create database test_db$$); + result +--------------------------------------------------------------------- + CREATE DATABASE + CREATE DATABASE +(2 rows) + +revoke connect,temp,temporary on database test_db from public; +grant CREATE,CONNECT,TEMP,TEMPORARY on database regression,test_db to myuser,myuser_1; +select has_database_privilege('myuser','regression', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','regression', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','regression', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','regression', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','test_db', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','test_db', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','test_db', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','test_db', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser_1','regression', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser_1','regression', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser_1','regression', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser_1','regression', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser_1','test_db', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser_1','test_db', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser_1','test_db', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser_1','test_db', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +\c - - - :worker_1_port +select has_database_privilege('myuser','regression', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','regression', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','regression', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','regression', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','test_db', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','test_db', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','test_db', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser','test_db', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser_1','regression', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser_1','regression', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser_1','regression', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser_1','regression', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser_1','test_db', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser_1','test_db', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser_1','test_db', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +select has_database_privilege('myuser_1','test_db', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + t +(1 row) + +\c - - - :master_port +RESET ROLE; +--below test should fail and should throw an error +revoke CREATE,CONNECT,TEMP,TEMPORARY on database regression,test_db from myuser ; +--below test should succeed and should not throw any error +revoke CREATE,CONNECT,TEMP,TEMPORARY on database regression,test_db from myuser_1; +--below test should succeed and should not throw any error +revoke CREATE,CONNECT,TEMP,TEMPORARY on database regression,test_db from myuser cascade; +select has_database_privilege('myuser','regression', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser','regression', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser','regression', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser','regression', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser','test_db', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser','test_db', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser','test_db', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser','test_db', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser_1','regression', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser_1','regression', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser_1','regression', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser_1','regression', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser_1','test_db', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser_1','test_db', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser_1','test_db', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser_1','test_db', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +\c - - - :worker_1_port +select has_database_privilege('myuser','regression', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser','regression', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser','regression', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser','regression', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser','test_db', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser','test_db', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser','test_db', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser','test_db', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser_1','regression', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser_1','regression', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser_1','regression', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser_1','regression', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser_1','test_db', 'CREATE'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser_1','test_db', 'CONNECT'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser_1','test_db', 'TEMP'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +select has_database_privilege('myuser_1','test_db', 'TEMPORARY'); + has_database_privilege +--------------------------------------------------------------------- + f +(1 row) + +\c - - - :master_port +reset role; +drop user myuser_1; +drop user myuser; +drop database test_db; +SELECT result FROM run_command_on_workers($$drop database test_db$$); + result +--------------------------------------------------------------------- + DROP DATABASE + DROP DATABASE +(2 rows) + +--------------------------------------------------------------------- +-- rollbacks public role database privileges to original state +grant connect,temp,temporary on database regression to public; +SET client_min_messages TO ERROR; +DROP SCHEMA grant_on_database_propagation CASCADE; +--------------------------------------------------------------------- diff --git a/src/test/regress/expected/intermediate_results.out b/src/test/regress/expected/intermediate_results.out index 8b2e200f733..140b7aacf2e 100644 --- a/src/test/regress/expected/intermediate_results.out +++ b/src/test/regress/expected/intermediate_results.out @@ -572,13 +572,6 @@ WARNING: Query could not find the intermediate result file "squares_2", it was -- test refreshing mat views SET client_min_messages TO ERROR; CREATE USER some_other_user; -SELECT run_command_on_workers($$GRANT ALL ON DATABASE regression TO some_other_user;$$); - run_command_on_workers ---------------------------------------------------------------------- - (localhost,57637,t,GRANT) - (localhost,57638,t,GRANT) -(2 rows) - GRANT ALL ON DATABASE regression TO some_other_user; RESET client_min_messages; \c - some_other_user diff --git a/src/test/regress/expected/multi_deparse_shard_query.out b/src/test/regress/expected/multi_deparse_shard_query.out index b24c0e4cb7f..4657db10d24 100644 --- a/src/test/regress/expected/multi_deparse_shard_query.out +++ b/src/test/regress/expected/multi_deparse_shard_query.out @@ -12,6 +12,8 @@ SELECT substring(:'server_version', '\d+')::int >= 15 AS server_version_ge_15; t (1 row) +CREATE SCHEMA multi_deparse_shard_query; +SET search_path TO multi_deparse_shard_query; SET citus.next_shard_id TO 13100000; SET citus.shard_replication_factor TO 1; CREATE FUNCTION deparse_shard_query_test(text) @@ -74,7 +76,7 @@ SELECT deparse_shard_query_test(' INSERT INTO raw_events_1 SELECT * FROM raw_events_1; '); -INFO: query: INSERT INTO public.raw_events_1 (tenant_id, value_1, value_2, value_3, value_4, value_5, value_6, value_7, event_at) SELECT raw_events_1_1.tenant_id, raw_events_1_1.value_1, raw_events_1_1.value_2, raw_events_1_1.value_3, raw_events_1_1.value_4, raw_events_1_1.value_5, raw_events_1_1.value_6, raw_events_1_1.value_7, raw_events_1_1.event_at FROM public.raw_events_1 raw_events_1_1 +INFO: query: INSERT INTO multi_deparse_shard_query.raw_events_1 (tenant_id, value_1, value_2, value_3, value_4, value_5, value_6, value_7, event_at) SELECT raw_events_1_1.tenant_id, raw_events_1_1.value_1, raw_events_1_1.value_2, raw_events_1_1.value_3, raw_events_1_1.value_4, raw_events_1_1.value_5, raw_events_1_1.value_6, raw_events_1_1.value_7, raw_events_1_1.event_at FROM multi_deparse_shard_query.raw_events_1 raw_events_1_1 deparse_shard_query_test --------------------------------------------------------------------- @@ -87,7 +89,7 @@ SELECT FROM raw_events_1; '); -INFO: query: INSERT INTO public.raw_events_1 (tenant_id, value_4, value_6, event_at) SELECT raw_events_1_1.tenant_id, raw_events_1_1.value_4, 10 AS value_6, (now())::date AS event_at FROM public.raw_events_1 raw_events_1_1 +INFO: query: INSERT INTO multi_deparse_shard_query.raw_events_1 (tenant_id, value_4, value_6, event_at) SELECT raw_events_1_1.tenant_id, raw_events_1_1.value_4, 10 AS value_6, (now())::date AS event_at FROM multi_deparse_shard_query.raw_events_1 raw_events_1_1 deparse_shard_query_test --------------------------------------------------------------------- @@ -101,7 +103,7 @@ SELECT FROM raw_events_1; '); -INFO: query: INSERT INTO public.raw_events_1 (tenant_id, value_2, value_4, value_5, value_6, event_at) SELECT raw_events_1_1.tenant_id, (raw_events_1_1.value_5)::integer AS value_5, raw_events_1_1.value_4, (raw_events_1_1.value_2)::text AS value_2, 10 AS value_6, (now())::date AS event_at FROM public.raw_events_1 raw_events_1_1 +INFO: query: INSERT INTO multi_deparse_shard_query.raw_events_1 (tenant_id, value_2, value_4, value_5, value_6, event_at) SELECT raw_events_1_1.tenant_id, (raw_events_1_1.value_5)::integer AS value_5, raw_events_1_1.value_4, (raw_events_1_1.value_2)::text AS value_2, 10 AS value_6, (now())::date AS event_at FROM multi_deparse_shard_query.raw_events_1 raw_events_1_1 deparse_shard_query_test --------------------------------------------------------------------- @@ -115,7 +117,7 @@ SELECT FROM raw_events_2; '); -INFO: query: INSERT INTO public.raw_events_1 (tenant_id, value_2, value_4, value_5, value_6, event_at) SELECT raw_events_2.tenant_id, (raw_events_2.value_5)::integer AS value_5, raw_events_2.value_4, (raw_events_2.value_2)::text AS value_2, 10 AS value_6, (now())::date AS event_at FROM public.raw_events_2 +INFO: query: INSERT INTO multi_deparse_shard_query.raw_events_1 (tenant_id, value_2, value_4, value_5, value_6, event_at) SELECT raw_events_2.tenant_id, (raw_events_2.value_5)::integer AS value_5, raw_events_2.value_4, (raw_events_2.value_2)::text AS value_2, 10 AS value_6, (now())::date AS event_at FROM multi_deparse_shard_query.raw_events_2 deparse_shard_query_test --------------------------------------------------------------------- @@ -131,7 +133,7 @@ FROM GROUP BY tenant_id, date_trunc(\'hour\', event_at) '); -INFO: query: INSERT INTO public.aggregated_events (tenant_id, sum_value_1, average_value_3, sum_value_4, average_value_6, rollup_hour) SELECT raw_events_1.tenant_id, sum(raw_events_1.value_1) AS sum, avg(raw_events_1.value_3) AS avg, sum(raw_events_1.value_4) AS sum, avg(raw_events_1.value_6) AS avg, date_trunc('hour'::text, (raw_events_1.event_at)::timestamp with time zone) AS date_trunc FROM public.raw_events_1 GROUP BY raw_events_1.tenant_id, (date_trunc('hour'::text, (raw_events_1.event_at)::timestamp with time zone)) +INFO: query: INSERT INTO multi_deparse_shard_query.aggregated_events (tenant_id, sum_value_1, average_value_3, sum_value_4, average_value_6, rollup_hour) SELECT raw_events_1.tenant_id, sum(raw_events_1.value_1) AS sum, avg(raw_events_1.value_3) AS avg, sum(raw_events_1.value_4) AS sum, avg(raw_events_1.value_6) AS avg, date_trunc('hour'::text, (raw_events_1.event_at)::timestamp with time zone) AS date_trunc FROM multi_deparse_shard_query.raw_events_1 GROUP BY raw_events_1.tenant_id, (date_trunc('hour'::text, (raw_events_1.event_at)::timestamp with time zone)) deparse_shard_query_test --------------------------------------------------------------------- @@ -148,7 +150,7 @@ FROM WHERE raw_events_1.tenant_id = raw_events_2.tenant_id; '); -INFO: query: INSERT INTO public.raw_events_1 (tenant_id, value_3, value_6, event_at) SELECT raw_events_1_1.tenant_id, raw_events_2.value_3, 10 AS value_6, (now())::date AS event_at FROM public.raw_events_1 raw_events_1_1, public.raw_events_2 WHERE (raw_events_1_1.tenant_id OPERATOR(pg_catalog.=) raw_events_2.tenant_id) +INFO: query: INSERT INTO multi_deparse_shard_query.raw_events_1 (tenant_id, value_3, value_6, event_at) SELECT raw_events_1_1.tenant_id, raw_events_2.value_3, 10 AS value_6, (now())::date AS event_at FROM multi_deparse_shard_query.raw_events_1 raw_events_1_1, multi_deparse_shard_query.raw_events_2 WHERE (raw_events_1_1.tenant_id OPERATOR(pg_catalog.=) raw_events_2.tenant_id) deparse_shard_query_test --------------------------------------------------------------------- @@ -164,7 +166,7 @@ FROM WHERE raw_events_1.tenant_id = raw_events_2.tenant_id GROUP BY raw_events_1.event_at '); -INFO: query: INSERT INTO public.raw_events_1 (tenant_id, value_3, value_6, event_at) SELECT avg(raw_events_1_1.value_3) AS avg, max(raw_events_2.value_3) AS max, 10 AS value_6, (now())::date AS event_at FROM public.raw_events_1 raw_events_1_1, public.raw_events_2 WHERE (raw_events_1_1.tenant_id OPERATOR(pg_catalog.=) raw_events_2.tenant_id) GROUP BY raw_events_1_1.event_at +INFO: query: INSERT INTO multi_deparse_shard_query.raw_events_1 (tenant_id, value_3, value_6, event_at) SELECT avg(raw_events_1_1.value_3) AS avg, max(raw_events_2.value_3) AS max, 10 AS value_6, (now())::date AS event_at FROM multi_deparse_shard_query.raw_events_1 raw_events_1_1, multi_deparse_shard_query.raw_events_2 WHERE (raw_events_1_1.tenant_id OPERATOR(pg_catalog.=) raw_events_2.tenant_id) GROUP BY raw_events_1_1.event_at deparse_shard_query_test --------------------------------------------------------------------- @@ -184,7 +186,7 @@ GROUP BY ORDER BY r2.event_at DESC; '); -INFO: query: INSERT INTO public.aggregated_events (tenant_id, sum_value_4) SELECT r3.tenant_id, max(r1.value_4) AS max FROM public.raw_events_1 r1, public.raw_events_2 r2, public.raw_events_1 r3 WHERE ((r1.tenant_id OPERATOR(pg_catalog.=) r2.tenant_id) AND (r2.tenant_id OPERATOR(pg_catalog.=) r3.tenant_id)) GROUP BY r1.value_1, r3.tenant_id, r2.event_at ORDER BY r2.event_at DESC +INFO: query: INSERT INTO multi_deparse_shard_query.aggregated_events (tenant_id, sum_value_4) SELECT r3.tenant_id, max(r1.value_4) AS max FROM multi_deparse_shard_query.raw_events_1 r1, multi_deparse_shard_query.raw_events_2 r2, multi_deparse_shard_query.raw_events_1 r3 WHERE ((r1.tenant_id OPERATOR(pg_catalog.=) r2.tenant_id) AND (r2.tenant_id OPERATOR(pg_catalog.=) r3.tenant_id)) GROUP BY r1.value_1, r3.tenant_id, r2.event_at ORDER BY r2.event_at DESC deparse_shard_query_test --------------------------------------------------------------------- @@ -201,7 +203,7 @@ FROM GROUP BY event_at, tenant_id; '); -INFO: query: WITH first_tenant AS (SELECT raw_events_1.event_at, raw_events_1.value_5, raw_events_1.tenant_id FROM public.raw_events_1) INSERT INTO public.aggregated_events (tenant_id, sum_value_5, rollup_hour) SELECT raw_events_1.tenant_id, sum((raw_events_1.value_5)::integer) AS sum, raw_events_1.event_at FROM public.raw_events_1 GROUP BY raw_events_1.event_at, raw_events_1.tenant_id +INFO: query: WITH first_tenant AS (SELECT raw_events_1.event_at, raw_events_1.value_5, raw_events_1.tenant_id FROM multi_deparse_shard_query.raw_events_1) INSERT INTO multi_deparse_shard_query.aggregated_events (tenant_id, sum_value_5, rollup_hour) SELECT raw_events_1.tenant_id, sum((raw_events_1.value_5)::integer) AS sum, raw_events_1.event_at FROM multi_deparse_shard_query.raw_events_1 GROUP BY raw_events_1.event_at, raw_events_1.tenant_id deparse_shard_query_test --------------------------------------------------------------------- @@ -217,7 +219,7 @@ FROM GROUP BY event_at, tenant_id; '); -INFO: query: WITH first_tenant AS (SELECT raw_events_1.event_at, raw_events_1.value_5, raw_events_1.tenant_id FROM public.raw_events_1) INSERT INTO public.aggregated_events (tenant_id, sum_value_5) SELECT raw_events_1.tenant_id, sum((raw_events_1.value_5)::integer) AS sum FROM public.raw_events_1 GROUP BY raw_events_1.event_at, raw_events_1.tenant_id +INFO: query: WITH first_tenant AS (SELECT raw_events_1.event_at, raw_events_1.value_5, raw_events_1.tenant_id FROM multi_deparse_shard_query.raw_events_1) INSERT INTO multi_deparse_shard_query.aggregated_events (tenant_id, sum_value_5) SELECT raw_events_1.tenant_id, sum((raw_events_1.value_5)::integer) AS sum FROM multi_deparse_shard_query.raw_events_1 GROUP BY raw_events_1.event_at, raw_events_1.tenant_id deparse_shard_query_test --------------------------------------------------------------------- @@ -236,7 +238,7 @@ WITH RECURSIVE hierarchy as ( h.value_1 = re.value_6)) SELECT * FROM hierarchy WHERE LEVEL <= 2; '); -INFO: query: INSERT INTO public.aggregated_events (tenant_id, sum_value_1, sum_value_5) WITH RECURSIVE hierarchy AS (SELECT raw_events_1.value_1, 1 AS level, raw_events_1.tenant_id FROM public.raw_events_1 WHERE (raw_events_1.tenant_id OPERATOR(pg_catalog.=) 1) UNION SELECT re.value_2, (h.level OPERATOR(pg_catalog.+) 1), re.tenant_id FROM (hierarchy h JOIN public.raw_events_1 re ON (((h.tenant_id OPERATOR(pg_catalog.=) re.tenant_id) AND (h.value_1 OPERATOR(pg_catalog.=) re.value_6))))) SELECT hierarchy.tenant_id, hierarchy.value_1, hierarchy.level FROM hierarchy WHERE (hierarchy.level OPERATOR(pg_catalog.<=) 2) +INFO: query: INSERT INTO multi_deparse_shard_query.aggregated_events (tenant_id, sum_value_1, sum_value_5) WITH RECURSIVE hierarchy AS (SELECT raw_events_1.value_1, 1 AS level, raw_events_1.tenant_id FROM multi_deparse_shard_query.raw_events_1 WHERE (raw_events_1.tenant_id OPERATOR(pg_catalog.=) 1) UNION SELECT re.value_2, (h.level OPERATOR(pg_catalog.+) 1), re.tenant_id FROM (hierarchy h JOIN multi_deparse_shard_query.raw_events_1 re ON (((h.tenant_id OPERATOR(pg_catalog.=) re.tenant_id) AND (h.value_1 OPERATOR(pg_catalog.=) re.value_6))))) SELECT hierarchy.tenant_id, hierarchy.value_1, hierarchy.level FROM hierarchy WHERE (hierarchy.level OPERATOR(pg_catalog.<=) 2) deparse_shard_query_test --------------------------------------------------------------------- @@ -249,7 +251,7 @@ SELECT FROM raw_events_1; '); -INFO: query: INSERT INTO public.aggregated_events (sum_value_1) SELECT DISTINCT raw_events_1.value_1 FROM public.raw_events_1 +INFO: query: INSERT INTO multi_deparse_shard_query.aggregated_events (sum_value_1) SELECT DISTINCT raw_events_1.value_1 FROM multi_deparse_shard_query.raw_events_1 deparse_shard_query_test --------------------------------------------------------------------- @@ -262,7 +264,7 @@ SELECT value_3, value_2, tenant_id FROM raw_events_1 WHERE (value_5 like \'%s\' or value_5 like \'%a\') and (tenant_id = 1) and (value_6 < 3000 or value_3 > 8000); '); -INFO: query: INSERT INTO public.aggregated_events (tenant_id, sum_value_1, sum_value_5) SELECT raw_events_1.tenant_id, raw_events_1.value_2, raw_events_1.value_3 FROM public.raw_events_1 WHERE (((raw_events_1.value_5 OPERATOR(pg_catalog.~~) '%s'::text) OR (raw_events_1.value_5 OPERATOR(pg_catalog.~~) '%a'::text)) AND (raw_events_1.tenant_id OPERATOR(pg_catalog.=) 1) AND ((raw_events_1.value_6 OPERATOR(pg_catalog.<) 3000) OR (raw_events_1.value_3 OPERATOR(pg_catalog.>) (8000)::double precision))) +INFO: query: INSERT INTO multi_deparse_shard_query.aggregated_events (tenant_id, sum_value_1, sum_value_5) SELECT raw_events_1.tenant_id, raw_events_1.value_2, raw_events_1.value_3 FROM multi_deparse_shard_query.raw_events_1 WHERE (((raw_events_1.value_5 OPERATOR(pg_catalog.~~) '%s'::text) OR (raw_events_1.value_5 OPERATOR(pg_catalog.~~) '%a'::text)) AND (raw_events_1.tenant_id OPERATOR(pg_catalog.=) 1) AND ((raw_events_1.value_6 OPERATOR(pg_catalog.<) 3000) OR (raw_events_1.value_3 OPERATOR(pg_catalog.>) (8000)::double precision))) deparse_shard_query_test --------------------------------------------------------------------- @@ -274,7 +276,7 @@ SELECT rank() OVER (PARTITION BY tenant_id ORDER BY value_6), tenant_id FROM raw_events_1 WHERE event_at = now(); '); -INFO: query: INSERT INTO public.aggregated_events (tenant_id, sum_value_5) SELECT raw_events_1.tenant_id, rank() OVER (PARTITION BY raw_events_1.tenant_id ORDER BY raw_events_1.value_6) AS rank FROM public.raw_events_1 WHERE (raw_events_1.event_at OPERATOR(pg_catalog.=) now()) +INFO: query: INSERT INTO multi_deparse_shard_query.aggregated_events (tenant_id, sum_value_5) SELECT raw_events_1.tenant_id, rank() OVER (PARTITION BY raw_events_1.tenant_id ORDER BY raw_events_1.value_6) AS rank FROM multi_deparse_shard_query.raw_events_1 WHERE (raw_events_1.event_at OPERATOR(pg_catalog.=) now()) deparse_shard_query_test --------------------------------------------------------------------- @@ -287,7 +289,7 @@ SELECT random(), int4eq(1, max(value_1))::int, value_6 WHERE event_at = now() GROUP BY event_at, value_7, value_6; '); -INFO: query: INSERT INTO public.aggregated_events (tenant_id, sum_value_4, sum_value_5) SELECT (int4eq(1, max(raw_events_1.value_1)))::integer AS int4eq, raw_events_1.value_6, random() AS random FROM public.raw_events_1 WHERE (raw_events_1.event_at OPERATOR(pg_catalog.=) now()) GROUP BY raw_events_1.event_at, raw_events_1.value_7, raw_events_1.value_6 +INFO: query: INSERT INTO multi_deparse_shard_query.aggregated_events (tenant_id, sum_value_4, sum_value_5) SELECT (int4eq(1, max(raw_events_1.value_1)))::integer AS int4eq, raw_events_1.value_6, random() AS random FROM multi_deparse_shard_query.raw_events_1 WHERE (raw_events_1.event_at OPERATOR(pg_catalog.=) now()) GROUP BY raw_events_1.event_at, raw_events_1.value_7, raw_events_1.value_6 deparse_shard_query_test --------------------------------------------------------------------- @@ -308,7 +310,7 @@ SELECT FROM raw_events_1; '); -INFO: query: INSERT INTO public.aggregated_events (tenant_id, sum_value_1) SELECT max(raw_events_1.tenant_id) AS max, count(DISTINCT CASE WHEN (raw_events_1.value_1 OPERATOR(pg_catalog.>) 100) THEN raw_events_1.tenant_id ELSE (raw_events_1.value_6)::bigint END) AS c FROM public.raw_events_1 +INFO: query: INSERT INTO multi_deparse_shard_query.aggregated_events (tenant_id, sum_value_1) SELECT max(raw_events_1.tenant_id) AS max, count(DISTINCT CASE WHEN (raw_events_1.value_1 OPERATOR(pg_catalog.>) 100) THEN raw_events_1.tenant_id ELSE (raw_events_1.value_6)::bigint END) AS c FROM multi_deparse_shard_query.raw_events_1 deparse_shard_query_test --------------------------------------------------------------------- @@ -325,7 +327,7 @@ FROM raw_events_2 ) as foo '); -INFO: query: INSERT INTO public.raw_events_1 (tenant_id, value_1, value_6, value_7, event_at) SELECT foo.tenant_id, foo.value_1, 10 AS value_6, foo.value_7, (now())::date AS event_at FROM (SELECT raw_events_2.tenant_id, raw_events_2.value_2 AS value_7, raw_events_2.value_1 FROM public.raw_events_2) foo +INFO: query: INSERT INTO multi_deparse_shard_query.raw_events_1 (tenant_id, value_1, value_6, value_7, event_at) SELECT foo.tenant_id, foo.value_1, 10 AS value_6, foo.value_7, (now())::date AS event_at FROM (SELECT raw_events_2.tenant_id, raw_events_2.value_2 AS value_7, raw_events_2.value_1 FROM multi_deparse_shard_query.raw_events_2) foo deparse_shard_query_test --------------------------------------------------------------------- @@ -346,7 +348,7 @@ FROM GROUP BY tenant_id, date_trunc(\'hour\', event_at) '); -INFO: query: INSERT INTO public.aggregated_events (tenant_id, sum_value_1, sum_value_5) SELECT foo.tenant_id, sum(foo.value_1) AS sum, sum((foo.value_5)::bigint) AS sum FROM (SELECT raw_events_1.event_at, raw_events_2.tenant_id, raw_events_2.value_5, raw_events_1.value_1 FROM public.raw_events_2, public.raw_events_1 WHERE (raw_events_1.tenant_id OPERATOR(pg_catalog.=) raw_events_2.tenant_id)) foo GROUP BY foo.tenant_id, (date_trunc('hour'::text, (foo.event_at)::timestamp with time zone)) +INFO: query: INSERT INTO multi_deparse_shard_query.aggregated_events (tenant_id, sum_value_1, sum_value_5) SELECT foo.tenant_id, sum(foo.value_1) AS sum, sum((foo.value_5)::bigint) AS sum FROM (SELECT raw_events_1.event_at, raw_events_2.tenant_id, raw_events_2.value_5, raw_events_1.value_1 FROM multi_deparse_shard_query.raw_events_2, multi_deparse_shard_query.raw_events_1 WHERE (raw_events_1.tenant_id OPERATOR(pg_catalog.=) raw_events_2.tenant_id)) foo GROUP BY foo.tenant_id, (date_trunc('hour'::text, (foo.event_at)::timestamp with time zone)) deparse_shard_query_test --------------------------------------------------------------------- @@ -363,7 +365,7 @@ FROM raw_events_1 ) as foo '); -INFO: query: INSERT INTO public.raw_events_2 (tenant_id, value_1, value_2, value_3, value_4, value_6, event_at) SELECT foo.tenant_id, foo.value_1, foo.value_2, foo.value_3, foo.value_4, (random() OPERATOR(pg_catalog.*) (100)::double precision) AS value_6, (now())::date AS event_at FROM (SELECT raw_events_1.value_2, raw_events_1.value_4, raw_events_1.tenant_id, raw_events_1.value_1, raw_events_1.value_3 FROM public.raw_events_1) foo +INFO: query: INSERT INTO multi_deparse_shard_query.raw_events_2 (tenant_id, value_1, value_2, value_3, value_4, value_6, event_at) SELECT foo.tenant_id, foo.value_1, foo.value_2, foo.value_3, foo.value_4, (random() OPERATOR(pg_catalog.*) (100)::double precision) AS value_6, (now())::date AS event_at FROM (SELECT raw_events_1.value_2, raw_events_1.value_4, raw_events_1.tenant_id, raw_events_1.value_1, raw_events_1.value_3 FROM multi_deparse_shard_query.raw_events_1) foo deparse_shard_query_test --------------------------------------------------------------------- @@ -380,7 +382,7 @@ FROM raw_events_1 ) as foo '); -INFO: query: INSERT INTO public.raw_events_2 (tenant_id, value_1, value_2, value_3, value_4, value_6, event_at) SELECT foo.value_2, foo.value_4, foo.value_1, foo.value_3, foo.tenant_id, (random() OPERATOR(pg_catalog.*) (100)::double precision) AS value_6, (now())::date AS event_at FROM (SELECT raw_events_1.value_2, raw_events_1.value_4, raw_events_1.tenant_id, raw_events_1.value_1, raw_events_1.value_3 FROM public.raw_events_1) foo +INFO: query: INSERT INTO multi_deparse_shard_query.raw_events_2 (tenant_id, value_1, value_2, value_3, value_4, value_6, event_at) SELECT foo.value_2, foo.value_4, foo.value_1, foo.value_3, foo.tenant_id, (random() OPERATOR(pg_catalog.*) (100)::double precision) AS value_6, (now())::date AS event_at FROM (SELECT raw_events_1.value_2, raw_events_1.value_4, raw_events_1.tenant_id, raw_events_1.value_1, raw_events_1.value_3 FROM multi_deparse_shard_query.raw_events_1) foo deparse_shard_query_test --------------------------------------------------------------------- @@ -396,7 +398,7 @@ FROM ORDER BY value_2, value_1; '); -INFO: query: INSERT INTO public.raw_events_1 (tenant_id, value_4, value_6, value_7, event_at) SELECT raw_events_1_1.tenant_id, raw_events_1_1.value_7, 10 AS value_6, raw_events_1_1.value_7, (now())::date AS event_at FROM public.raw_events_1 raw_events_1_1 ORDER BY raw_events_1_1.value_2, raw_events_1_1.value_1 +INFO: query: INSERT INTO multi_deparse_shard_query.raw_events_1 (tenant_id, value_4, value_6, value_7, event_at) SELECT raw_events_1_1.tenant_id, raw_events_1_1.value_7, 10 AS value_6, raw_events_1_1.value_7, (now())::date AS event_at FROM multi_deparse_shard_query.raw_events_1 raw_events_1_1 ORDER BY raw_events_1_1.value_2, raw_events_1_1.value_1 deparse_shard_query_test --------------------------------------------------------------------- @@ -411,9 +413,11 @@ SELECT FROM raw_events_1; '); -INFO: query: INSERT INTO public.raw_events_1 (tenant_id, value_4, value_6, value_7, event_at) SELECT raw_events_1_1.tenant_id, raw_events_1_1.value_4, 10 AS value_6, raw_events_1_1.value_7, (now())::date AS event_at FROM public.raw_events_1 raw_events_1_1 +INFO: query: INSERT INTO multi_deparse_shard_query.raw_events_1 (tenant_id, value_4, value_6, value_7, event_at) SELECT raw_events_1_1.tenant_id, raw_events_1_1.value_4, 10 AS value_6, raw_events_1_1.value_7, (now())::date AS event_at FROM multi_deparse_shard_query.raw_events_1 raw_events_1_1 deparse_shard_query_test --------------------------------------------------------------------- (1 row) +SET client_min_messages TO ERROR; +DROP SCHEMA multi_deparse_shard_query CASCADE; diff --git a/src/test/regress/expected/multi_deparse_shard_query_0.out b/src/test/regress/expected/multi_deparse_shard_query_0.out index 71742c589d9..4f2ca98b835 100644 --- a/src/test/regress/expected/multi_deparse_shard_query_0.out +++ b/src/test/regress/expected/multi_deparse_shard_query_0.out @@ -12,6 +12,8 @@ SELECT substring(:'server_version', '\d+')::int >= 15 AS server_version_ge_15; f (1 row) +CREATE SCHEMA multi_deparse_shard_query; +SET search_path TO multi_deparse_shard_query; SET citus.next_shard_id TO 13100000; SET citus.shard_replication_factor TO 1; CREATE FUNCTION deparse_shard_query_test(text) @@ -74,7 +76,7 @@ SELECT deparse_shard_query_test(' INSERT INTO raw_events_1 SELECT * FROM raw_events_1; '); -INFO: query: INSERT INTO public.raw_events_1 (tenant_id, value_1, value_2, value_3, value_4, value_5, value_6, value_7, event_at) SELECT tenant_id, value_1, value_2, value_3, value_4, value_5, value_6, value_7, event_at FROM public.raw_events_1 +INFO: query: INSERT INTO multi_deparse_shard_query.raw_events_1 (tenant_id, value_1, value_2, value_3, value_4, value_5, value_6, value_7, event_at) SELECT tenant_id, value_1, value_2, value_3, value_4, value_5, value_6, value_7, event_at FROM multi_deparse_shard_query.raw_events_1 deparse_shard_query_test --------------------------------------------------------------------- @@ -87,7 +89,7 @@ SELECT FROM raw_events_1; '); -INFO: query: INSERT INTO public.raw_events_1 (tenant_id, value_4, value_6, event_at) SELECT tenant_id, value_4, 10 AS value_6, (now())::date AS event_at FROM public.raw_events_1 +INFO: query: INSERT INTO multi_deparse_shard_query.raw_events_1 (tenant_id, value_4, value_6, event_at) SELECT tenant_id, value_4, 10 AS value_6, (now())::date AS event_at FROM multi_deparse_shard_query.raw_events_1 deparse_shard_query_test --------------------------------------------------------------------- @@ -101,7 +103,7 @@ SELECT FROM raw_events_1; '); -INFO: query: INSERT INTO public.raw_events_1 (tenant_id, value_2, value_4, value_5, value_6, event_at) SELECT tenant_id, (value_5)::integer AS value_5, value_4, (value_2)::text AS value_2, 10 AS value_6, (now())::date AS event_at FROM public.raw_events_1 +INFO: query: INSERT INTO multi_deparse_shard_query.raw_events_1 (tenant_id, value_2, value_4, value_5, value_6, event_at) SELECT tenant_id, (value_5)::integer AS value_5, value_4, (value_2)::text AS value_2, 10 AS value_6, (now())::date AS event_at FROM multi_deparse_shard_query.raw_events_1 deparse_shard_query_test --------------------------------------------------------------------- @@ -115,7 +117,7 @@ SELECT FROM raw_events_2; '); -INFO: query: INSERT INTO public.raw_events_1 (tenant_id, value_2, value_4, value_5, value_6, event_at) SELECT tenant_id, (value_5)::integer AS value_5, value_4, (value_2)::text AS value_2, 10 AS value_6, (now())::date AS event_at FROM public.raw_events_2 +INFO: query: INSERT INTO multi_deparse_shard_query.raw_events_1 (tenant_id, value_2, value_4, value_5, value_6, event_at) SELECT tenant_id, (value_5)::integer AS value_5, value_4, (value_2)::text AS value_2, 10 AS value_6, (now())::date AS event_at FROM multi_deparse_shard_query.raw_events_2 deparse_shard_query_test --------------------------------------------------------------------- @@ -131,7 +133,7 @@ FROM GROUP BY tenant_id, date_trunc(\'hour\', event_at) '); -INFO: query: INSERT INTO public.aggregated_events (tenant_id, sum_value_1, average_value_3, sum_value_4, average_value_6, rollup_hour) SELECT tenant_id, sum(value_1) AS sum, avg(value_3) AS avg, sum(value_4) AS sum, avg(value_6) AS avg, date_trunc('hour'::text, (event_at)::timestamp with time zone) AS date_trunc FROM public.raw_events_1 GROUP BY tenant_id, (date_trunc('hour'::text, (event_at)::timestamp with time zone)) +INFO: query: INSERT INTO multi_deparse_shard_query.aggregated_events (tenant_id, sum_value_1, average_value_3, sum_value_4, average_value_6, rollup_hour) SELECT tenant_id, sum(value_1) AS sum, avg(value_3) AS avg, sum(value_4) AS sum, avg(value_6) AS avg, date_trunc('hour'::text, (event_at)::timestamp with time zone) AS date_trunc FROM multi_deparse_shard_query.raw_events_1 GROUP BY tenant_id, (date_trunc('hour'::text, (event_at)::timestamp with time zone)) deparse_shard_query_test --------------------------------------------------------------------- @@ -148,7 +150,7 @@ FROM WHERE raw_events_1.tenant_id = raw_events_2.tenant_id; '); -INFO: query: INSERT INTO public.raw_events_1 (tenant_id, value_3, value_6, event_at) SELECT raw_events_1.tenant_id, raw_events_2.value_3, 10 AS value_6, (now())::date AS event_at FROM public.raw_events_1, public.raw_events_2 WHERE (raw_events_1.tenant_id OPERATOR(pg_catalog.=) raw_events_2.tenant_id) +INFO: query: INSERT INTO multi_deparse_shard_query.raw_events_1 (tenant_id, value_3, value_6, event_at) SELECT raw_events_1.tenant_id, raw_events_2.value_3, 10 AS value_6, (now())::date AS event_at FROM multi_deparse_shard_query.raw_events_1, multi_deparse_shard_query.raw_events_2 WHERE (raw_events_1.tenant_id OPERATOR(pg_catalog.=) raw_events_2.tenant_id) deparse_shard_query_test --------------------------------------------------------------------- @@ -164,7 +166,7 @@ FROM WHERE raw_events_1.tenant_id = raw_events_2.tenant_id GROUP BY raw_events_1.event_at '); -INFO: query: INSERT INTO public.raw_events_1 (tenant_id, value_3, value_6, event_at) SELECT avg(raw_events_1.value_3) AS avg, max(raw_events_2.value_3) AS max, 10 AS value_6, (now())::date AS event_at FROM public.raw_events_1, public.raw_events_2 WHERE (raw_events_1.tenant_id OPERATOR(pg_catalog.=) raw_events_2.tenant_id) GROUP BY raw_events_1.event_at +INFO: query: INSERT INTO multi_deparse_shard_query.raw_events_1 (tenant_id, value_3, value_6, event_at) SELECT avg(raw_events_1.value_3) AS avg, max(raw_events_2.value_3) AS max, 10 AS value_6, (now())::date AS event_at FROM multi_deparse_shard_query.raw_events_1, multi_deparse_shard_query.raw_events_2 WHERE (raw_events_1.tenant_id OPERATOR(pg_catalog.=) raw_events_2.tenant_id) GROUP BY raw_events_1.event_at deparse_shard_query_test --------------------------------------------------------------------- @@ -184,7 +186,7 @@ GROUP BY ORDER BY r2.event_at DESC; '); -INFO: query: INSERT INTO public.aggregated_events (tenant_id, sum_value_4) SELECT r3.tenant_id, max(r1.value_4) AS max FROM public.raw_events_1 r1, public.raw_events_2 r2, public.raw_events_1 r3 WHERE ((r1.tenant_id OPERATOR(pg_catalog.=) r2.tenant_id) AND (r2.tenant_id OPERATOR(pg_catalog.=) r3.tenant_id)) GROUP BY r1.value_1, r3.tenant_id, r2.event_at ORDER BY r2.event_at DESC +INFO: query: INSERT INTO multi_deparse_shard_query.aggregated_events (tenant_id, sum_value_4) SELECT r3.tenant_id, max(r1.value_4) AS max FROM multi_deparse_shard_query.raw_events_1 r1, multi_deparse_shard_query.raw_events_2 r2, multi_deparse_shard_query.raw_events_1 r3 WHERE ((r1.tenant_id OPERATOR(pg_catalog.=) r2.tenant_id) AND (r2.tenant_id OPERATOR(pg_catalog.=) r3.tenant_id)) GROUP BY r1.value_1, r3.tenant_id, r2.event_at ORDER BY r2.event_at DESC deparse_shard_query_test --------------------------------------------------------------------- @@ -201,7 +203,7 @@ FROM GROUP BY event_at, tenant_id; '); -INFO: query: WITH first_tenant AS (SELECT raw_events_1.event_at, raw_events_1.value_5, raw_events_1.tenant_id FROM public.raw_events_1) INSERT INTO public.aggregated_events (tenant_id, sum_value_5, rollup_hour) SELECT tenant_id, sum((value_5)::integer) AS sum, event_at FROM public.raw_events_1 GROUP BY event_at, tenant_id +INFO: query: WITH first_tenant AS (SELECT raw_events_1.event_at, raw_events_1.value_5, raw_events_1.tenant_id FROM multi_deparse_shard_query.raw_events_1) INSERT INTO multi_deparse_shard_query.aggregated_events (tenant_id, sum_value_5, rollup_hour) SELECT tenant_id, sum((value_5)::integer) AS sum, event_at FROM multi_deparse_shard_query.raw_events_1 GROUP BY event_at, tenant_id deparse_shard_query_test --------------------------------------------------------------------- @@ -217,7 +219,7 @@ FROM GROUP BY event_at, tenant_id; '); -INFO: query: WITH first_tenant AS (SELECT raw_events_1.event_at, raw_events_1.value_5, raw_events_1.tenant_id FROM public.raw_events_1) INSERT INTO public.aggregated_events (tenant_id, sum_value_5) SELECT tenant_id, sum((value_5)::integer) AS sum FROM public.raw_events_1 GROUP BY event_at, tenant_id +INFO: query: WITH first_tenant AS (SELECT raw_events_1.event_at, raw_events_1.value_5, raw_events_1.tenant_id FROM multi_deparse_shard_query.raw_events_1) INSERT INTO multi_deparse_shard_query.aggregated_events (tenant_id, sum_value_5) SELECT tenant_id, sum((value_5)::integer) AS sum FROM multi_deparse_shard_query.raw_events_1 GROUP BY event_at, tenant_id deparse_shard_query_test --------------------------------------------------------------------- @@ -236,7 +238,7 @@ WITH RECURSIVE hierarchy as ( h.value_1 = re.value_6)) SELECT * FROM hierarchy WHERE LEVEL <= 2; '); -INFO: query: INSERT INTO public.aggregated_events (tenant_id, sum_value_1, sum_value_5) WITH RECURSIVE hierarchy AS (SELECT raw_events_1.value_1, 1 AS level, raw_events_1.tenant_id FROM public.raw_events_1 WHERE (raw_events_1.tenant_id OPERATOR(pg_catalog.=) 1) UNION SELECT re.value_2, (h.level OPERATOR(pg_catalog.+) 1), re.tenant_id FROM (hierarchy h JOIN public.raw_events_1 re ON (((h.tenant_id OPERATOR(pg_catalog.=) re.tenant_id) AND (h.value_1 OPERATOR(pg_catalog.=) re.value_6))))) SELECT tenant_id, value_1, level FROM hierarchy WHERE (level OPERATOR(pg_catalog.<=) 2) +INFO: query: INSERT INTO multi_deparse_shard_query.aggregated_events (tenant_id, sum_value_1, sum_value_5) WITH RECURSIVE hierarchy AS (SELECT raw_events_1.value_1, 1 AS level, raw_events_1.tenant_id FROM multi_deparse_shard_query.raw_events_1 WHERE (raw_events_1.tenant_id OPERATOR(pg_catalog.=) 1) UNION SELECT re.value_2, (h.level OPERATOR(pg_catalog.+) 1), re.tenant_id FROM (hierarchy h JOIN multi_deparse_shard_query.raw_events_1 re ON (((h.tenant_id OPERATOR(pg_catalog.=) re.tenant_id) AND (h.value_1 OPERATOR(pg_catalog.=) re.value_6))))) SELECT tenant_id, value_1, level FROM hierarchy WHERE (level OPERATOR(pg_catalog.<=) 2) deparse_shard_query_test --------------------------------------------------------------------- @@ -249,7 +251,7 @@ SELECT FROM raw_events_1; '); -INFO: query: INSERT INTO public.aggregated_events (sum_value_1) SELECT DISTINCT value_1 FROM public.raw_events_1 +INFO: query: INSERT INTO multi_deparse_shard_query.aggregated_events (sum_value_1) SELECT DISTINCT value_1 FROM multi_deparse_shard_query.raw_events_1 deparse_shard_query_test --------------------------------------------------------------------- @@ -262,7 +264,7 @@ SELECT value_3, value_2, tenant_id FROM raw_events_1 WHERE (value_5 like \'%s\' or value_5 like \'%a\') and (tenant_id = 1) and (value_6 < 3000 or value_3 > 8000); '); -INFO: query: INSERT INTO public.aggregated_events (tenant_id, sum_value_1, sum_value_5) SELECT tenant_id, value_2, value_3 FROM public.raw_events_1 WHERE (((value_5 OPERATOR(pg_catalog.~~) '%s'::text) OR (value_5 OPERATOR(pg_catalog.~~) '%a'::text)) AND (tenant_id OPERATOR(pg_catalog.=) 1) AND ((value_6 OPERATOR(pg_catalog.<) 3000) OR (value_3 OPERATOR(pg_catalog.>) (8000)::double precision))) +INFO: query: INSERT INTO multi_deparse_shard_query.aggregated_events (tenant_id, sum_value_1, sum_value_5) SELECT tenant_id, value_2, value_3 FROM multi_deparse_shard_query.raw_events_1 WHERE (((value_5 OPERATOR(pg_catalog.~~) '%s'::text) OR (value_5 OPERATOR(pg_catalog.~~) '%a'::text)) AND (tenant_id OPERATOR(pg_catalog.=) 1) AND ((value_6 OPERATOR(pg_catalog.<) 3000) OR (value_3 OPERATOR(pg_catalog.>) (8000)::double precision))) deparse_shard_query_test --------------------------------------------------------------------- @@ -274,7 +276,7 @@ SELECT rank() OVER (PARTITION BY tenant_id ORDER BY value_6), tenant_id FROM raw_events_1 WHERE event_at = now(); '); -INFO: query: INSERT INTO public.aggregated_events (tenant_id, sum_value_5) SELECT tenant_id, rank() OVER (PARTITION BY tenant_id ORDER BY value_6) AS rank FROM public.raw_events_1 WHERE (event_at OPERATOR(pg_catalog.=) now()) +INFO: query: INSERT INTO multi_deparse_shard_query.aggregated_events (tenant_id, sum_value_5) SELECT tenant_id, rank() OVER (PARTITION BY tenant_id ORDER BY value_6) AS rank FROM multi_deparse_shard_query.raw_events_1 WHERE (event_at OPERATOR(pg_catalog.=) now()) deparse_shard_query_test --------------------------------------------------------------------- @@ -287,7 +289,7 @@ SELECT random(), int4eq(1, max(value_1))::int, value_6 WHERE event_at = now() GROUP BY event_at, value_7, value_6; '); -INFO: query: INSERT INTO public.aggregated_events (tenant_id, sum_value_4, sum_value_5) SELECT (int4eq(1, max(value_1)))::integer AS int4eq, value_6, random() AS random FROM public.raw_events_1 WHERE (event_at OPERATOR(pg_catalog.=) now()) GROUP BY event_at, value_7, value_6 +INFO: query: INSERT INTO multi_deparse_shard_query.aggregated_events (tenant_id, sum_value_4, sum_value_5) SELECT (int4eq(1, max(value_1)))::integer AS int4eq, value_6, random() AS random FROM multi_deparse_shard_query.raw_events_1 WHERE (event_at OPERATOR(pg_catalog.=) now()) GROUP BY event_at, value_7, value_6 deparse_shard_query_test --------------------------------------------------------------------- @@ -308,7 +310,7 @@ SELECT FROM raw_events_1; '); -INFO: query: INSERT INTO public.aggregated_events (tenant_id, sum_value_1) SELECT max(tenant_id) AS max, count(DISTINCT CASE WHEN (value_1 OPERATOR(pg_catalog.>) 100) THEN tenant_id ELSE (value_6)::bigint END) AS c FROM public.raw_events_1 +INFO: query: INSERT INTO multi_deparse_shard_query.aggregated_events (tenant_id, sum_value_1) SELECT max(tenant_id) AS max, count(DISTINCT CASE WHEN (value_1 OPERATOR(pg_catalog.>) 100) THEN tenant_id ELSE (value_6)::bigint END) AS c FROM multi_deparse_shard_query.raw_events_1 deparse_shard_query_test --------------------------------------------------------------------- @@ -325,7 +327,7 @@ FROM raw_events_2 ) as foo '); -INFO: query: INSERT INTO public.raw_events_1 (tenant_id, value_1, value_6, value_7, event_at) SELECT tenant_id, value_1, 10 AS value_6, value_7, (now())::date AS event_at FROM (SELECT raw_events_2.tenant_id, raw_events_2.value_2 AS value_7, raw_events_2.value_1 FROM public.raw_events_2) foo +INFO: query: INSERT INTO multi_deparse_shard_query.raw_events_1 (tenant_id, value_1, value_6, value_7, event_at) SELECT tenant_id, value_1, 10 AS value_6, value_7, (now())::date AS event_at FROM (SELECT raw_events_2.tenant_id, raw_events_2.value_2 AS value_7, raw_events_2.value_1 FROM multi_deparse_shard_query.raw_events_2) foo deparse_shard_query_test --------------------------------------------------------------------- @@ -341,12 +343,12 @@ FROM FROM raw_events_2, raw_events_1 WHERE - raw_events_1.tenant_id = raw_events_2.tenant_id + raw_events_1.tenant_id = raw_events_2.tenant_id ) as foo GROUP BY tenant_id, date_trunc(\'hour\', event_at) '); -INFO: query: INSERT INTO public.aggregated_events (tenant_id, sum_value_1, sum_value_5) SELECT tenant_id, sum(value_1) AS sum, sum((value_5)::bigint) AS sum FROM (SELECT raw_events_1.event_at, raw_events_2.tenant_id, raw_events_2.value_5, raw_events_1.value_1 FROM public.raw_events_2, public.raw_events_1 WHERE (raw_events_1.tenant_id OPERATOR(pg_catalog.=) raw_events_2.tenant_id)) foo GROUP BY tenant_id, (date_trunc('hour'::text, (event_at)::timestamp with time zone)) +INFO: query: INSERT INTO multi_deparse_shard_query.aggregated_events (tenant_id, sum_value_1, sum_value_5) SELECT tenant_id, sum(value_1) AS sum, sum((value_5)::bigint) AS sum FROM (SELECT raw_events_1.event_at, raw_events_2.tenant_id, raw_events_2.value_5, raw_events_1.value_1 FROM multi_deparse_shard_query.raw_events_2, multi_deparse_shard_query.raw_events_1 WHERE (raw_events_1.tenant_id OPERATOR(pg_catalog.=) raw_events_2.tenant_id)) foo GROUP BY tenant_id, (date_trunc('hour'::text, (event_at)::timestamp with time zone)) deparse_shard_query_test --------------------------------------------------------------------- @@ -363,7 +365,7 @@ FROM raw_events_1 ) as foo '); -INFO: query: INSERT INTO public.raw_events_2 (tenant_id, value_1, value_2, value_3, value_4, value_6, event_at) SELECT tenant_id, value_1, value_2, value_3, value_4, (random() OPERATOR(pg_catalog.*) (100)::double precision) AS value_6, (now())::date AS event_at FROM (SELECT raw_events_1.value_2, raw_events_1.value_4, raw_events_1.tenant_id, raw_events_1.value_1, raw_events_1.value_3 FROM public.raw_events_1) foo +INFO: query: INSERT INTO multi_deparse_shard_query.raw_events_2 (tenant_id, value_1, value_2, value_3, value_4, value_6, event_at) SELECT tenant_id, value_1, value_2, value_3, value_4, (random() OPERATOR(pg_catalog.*) (100)::double precision) AS value_6, (now())::date AS event_at FROM (SELECT raw_events_1.value_2, raw_events_1.value_4, raw_events_1.tenant_id, raw_events_1.value_1, raw_events_1.value_3 FROM multi_deparse_shard_query.raw_events_1) foo deparse_shard_query_test --------------------------------------------------------------------- @@ -380,7 +382,7 @@ FROM raw_events_1 ) as foo '); -INFO: query: INSERT INTO public.raw_events_2 (tenant_id, value_1, value_2, value_3, value_4, value_6, event_at) SELECT value_2, value_4, value_1, value_3, tenant_id, (random() OPERATOR(pg_catalog.*) (100)::double precision) AS value_6, (now())::date AS event_at FROM (SELECT raw_events_1.value_2, raw_events_1.value_4, raw_events_1.tenant_id, raw_events_1.value_1, raw_events_1.value_3 FROM public.raw_events_1) foo +INFO: query: INSERT INTO multi_deparse_shard_query.raw_events_2 (tenant_id, value_1, value_2, value_3, value_4, value_6, event_at) SELECT value_2, value_4, value_1, value_3, tenant_id, (random() OPERATOR(pg_catalog.*) (100)::double precision) AS value_6, (now())::date AS event_at FROM (SELECT raw_events_1.value_2, raw_events_1.value_4, raw_events_1.tenant_id, raw_events_1.value_1, raw_events_1.value_3 FROM multi_deparse_shard_query.raw_events_1) foo deparse_shard_query_test --------------------------------------------------------------------- @@ -396,7 +398,7 @@ FROM ORDER BY value_2, value_1; '); -INFO: query: INSERT INTO public.raw_events_1 (tenant_id, value_4, value_6, value_7, event_at) SELECT tenant_id, value_7, 10 AS value_6, value_7, (now())::date AS event_at FROM public.raw_events_1 ORDER BY value_2, value_1 +INFO: query: INSERT INTO multi_deparse_shard_query.raw_events_1 (tenant_id, value_4, value_6, value_7, event_at) SELECT tenant_id, value_7, 10 AS value_6, value_7, (now())::date AS event_at FROM multi_deparse_shard_query.raw_events_1 ORDER BY value_2, value_1 deparse_shard_query_test --------------------------------------------------------------------- @@ -411,9 +413,11 @@ SELECT FROM raw_events_1; '); -INFO: query: INSERT INTO public.raw_events_1 (tenant_id, value_4, value_6, value_7, event_at) SELECT tenant_id, value_4, 10 AS value_6, value_7, (now())::date AS event_at FROM public.raw_events_1 +INFO: query: INSERT INTO multi_deparse_shard_query.raw_events_1 (tenant_id, value_4, value_6, value_7, event_at) SELECT tenant_id, value_4, 10 AS value_6, value_7, (now())::date AS event_at FROM multi_deparse_shard_query.raw_events_1 deparse_shard_query_test --------------------------------------------------------------------- (1 row) +SET client_min_messages TO ERROR; +DROP SCHEMA multi_deparse_shard_query CASCADE; diff --git a/src/test/regress/expected/schema_based_sharding.out b/src/test/regress/expected/schema_based_sharding.out index 4493f96141a..2dd85429d02 100644 --- a/src/test/regress/expected/schema_based_sharding.out +++ b/src/test/regress/expected/schema_based_sharding.out @@ -1345,13 +1345,6 @@ DROP USER test_other_super_user; CREATE ROLE test_non_super_user WITH LOGIN; ALTER ROLE test_non_super_user NOSUPERUSER; GRANT CREATE ON DATABASE regression TO test_non_super_user; -SELECT result FROM run_command_on_workers($$GRANT CREATE ON DATABASE regression TO test_non_super_user$$); - result ---------------------------------------------------------------------- - GRANT - GRANT -(2 rows) - GRANT CREATE ON SCHEMA public TO test_non_super_user ; \c - test_non_super_user SET search_path TO regular_schema; @@ -1487,13 +1480,6 @@ $$); \c - postgres REVOKE CREATE ON DATABASE regression FROM test_non_super_user; -SELECT result FROM run_command_on_workers($$REVOKE CREATE ON DATABASE regression FROM test_non_super_user$$); - result ---------------------------------------------------------------------- - REVOKE - REVOKE -(2 rows) - REVOKE CREATE ON SCHEMA public FROM test_non_super_user; DROP ROLE test_non_super_user; \c - - - :worker_1_port diff --git a/src/test/regress/multi_1_schedule b/src/test/regress/multi_1_schedule index 947c42ec81d..a451c43ae76 100644 --- a/src/test/regress/multi_1_schedule +++ b/src/test/regress/multi_1_schedule @@ -50,6 +50,8 @@ test: multi_metadata_attributes test: multi_read_from_secondaries +test: grant_on_database_propagation + # ---------- # multi_citus_tools tests utility functions written for citus tools # ---------- diff --git a/src/test/regress/sql/grant_on_database_propagation.sql b/src/test/regress/sql/grant_on_database_propagation.sql new file mode 100644 index 00000000000..00b9cddb84d --- /dev/null +++ b/src/test/regress/sql/grant_on_database_propagation.sql @@ -0,0 +1,378 @@ +-- Public role has connect,temp,temporary privileges on database +-- To test these scenarios, we need to revoke these privileges from public role +-- since public role privileges are inherited by new roles/users +revoke connect,temp,temporary on database regression from public; + +CREATE SCHEMA grant_on_database_propagation; +SET search_path TO grant_on_database_propagation; + +-- test grant/revoke CREATE privilege propagation on database +create user myuser; + +grant create on database regression to myuser; + +select has_database_privilege('myuser','regression', 'CREATE'); +\c - - - :worker_1_port; +select has_database_privilege('myuser','regression', 'CREATE'); +\c - - - :master_port + +revoke create on database regression from myuser; + + +select has_database_privilege('myuser','regression', 'CREATE'); +\c - - - :worker_1_port + +select has_database_privilege('myuser','regression', 'CREATE'); +\c - - - :master_port + +drop user myuser; +----------------------------------------------------------------------- + +-- test grant/revoke CONNECT privilege propagation on database +create user myuser; + +grant CONNECT on database regression to myuser; + +select has_database_privilege('myuser','regression', 'CONNECT'); +\c - - - :worker_1_port; +select has_database_privilege('myuser','regression', 'CONNECT'); +\c - - - :master_port + +revoke connect on database regression from myuser; + +select has_database_privilege('myuser','regression', 'CONNECT'); +\c - - - :worker_1_port + +select has_database_privilege('myuser','regression', 'CONNECT'); +\c - - - :master_port + +drop user myuser; + +----------------------------------------------------------------------- + +-- test grant/revoke TEMP privilege propagation on database +create user myuser; + +-- test grant/revoke temp on database +grant TEMP on database regression to myuser; + +select has_database_privilege('myuser','regression', 'TEMP'); +\c - - - :worker_1_port; +select has_database_privilege('myuser','regression', 'TEMP'); +\c - - - :master_port + +revoke TEMP on database regression from myuser; + +select has_database_privilege('myuser','regression', 'TEMP'); +\c - - - :worker_1_port + +select has_database_privilege('myuser','regression', 'TEMP'); +\c - - - :master_port + +drop user myuser; + +----------------------------------------------------------------------- + +-- test temporary privilege on database +create user myuser; + +-- test grant/revoke temporary on database +grant TEMPORARY on database regression to myuser; + +select has_database_privilege('myuser','regression', 'TEMPORARY'); +\c - - - :worker_1_port; +select has_database_privilege('myuser','regression', 'TEMPORARY'); +\c - - - :master_port + +revoke TEMPORARY on database regression from myuser; + +select has_database_privilege('myuser','regression', 'TEMPORARY'); +\c - - - :worker_1_port + +select has_database_privilege('myuser','regression', 'TEMPORARY'); +\c - - - :master_port + +drop user myuser; +----------------------------------------------------------------------- + +-- test ALL privileges with ALL statement on database +create user myuser; + +grant ALL on database regression to myuser; + +select has_database_privilege('myuser','regression', 'CREATE'); +select has_database_privilege('myuser','regression', 'CONNECT'); +select has_database_privilege('myuser','regression', 'TEMP'); +select has_database_privilege('myuser','regression', 'TEMPORARY'); +\c - - - :worker_1_port; +select has_database_privilege('myuser','regression', 'CREATE'); +select has_database_privilege('myuser','regression', 'CONNECT'); +select has_database_privilege('myuser','regression', 'TEMP'); +select has_database_privilege('myuser','regression', 'TEMPORARY'); +\c - - - :master_port + + +revoke ALL on database regression from myuser; + +select has_database_privilege('myuser','regression', 'CREATE'); +select has_database_privilege('myuser','regression', 'CONNECT'); +select has_database_privilege('myuser','regression', 'TEMP'); +select has_database_privilege('myuser','regression', 'TEMPORARY'); +\c - - - :worker_1_port + +select has_database_privilege('myuser','regression', 'CREATE'); +select has_database_privilege('myuser','regression', 'CONNECT'); +select has_database_privilege('myuser','regression', 'TEMP'); +select has_database_privilege('myuser','regression', 'TEMPORARY'); +\c - - - :master_port + +drop user myuser; +----------------------------------------------------------------------- + +-- test CREATE,CONNECT,TEMP,TEMPORARY privileges one by one on database +create user myuser; + +grant CREATE,CONNECT,TEMP,TEMPORARY on database regression to myuser; + +select has_database_privilege('myuser','regression', 'CREATE'); +select has_database_privilege('myuser','regression', 'CONNECT'); +select has_database_privilege('myuser','regression', 'TEMP'); +select has_database_privilege('myuser','regression', 'TEMPORARY'); +\c - - - :worker_1_port; +select has_database_privilege('myuser','regression', 'CREATE'); +select has_database_privilege('myuser','regression', 'CONNECT'); +select has_database_privilege('myuser','regression', 'TEMP'); +select has_database_privilege('myuser','regression', 'TEMPORARY'); +\c - - - :master_port + +RESET ROLE; + +revoke CREATE,CONNECT,TEMP,TEMPORARY on database regression from myuser; + +select has_database_privilege('myuser','regression', 'CREATE'); +select has_database_privilege('myuser','regression', 'CONNECT'); +select has_database_privilege('myuser','regression', 'TEMP'); +select has_database_privilege('myuser','regression', 'TEMPORARY'); +\c - - - :worker_1_port + +select has_database_privilege('myuser','regression', 'CREATE'); +select has_database_privilege('myuser','regression', 'CONNECT'); +select has_database_privilege('myuser','regression', 'TEMP'); +select has_database_privilege('myuser','regression', 'TEMPORARY'); +\c - - - :master_port + +drop user myuser; +----------------------------------------------------------------------- + +-- test CREATE,CONNECT,TEMP,TEMPORARY privileges one by one on database with grant option +create user myuser; +create user myuser_1; + +grant CREATE,CONNECT,TEMP,TEMPORARY on database regression to myuser; + +set role myuser; +--here since myuser does not have grant option, it should fail +grant CREATE,CONNECT,TEMP,TEMPORARY on database regression to myuser_1; + +select has_database_privilege('myuser_1','regression', 'CREATE'); +select has_database_privilege('myuser_1','regression', 'CONNECT'); +select has_database_privilege('myuser_1','regression', 'TEMP'); +select has_database_privilege('myuser_1','regression', 'TEMPORARY'); + +\c - - - :worker_1_port + +select has_database_privilege('myuser_1','regression', 'CREATE'); +select has_database_privilege('myuser_1','regression', 'CONNECT'); +select has_database_privilege('myuser_1','regression', 'TEMP'); +select has_database_privilege('myuser_1','regression', 'TEMPORARY'); + +\c - - - :master_port + +RESET ROLE; + +grant CREATE,CONNECT,TEMP,TEMPORARY on database regression to myuser with grant option; +set role myuser; + +--here since myuser have grant option, it should succeed +grant CREATE,CONNECT,TEMP,TEMPORARY on database regression to myuser_1 granted by myuser; + +select has_database_privilege('myuser_1','regression', 'CREATE'); +select has_database_privilege('myuser_1','regression', 'CONNECT'); +select has_database_privilege('myuser_1','regression', 'TEMP'); +select has_database_privilege('myuser_1','regression', 'TEMPORARY'); + +\c - - - :worker_1_port + +select has_database_privilege('myuser_1','regression', 'CREATE'); +select has_database_privilege('myuser_1','regression', 'CONNECT'); +select has_database_privilege('myuser_1','regression', 'TEMP'); +select has_database_privilege('myuser_1','regression', 'TEMPORARY'); + +\c - - - :master_port + + +RESET ROLE; + +--below test should fail and should throw an error since myuser_1 still have the dependent privileges +revoke CREATE,CONNECT,TEMP,TEMPORARY on database regression from myuser restrict; +--below test should fail and should throw an error since myuser_1 still have the dependent privileges +revoke grant option for CREATE,CONNECT,TEMP,TEMPORARY on database regression from myuser restrict ; + +--below test should succeed and should not throw any error since myuser_1 privileges are revoked with cascade +revoke grant option for CREATE,CONNECT,TEMP,TEMPORARY on database regression from myuser cascade ; + +--here we test if myuser still have the privileges after revoke grant option for + +select has_database_privilege('myuser','regression', 'CREATE'); +select has_database_privilege('myuser','regression', 'CONNECT'); +select has_database_privilege('myuser','regression', 'TEMP'); +select has_database_privilege('myuser','regression', 'TEMPORARY'); + +\c - - - :worker_1_port + +select has_database_privilege('myuser','regression', 'CREATE'); +select has_database_privilege('myuser','regression', 'CONNECT'); +select has_database_privilege('myuser','regression', 'TEMP'); +select has_database_privilege('myuser','regression', 'TEMPORARY'); + +\c - - - :master_port + +reset role; + + + +revoke CREATE,CONNECT,TEMP,TEMPORARY on database regression from myuser; +revoke CREATE,CONNECT,TEMP,TEMPORARY on database regression from myuser_1; + +drop user myuser_1; +drop user myuser; + +----------------------------------------------------------------------- + +-- test CREATE,CONNECT,TEMP,TEMPORARY privileges one by one on database multi database +-- and multi user + +create user myuser; +create user myuser_1; + +create database test_db; +SELECT result FROM run_command_on_workers($$create database test_db$$); +revoke connect,temp,temporary on database test_db from public; + +grant CREATE,CONNECT,TEMP,TEMPORARY on database regression,test_db to myuser,myuser_1; + +select has_database_privilege('myuser','regression', 'CREATE'); +select has_database_privilege('myuser','regression', 'CONNECT'); +select has_database_privilege('myuser','regression', 'TEMP'); +select has_database_privilege('myuser','regression', 'TEMPORARY'); + +select has_database_privilege('myuser','test_db', 'CREATE'); +select has_database_privilege('myuser','test_db', 'CONNECT'); +select has_database_privilege('myuser','test_db', 'TEMP'); +select has_database_privilege('myuser','test_db', 'TEMPORARY'); + +select has_database_privilege('myuser_1','regression', 'CREATE'); +select has_database_privilege('myuser_1','regression', 'CONNECT'); +select has_database_privilege('myuser_1','regression', 'TEMP'); +select has_database_privilege('myuser_1','regression', 'TEMPORARY'); + +select has_database_privilege('myuser_1','test_db', 'CREATE'); +select has_database_privilege('myuser_1','test_db', 'CONNECT'); +select has_database_privilege('myuser_1','test_db', 'TEMP'); +select has_database_privilege('myuser_1','test_db', 'TEMPORARY'); + +\c - - - :worker_1_port + +select has_database_privilege('myuser','regression', 'CREATE'); +select has_database_privilege('myuser','regression', 'CONNECT'); +select has_database_privilege('myuser','regression', 'TEMP'); +select has_database_privilege('myuser','regression', 'TEMPORARY'); + +select has_database_privilege('myuser','test_db', 'CREATE'); +select has_database_privilege('myuser','test_db', 'CONNECT'); +select has_database_privilege('myuser','test_db', 'TEMP'); +select has_database_privilege('myuser','test_db', 'TEMPORARY'); + +select has_database_privilege('myuser_1','regression', 'CREATE'); +select has_database_privilege('myuser_1','regression', 'CONNECT'); +select has_database_privilege('myuser_1','regression', 'TEMP'); +select has_database_privilege('myuser_1','regression', 'TEMPORARY'); + +select has_database_privilege('myuser_1','test_db', 'CREATE'); +select has_database_privilege('myuser_1','test_db', 'CONNECT'); +select has_database_privilege('myuser_1','test_db', 'TEMP'); +select has_database_privilege('myuser_1','test_db', 'TEMPORARY'); + +\c - - - :master_port + +RESET ROLE; +--below test should fail and should throw an error +revoke CREATE,CONNECT,TEMP,TEMPORARY on database regression,test_db from myuser ; + +--below test should succeed and should not throw any error +revoke CREATE,CONNECT,TEMP,TEMPORARY on database regression,test_db from myuser_1; + +--below test should succeed and should not throw any error +revoke CREATE,CONNECT,TEMP,TEMPORARY on database regression,test_db from myuser cascade; + + +select has_database_privilege('myuser','regression', 'CREATE'); +select has_database_privilege('myuser','regression', 'CONNECT'); +select has_database_privilege('myuser','regression', 'TEMP'); +select has_database_privilege('myuser','regression', 'TEMPORARY'); + +select has_database_privilege('myuser','test_db', 'CREATE'); +select has_database_privilege('myuser','test_db', 'CONNECT'); +select has_database_privilege('myuser','test_db', 'TEMP'); +select has_database_privilege('myuser','test_db', 'TEMPORARY'); + +select has_database_privilege('myuser_1','regression', 'CREATE'); +select has_database_privilege('myuser_1','regression', 'CONNECT'); +select has_database_privilege('myuser_1','regression', 'TEMP'); +select has_database_privilege('myuser_1','regression', 'TEMPORARY'); + +select has_database_privilege('myuser_1','test_db', 'CREATE'); +select has_database_privilege('myuser_1','test_db', 'CONNECT'); +select has_database_privilege('myuser_1','test_db', 'TEMP'); +select has_database_privilege('myuser_1','test_db', 'TEMPORARY'); + +\c - - - :worker_1_port + +select has_database_privilege('myuser','regression', 'CREATE'); +select has_database_privilege('myuser','regression', 'CONNECT'); +select has_database_privilege('myuser','regression', 'TEMP'); +select has_database_privilege('myuser','regression', 'TEMPORARY'); + +select has_database_privilege('myuser','test_db', 'CREATE'); +select has_database_privilege('myuser','test_db', 'CONNECT'); +select has_database_privilege('myuser','test_db', 'TEMP'); +select has_database_privilege('myuser','test_db', 'TEMPORARY'); + +select has_database_privilege('myuser_1','regression', 'CREATE'); +select has_database_privilege('myuser_1','regression', 'CONNECT'); +select has_database_privilege('myuser_1','regression', 'TEMP'); +select has_database_privilege('myuser_1','regression', 'TEMPORARY'); + +select has_database_privilege('myuser_1','test_db', 'CREATE'); +select has_database_privilege('myuser_1','test_db', 'CONNECT'); +select has_database_privilege('myuser_1','test_db', 'TEMP'); +select has_database_privilege('myuser_1','test_db', 'TEMPORARY'); + +\c - - - :master_port + +reset role; + +drop user myuser_1; +drop user myuser; + +drop database test_db; +SELECT result FROM run_command_on_workers($$drop database test_db$$); +--------------------------------------------------------------------------- +-- rollbacks public role database privileges to original state +grant connect,temp,temporary on database regression to public; + + +SET client_min_messages TO ERROR; +DROP SCHEMA grant_on_database_propagation CASCADE; + +--------------------------------------------------------------------------- diff --git a/src/test/regress/sql/intermediate_results.sql b/src/test/regress/sql/intermediate_results.sql index 4cd54b29ba9..2eaa6e71545 100644 --- a/src/test/regress/sql/intermediate_results.sql +++ b/src/test/regress/sql/intermediate_results.sql @@ -255,7 +255,6 @@ SELECT * FROM read_intermediate_results(ARRAY['squares_1', 'squares_2']::text[], -- test refreshing mat views SET client_min_messages TO ERROR; CREATE USER some_other_user; -SELECT run_command_on_workers($$GRANT ALL ON DATABASE regression TO some_other_user;$$); GRANT ALL ON DATABASE regression TO some_other_user; RESET client_min_messages; diff --git a/src/test/regress/sql/multi_deparse_shard_query.sql b/src/test/regress/sql/multi_deparse_shard_query.sql index 252f22fb98f..faffdf86225 100644 --- a/src/test/regress/sql/multi_deparse_shard_query.sql +++ b/src/test/regress/sql/multi_deparse_shard_query.sql @@ -8,6 +8,9 @@ SHOW server_version \gset SELECT substring(:'server_version', '\d+')::int >= 15 AS server_version_ge_15; +CREATE SCHEMA multi_deparse_shard_query; +SET search_path TO multi_deparse_shard_query; + SET citus.next_shard_id TO 13100000; SET citus.shard_replication_factor TO 1; @@ -304,3 +307,6 @@ SELECT FROM raw_events_1; '); + +SET client_min_messages TO ERROR; +DROP SCHEMA multi_deparse_shard_query CASCADE; diff --git a/src/test/regress/sql/schema_based_sharding.sql b/src/test/regress/sql/schema_based_sharding.sql index 2b9bbf51627..bd8065ab938 100644 --- a/src/test/regress/sql/schema_based_sharding.sql +++ b/src/test/regress/sql/schema_based_sharding.sql @@ -905,7 +905,6 @@ CREATE ROLE test_non_super_user WITH LOGIN; ALTER ROLE test_non_super_user NOSUPERUSER; GRANT CREATE ON DATABASE regression TO test_non_super_user; -SELECT result FROM run_command_on_workers($$GRANT CREATE ON DATABASE regression TO test_non_super_user$$); GRANT CREATE ON SCHEMA public TO test_non_super_user ; @@ -997,7 +996,6 @@ $$); \c - postgres REVOKE CREATE ON DATABASE regression FROM test_non_super_user; -SELECT result FROM run_command_on_workers($$REVOKE CREATE ON DATABASE regression FROM test_non_super_user$$); REVOKE CREATE ON SCHEMA public FROM test_non_super_user; From 70c8aba967c3ffd6b03f22e1a0396064ff65fbe7 Mon Sep 17 00:00:00 2001 From: Naisila Puka <37271756+naisila@users.noreply.github.com> Date: Thu, 24 Aug 2023 15:26:40 +0300 Subject: [PATCH 08/10] PG16 - Add tests for CREATE/ALTER TABLE .. STORAGE (#7140) Relevant PG commits: https://github.com/postgres/postgres/commit/784cedd https://github.com/postgres/postgres/commit/b9424d0 --- src/test/regress/expected/pg16.out | 75 ++++++++++++++++++++++++++++++ src/test/regress/sql/pg16.sql | 28 +++++++++++ 2 files changed, 103 insertions(+) diff --git a/src/test/regress/expected/pg16.out b/src/test/regress/expected/pg16.out index 4a8703ad70b..829f91eafa8 100644 --- a/src/test/regress/expected/pg16.out +++ b/src/test/regress/expected/pg16.out @@ -11,6 +11,7 @@ SELECT substring(:'server_version', '\d+')::int >= 16 AS server_version_ge_16 CREATE SCHEMA pg16; SET search_path TO pg16; SET citus.next_shard_id TO 950000; +ALTER SEQUENCE pg_catalog.pg_dist_colocationid_seq RESTART 1400000; SET citus.shard_count TO 1; SET citus.shard_replication_factor TO 1; -- test the new vacuum and analyze options @@ -127,6 +128,80 @@ HINT: Consider specifying a name for the statistics CREATE STATISTICS (ndistinct, dependencies, mcv) on a, b from test_stats; ERROR: cannot create statistics without a name on a Citus table HINT: Consider specifying a name for the statistics +-- STORAGE option in CREATE is already propagated by Citus +-- Relevant PG commit: +-- https://github.com/postgres/postgres/commit/784cedd +CREATE TABLE test_storage (a text, c text STORAGE plain); +SELECT create_distributed_table('test_storage', 'a', shard_count := 2); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT result FROM run_command_on_all_nodes +($$ SELECT array_agg(DISTINCT (attname, attstorage)) FROM pg_attribute + WHERE attrelid::regclass::text ILIKE 'pg16.test_storage%' AND attnum > 0;$$) ORDER BY 1; + result +--------------------------------------------------------------------- + {"(a,x)","(c,p)"} + {"(a,x)","(c,p)"} + {"(a,x)","(c,p)"} +(3 rows) + +SELECT alter_distributed_table('test_storage', shard_count := 4); +NOTICE: creating a new table for pg16.test_storage +NOTICE: moving the data of pg16.test_storage +NOTICE: dropping the old pg16.test_storage +NOTICE: renaming the new table to pg16.test_storage + alter_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT result FROM run_command_on_all_nodes +($$ SELECT array_agg(DISTINCT (attname, attstorage)) FROM pg_attribute + WHERE attrelid::regclass::text ILIKE 'pg16.test_storage%' AND attnum > 0;$$) ORDER BY 1; + result +--------------------------------------------------------------------- + {"(a,x)","(c,p)"} + {"(a,x)","(c,p)"} + {"(a,x)","(c,p)"} +(3 rows) + +SELECT undistribute_table('test_storage'); +NOTICE: creating a new table for pg16.test_storage +NOTICE: moving the data of pg16.test_storage +NOTICE: dropping the old pg16.test_storage +NOTICE: renaming the new table to pg16.test_storage + undistribute_table +--------------------------------------------------------------------- + +(1 row) + +SELECT result FROM run_command_on_all_nodes +($$ SELECT array_agg(DISTINCT (attname, attstorage)) FROM pg_attribute + WHERE attrelid::regclass::text ILIKE 'pg16.test_storage%' AND attnum > 0;$$) ORDER BY 1; + result +--------------------------------------------------------------------- + + + {"(a,x)","(c,p)"} +(3 rows) + +-- New option to change storage to DEFAULT in PG16 +-- ALTER TABLE .. ALTER COLUMN .. SET STORAGE is already +-- not supported by Citus, so this is also not supported +-- Relevant PG commit: +-- https://github.com/postgres/postgres/commit/b9424d0 +SELECT create_distributed_table('test_storage', 'a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +ALTER TABLE test_storage ALTER a SET STORAGE default; +ERROR: alter table command is currently unsupported +DETAIL: Only ADD|DROP COLUMN, SET|DROP NOT NULL, SET|DROP DEFAULT, ADD|DROP|VALIDATE CONSTRAINT, SET (), RESET (), ENABLE|DISABLE|NO FORCE|FORCE ROW LEVEL SECURITY, ATTACH|DETACH PARTITION and TYPE subcommands are supported. -- Tests for SQL/JSON: support the IS JSON predicate -- Relevant PG commit: -- https://github.com/postgres/postgres/commit/6ee30209 diff --git a/src/test/regress/sql/pg16.sql b/src/test/regress/sql/pg16.sql index 85df9eabb0a..f60f5a6ad0d 100644 --- a/src/test/regress/sql/pg16.sql +++ b/src/test/regress/sql/pg16.sql @@ -12,6 +12,7 @@ SELECT substring(:'server_version', '\d+')::int >= 16 AS server_version_ge_16 CREATE SCHEMA pg16; SET search_path TO pg16; SET citus.next_shard_id TO 950000; +ALTER SEQUENCE pg_catalog.pg_dist_colocationid_seq RESTART 1400000; SET citus.shard_count TO 1; SET citus.shard_replication_factor TO 1; @@ -76,6 +77,33 @@ CREATE STATISTICS (dependencies) ON a, b FROM test_stats; CREATE STATISTICS (ndistinct, dependencies) on a, b from test_stats; CREATE STATISTICS (ndistinct, dependencies, mcv) on a, b from test_stats; +-- STORAGE option in CREATE is already propagated by Citus +-- Relevant PG commit: +-- https://github.com/postgres/postgres/commit/784cedd +CREATE TABLE test_storage (a text, c text STORAGE plain); +SELECT create_distributed_table('test_storage', 'a', shard_count := 2); +SELECT result FROM run_command_on_all_nodes +($$ SELECT array_agg(DISTINCT (attname, attstorage)) FROM pg_attribute + WHERE attrelid::regclass::text ILIKE 'pg16.test_storage%' AND attnum > 0;$$) ORDER BY 1; + +SELECT alter_distributed_table('test_storage', shard_count := 4); +SELECT result FROM run_command_on_all_nodes +($$ SELECT array_agg(DISTINCT (attname, attstorage)) FROM pg_attribute + WHERE attrelid::regclass::text ILIKE 'pg16.test_storage%' AND attnum > 0;$$) ORDER BY 1; + +SELECT undistribute_table('test_storage'); +SELECT result FROM run_command_on_all_nodes +($$ SELECT array_agg(DISTINCT (attname, attstorage)) FROM pg_attribute + WHERE attrelid::regclass::text ILIKE 'pg16.test_storage%' AND attnum > 0;$$) ORDER BY 1; + +-- New option to change storage to DEFAULT in PG16 +-- ALTER TABLE .. ALTER COLUMN .. SET STORAGE is already +-- not supported by Citus, so this is also not supported +-- Relevant PG commit: +-- https://github.com/postgres/postgres/commit/b9424d0 +SELECT create_distributed_table('test_storage', 'a'); +ALTER TABLE test_storage ALTER a SET STORAGE default; + -- Tests for SQL/JSON: support the IS JSON predicate -- Relevant PG commit: -- https://github.com/postgres/postgres/commit/6ee30209 From afab879de3da6e330f8f29188878d00e7ee6e1b0 Mon Sep 17 00:00:00 2001 From: Naisila Puka <37271756+naisila@users.noreply.github.com> Date: Thu, 24 Aug 2023 15:52:09 +0300 Subject: [PATCH 09/10] PG16 - Add COPY FROM default tests (#7143) Already supported in Citus, adding the same tests as in PG Relevant PG commit: https://github.com/postgres/postgres/commit/9f8377f --- src/test/regress/expected/pg16.out | 132 +++++++++++++++++++++++++++++ src/test/regress/sql/pg16.sql | 115 +++++++++++++++++++++++++ 2 files changed, 247 insertions(+) diff --git a/src/test/regress/expected/pg16.out b/src/test/regress/expected/pg16.out index 829f91eafa8..d2241c0c6b1 100644 --- a/src/test/regress/expected/pg16.out +++ b/src/test/regress/expected/pg16.out @@ -202,6 +202,138 @@ SELECT create_distributed_table('test_storage', 'a'); ALTER TABLE test_storage ALTER a SET STORAGE default; ERROR: alter table command is currently unsupported DETAIL: Only ADD|DROP COLUMN, SET|DROP NOT NULL, SET|DROP DEFAULT, ADD|DROP|VALIDATE CONSTRAINT, SET (), RESET (), ENABLE|DISABLE|NO FORCE|FORCE ROW LEVEL SECURITY, ATTACH|DETACH PARTITION and TYPE subcommands are supported. +-- +-- COPY FROM ... DEFAULT +-- Already supported in Citus, adding all PG tests with a distributed table +-- Relevant PG commit: +-- https://github.com/postgres/postgres/commit/9f8377f +CREATE TABLE copy_default ( + id integer PRIMARY KEY, + text_value text NOT NULL DEFAULT 'test', + ts_value timestamp without time zone NOT NULL DEFAULT '2022-07-05' +); +SELECT create_distributed_table('copy_default', 'id'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- if DEFAULT is not specified, then the marker will be regular data +COPY copy_default FROM stdin; +SELECT * FROM copy_default ORDER BY id; + id | text_value | ts_value +--------------------------------------------------------------------- + 1 | value | Mon Jul 04 00:00:00 2022 + 2 | D | Tue Jul 05 00:00:00 2022 +(2 rows) + +TRUNCATE copy_default; +COPY copy_default FROM stdin WITH (format csv); +SELECT * FROM copy_default ORDER BY id; + id | text_value | ts_value +--------------------------------------------------------------------- + 1 | value | Mon Jul 04 00:00:00 2022 + 2 | \D | Tue Jul 05 00:00:00 2022 +(2 rows) + +TRUNCATE copy_default; +-- DEFAULT cannot be used in binary mode +COPY copy_default FROM stdin WITH (format binary, default '\D'); +ERROR: cannot specify DEFAULT in BINARY mode +-- DEFAULT cannot be new line nor carriage return +COPY copy_default FROM stdin WITH (default E'\n'); +ERROR: COPY default representation cannot use newline or carriage return +COPY copy_default FROM stdin WITH (default E'\r'); +ERROR: COPY default representation cannot use newline or carriage return +-- DELIMITER cannot appear in DEFAULT spec +COPY copy_default FROM stdin WITH (delimiter ';', default 'test;test'); +ERROR: COPY delimiter must not appear in the DEFAULT specification +-- CSV quote cannot appear in DEFAULT spec +COPY copy_default FROM stdin WITH (format csv, quote '"', default 'test"test'); +ERROR: CSV quote character must not appear in the DEFAULT specification +-- NULL and DEFAULT spec must be different +COPY copy_default FROM stdin WITH (default '\N'); +ERROR: NULL specification and DEFAULT specification cannot be the same +-- cannot use DEFAULT marker in column that has no DEFAULT value +COPY copy_default FROM stdin WITH (default '\D'); +ERROR: unexpected default marker in COPY data +DETAIL: Column "id" has no default value. +CONTEXT: COPY copy_default, line 1: "\D value '2022-07-04'" +COPY copy_default FROM stdin WITH (format csv, default '\D'); +ERROR: unexpected default marker in COPY data +DETAIL: Column "id" has no default value. +CONTEXT: COPY copy_default, line 1: "\D,value,2022-07-04" +-- The DEFAULT marker must be unquoted and unescaped or it's not recognized +COPY copy_default FROM stdin WITH (default '\D'); +SELECT * FROM copy_default ORDER BY id; + id | text_value | ts_value +--------------------------------------------------------------------- + 1 | test | Mon Jul 04 00:00:00 2022 + 2 | \D | Mon Jul 04 00:00:00 2022 + 3 | "D" | Mon Jul 04 00:00:00 2022 +(3 rows) + +TRUNCATE copy_default; +COPY copy_default FROM stdin WITH (format csv, default '\D'); +SELECT * FROM copy_default ORDER BY id; + id | text_value | ts_value +--------------------------------------------------------------------- + 1 | test | Mon Jul 04 00:00:00 2022 + 2 | \\D | Mon Jul 04 00:00:00 2022 + 3 | \D | Mon Jul 04 00:00:00 2022 +(3 rows) + +TRUNCATE copy_default; +-- successful usage of DEFAULT option in COPY +COPY copy_default FROM stdin WITH (default '\D'); +SELECT * FROM copy_default ORDER BY id; + id | text_value | ts_value +--------------------------------------------------------------------- + 1 | value | Mon Jul 04 00:00:00 2022 + 2 | test | Sun Jul 03 00:00:00 2022 + 3 | test | Tue Jul 05 00:00:00 2022 +(3 rows) + +TRUNCATE copy_default; +COPY copy_default FROM stdin WITH (format csv, default '\D'); +SELECT * FROM copy_default ORDER BY id; + id | text_value | ts_value +--------------------------------------------------------------------- + 1 | value | Mon Jul 04 00:00:00 2022 + 2 | test | Sun Jul 03 00:00:00 2022 + 3 | test | Tue Jul 05 00:00:00 2022 +(3 rows) + +TRUNCATE copy_default; +\c - - - :worker_1_port +COPY pg16.copy_default FROM stdin WITH (format csv, default '\D'); +SELECT * FROM pg16.copy_default ORDER BY id; + id | text_value | ts_value +--------------------------------------------------------------------- + 1 | value | Mon Jul 04 00:00:00 2022 + 2 | test | Sun Jul 03 00:00:00 2022 + 3 | test | Tue Jul 05 00:00:00 2022 +(3 rows) + +\c - - - :master_port +TRUNCATE pg16.copy_default; +\c - - - :worker_2_port +COPY pg16.copy_default FROM stdin WITH (format csv, default '\D'); +SELECT * FROM pg16.copy_default ORDER BY id; + id | text_value | ts_value +--------------------------------------------------------------------- + 1 | value | Mon Jul 04 00:00:00 2022 + 2 | test | Sun Jul 03 00:00:00 2022 + 3 | test | Tue Jul 05 00:00:00 2022 +(3 rows) + +\c - - - :master_port +SET search_path TO pg16; +SET citus.shard_count TO 1; +SET citus.shard_replication_factor TO 1; +-- DEFAULT cannot be used in COPY TO +COPY (select 1 as test) TO stdout WITH (default '\D'); +ERROR: COPY DEFAULT only available using COPY FROM -- Tests for SQL/JSON: support the IS JSON predicate -- Relevant PG commit: -- https://github.com/postgres/postgres/commit/6ee30209 diff --git a/src/test/regress/sql/pg16.sql b/src/test/regress/sql/pg16.sql index f60f5a6ad0d..4ce1321ee8f 100644 --- a/src/test/regress/sql/pg16.sql +++ b/src/test/regress/sql/pg16.sql @@ -104,6 +104,121 @@ SELECT result FROM run_command_on_all_nodes SELECT create_distributed_table('test_storage', 'a'); ALTER TABLE test_storage ALTER a SET STORAGE default; +-- +-- COPY FROM ... DEFAULT +-- Already supported in Citus, adding all PG tests with a distributed table +-- Relevant PG commit: +-- https://github.com/postgres/postgres/commit/9f8377f +CREATE TABLE copy_default ( + id integer PRIMARY KEY, + text_value text NOT NULL DEFAULT 'test', + ts_value timestamp without time zone NOT NULL DEFAULT '2022-07-05' +); +SELECT create_distributed_table('copy_default', 'id'); + +-- if DEFAULT is not specified, then the marker will be regular data +COPY copy_default FROM stdin; +1 value '2022-07-04' +2 \D '2022-07-05' +\. +SELECT * FROM copy_default ORDER BY id; +TRUNCATE copy_default; + +COPY copy_default FROM stdin WITH (format csv); +1,value,2022-07-04 +2,\D,2022-07-05 +\. +SELECT * FROM copy_default ORDER BY id; +TRUNCATE copy_default; + +-- DEFAULT cannot be used in binary mode +COPY copy_default FROM stdin WITH (format binary, default '\D'); + +-- DEFAULT cannot be new line nor carriage return +COPY copy_default FROM stdin WITH (default E'\n'); +COPY copy_default FROM stdin WITH (default E'\r'); + +-- DELIMITER cannot appear in DEFAULT spec +COPY copy_default FROM stdin WITH (delimiter ';', default 'test;test'); + +-- CSV quote cannot appear in DEFAULT spec +COPY copy_default FROM stdin WITH (format csv, quote '"', default 'test"test'); + +-- NULL and DEFAULT spec must be different +COPY copy_default FROM stdin WITH (default '\N'); + +-- cannot use DEFAULT marker in column that has no DEFAULT value +COPY copy_default FROM stdin WITH (default '\D'); +\D value '2022-07-04' +2 \D '2022-07-05' +\. + +COPY copy_default FROM stdin WITH (format csv, default '\D'); +\D,value,2022-07-04 +2,\D,2022-07-05 +\. + +-- The DEFAULT marker must be unquoted and unescaped or it's not recognized +COPY copy_default FROM stdin WITH (default '\D'); +1 \D '2022-07-04' +2 \\D '2022-07-04' +3 "\D" '2022-07-04' +\. +SELECT * FROM copy_default ORDER BY id; +TRUNCATE copy_default; + +COPY copy_default FROM stdin WITH (format csv, default '\D'); +1,\D,2022-07-04 +2,\\D,2022-07-04 +3,"\D",2022-07-04 +\. +SELECT * FROM copy_default ORDER BY id; +TRUNCATE copy_default; + +-- successful usage of DEFAULT option in COPY +COPY copy_default FROM stdin WITH (default '\D'); +1 value '2022-07-04' +2 \D '2022-07-03' +3 \D \D +\. +SELECT * FROM copy_default ORDER BY id; +TRUNCATE copy_default; + +COPY copy_default FROM stdin WITH (format csv, default '\D'); +1,value,2022-07-04 +2,\D,2022-07-03 +3,\D,\D +\. +SELECT * FROM copy_default ORDER BY id; +TRUNCATE copy_default; + +\c - - - :worker_1_port +COPY pg16.copy_default FROM stdin WITH (format csv, default '\D'); +1,value,2022-07-04 +2,\D,2022-07-03 +3,\D,\D +\. +SELECT * FROM pg16.copy_default ORDER BY id; + +\c - - - :master_port +TRUNCATE pg16.copy_default; + +\c - - - :worker_2_port +COPY pg16.copy_default FROM stdin WITH (format csv, default '\D'); +1,value,2022-07-04 +2,\D,2022-07-03 +3,\D,\D +\. +SELECT * FROM pg16.copy_default ORDER BY id; + +\c - - - :master_port +SET search_path TO pg16; +SET citus.shard_count TO 1; +SET citus.shard_replication_factor TO 1; + +-- DEFAULT cannot be used in COPY TO +COPY (select 1 as test) TO stdout WITH (default '\D'); + -- Tests for SQL/JSON: support the IS JSON predicate -- Relevant PG commit: -- https://github.com/postgres/postgres/commit/6ee30209 From d5d1684c45ba4e6fcce4326bf7996f0b94c356fc Mon Sep 17 00:00:00 2001 From: Onur Tirtir Date: Mon, 28 Aug 2023 11:03:38 +0300 Subject: [PATCH 10/10] Use correct errorCode for the errors thrown during recovery (#7146) --- src/backend/distributed/commands/multi_copy.c | 3 ++- src/backend/distributed/metadata/metadata_cache.c | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/backend/distributed/commands/multi_copy.c b/src/backend/distributed/commands/multi_copy.c index eeed0a025fe..ec86f3f61ef 100644 --- a/src/backend/distributed/commands/multi_copy.c +++ b/src/backend/distributed/commands/multi_copy.c @@ -425,7 +425,8 @@ EnsureCopyCanRunOnRelation(Oid relationId) */ if (RecoveryInProgress() && WritableStandbyCoordinator) { - ereport(ERROR, (errmsg("COPY command to Citus tables is not allowed in " + ereport(ERROR, (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION), + errmsg("COPY command to Citus tables is not allowed in " "read-only mode"), errhint("All COPY commands to citus tables happen via 2PC, " "and 2PC requires the database to be in a writable state."), diff --git a/src/backend/distributed/metadata/metadata_cache.c b/src/backend/distributed/metadata/metadata_cache.c index 79c21030188..5ccd4a512a8 100644 --- a/src/backend/distributed/metadata/metadata_cache.c +++ b/src/backend/distributed/metadata/metadata_cache.c @@ -354,7 +354,8 @@ EnsureModificationsCanRun(void) { if (RecoveryInProgress() && !WritableStandbyCoordinator) { - ereport(ERROR, (errmsg("writing to worker nodes is not currently allowed"), + ereport(ERROR, (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION), + errmsg("writing to worker nodes is not currently allowed"), errdetail("the database is read-only"))); } @@ -415,7 +416,8 @@ EnsureModificationsCanRunOnRelation(Oid relationId) if (modifiedTableReplicated) { - ereport(ERROR, (errmsg("writing to worker nodes is not currently " + ereport(ERROR, (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION), + errmsg("writing to worker nodes is not currently " "allowed for replicated tables such as reference " "tables or hash distributed tables with replication " "factor greater than 1."),