From 9ba70696f73b87fe3674c6589f196f6a303358c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20Ozan=20Akg=C3=BCl?= Date: Wed, 12 Apr 2023 16:23:00 +0300 Subject: [PATCH 001/118] Add CPU usage to citus_stat_tenants (#6844) This PR adds CPU usage to `citus_stat_tenants` monitor. CPU usage is tracked in periods, similar to query counts. --- .../sql/udfs/citus_stat_tenants/11.3-1.sql | 8 ++- .../sql/udfs/citus_stat_tenants/latest.sql | 8 ++- .../udfs/citus_stat_tenants_local/11.3-1.sql | 6 ++- .../udfs/citus_stat_tenants_local/latest.sql | 6 ++- .../distributed/utils/citus_stat_tenants.c | 35 +++++++++++-- .../distributed/utils/citus_stat_tenants.h | 7 +++ .../regress/expected/citus_stat_tenants.out | 50 ++++++++++++------- src/test/regress/sql/citus_stat_tenants.sql | 20 ++++++-- 8 files changed, 108 insertions(+), 32 deletions(-) diff --git a/src/backend/distributed/sql/udfs/citus_stat_tenants/11.3-1.sql b/src/backend/distributed/sql/udfs/citus_stat_tenants/11.3-1.sql index bd294307c..8eea78b5c 100644 --- a/src/backend/distributed/sql/udfs/citus_stat_tenants/11.3-1.sql +++ b/src/backend/distributed/sql/udfs/citus_stat_tenants/11.3-1.sql @@ -8,6 +8,8 @@ CREATE OR REPLACE FUNCTION pg_catalog.citus_stat_tenants ( OUT read_count_in_last_period INT, OUT query_count_in_this_period INT, OUT query_count_in_last_period INT, + OUT cpu_usage_in_this_period DOUBLE PRECISION, + OUT cpu_usage_in_last_period DOUBLE PRECISION, OUT score BIGINT ) RETURNS SETOF record @@ -51,6 +53,8 @@ AS ( read_count_in_last_period INT, query_count_in_this_period INT, query_count_in_last_period INT, + cpu_usage_in_this_period DOUBLE PRECISION, + cpu_usage_in_last_period DOUBLE PRECISION, score BIGINT ) ORDER BY score DESC @@ -66,7 +70,9 @@ SELECT read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, - query_count_in_last_period + query_count_in_last_period, + cpu_usage_in_this_period, + cpu_usage_in_last_period FROM pg_catalog.citus_stat_tenants(FALSE); ALTER VIEW citus.citus_stat_tenants SET SCHEMA pg_catalog; diff --git a/src/backend/distributed/sql/udfs/citus_stat_tenants/latest.sql b/src/backend/distributed/sql/udfs/citus_stat_tenants/latest.sql index bd294307c..8eea78b5c 100644 --- a/src/backend/distributed/sql/udfs/citus_stat_tenants/latest.sql +++ b/src/backend/distributed/sql/udfs/citus_stat_tenants/latest.sql @@ -8,6 +8,8 @@ CREATE OR REPLACE FUNCTION pg_catalog.citus_stat_tenants ( OUT read_count_in_last_period INT, OUT query_count_in_this_period INT, OUT query_count_in_last_period INT, + OUT cpu_usage_in_this_period DOUBLE PRECISION, + OUT cpu_usage_in_last_period DOUBLE PRECISION, OUT score BIGINT ) RETURNS SETOF record @@ -51,6 +53,8 @@ AS ( read_count_in_last_period INT, query_count_in_this_period INT, query_count_in_last_period INT, + cpu_usage_in_this_period DOUBLE PRECISION, + cpu_usage_in_last_period DOUBLE PRECISION, score BIGINT ) ORDER BY score DESC @@ -66,7 +70,9 @@ SELECT read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, - query_count_in_last_period + query_count_in_last_period, + cpu_usage_in_this_period, + cpu_usage_in_last_period FROM pg_catalog.citus_stat_tenants(FALSE); ALTER VIEW citus.citus_stat_tenants SET SCHEMA pg_catalog; diff --git a/src/backend/distributed/sql/udfs/citus_stat_tenants_local/11.3-1.sql b/src/backend/distributed/sql/udfs/citus_stat_tenants_local/11.3-1.sql index 103ca34b4..c3383241c 100644 --- a/src/backend/distributed/sql/udfs/citus_stat_tenants_local/11.3-1.sql +++ b/src/backend/distributed/sql/udfs/citus_stat_tenants_local/11.3-1.sql @@ -6,6 +6,8 @@ CREATE OR REPLACE FUNCTION pg_catalog.citus_stat_tenants_local( OUT read_count_in_last_period INT, OUT query_count_in_this_period INT, OUT query_count_in_last_period INT, + OUT cpu_usage_in_this_period DOUBLE PRECISION, + OUT cpu_usage_in_last_period DOUBLE PRECISION, OUT score BIGINT) RETURNS SETOF RECORD LANGUAGE C @@ -19,7 +21,9 @@ SELECT read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, - query_count_in_last_period + query_count_in_last_period, + cpu_usage_in_this_period, + cpu_usage_in_last_period FROM pg_catalog.citus_stat_tenants_local() ORDER BY score DESC; diff --git a/src/backend/distributed/sql/udfs/citus_stat_tenants_local/latest.sql b/src/backend/distributed/sql/udfs/citus_stat_tenants_local/latest.sql index 103ca34b4..c3383241c 100644 --- a/src/backend/distributed/sql/udfs/citus_stat_tenants_local/latest.sql +++ b/src/backend/distributed/sql/udfs/citus_stat_tenants_local/latest.sql @@ -6,6 +6,8 @@ CREATE OR REPLACE FUNCTION pg_catalog.citus_stat_tenants_local( OUT read_count_in_last_period INT, OUT query_count_in_this_period INT, OUT query_count_in_last_period INT, + OUT cpu_usage_in_this_period DOUBLE PRECISION, + OUT cpu_usage_in_last_period DOUBLE PRECISION, OUT score BIGINT) RETURNS SETOF RECORD LANGUAGE C @@ -19,7 +21,9 @@ SELECT read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, - query_count_in_last_period + query_count_in_last_period, + cpu_usage_in_this_period, + cpu_usage_in_last_period FROM pg_catalog.citus_stat_tenants_local() ORDER BY score DESC; diff --git a/src/backend/distributed/utils/citus_stat_tenants.c b/src/backend/distributed/utils/citus_stat_tenants.c index 4bf66cb73..0d2d0754d 100644 --- a/src/backend/distributed/utils/citus_stat_tenants.c +++ b/src/backend/distributed/utils/citus_stat_tenants.c @@ -12,13 +12,14 @@ #include "unistd.h" #include "distributed/citus_safe_lib.h" +#include "distributed/colocation_utils.h" +#include "distributed/distributed_planner.h" +#include "distributed/jsonbutils.h" #include "distributed/log_utils.h" #include "distributed/listutils.h" #include "distributed/metadata_cache.h" -#include "distributed/jsonbutils.h" -#include "distributed/colocation_utils.h" +#include "distributed/multi_executor.h" #include "distributed/tuplestore.h" -#include "distributed/colocation_utils.h" #include "distributed/utils/citus_stat_tenants.h" #include "executor/execdesc.h" #include "storage/ipc.h" @@ -38,12 +39,14 @@ ExecutorEnd_hook_type prev_ExecutorEnd = NULL; #define ATTRIBUTE_PREFIX "/*{\"tId\":" #define ATTRIBUTE_STRING_FORMAT "/*{\"tId\":%s,\"cId\":%d}*/" -#define STAT_TENANTS_COLUMNS 7 +#define STAT_TENANTS_COLUMNS 9 #define ONE_QUERY_SCORE 1000000000 static char AttributeToTenant[MAX_TENANT_ATTRIBUTE_LENGTH] = ""; static CmdType AttributeToCommandType = CMD_UNKNOWN; static int AttributeToColocationGroupId = INVALID_COLOCATION_ID; +static clock_t QueryStartClock = { 0 }; +static clock_t QueryEndClock = { 0 }; static const char *SharedMemoryNameForMultiTenantMonitor = "Shared memory for multi tenant monitor"; @@ -142,7 +145,9 @@ citus_stat_tenants_local(PG_FUNCTION_ARGS) tenantStats->writesInThisPeriod); values[5] = Int32GetDatum(tenantStats->readsInLastPeriod + tenantStats->writesInLastPeriod); - values[6] = Int64GetDatum(tenantStats->score); + values[6] = Float8GetDatum(tenantStats->cpuUsageInThisPeriod); + values[7] = Float8GetDatum(tenantStats->cpuUsageInLastPeriod); + values[8] = Int64GetDatum(tenantStats->score); tuplestore_putvalues(tupleStore, tupleDescriptor, values, isNulls); } @@ -225,6 +230,7 @@ AttributeTask(char *tenantId, int colocationId, CmdType commandType) strncpy_s(AttributeToTenant, MAX_TENANT_ATTRIBUTE_LENGTH, tenantId, MAX_TENANT_ATTRIBUTE_LENGTH - 1); AttributeToCommandType = commandType; + QueryStartClock = clock(); } @@ -316,6 +322,17 @@ AttributeMetricsIfApplicable() return; } + /* + * return if we are not in the top level to make sure we are not + * stopping counting time for a sub-level execution + */ + if (ExecutorLevel != 0 || PlannerLevel != 0) + { + return; + } + + QueryEndClock = clock(); + TimestampTz queryTime = GetCurrentTimestamp(); MultiTenantMonitor *monitor = GetMultiTenantMonitor(); @@ -411,6 +428,9 @@ UpdatePeriodsIfNecessary(TenantStats *tenantStats, TimestampTz queryTime) tenantStats->readsInLastPeriod = tenantStats->readsInThisPeriod; tenantStats->readsInThisPeriod = 0; + + tenantStats->cpuUsageInLastPeriod = tenantStats->cpuUsageInThisPeriod; + tenantStats->cpuUsageInThisPeriod = 0; } /* @@ -422,6 +442,8 @@ UpdatePeriodsIfNecessary(TenantStats *tenantStats, TimestampTz queryTime) tenantStats->writesInLastPeriod = 0; tenantStats->readsInLastPeriod = 0; + + tenantStats->cpuUsageInLastPeriod = 0; } } @@ -524,6 +546,9 @@ RecordTenantStats(TenantStats *tenantStats, TimestampTz queryTime) tenantStats->writesInThisPeriod++; } + double queryCpuTime = ((double) (QueryEndClock - QueryStartClock)) / CLOCKS_PER_SEC; + tenantStats->cpuUsageInThisPeriod += queryCpuTime; + tenantStats->lastQueryTime = queryTime; } diff --git a/src/include/distributed/utils/citus_stat_tenants.h b/src/include/distributed/utils/citus_stat_tenants.h index dbc867071..aa413c29d 100644 --- a/src/include/distributed/utils/citus_stat_tenants.h +++ b/src/include/distributed/utils/citus_stat_tenants.h @@ -42,6 +42,13 @@ typedef struct TenantStats int writesInLastPeriod; int writesInThisPeriod; + + /* + * CPU time usage of this tenant in this and last periods. + */ + double cpuUsageInLastPeriod; + double cpuUsageInThisPeriod; + /* * The latest time this tenant ran a query. This value is used to update the score later. */ diff --git a/src/test/regress/expected/citus_stat_tenants.out b/src/test/regress/expected/citus_stat_tenants.out index 44376dc3d..7e75b67fe 100644 --- a/src/test/regress/expected/citus_stat_tenants.out +++ b/src/test/regress/expected/citus_stat_tenants.out @@ -71,14 +71,17 @@ INSERT INTO dist_tbl VALUES (2, 'abcd'); UPDATE dist_tbl SET b = a + 1 WHERE a = 3; UPDATE dist_tbl SET b = a + 1 WHERE a = 4; DELETE FROM dist_tbl WHERE a = 5; -SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants(true) ORDER BY tenant_attribute; - tenant_attribute | read_count_in_this_period | read_count_in_last_period | query_count_in_this_period | query_count_in_last_period +SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period, + (cpu_usage_in_this_period>0) AS cpu_is_used_in_this_period, (cpu_usage_in_last_period>0) AS cpu_is_used_in_last_period +FROM citus_stat_tenants(true) +ORDER BY tenant_attribute; + tenant_attribute | read_count_in_this_period | read_count_in_last_period | query_count_in_this_period | query_count_in_last_period | cpu_is_used_in_this_period | cpu_is_used_in_last_period --------------------------------------------------------------------- - 1 | 0 | 0 | 1 | 0 - 2 | 0 | 0 | 1 | 0 - 3 | 0 | 0 | 1 | 0 - 4 | 0 | 0 | 1 | 0 - 5 | 0 | 0 | 1 | 0 + 1 | 0 | 0 | 1 | 0 | t | f + 2 | 0 | 0 | 1 | 0 | t | f + 3 | 0 | 0 | 1 | 0 | t | f + 4 | 0 | 0 | 1 | 0 | t | f + 5 | 0 | 0 | 1 | 0 | t | f (5 rows) SELECT citus_stat_tenants_reset(); @@ -241,11 +244,14 @@ SELECT count(*)>=0 FROM dist_tbl WHERE a = 1; INSERT INTO dist_tbl VALUES (5, 'abcd'); \c - - - :worker_1_port -SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants_local ORDER BY tenant_attribute; - tenant_attribute | read_count_in_this_period | read_count_in_last_period | query_count_in_this_period | query_count_in_last_period +SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period, + (cpu_usage_in_this_period>0) AS cpu_is_used_in_this_period, (cpu_usage_in_last_period>0) AS cpu_is_used_in_last_period +FROM citus_stat_tenants_local +ORDER BY tenant_attribute; + tenant_attribute | read_count_in_this_period | read_count_in_last_period | query_count_in_this_period | query_count_in_last_period | cpu_is_used_in_this_period | cpu_is_used_in_last_period --------------------------------------------------------------------- - 1 | 1 | 0 | 1 | 0 - 5 | 0 | 0 | 1 | 0 + 1 | 1 | 0 | 1 | 0 | t | f + 5 | 0 | 0 | 1 | 0 | t | f (2 rows) -- simulate passing the period @@ -256,11 +262,14 @@ SELECT sleep_until_next_period(); (1 row) -SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants_local ORDER BY tenant_attribute; - tenant_attribute | read_count_in_this_period | read_count_in_last_period | query_count_in_this_period | query_count_in_last_period +SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period, + (cpu_usage_in_this_period>0) AS cpu_is_used_in_this_period, (cpu_usage_in_last_period>0) AS cpu_is_used_in_last_period +FROM citus_stat_tenants_local +ORDER BY tenant_attribute; + tenant_attribute | read_count_in_this_period | read_count_in_last_period | query_count_in_this_period | query_count_in_last_period | cpu_is_used_in_this_period | cpu_is_used_in_last_period --------------------------------------------------------------------- - 1 | 0 | 1 | 0 | 1 - 5 | 0 | 0 | 0 | 1 + 1 | 0 | 1 | 0 | 1 | f | t + 5 | 0 | 0 | 0 | 1 | f | t (2 rows) SELECT sleep_until_next_period(); @@ -269,11 +278,14 @@ SELECT sleep_until_next_period(); (1 row) -SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants_local ORDER BY tenant_attribute; - tenant_attribute | read_count_in_this_period | read_count_in_last_period | query_count_in_this_period | query_count_in_last_period +SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period, + (cpu_usage_in_this_period>0) AS cpu_is_used_in_this_period, (cpu_usage_in_last_period>0) AS cpu_is_used_in_last_period +FROM citus_stat_tenants_local +ORDER BY tenant_attribute; + tenant_attribute | read_count_in_this_period | read_count_in_last_period | query_count_in_this_period | query_count_in_last_period | cpu_is_used_in_this_period | cpu_is_used_in_last_period --------------------------------------------------------------------- - 1 | 0 | 0 | 0 | 0 - 5 | 0 | 0 | 0 | 0 + 1 | 0 | 0 | 0 | 0 | f | f + 5 | 0 | 0 | 0 | 0 | f | f (2 rows) \c - - - :master_port diff --git a/src/test/regress/sql/citus_stat_tenants.sql b/src/test/regress/sql/citus_stat_tenants.sql index eb1e0af42..f327aefa6 100644 --- a/src/test/regress/sql/citus_stat_tenants.sql +++ b/src/test/regress/sql/citus_stat_tenants.sql @@ -35,7 +35,10 @@ UPDATE dist_tbl SET b = a + 1 WHERE a = 3; UPDATE dist_tbl SET b = a + 1 WHERE a = 4; DELETE FROM dist_tbl WHERE a = 5; -SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants(true) ORDER BY tenant_attribute; +SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period, + (cpu_usage_in_this_period>0) AS cpu_is_used_in_this_period, (cpu_usage_in_last_period>0) AS cpu_is_used_in_last_period +FROM citus_stat_tenants(true) +ORDER BY tenant_attribute; SELECT citus_stat_tenants_reset(); @@ -84,17 +87,26 @@ SELECT count(*)>=0 FROM dist_tbl WHERE a = 1; INSERT INTO dist_tbl VALUES (5, 'abcd'); \c - - - :worker_1_port -SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants_local ORDER BY tenant_attribute; +SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period, + (cpu_usage_in_this_period>0) AS cpu_is_used_in_this_period, (cpu_usage_in_last_period>0) AS cpu_is_used_in_last_period +FROM citus_stat_tenants_local +ORDER BY tenant_attribute; -- simulate passing the period SET citus.stat_tenants_period TO 2; SELECT sleep_until_next_period(); -SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants_local ORDER BY tenant_attribute; +SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period, + (cpu_usage_in_this_period>0) AS cpu_is_used_in_this_period, (cpu_usage_in_last_period>0) AS cpu_is_used_in_last_period +FROM citus_stat_tenants_local +ORDER BY tenant_attribute; SELECT sleep_until_next_period(); -SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants_local ORDER BY tenant_attribute; +SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period, + (cpu_usage_in_this_period>0) AS cpu_is_used_in_this_period, (cpu_usage_in_last_period>0) AS cpu_is_used_in_last_period +FROM citus_stat_tenants_local +ORDER BY tenant_attribute; \c - - - :master_port SET search_path TO citus_stat_tenants; From 3286ec59e91e2fe4bf7cb3a63df55d9f4266e3d8 Mon Sep 17 00:00:00 2001 From: aykut-bozkurt <51649454+aykut-bozkurt@users.noreply.github.com> Date: Thu, 13 Apr 2023 13:13:28 +0300 Subject: [PATCH 002/118] fix 3 flaky tests in failure schedule (#6846) Fixed 3 flaky tests in failure tests which caused flakiness in other tests due to changed node and group sequence ids during node addition-removal. --- .../expected/failure_add_disable_node.out | 4 +- ..._create_distributed_table_concurrently.out | 8 +++- .../failure_mx_metadata_sync_multi_trans.out | 42 ++++++++----------- .../regress/sql/failure_add_disable_node.sql | 2 + ..._create_distributed_table_concurrently.sql | 5 +++ .../failure_mx_metadata_sync_multi_trans.sql | 7 ++-- 6 files changed, 36 insertions(+), 32 deletions(-) diff --git a/src/test/regress/expected/failure_add_disable_node.out b/src/test/regress/expected/failure_add_disable_node.out index d2a389d96..395ad5097 100644 --- a/src/test/regress/expected/failure_add_disable_node.out +++ b/src/test/regress/expected/failure_add_disable_node.out @@ -187,6 +187,8 @@ ORDER BY placementid; (1 row) -- reset cluster to original state +ALTER SEQUENCE pg_dist_node_nodeid_seq RESTART 2; +ALTER SEQUENCE pg_dist_groupid_seq RESTART 2; SELECT citus.mitmproxy('conn.allow()'); mitmproxy --------------------------------------------------------------------- @@ -196,7 +198,7 @@ SELECT citus.mitmproxy('conn.allow()'); SELECT master_add_node('localhost', :worker_2_proxy_port); master_add_node --------------------------------------------------------------------- - 4 + 2 (1 row) -- verify node is added diff --git a/src/test/regress/expected/failure_create_distributed_table_concurrently.out b/src/test/regress/expected/failure_create_distributed_table_concurrently.out index bd3382256..cf1df651a 100644 --- a/src/test/regress/expected/failure_create_distributed_table_concurrently.out +++ b/src/test/regress/expected/failure_create_distributed_table_concurrently.out @@ -12,6 +12,8 @@ SET citus.shard_count TO 2; SET citus.shard_replication_factor TO 1; SET citus.max_adaptive_executor_pool_size TO 1; SELECT pg_backend_pid() as pid \gset +ALTER SEQUENCE pg_catalog.pg_dist_shardid_seq RESTART 222222; +ALTER SEQUENCE pg_catalog.pg_dist_placement_placementid_seq RESTART 333333; -- make sure coordinator is in the metadata SELECT citus_set_coordinator_host('localhost', 57636); citus_set_coordinator_host @@ -189,8 +191,8 @@ SELECT create_distributed_table_concurrently('table_1', 'id'); SELECT * FROM pg_dist_shard WHERE logicalrelid = 'table_1'::regclass; logicalrelid | shardid | shardstorage | shardminvalue | shardmaxvalue --------------------------------------------------------------------- - table_1 | 1880080 | t | -2147483648 | -1 - table_1 | 1880081 | t | 0 | 2147483647 + table_1 | 222247 | t | -2147483648 | -1 + table_1 | 222248 | t | 0 | 2147483647 (2 rows) DROP SCHEMA create_dist_tbl_con CASCADE; @@ -201,3 +203,5 @@ SELECT citus_remove_node('localhost', 57636); (1 row) +ALTER SEQUENCE pg_dist_node_nodeid_seq RESTART 3; +ALTER SEQUENCE pg_dist_groupid_seq RESTART 3; diff --git a/src/test/regress/expected/failure_mx_metadata_sync_multi_trans.out b/src/test/regress/expected/failure_mx_metadata_sync_multi_trans.out index fd0fc632b..0c66b2548 100644 --- a/src/test/regress/expected/failure_mx_metadata_sync_multi_trans.out +++ b/src/test/regress/expected/failure_mx_metadata_sync_multi_trans.out @@ -597,8 +597,8 @@ ERROR: connection not open SELECT * FROM pg_dist_node ORDER BY nodeport; nodeid | groupid | nodename | nodeport | noderack | hasmetadata | isactive | noderole | nodecluster | metadatasynced | shouldhaveshards --------------------------------------------------------------------- - 4 | 4 | localhost | 9060 | default | f | t | primary | default | f | t - 6 | 0 | localhost | 57636 | default | t | t | primary | default | t | f + 2 | 2 | localhost | 9060 | default | f | t | primary | default | f | t + 3 | 0 | localhost | 57636 | default | t | t | primary | default | t | f 1 | 1 | localhost | 57637 | default | t | t | primary | default | t | t (3 rows) @@ -626,24 +626,14 @@ UPDATE dist1 SET id = :failed_node_val WHERE id = :failed_node_val; -- Show that we can still delete from a shard at the node from coordinator DELETE FROM dist1 WHERE id = :failed_node_val; -- Show that DDL would still propagate to the node -SET client_min_messages TO NOTICE; -SET citus.log_remote_commands TO 1; CREATE SCHEMA dummy; -NOTICE: issuing BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;SELECT assign_distributed_transaction_id(xx, xx, 'xxxxxxx'); -NOTICE: issuing BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;SELECT assign_distributed_transaction_id(xx, xx, 'xxxxxxx'); -NOTICE: issuing SET citus.enable_ddl_propagation TO 'off' -NOTICE: issuing CREATE SCHEMA dummy -NOTICE: issuing SET citus.enable_ddl_propagation TO 'on' -NOTICE: issuing SET citus.enable_ddl_propagation TO 'off' -NOTICE: issuing CREATE SCHEMA dummy -NOTICE: issuing SET citus.enable_ddl_propagation TO 'on' -NOTICE: issuing WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('schema', ARRAY['dummy']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; -NOTICE: issuing PREPARE TRANSACTION 'citus_xx_xx_xx_xx' -NOTICE: issuing PREPARE TRANSACTION 'citus_xx_xx_xx_xx' -NOTICE: issuing COMMIT PREPARED 'citus_xx_xx_xx_xx' -NOTICE: issuing COMMIT PREPARED 'citus_xx_xx_xx_xx' -SET citus.log_remote_commands TO 0; -SET client_min_messages TO ERROR; +SELECT * FROM run_command_on_workers($$SELECT nspname FROM pg_namespace WHERE nspname = 'dummy'$$); + nodename | nodeport | success | result +--------------------------------------------------------------------- + localhost | 9060 | t | dummy + localhost | 57637 | t | dummy +(2 rows) + -- Successfully activate the node after many failures SELECT citus.mitmproxy('conn.allow()'); mitmproxy @@ -654,14 +644,14 @@ SELECT citus.mitmproxy('conn.allow()'); SELECT citus_activate_node('localhost', :worker_2_proxy_port); citus_activate_node --------------------------------------------------------------------- - 4 + 2 (1 row) -- Activate the node once more to verify it works again with already synced metadata SELECT citus_activate_node('localhost', :worker_2_proxy_port); citus_activate_node --------------------------------------------------------------------- - 4 + 2 (1 row) -- Show node metadata info on worker2 and coordinator after success @@ -669,8 +659,8 @@ SELECT citus_activate_node('localhost', :worker_2_proxy_port); SELECT * FROM pg_dist_node ORDER BY nodeport; nodeid | groupid | nodename | nodeport | noderack | hasmetadata | isactive | noderole | nodecluster | metadatasynced | shouldhaveshards --------------------------------------------------------------------- - 4 | 4 | localhost | 9060 | default | t | t | primary | default | t | t - 6 | 0 | localhost | 57636 | default | t | t | primary | default | t | f + 2 | 2 | localhost | 9060 | default | t | t | primary | default | t | t + 3 | 0 | localhost | 57636 | default | t | t | primary | default | t | f 1 | 1 | localhost | 57637 | default | t | t | primary | default | t | t (3 rows) @@ -678,8 +668,8 @@ SELECT * FROM pg_dist_node ORDER BY nodeport; SELECT * FROM pg_dist_node ORDER BY nodeport; nodeid | groupid | nodename | nodeport | noderack | hasmetadata | isactive | noderole | nodecluster | metadatasynced | shouldhaveshards --------------------------------------------------------------------- - 4 | 4 | localhost | 9060 | default | t | t | primary | default | t | t - 6 | 0 | localhost | 57636 | default | t | t | primary | default | t | f + 2 | 2 | localhost | 9060 | default | t | t | primary | default | t | t + 3 | 0 | localhost | 57636 | default | t | t | primary | default | t | f 1 | 1 | localhost | 57637 | default | t | t | primary | default | t | t (3 rows) @@ -701,3 +691,5 @@ SELECT citus_remove_node('localhost', :master_port); (1 row) +ALTER SEQUENCE pg_dist_node_nodeid_seq RESTART 3; +ALTER SEQUENCE pg_dist_groupid_seq RESTART 3; diff --git a/src/test/regress/sql/failure_add_disable_node.sql b/src/test/regress/sql/failure_add_disable_node.sql index 75a92456c..167439549 100644 --- a/src/test/regress/sql/failure_add_disable_node.sql +++ b/src/test/regress/sql/failure_add_disable_node.sql @@ -97,6 +97,8 @@ WHERE s.logicalrelid = 'user_table'::regclass AND n.isactive ORDER BY placementid; -- reset cluster to original state +ALTER SEQUENCE pg_dist_node_nodeid_seq RESTART 2; +ALTER SEQUENCE pg_dist_groupid_seq RESTART 2; SELECT citus.mitmproxy('conn.allow()'); SELECT master_add_node('localhost', :worker_2_proxy_port); diff --git a/src/test/regress/sql/failure_create_distributed_table_concurrently.sql b/src/test/regress/sql/failure_create_distributed_table_concurrently.sql index 502c3940a..3fd2a217d 100644 --- a/src/test/regress/sql/failure_create_distributed_table_concurrently.sql +++ b/src/test/regress/sql/failure_create_distributed_table_concurrently.sql @@ -15,6 +15,9 @@ SET citus.shard_replication_factor TO 1; SET citus.max_adaptive_executor_pool_size TO 1; SELECT pg_backend_pid() as pid \gset +ALTER SEQUENCE pg_catalog.pg_dist_shardid_seq RESTART 222222; +ALTER SEQUENCE pg_catalog.pg_dist_placement_placementid_seq RESTART 333333; + -- make sure coordinator is in the metadata SELECT citus_set_coordinator_host('localhost', 57636); @@ -108,3 +111,5 @@ SELECT * FROM pg_dist_shard WHERE logicalrelid = 'table_1'::regclass; DROP SCHEMA create_dist_tbl_con CASCADE; SET search_path TO default; SELECT citus_remove_node('localhost', 57636); +ALTER SEQUENCE pg_dist_node_nodeid_seq RESTART 3; +ALTER SEQUENCE pg_dist_groupid_seq RESTART 3; diff --git a/src/test/regress/sql/failure_mx_metadata_sync_multi_trans.sql b/src/test/regress/sql/failure_mx_metadata_sync_multi_trans.sql index 0540827e7..ec05167fc 100644 --- a/src/test/regress/sql/failure_mx_metadata_sync_multi_trans.sql +++ b/src/test/regress/sql/failure_mx_metadata_sync_multi_trans.sql @@ -260,11 +260,8 @@ UPDATE dist1 SET id = :failed_node_val WHERE id = :failed_node_val; DELETE FROM dist1 WHERE id = :failed_node_val; -- Show that DDL would still propagate to the node -SET client_min_messages TO NOTICE; -SET citus.log_remote_commands TO 1; CREATE SCHEMA dummy; -SET citus.log_remote_commands TO 0; -SET client_min_messages TO ERROR; +SELECT * FROM run_command_on_workers($$SELECT nspname FROM pg_namespace WHERE nspname = 'dummy'$$); -- Successfully activate the node after many failures SELECT citus.mitmproxy('conn.allow()'); @@ -285,3 +282,5 @@ DROP SCHEMA mx_metadata_sync_multi_trans CASCADE; DROP ROLE foo1; DROP ROLE foo2; SELECT citus_remove_node('localhost', :master_port); +ALTER SEQUENCE pg_dist_node_nodeid_seq RESTART 3; +ALTER SEQUENCE pg_dist_groupid_seq RESTART 3; From f87a2d02b0ceccba0bf0ad57bc366eb320590d39 Mon Sep 17 00:00:00 2001 From: Onur Tirtir Date: Fri, 14 Apr 2023 16:13:39 +0300 Subject: [PATCH 003/118] Move the common logic related to creating a Citus table down to CreateCitusTable (#6836) .. rather than having it in user facing functions. That way, we can use the same logic for creating Citus tables from other places too. This would be useful for creating tenant tables via a simple function call in the utility hook, for schema-based sharding purposes. --- .../commands/create_distributed_table.c | 65 ++++--------------- 1 file changed, 14 insertions(+), 51 deletions(-) diff --git a/src/backend/distributed/commands/create_distributed_table.c b/src/backend/distributed/commands/create_distributed_table.c index a4fb89b87..7e907c8a8 100644 --- a/src/backend/distributed/commands/create_distributed_table.c +++ b/src/backend/distributed/commands/create_distributed_table.c @@ -247,25 +247,6 @@ create_distributed_table(PG_FUNCTION_ARGS) shardCountIsStrict = true; } - EnsureCitusTableCanBeCreated(relationId); - - /* enable create_distributed_table on an empty node */ - InsertCoordinatorIfClusterEmpty(); - - /* - * Lock target relation with an exclusive lock - there's no way to make - * sense of this table until we've committed, and we don't want multiple - * backends manipulating this relation. - */ - Relation relation = try_relation_open(relationId, ExclusiveLock); - if (relation == NULL) - { - ereport(ERROR, (errmsg("could not create distributed table: " - "relation does not exist"))); - } - - relation_close(relation, NoLock); - char *distributionColumnName = text_to_cstring(distributionColumnText); Assert(distributionColumnName != NULL); @@ -887,38 +868,6 @@ create_reference_table(PG_FUNCTION_ARGS) CheckCitusVersion(ERROR); Oid relationId = PG_GETARG_OID(0); - EnsureCitusTableCanBeCreated(relationId); - - /* enable create_reference_table on an empty node */ - InsertCoordinatorIfClusterEmpty(); - - /* - * Lock target relation with an exclusive lock - there's no way to make - * sense of this table until we've committed, and we don't want multiple - * backends manipulating this relation. - */ - Relation relation = try_relation_open(relationId, ExclusiveLock); - if (relation == NULL) - { - ereport(ERROR, (errmsg("could not create reference table: " - "relation does not exist"))); - } - - relation_close(relation, NoLock); - - List *workerNodeList = ActivePrimaryNodeList(ShareLock); - int workerCount = list_length(workerNodeList); - - /* if there are no workers, error out */ - if (workerCount == 0) - { - char *relationName = get_rel_name(relationId); - - ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("cannot create reference table \"%s\"", relationName), - errdetail("There are no active worker nodes."))); - } - CreateReferenceTable(relationId); PG_RETURN_VOID(); } @@ -1058,6 +1007,20 @@ CreateCitusTable(Oid relationId, CitusTableType tableType, "not be otherwise"))); } + EnsureCitusTableCanBeCreated(relationId); + + /* allow creating a Citus table on an empty cluster */ + InsertCoordinatorIfClusterEmpty(); + + Relation relation = try_relation_open(relationId, ExclusiveLock); + if (relation == NULL) + { + ereport(ERROR, (errmsg("could not create Citus table: " + "relation does not exist"))); + } + + relation_close(relation, NoLock); + /* * EnsureTableNotDistributed errors out when relation is a citus table but * we don't want to ask user to first undistribute their citus local tables From 8782ea158247f24f6789025e81e6f2dfd9d65173 Mon Sep 17 00:00:00 2001 From: Gokhan Gulbiz Date: Mon, 17 Apr 2023 09:35:26 +0300 Subject: [PATCH 004/118] Ensure partitionKeyValue and colocationId are set for proper tenant stats gathering (#6834) This PR updates the tenant stats implementation to set partitionKeyValue and colocationId in ExecuteLocalTaskListExtended, in addition to LocallyExecuteTaskPlan. This ensures that tenant stats can be properly gathered regardless of the code path taken. The changes were initially made while testing stored procedure calls for tenant stats. --- .../distributed/executor/local_executor.c | 33 ++++ .../regress/expected/citus_stat_tenants.out | 156 ++++++++++++++++-- src/test/regress/sql/citus_stat_tenants.sql | 66 +++++++- 3 files changed, 241 insertions(+), 14 deletions(-) diff --git a/src/backend/distributed/executor/local_executor.c b/src/backend/distributed/executor/local_executor.c index 7cdc896e3..c9f4c8fb9 100644 --- a/src/backend/distributed/executor/local_executor.c +++ b/src/backend/distributed/executor/local_executor.c @@ -129,6 +129,8 @@ static void LogLocalCommand(Task *task); static uint64 LocallyPlanAndExecuteMultipleQueries(List *queryStrings, TupleDestination *tupleDest, Task *task); +static void SetColocationIdAndPartitionKeyValueForTasks(List *taskList, + Job *distributedPlan); static void LocallyExecuteUtilityTask(Task *task); static void ExecuteUdfTaskQuery(Query *localUdfCommandQuery); static void EnsureTransitionPossible(LocalExecutionStatus from, @@ -228,6 +230,17 @@ ExecuteLocalTaskListExtended(List *taskList, EnsureTaskExecutionAllowed(isRemote); } + /* + * If workerJob has a partitionKeyValue, we need to set the colocation id + * and partition key value for each task before we start executing them + * because tenant stats are collected based on these values of a task. + */ + if (distributedPlan != NULL && distributedPlan->workerJob != NULL && taskList != NIL) + { + SetJobColocationId(distributedPlan->workerJob); + SetColocationIdAndPartitionKeyValueForTasks(taskList, distributedPlan->workerJob); + } + /* * Use a new memory context that gets reset after every task to free * the deparsed query string and query plan. @@ -367,6 +380,26 @@ ExecuteLocalTaskListExtended(List *taskList, } +/* + * SetColocationIdAndPartitionKeyValueForTasks sets colocationId and partitionKeyValue + * for the tasks in the taskList if workerJob has a colocationId and partitionKeyValue. + */ +static void +SetColocationIdAndPartitionKeyValueForTasks(List *taskList, Job *workerJob) +{ + if (workerJob->colocationId != 0 && + workerJob->partitionKeyValue != NULL) + { + Task *task = NULL; + foreach_ptr(task, taskList) + { + task->colocationId = workerJob->colocationId; + task->partitionKeyValue = workerJob->partitionKeyValue; + } + } +} + + /* * LocallyPlanAndExecuteMultipleQueries plans and executes the given query strings * one by one. diff --git a/src/test/regress/expected/citus_stat_tenants.out b/src/test/regress/expected/citus_stat_tenants.out index 7e75b67fe..c1f07ccaa 100644 --- a/src/test/regress/expected/citus_stat_tenants.out +++ b/src/test/regress/expected/citus_stat_tenants.out @@ -255,7 +255,7 @@ ORDER BY tenant_attribute; (2 rows) -- simulate passing the period -SET citus.stat_tenants_period TO 2; +SET citus.stat_tenants_period TO 5; SELECT sleep_until_next_period(); sleep_until_next_period --------------------------------------------------------------------- @@ -503,13 +503,17 @@ SELECT count(*)>=0 FROM dist_tbl_text WHERE a = 'bcde*'; t (1 row) +DELETE FROM dist_tbl_text WHERE a = '/b*c/de'; +DELETE FROM dist_tbl_text WHERE a = '/bcde'; +DELETE FROM dist_tbl_text WHERE a = U&'\0061\0308bc'; +DELETE FROM dist_tbl_text WHERE a = 'bcde*'; SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants_local ORDER BY tenant_attribute; tenant_attribute | read_count_in_this_period | read_count_in_last_period | query_count_in_this_period | query_count_in_last_period --------------------------------------------------------------------- - /b*c/de | 1 | 0 | 1 | 0 - /bcde | 1 | 0 | 1 | 0 - äbc | 1 | 0 | 1 | 0 - bcde* | 1 | 0 | 1 | 0 + /b*c/de | 1 | 0 | 2 | 0 + /bcde | 1 | 0 | 2 | 0 + äbc | 1 | 0 | 2 | 0 + bcde* | 1 | 0 | 2 | 0 (4 rows) -- test local cached queries & prepared statements @@ -589,10 +593,10 @@ EXECUTE dist_tbl_text_select_plan('bcde*'); SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants_local ORDER BY tenant_attribute; tenant_attribute | read_count_in_this_period | read_count_in_last_period | query_count_in_this_period | query_count_in_last_period --------------------------------------------------------------------- - /b*c/de | 4 | 0 | 4 | 0 - /bcde | 4 | 0 | 4 | 0 - äbc | 4 | 0 | 4 | 0 - bcde* | 4 | 0 | 4 | 0 + /b*c/de | 4 | 0 | 5 | 0 + /bcde | 4 | 0 | 5 | 0 + äbc | 4 | 0 | 5 | 0 + bcde* | 4 | 0 | 5 | 0 (4 rows) \c - - - :master_port @@ -675,10 +679,10 @@ SET search_path TO citus_stat_tenants; SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants ORDER BY tenant_attribute; tenant_attribute | read_count_in_this_period | read_count_in_last_period | query_count_in_this_period | query_count_in_last_period --------------------------------------------------------------------- - /b*c/de | 7 | 0 | 7 | 0 - /bcde | 7 | 0 | 7 | 0 - äbc | 7 | 0 | 7 | 0 - bcde* | 7 | 0 | 7 | 0 + /b*c/de | 7 | 0 | 8 | 0 + /bcde | 7 | 0 | 8 | 0 + äbc | 7 | 0 | 8 | 0 + bcde* | 7 | 0 | 8 | 0 (4 rows) \c - - - :master_port @@ -741,5 +745,131 @@ SELECT count(*)>=0 FROM citus_stat_tenants_local(); RESET ROLE; DROP ROLE stats_non_superuser; +-- test function push down +CREATE OR REPLACE FUNCTION + select_from_dist_tbl_text(p_keyword text) +RETURNS boolean LANGUAGE plpgsql AS $fn$ +BEGIN + RETURN(SELECT count(*)>=0 FROM citus_stat_tenants.dist_tbl_text WHERE a = $1); +END; +$fn$; +SELECT create_distributed_function( + 'select_from_dist_tbl_text(text)', 'p_keyword', colocate_with => 'dist_tbl_text' +); + create_distributed_function +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_stat_tenants_reset(); + citus_stat_tenants_reset +--------------------------------------------------------------------- + +(1 row) + +SELECT select_from_dist_tbl_text('/b*c/de'); + select_from_dist_tbl_text +--------------------------------------------------------------------- + t +(1 row) + +SELECT select_from_dist_tbl_text('/b*c/de'); + select_from_dist_tbl_text +--------------------------------------------------------------------- + t +(1 row) + +SELECT select_from_dist_tbl_text(U&'\0061\0308bc'); + select_from_dist_tbl_text +--------------------------------------------------------------------- + t +(1 row) + +SELECT select_from_dist_tbl_text(U&'\0061\0308bc'); + select_from_dist_tbl_text +--------------------------------------------------------------------- + t +(1 row) + +SELECT tenant_attribute, query_count_in_this_period FROM citus_stat_tenants; + tenant_attribute | query_count_in_this_period +--------------------------------------------------------------------- + /b*c/de | 2 + äbc | 2 +(2 rows) + +CREATE OR REPLACE PROCEDURE select_from_dist_tbl_text_proc( + p_keyword text +) +LANGUAGE plpgsql +AS $$ +BEGIN + PERFORM select_from_dist_tbl_text(p_keyword); + PERFORM count(*)>=0 FROM citus_stat_tenants.dist_tbl_text WHERE b < 0; + PERFORM count(*)>=0 FROM citus_stat_tenants.dist_tbl_text; + PERFORM count(*)>=0 FROM citus_stat_tenants.dist_tbl_text WHERE a = p_keyword; + COMMIT; +END;$$; +CALL citus_stat_tenants.select_from_dist_tbl_text_proc('/b*c/de'); +CALL citus_stat_tenants.select_from_dist_tbl_text_proc('/b*c/de'); +CALL citus_stat_tenants.select_from_dist_tbl_text_proc('/b*c/de'); +CALL citus_stat_tenants.select_from_dist_tbl_text_proc(U&'\0061\0308bc'); +CALL citus_stat_tenants.select_from_dist_tbl_text_proc(U&'\0061\0308bc'); +CALL citus_stat_tenants.select_from_dist_tbl_text_proc(U&'\0061\0308bc'); +CALL citus_stat_tenants.select_from_dist_tbl_text_proc(NULL); +SELECT tenant_attribute, query_count_in_this_period FROM citus_stat_tenants; + tenant_attribute | query_count_in_this_period +--------------------------------------------------------------------- + /b*c/de | 8 + äbc | 8 +(2 rows) + +CREATE OR REPLACE VIEW + select_from_dist_tbl_text_view +AS + SELECT * FROM citus_stat_tenants.dist_tbl_text; +SELECT count(*)>=0 FROM select_from_dist_tbl_text_view WHERE a = '/b*c/de'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SELECT count(*)>=0 FROM select_from_dist_tbl_text_view WHERE a = '/b*c/de'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SELECT count(*)>=0 FROM select_from_dist_tbl_text_view WHERE a = '/b*c/de'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SELECT count(*)>=0 FROM select_from_dist_tbl_text_view WHERE a = U&'\0061\0308bc'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SELECT count(*)>=0 FROM select_from_dist_tbl_text_view WHERE a = U&'\0061\0308bc'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SELECT count(*)>=0 FROM select_from_dist_tbl_text_view WHERE a = U&'\0061\0308bc'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SELECT tenant_attribute, query_count_in_this_period FROM citus_stat_tenants; + tenant_attribute | query_count_in_this_period +--------------------------------------------------------------------- + /b*c/de | 11 + äbc | 11 +(2 rows) + SET client_min_messages TO ERROR; DROP SCHEMA citus_stat_tenants CASCADE; diff --git a/src/test/regress/sql/citus_stat_tenants.sql b/src/test/regress/sql/citus_stat_tenants.sql index f327aefa6..af44c7f1e 100644 --- a/src/test/regress/sql/citus_stat_tenants.sql +++ b/src/test/regress/sql/citus_stat_tenants.sql @@ -93,7 +93,7 @@ FROM citus_stat_tenants_local ORDER BY tenant_attribute; -- simulate passing the period -SET citus.stat_tenants_period TO 2; +SET citus.stat_tenants_period TO 5; SELECT sleep_until_next_period(); SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period, @@ -174,6 +174,11 @@ SELECT count(*)>=0 FROM dist_tbl_text WHERE a = '/bcde'; SELECT count(*)>=0 FROM dist_tbl_text WHERE a = U&'\0061\0308bc'; SELECT count(*)>=0 FROM dist_tbl_text WHERE a = 'bcde*'; +DELETE FROM dist_tbl_text WHERE a = '/b*c/de'; +DELETE FROM dist_tbl_text WHERE a = '/bcde'; +DELETE FROM dist_tbl_text WHERE a = U&'\0061\0308bc'; +DELETE FROM dist_tbl_text WHERE a = 'bcde*'; + SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants_local ORDER BY tenant_attribute; -- test local cached queries & prepared statements @@ -247,5 +252,64 @@ SELECT count(*)>=0 FROM citus_stat_tenants_local(); RESET ROLE; DROP ROLE stats_non_superuser; +-- test function push down +CREATE OR REPLACE FUNCTION + select_from_dist_tbl_text(p_keyword text) +RETURNS boolean LANGUAGE plpgsql AS $fn$ +BEGIN + RETURN(SELECT count(*)>=0 FROM citus_stat_tenants.dist_tbl_text WHERE a = $1); +END; +$fn$; + +SELECT create_distributed_function( + 'select_from_dist_tbl_text(text)', 'p_keyword', colocate_with => 'dist_tbl_text' +); + +SELECT citus_stat_tenants_reset(); + +SELECT select_from_dist_tbl_text('/b*c/de'); +SELECT select_from_dist_tbl_text('/b*c/de'); +SELECT select_from_dist_tbl_text(U&'\0061\0308bc'); +SELECT select_from_dist_tbl_text(U&'\0061\0308bc'); + +SELECT tenant_attribute, query_count_in_this_period FROM citus_stat_tenants; + +CREATE OR REPLACE PROCEDURE select_from_dist_tbl_text_proc( + p_keyword text +) +LANGUAGE plpgsql +AS $$ +BEGIN + PERFORM select_from_dist_tbl_text(p_keyword); + PERFORM count(*)>=0 FROM citus_stat_tenants.dist_tbl_text WHERE b < 0; + PERFORM count(*)>=0 FROM citus_stat_tenants.dist_tbl_text; + PERFORM count(*)>=0 FROM citus_stat_tenants.dist_tbl_text WHERE a = p_keyword; + COMMIT; +END;$$; + +CALL citus_stat_tenants.select_from_dist_tbl_text_proc('/b*c/de'); +CALL citus_stat_tenants.select_from_dist_tbl_text_proc('/b*c/de'); +CALL citus_stat_tenants.select_from_dist_tbl_text_proc('/b*c/de'); +CALL citus_stat_tenants.select_from_dist_tbl_text_proc(U&'\0061\0308bc'); +CALL citus_stat_tenants.select_from_dist_tbl_text_proc(U&'\0061\0308bc'); +CALL citus_stat_tenants.select_from_dist_tbl_text_proc(U&'\0061\0308bc'); +CALL citus_stat_tenants.select_from_dist_tbl_text_proc(NULL); + +SELECT tenant_attribute, query_count_in_this_period FROM citus_stat_tenants; + +CREATE OR REPLACE VIEW + select_from_dist_tbl_text_view +AS + SELECT * FROM citus_stat_tenants.dist_tbl_text; + +SELECT count(*)>=0 FROM select_from_dist_tbl_text_view WHERE a = '/b*c/de'; +SELECT count(*)>=0 FROM select_from_dist_tbl_text_view WHERE a = '/b*c/de'; +SELECT count(*)>=0 FROM select_from_dist_tbl_text_view WHERE a = '/b*c/de'; +SELECT count(*)>=0 FROM select_from_dist_tbl_text_view WHERE a = U&'\0061\0308bc'; +SELECT count(*)>=0 FROM select_from_dist_tbl_text_view WHERE a = U&'\0061\0308bc'; +SELECT count(*)>=0 FROM select_from_dist_tbl_text_view WHERE a = U&'\0061\0308bc'; + +SELECT tenant_attribute, query_count_in_this_period FROM citus_stat_tenants; + SET client_min_messages TO ERROR; DROP SCHEMA citus_stat_tenants CASCADE; From 2675a682184a1d7f7a29e4569a10e605fb549fd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emel=20=C5=9Eim=C5=9Fek?= Date: Mon, 17 Apr 2023 14:14:37 +0300 Subject: [PATCH 005/118] Make coordinator always in metadata by default in regression tests. (#6847) DESCRIPTION: Changes the regression test setups adding the coordinator to metadata by default. When creating a Citus cluster, coordinator can be added in metadata explicitly by running `citus_set_coordinator_host ` function. Adding the coordinator to metadata allows to create citus managed local tables. Other Citus functionality is expected to be unaffected. This change adds the coordinator to metadata by default when creating test clusters in regression tests. There are 3 ways to run commands in a sql file (or a schedule which is a sequence of sql files) with Citus regression tests. Below is how this PR adds the coordinator to metadata for each. 1. `make ` Changed the sql files (sql/multi_cluster_management.sql and sql/minimal_cluster_management.sql) which sets up the test clusters such that they call `citus_set_coordinator_host`. This ensures any following tests will have the coordinator in metadata by default. 2. `citus_tests/run_test.py ` Changed the python code that sets up the cluster to always call ` citus_set_coordinator_host`. For the upgrade tests, a version check is included to make sure `citus_set_coordinator_host` function is available for a given version. 3. ` make check-arbitrary-configs ` Changed the python code that sets up the cluster to always call `citus_set_coordinator_host `. #6864 will be used to track the remaining work which is to change the tests where coordinator is added/removed as a node. --- src/test/regress/citus_tests/common.py | 20 +++++++++---- src/test/regress/citus_tests/config.py | 3 -- src/test/regress/citus_tests/run_test.py | 3 ++ .../expected/minimal_cluster_management.out | 7 +++++ .../expected/multi_cluster_management.out | 30 ++++++++++++------- .../sql/minimal_cluster_management.sql | 3 ++ .../regress/sql/multi_cluster_management.sql | 4 +++ 7 files changed, 52 insertions(+), 18 deletions(-) diff --git a/src/test/regress/citus_tests/common.py b/src/test/regress/citus_tests/common.py index d751bad5a..d0ac688f9 100644 --- a/src/test/regress/citus_tests/common.py +++ b/src/test/regress/citus_tests/common.py @@ -25,6 +25,9 @@ import utils from psycopg import sql from utils import USER +# This SQL returns true ( 't' ) if the Citus version >= 11.0. +IS_CITUS_VERSION_11_SQL = "SELECT (split_part(extversion, '.', 1)::int >= 11) as is_11 FROM pg_extension WHERE extname = 'citus';" + LINUX = False MACOS = False FREEBSD = False @@ -272,9 +275,7 @@ def stop_metadata_to_workers(pg_path, worker_ports, coordinator_port): def add_coordinator_to_metadata(pg_path, coordinator_port): - command = "SELECT citus_add_node('localhost', {}, groupId := 0)".format( - coordinator_port - ) + command = "SELECT citus_set_coordinator_host('localhost');" utils.psql(pg_path, coordinator_port, command) @@ -327,6 +328,10 @@ def stop_databases( stop(node_name) +def is_citus_set_coordinator_host_udf_exist(pg_path, port): + return utils.psql_capture(pg_path, port, IS_CITUS_VERSION_11_SQL) == b" t\n\n" + + def initialize_citus_cluster(bindir, datadir, settings, config): # In case there was a leftover from previous runs, stop the databases stop_databases( @@ -339,11 +344,16 @@ def initialize_citus_cluster(bindir, datadir, settings, config): bindir, datadir, config.node_name_to_ports, config.name, config.env_variables ) create_citus_extension(bindir, config.node_name_to_ports.values()) + + # In upgrade tests, it is possible that Citus version < 11.0 + # where the citus_set_coordinator_host UDF does not exist. + if is_citus_set_coordinator_host_udf_exist(bindir, config.coordinator_port()): + add_coordinator_to_metadata(bindir, config.coordinator_port()) + add_workers(bindir, config.worker_ports, config.coordinator_port()) if not config.is_mx: stop_metadata_to_workers(bindir, config.worker_ports, config.coordinator_port()) - if config.add_coordinator_to_metadata: - add_coordinator_to_metadata(bindir, config.coordinator_port()) + config.setup_steps() diff --git a/src/test/regress/citus_tests/config.py b/src/test/regress/citus_tests/config.py index f25f0a477..c5b7ce010 100644 --- a/src/test/regress/citus_tests/config.py +++ b/src/test/regress/citus_tests/config.py @@ -110,7 +110,6 @@ class CitusBaseClusterConfig(object, metaclass=NewInitCaller): "max_connections": 1200, } self.new_settings = {} - self.add_coordinator_to_metadata = False self.env_variables = {} self.skip_tests = [] @@ -166,7 +165,6 @@ class CitusDefaultClusterConfig(CitusBaseClusterConfig): "citus.use_citus_managed_tables": True, } self.settings.update(new_settings) - self.add_coordinator_to_metadata = True self.skip_tests = [ # Alter Table statement cannot be run from an arbitrary node so this test will fail "arbitrary_configs_alter_table_add_constraint_without_name_create", @@ -380,4 +378,3 @@ class PGUpgradeConfig(CitusBaseClusterConfig): self.old_datadir = self.temp_dir + "/oldData" self.new_datadir = self.temp_dir + "/newData" self.user = SUPER_USER_NAME - self.add_coordinator_to_metadata = True diff --git a/src/test/regress/citus_tests/run_test.py b/src/test/regress/citus_tests/run_test.py index a34a6e0a7..ca969b9c1 100755 --- a/src/test/regress/citus_tests/run_test.py +++ b/src/test/regress/citus_tests/run_test.py @@ -96,6 +96,9 @@ DEPS = { "multi_cluster_management": TestDeps( None, ["multi_test_helpers_superuser"], repeatable=False ), + "minimal_cluster_management": TestDeps( + None, ["multi_test_helpers_superuser"], repeatable=False + ), "create_role_propagation": TestDeps(None, ["multi_cluster_management"]), "single_node_enterprise": TestDeps(None), "single_node": TestDeps(None), diff --git a/src/test/regress/expected/minimal_cluster_management.out b/src/test/regress/expected/minimal_cluster_management.out index af3ac84f3..d05e83ed5 100644 --- a/src/test/regress/expected/minimal_cluster_management.out +++ b/src/test/regress/expected/minimal_cluster_management.out @@ -20,6 +20,13 @@ SELECT 1 FROM master_add_node('localhost', :worker_1_port); 1 (1 row) +-- make sure coordinator is always in metadata. +SELECT citus_set_coordinator_host('localhost'); + citus_set_coordinator_host +--------------------------------------------------------------------- + +(1 row) + -- Create the same colocation groups as multi_cluster_management.sql SET citus.shard_count TO 16; SET citus.shard_replication_factor TO 1; diff --git a/src/test/regress/expected/multi_cluster_management.out b/src/test/regress/expected/multi_cluster_management.out index 08ac5a807..7d5d25d57 100644 --- a/src/test/regress/expected/multi_cluster_management.out +++ b/src/test/regress/expected/multi_cluster_management.out @@ -25,6 +25,13 @@ SELECT citus_is_coordinator(); t (1 row) +-- make sure coordinator is always in metadata. +SELECT citus_set_coordinator_host('localhost'); + citus_set_coordinator_host +--------------------------------------------------------------------- + +(1 row) + -- workers are not coordinator SELECT result FROM run_command_on_workers('SELECT citus_is_coordinator()'); result @@ -144,9 +151,9 @@ SELECT citus_disable_node('localhost', :worker_1_port, synchronous:=true); (1 row) SELECT run_command_on_workers($$SELECT array_agg(isactive ORDER BY nodeport) FROM pg_dist_node WHERE hasmetadata and noderole='primary'::noderole AND nodecluster='default'$$); - run_command_on_workers + run_command_on_workers --------------------------------------------------------------------- - (localhost,57638,t,"{f,t}") + (localhost,57638,t,"{t,f,t}") (1 row) SELECT 1 FROM citus_activate_node('localhost', :worker_1_port); @@ -163,9 +170,9 @@ SELECT citus_disable_node('localhost', :worker_2_port, synchronous:=true); (1 row) SELECT run_command_on_workers($$SELECT array_agg(isactive ORDER BY nodeport) FROM pg_dist_node WHERE hasmetadata and noderole='primary'::noderole AND nodecluster='default'$$); - run_command_on_workers + run_command_on_workers --------------------------------------------------------------------- - (localhost,57637,t,"{t,f}") + (localhost,57637,t,"{t,t,f}") (1 row) SELECT 1 FROM citus_activate_node('localhost', :worker_2_port); @@ -248,7 +255,7 @@ ERROR: node at "localhost.noexist:2345" does not exist SELECT master_activate_node('localhost', :worker_2_port); master_activate_node --------------------------------------------------------------------- - 3 + 4 (1 row) DROP TABLE test_reference_table, cluster_management_test; @@ -358,9 +365,10 @@ SELECT master_update_node(nodeid, 'localhost', :worker_2_port + 3) FROM pg_dist_ SELECT nodename, nodeport, noderole FROM pg_dist_node ORDER BY nodeport; nodename | nodeport | noderole --------------------------------------------------------------------- + localhost | 57636 | primary localhost | 57637 | primary localhost | 57640 | secondary -(2 rows) +(3 rows) ABORT; \c - postgres - :master_port @@ -377,7 +385,7 @@ SELECT master_get_active_worker_nodes(); SELECT * FROM master_add_node('localhost', :worker_2_port); master_add_node --------------------------------------------------------------------- - 6 + 7 (1 row) ALTER SEQUENCE pg_dist_node_nodeid_seq RESTART WITH 7; @@ -579,7 +587,7 @@ SELECT SELECT count(1) FROM pg_dist_node; count --------------------------------------------------------------------- - 0 + 1 (1 row) -- check that adding two nodes in the same transaction works @@ -594,9 +602,10 @@ SELECT SELECT * FROM pg_dist_node ORDER BY nodeid; nodeid | groupid | nodename | nodeport | noderack | hasmetadata | isactive | noderole | nodecluster | metadatasynced | shouldhaveshards --------------------------------------------------------------------- + 3 | 0 | localhost | 57636 | default | t | t | primary | default | t | f 11 | 9 | localhost | 57637 | default | t | t | primary | default | t | t 12 | 10 | localhost | 57638 | default | t | t | primary | default | t | t -(2 rows) +(3 rows) -- check that mixed add/remove node commands work fine inside transaction BEGIN; @@ -669,7 +678,8 @@ SELECT master_remove_node(nodename, nodeport) FROM pg_dist_node; --------------------------------------------------------------------- -(2 rows) + +(3 rows) SELECT 1 FROM master_add_node('localhost', :worker_1_port); ?column? diff --git a/src/test/regress/sql/minimal_cluster_management.sql b/src/test/regress/sql/minimal_cluster_management.sql index 424daccac..30f69d43d 100644 --- a/src/test/regress/sql/minimal_cluster_management.sql +++ b/src/test/regress/sql/minimal_cluster_management.sql @@ -13,6 +13,9 @@ ALTER SEQUENCE pg_catalog.pg_dist_node_nodeid_seq RESTART 16; ALTER SEQUENCE pg_catalog.pg_dist_groupid_seq RESTART 14; SELECT 1 FROM master_add_node('localhost', :worker_1_port); +-- make sure coordinator is always in metadata. +SELECT citus_set_coordinator_host('localhost'); + -- Create the same colocation groups as multi_cluster_management.sql SET citus.shard_count TO 16; SET citus.shard_replication_factor TO 1; diff --git a/src/test/regress/sql/multi_cluster_management.sql b/src/test/regress/sql/multi_cluster_management.sql index 126d8385e..367fa9d58 100644 --- a/src/test/regress/sql/multi_cluster_management.sql +++ b/src/test/regress/sql/multi_cluster_management.sql @@ -13,6 +13,10 @@ RESET citus.metadata_sync_mode; -- I am coordinator SELECT citus_is_coordinator(); + +-- make sure coordinator is always in metadata. +SELECT citus_set_coordinator_host('localhost'); + -- workers are not coordinator SELECT result FROM run_command_on_workers('SELECT citus_is_coordinator()'); From 08e2820c6762feffebb4559c0b73ab14811f646f Mon Sep 17 00:00:00 2001 From: aykut-bozkurt <51649454+aykut-bozkurt@users.noreply.github.com> Date: Mon, 17 Apr 2023 18:14:01 +0300 Subject: [PATCH 006/118] skip restriction clause if it contains placeholdervar (#6857) `PlaceHolderVar` is not relevant to be processed inside a restriction clause. Otherwise, `pull_var_clause_default` would throw error. PG would create the restriction to physical `Var` that `PlaceHolderVar` points to anyway, so it is safe to skip this restriction. DESCRIPTION: Fixes a bug related to WHERE clause list which contains placeholder. Fixes https://github.com/citusdata/citus/issues/6758 --- .../relation_restriction_equivalence.c | 22 ++++++ src/test/regress/expected/issue_6758.out | 69 +++++++++++++++++++ src/test/regress/multi_schedule | 2 +- src/test/regress/sql/issue_6758.sql | 55 +++++++++++++++ 4 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 src/test/regress/expected/issue_6758.out create mode 100644 src/test/regress/sql/issue_6758.sql diff --git a/src/backend/distributed/planner/relation_restriction_equivalence.c b/src/backend/distributed/planner/relation_restriction_equivalence.c index d39c4affb..ac36842de 100644 --- a/src/backend/distributed/planner/relation_restriction_equivalence.c +++ b/src/backend/distributed/planner/relation_restriction_equivalence.c @@ -155,6 +155,7 @@ static bool AllDistributedRelationsInRestrictionContextColocated( RelationRestrictionContext * restrictionContext); static bool IsNotSafeRestrictionToRecursivelyPlan(Node *node); +static bool HasPlaceHolderVar(Node *node); static JoinRestrictionContext * FilterJoinRestrictionContext( JoinRestrictionContext *joinRestrictionContext, Relids queryRteIdentities); @@ -2149,6 +2150,17 @@ GetRestrictInfoListForRelation(RangeTblEntry *rangeTblEntry, continue; } + /* + * PlaceHolderVar is not relevant to be processed inside a restriction clause. + * Otherwise, pull_var_clause_default would throw error. PG would create + * the restriction to physical Var that PlaceHolderVar points anyway, so it is + * safe to skip this restriction. + */ + if (FindNodeMatchingCheckFunction((Node *) restrictionClause, HasPlaceHolderVar)) + { + continue; + } + /* * We're going to add this restriction expression to a subquery * which consists of only one relation in its jointree. Thus, @@ -2214,6 +2226,16 @@ IsNotSafeRestrictionToRecursivelyPlan(Node *node) } +/* + * HasPlaceHolderVar returns true if given node contains any PlaceHolderVar. + */ +static bool +HasPlaceHolderVar(Node *node) +{ + return IsA(node, PlaceHolderVar); +} + + /* * FilterRelationRestrictionContext gets a relation restriction context and * set of rte identities. It returns the relation restrictions that that appear diff --git a/src/test/regress/expected/issue_6758.out b/src/test/regress/expected/issue_6758.out new file mode 100644 index 000000000..34f20593f --- /dev/null +++ b/src/test/regress/expected/issue_6758.out @@ -0,0 +1,69 @@ +CREATE SCHEMA issue_6758; +SET search_path to 'issue_6758'; +CREATE TABLE dist0(id int); +SELECT create_distributed_table('dist0','id'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE dist1(id int); +SELECT create_distributed_table('dist1','id'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- added to verify we fixed the issue https://github.com/citusdata/citus/issues/6758 +-- generated by Citus query generator tool +SELECT + avg(avgsub.id) +FROM + ( + SELECT + table_0.id + FROM + ( + SELECT + table_1.id + FROM + ( + SELECT + table_2.id + FROM + ( + SELECT + table_3.id + FROM + ( + VALUES + (838) + ) AS table_3(id) FULL + JOIN dist0 AS table_4 USING (id) + WHERE + table_4.id = 3 + ) AS table_2 + WHERE + table_2.id = 2 + ORDER BY + id + LIMIT + 77 + ) AS table_1 + LEFT JOIN dist0 AS table_5 USING (id) + ORDER BY + id + LIMIT + 44 + ) AS table_0 FULL + JOIN dist1 AS table_6 USING (id) + ) AS avgsub; + avg +--------------------------------------------------------------------- + +(1 row) + +DROP SCHEMA issue_6758 CASCADE; +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to table dist0 +drop cascades to table dist1 diff --git a/src/test/regress/multi_schedule b/src/test/regress/multi_schedule index 1d5ce0798..a78ee6088 100644 --- a/src/test/regress/multi_schedule +++ b/src/test/regress/multi_schedule @@ -95,7 +95,7 @@ test: multi_dropped_column_aliases foreign_key_restriction_enforcement test: binary_protocol test: alter_table_set_access_method test: alter_distributed_table -test: issue_5248 issue_5099 issue_5763 issue_6543 +test: issue_5248 issue_5099 issue_5763 issue_6543 issue_6758 test: object_propagation_debug test: undistribute_table test: run_command_on_all_nodes diff --git a/src/test/regress/sql/issue_6758.sql b/src/test/regress/sql/issue_6758.sql new file mode 100644 index 000000000..dae340526 --- /dev/null +++ b/src/test/regress/sql/issue_6758.sql @@ -0,0 +1,55 @@ +CREATE SCHEMA issue_6758; +SET search_path to 'issue_6758'; + +CREATE TABLE dist0(id int); +SELECT create_distributed_table('dist0','id'); + +CREATE TABLE dist1(id int); +SELECT create_distributed_table('dist1','id'); + +-- added to verify we fixed the issue https://github.com/citusdata/citus/issues/6758 +-- generated by Citus query generator tool +SELECT + avg(avgsub.id) +FROM + ( + SELECT + table_0.id + FROM + ( + SELECT + table_1.id + FROM + ( + SELECT + table_2.id + FROM + ( + SELECT + table_3.id + FROM + ( + VALUES + (838) + ) AS table_3(id) FULL + JOIN dist0 AS table_4 USING (id) + WHERE + table_4.id = 3 + ) AS table_2 + WHERE + table_2.id = 2 + ORDER BY + id + LIMIT + 77 + ) AS table_1 + LEFT JOIN dist0 AS table_5 USING (id) + ORDER BY + id + LIMIT + 44 + ) AS table_0 FULL + JOIN dist1 AS table_6 USING (id) + ) AS avgsub; + +DROP SCHEMA issue_6758 CASCADE; From a6a7271e637931e51bdcc2e0c47b444a3ec06ec0 Mon Sep 17 00:00:00 2001 From: aykut-bozkurt <51649454+aykut-bozkurt@users.noreply.github.com> Date: Sun, 23 Apr 2023 20:28:26 +0300 Subject: [PATCH 007/118] Query generator test tool (#6686) - Query generator is used to create queries, allowed by the grammar which is documented at `query_generator/query_gen.py` (currently contains only joins). - This PR adds a CI test which utilizes the query generator to compare the results of generated queries that are executed on Citus tables and local (undistributed) tables. It fails if there is an unexpected error at results. The error can be related to Citus, the query generator, or even Postgres. - The tool is configured by the file `query_generator/config/config.yaml`, which limits table counts at generated queries and sets many table related parameters (e.g. row count). - Run time of the CI task can be configured from the config file. By default, we run 250 queries with maximum table count of 40 inside each query. --- .circleci/config.yml | 80 +++- src/test/regress/Makefile | 6 +- src/test/regress/Pipfile | 1 + src/test/regress/Pipfile.lock | 252 +++++----- src/test/regress/citus_tests/config.py | 3 + .../citus_tests/query_generator/.gitignore | 2 + .../citus_tests/query_generator/README.md | 223 +++++++++ .../bin/citus_compare_dist_local_joins.sh | 60 +++ .../query_generator/bin/diff-checker.py | 122 +++++ .../bin/run_query_compare_test.py | 63 +++ .../query_generator/config/config.py | 129 ++++++ .../query_generator/config/config.yaml | 65 +++ .../query_generator/config/config_parser.py | 81 ++++ .../citus_tests/query_generator/data_gen.py | 81 ++++ .../citus_tests/query_generator/ddl_gen.py | 48 ++ .../query_generator/generate_queries.py | 65 +++ .../citus_tests/query_generator/node_defs.py | 55 +++ .../query_generator/out/.gitignore | 2 + .../citus_tests/query_generator/query_gen.py | 434 ++++++++++++++++++ .../query_generator/random_selections.py | 39 ++ 20 files changed, 1695 insertions(+), 116 deletions(-) create mode 100644 src/test/regress/citus_tests/query_generator/.gitignore create mode 100644 src/test/regress/citus_tests/query_generator/README.md create mode 100644 src/test/regress/citus_tests/query_generator/bin/citus_compare_dist_local_joins.sh create mode 100644 src/test/regress/citus_tests/query_generator/bin/diff-checker.py create mode 100755 src/test/regress/citus_tests/query_generator/bin/run_query_compare_test.py create mode 100644 src/test/regress/citus_tests/query_generator/config/config.py create mode 100644 src/test/regress/citus_tests/query_generator/config/config.yaml create mode 100755 src/test/regress/citus_tests/query_generator/config/config_parser.py create mode 100644 src/test/regress/citus_tests/query_generator/data_gen.py create mode 100755 src/test/regress/citus_tests/query_generator/ddl_gen.py create mode 100755 src/test/regress/citus_tests/query_generator/generate_queries.py create mode 100755 src/test/regress/citus_tests/query_generator/node_defs.py create mode 100644 src/test/regress/citus_tests/query_generator/out/.gitignore create mode 100644 src/test/regress/citus_tests/query_generator/query_gen.py create mode 100644 src/test/regress/citus_tests/query_generator/random_selections.py diff --git a/.circleci/config.yml b/.circleci/config.yml index 225195d27..1f97c5635 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,7 +6,7 @@ orbs: parameters: image_suffix: type: string - default: '-v3417e8d' + default: '-vabeb997' pg13_version: type: string default: '13.10' @@ -421,6 +421,63 @@ jobs: - coverage: flags: 'test_<< parameters.pg_major >>,upgrade' + test-query-generator: + description: Expects that the generated queries that are run on distributed and local tables would have the same results + parameters: + pg_major: + description: 'postgres major version' + type: integer + image: + description: 'docker image to use as for the tests' + type: string + default: citus/failtester + image_tag: + description: 'docker image tag to use' + type: string + docker: + - image: '<< parameters.image >>:<< parameters.image_tag >><< pipeline.parameters.image_suffix >>' + working_directory: /home/circleci/project + steps: + - checkout + - attach_workspace: + at: . + - install_extension: + pg_major: << parameters.pg_major >> + - configure + - enable_core + - run: + name: 'Run Test' + command: | + gosu circleci make -C src/test/regress check-query-generator + no_output_timeout: 5m + - run: + name: 'Show regressions' + command: | + find src/test/regress/citus_tests/query_generator/out/ -name "local_dist.diffs" -exec cat {} + + lines=$(find src/test/regress/citus_tests/query_generator/out/ -name "local_dist.diffs" | wc -l) + if [ $lines -ne 0 ]; then + exit 1 + fi + when: on_fail + - run: + name: 'Copy logfiles' + command: | + mkdir src/test/regress/tmp_citus_test/logfiles + find src/test/regress/tmp_citus_test/ -name "logfile_*" -exec cp -t src/test/regress/tmp_citus_test/logfiles/ {} + + when: on_fail + - store_artifacts: + name: 'Save logfiles' + path: src/test/regress/tmp_citus_test/logfiles + - store_artifacts: + name: 'Save ddls' + path: src/test/regress/citus_tests/query_generator/out/ddls.sql + - store_artifacts: + name: 'Save dmls' + path: src/test/regress/citus_tests/query_generator/out/queries.sql + - stack_trace + - coverage: + flags: 'test_<< parameters.pg_major >>,querygen' + test-citus: description: Runs the common tests of citus parameters: @@ -935,6 +992,24 @@ workflows: image_tag: '<< pipeline.parameters.pg15_version >>' requires: [build-15] + - test-query-generator: + name: 'test-13_check-query-generator' + pg_major: 13 + image_tag: '<< pipeline.parameters.pg13_version >>' + requires: [build-13] + + - test-query-generator: + name: 'test-14_check-query-generator' + pg_major: 14 + image_tag: '<< pipeline.parameters.pg14_version >>' + requires: [build-14] + + - test-query-generator: + name: 'test-15_check-query-generator' + pg_major: 15 + image_tag: '<< pipeline.parameters.pg15_version >>' + requires: [build-15] + - test-pg-upgrade: name: 'test-13-14_check-pg-upgrade' old_pg_major: 13 @@ -975,6 +1050,7 @@ workflows: - test-13_check-enterprise-failure - test-13_check-split - test-13_check-arbitrary-configs + - test-13_check-query-generator - test-14_check-multi - test-14_check-multi-1 - test-14_check-mx @@ -993,6 +1069,7 @@ workflows: - test-14_check-enterprise-failure - test-14_check-split - test-14_check-arbitrary-configs + - test-14_check-query-generator - test-15_check-multi - test-15_check-multi-1 - test-15_check-mx @@ -1011,6 +1088,7 @@ workflows: - test-15_check-enterprise-failure - test-15_check-split - test-15_check-arbitrary-configs + - test-15_check-query-generator - test-13-14_check-pg-upgrade - test-14-15_check-pg-upgrade - test-13_check-citus-upgrade diff --git a/src/test/regress/Makefile b/src/test/regress/Makefile index b801f33ff..04c38033d 100644 --- a/src/test/regress/Makefile +++ b/src/test/regress/Makefile @@ -34,6 +34,7 @@ MULTI_REGRESS_OPTS = --inputdir=$(citus_abs_srcdir) $(pg_regress_locale_flags) - pg_upgrade_check = $(citus_abs_srcdir)/citus_tests/upgrade/pg_upgrade_test.py citus_upgrade_check =CITUS_OLD_VERSION=$(citus-old-version) $(citus_abs_srcdir)/citus_tests/upgrade/citus_upgrade_test.py arbitrary_config_check = $(citus_abs_srcdir)/citus_tests/arbitrary_configs/citus_arbitrary_configs.py +query_generator_check = $(citus_abs_srcdir)/citus_tests/query_generator/bin/run_query_compare_test.py template_isolation_files = $(shell find $(citus_abs_srcdir)/spec/ -name '*.spec') generated_isolation_files = $(patsubst $(citus_abs_srcdir)/spec/%,$(citus_abs_srcdir)/build/specs/%,$(template_isolation_files)) @@ -44,7 +45,7 @@ vanilla_diffs_file = $(citus_abs_srcdir)/pg_vanilla_outputs/$(MAJORVERSION)/regr # intermediate, for muscle memory backward compatibility. check: check-full check-enterprise-full # check-full triggers all tests that ought to be run routinely -check-full: check-multi check-multi-mx check-multi-1 check-operations check-follower-cluster check-isolation check-failure check-split check-vanilla check-columnar check-columnar-isolation check-pg-upgrade check-arbitrary-configs check-citus-upgrade check-citus-upgrade-mixed check-citus-upgrade-local check-citus-upgrade-mixed-local check-pytest +check-full: check-multi check-multi-mx check-multi-1 check-operations check-follower-cluster check-isolation check-failure check-split check-vanilla check-columnar check-columnar-isolation check-pg-upgrade check-arbitrary-configs check-citus-upgrade check-citus-upgrade-mixed check-citus-upgrade-local check-citus-upgrade-mixed-local check-pytest check-query-generator # check-enterprise-full triggers all enterprise specific tests check-enterprise-full: check-enterprise check-enterprise-isolation check-enterprise-failure check-enterprise-isolation-logicalrep-1 check-enterprise-isolation-logicalrep-2 check-enterprise-isolation-logicalrep-3 @@ -262,6 +263,9 @@ check-arbitrary-base: all check-pytest: pytest -n auto +check-query-generator: all + ${query_generator_check} --bindir=$(bindir) --pgxsdir=$(pgxsdir) + check-citus-upgrade: all $(citus_upgrade_check) \ --bindir=$(bindir) \ diff --git a/src/test/regress/Pipfile b/src/test/regress/Pipfile index 663785a3d..5bce63004 100644 --- a/src/test/regress/Pipfile +++ b/src/test/regress/Pipfile @@ -15,6 +15,7 @@ pytest-asyncio = "*" pytest-timeout = "*" pytest-xdist = "*" pytest-repeat = "*" +pyyaml = "*" [dev-packages] black = "*" diff --git a/src/test/regress/Pipfile.lock b/src/test/regress/Pipfile.lock index 4a86e09a8..ed604c2c0 100644 --- a/src/test/regress/Pipfile.lock +++ b/src/test/regress/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "eb9ca3a7b05e76c7ac60179a1755f89600dfb215e02bf08c258d548df1d96025" + "sha256": "9568b1f3e4d4fd408e5e263f6346b0a4f479ac88e02f64bb79a9d482096e6a03" }, "pipfile-spec": 6, "requires": { @@ -24,14 +24,6 @@ "markers": "python_version >= '3.6'", "version": "==3.4.1" }, - "attrs": { - "hashes": [ - "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836", - "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99" - ], - "markers": "python_version >= '3.6'", - "version": "==22.2.0" - }, "blinker": { "hashes": [ "sha256:471aee25f3992bd325afa3772f1063dbdbbca947a041b8b89466dc00d606f8b6" @@ -219,32 +211,28 @@ }, "cryptography": { "hashes": [ - "sha256:103e8f7155f3ce2ffa0049fe60169878d47a4364b277906386f8de21c9234aa1", - "sha256:23df8ca3f24699167daf3e23e51f7ba7334d504af63a94af468f468b975b7dd7", - "sha256:2725672bb53bb92dc7b4150d233cd4b8c59615cd8288d495eaa86db00d4e5c06", - "sha256:30b1d1bfd00f6fc80d11300a29f1d8ab2b8d9febb6ed4a38a76880ec564fae84", - "sha256:35d658536b0a4117c885728d1a7032bdc9a5974722ae298d6c533755a6ee3915", - "sha256:50cadb9b2f961757e712a9737ef33d89b8190c3ea34d0fb6675e00edbe35d074", - "sha256:5f8c682e736513db7d04349b4f6693690170f95aac449c56f97415c6980edef5", - "sha256:6236a9610c912b129610eb1a274bdc1350b5df834d124fa84729ebeaf7da42c3", - "sha256:788b3921d763ee35dfdb04248d0e3de11e3ca8eb22e2e48fef880c42e1f3c8f9", - "sha256:8bc0008ef798231fac03fe7d26e82d601d15bd16f3afaad1c6113771566570f3", - "sha256:8f35c17bd4faed2bc7797d2a66cbb4f986242ce2e30340ab832e5d99ae60e011", - "sha256:b49a88ff802e1993b7f749b1eeb31134f03c8d5c956e3c125c75558955cda536", - "sha256:bc0521cce2c1d541634b19f3ac661d7a64f9555135e9d8af3980965be717fd4a", - "sha256:bc5b871e977c8ee5a1bbc42fa8d19bcc08baf0c51cbf1586b0e87a2694dde42f", - "sha256:c43ac224aabcbf83a947eeb8b17eaf1547bce3767ee2d70093b461f31729a480", - "sha256:d15809e0dbdad486f4ad0979753518f47980020b7a34e9fc56e8be4f60702fac", - "sha256:d7d84a512a59f4412ca8549b01f94be4161c94efc598bf09d027d67826beddc0", - "sha256:e029b844c21116564b8b61216befabca4b500e6816fa9f0ba49527653cae2108", - "sha256:e8a0772016feeb106efd28d4a328e77dc2edae84dfbac06061319fdb669ff828", - "sha256:e944fe07b6f229f4c1a06a7ef906a19652bdd9fd54c761b0ff87e83ae7a30354", - "sha256:eb40fe69cfc6f5cdab9a5ebd022131ba21453cf7b8a7fd3631f45bbf52bed612", - "sha256:fa507318e427169ade4e9eccef39e9011cdc19534f55ca2f36ec3f388c1f70f3", - "sha256:ffd394c7896ed7821a6d13b24657c6a34b6e2650bd84ae063cf11ccffa4f1a97" + "sha256:05dc219433b14046c476f6f09d7636b92a1c3e5808b9a6536adf4932b3b2c440", + "sha256:0dcca15d3a19a66e63662dc8d30f8036b07be851a8680eda92d079868f106288", + "sha256:142bae539ef28a1c76794cca7f49729e7c54423f615cfd9b0b1fa90ebe53244b", + "sha256:3daf9b114213f8ba460b829a02896789751626a2a4e7a43a28ee77c04b5e4958", + "sha256:48f388d0d153350f378c7f7b41497a54ff1513c816bcbbcafe5b829e59b9ce5b", + "sha256:4df2af28d7bedc84fe45bd49bc35d710aede676e2a4cb7fc6d103a2adc8afe4d", + "sha256:4f01c9863da784558165f5d4d916093737a75203a5c5286fde60e503e4276c7a", + "sha256:7a38250f433cd41df7fcb763caa3ee9362777fdb4dc642b9a349721d2bf47404", + "sha256:8f79b5ff5ad9d3218afb1e7e20ea74da5f76943ee5edb7f76e56ec5161ec782b", + "sha256:956ba8701b4ffe91ba59665ed170a2ebbdc6fc0e40de5f6059195d9f2b33ca0e", + "sha256:a04386fb7bc85fab9cd51b6308633a3c271e3d0d3eae917eebab2fac6219b6d2", + "sha256:a95f4802d49faa6a674242e25bfeea6fc2acd915b5e5e29ac90a32b1139cae1c", + "sha256:adc0d980fd2760c9e5de537c28935cc32b9353baaf28e0814df417619c6c8c3b", + "sha256:aecbb1592b0188e030cb01f82d12556cf72e218280f621deed7d806afd2113f9", + "sha256:b12794f01d4cacfbd3177b9042198f3af1c856eedd0a98f10f141385c809a14b", + "sha256:c0764e72b36a3dc065c155e5b22f93df465da9c39af65516fe04ed3c68c92636", + "sha256:c33c0d32b8594fa647d2e01dbccc303478e16fdd7cf98652d5b3ed11aa5e5c99", + "sha256:cbaba590180cba88cb99a5f76f90808a624f18b169b90a4abb40c1fd8c19420e", + "sha256:d5a1bd0e9e2031465761dfa920c16b0065ad77321d8a8c1f5ee331021fda65e9" ], "index": "pypi", - "version": "==39.0.2" + "version": "==40.0.2" }, "docopt": { "hashes": [ @@ -271,11 +259,11 @@ }, "filelock": { "hashes": [ - "sha256:3199fd0d3faea8b911be52b663dfccceb84c95949dd13179aa21436d1a79c4ce", - "sha256:e90b34656470756edf8b19656785c5fea73afa1953f3e1b0d645cef11cab3182" + "sha256:ad98852315c2ab702aeb628412cbf7e95b7ce8c3bf9565670b4eaecf1db370a9", + "sha256:fc03ae43288c013d2ea83c8597001b1129db351aad9c57fe2409327916b8e718" ], "index": "pypi", - "version": "==3.10.0" + "version": "==3.12.0" }, "flask": { "hashes": [ @@ -488,11 +476,11 @@ }, "packaging": { "hashes": [ - "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2", - "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97" + "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61", + "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f" ], "markers": "python_version >= '3.7'", - "version": "==23.0" + "version": "==23.1" }, "passlib": { "hashes": [ @@ -553,21 +541,11 @@ }, "pyasn1": { "hashes": [ - "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359", - "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576", - "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf", - "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7", - "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d", - "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00", - "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8", - "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86", - "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12", - "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776", - "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba", - "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2", - "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3" + "sha256:87a2121042a1ac9358cabcaf1d07680ff97ee6404333bacca15f76aa8ad01a57", + "sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde" ], - "version": "==0.4.8" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", + "version": "==0.5.0" }, "pycparser": { "hashes": [ @@ -578,11 +556,11 @@ }, "pyopenssl": { "hashes": [ - "sha256:c1cc5f86bcacefc84dada7d31175cae1b1518d5f60d3d0bb595a67822a868a6f", - "sha256:df5fc28af899e74e19fccb5510df423581047e10ab6f1f4ba1763ff5fde844c0" + "sha256:841498b9bec61623b1b6c47ebbc02367c07d60e0e195f19790817f10cc8db0b7", + "sha256:9e0c526404a210df9d2b18cd33364beadb0dc858a739b885677bc65e105d4a4c" ], "markers": "python_version >= '3.6'", - "version": "==23.0.0" + "version": "==23.1.1" }, "pyparsing": { "hashes": [ @@ -600,19 +578,19 @@ }, "pytest": { "hashes": [ - "sha256:130328f552dcfac0b1cec75c12e3f005619dc5f874f0a06e8ff7263f0ee6225e", - "sha256:c99ab0c73aceb050f68929bc93af19ab6db0558791c6a0715723abe9d0ade9d4" + "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362", + "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3" ], "index": "pypi", - "version": "==7.2.2" + "version": "==7.3.1" }, "pytest-asyncio": { "hashes": [ - "sha256:83cbf01169ce3e8eb71c6c278ccb0574d1a7a3bb8eaaf5e50e0ad342afb33b36", - "sha256:f129998b209d04fcc65c96fc85c11e5316738358909a8399e93be553d7656442" + "sha256:2b38a496aef56f56b0e87557ec313e11e1ab9276fc3863f6a7be0f1d0e415e1b", + "sha256:f2b3366b7cd501a4056858bd39349d5af19742aed2d81660b7998b6341c7eb9c" ], "index": "pypi", - "version": "==0.20.3" + "version": "==0.21.0" }, "pytest-repeat": { "hashes": [ @@ -638,6 +616,52 @@ "index": "pypi", "version": "==3.2.1" }, + "pyyaml": { + "hashes": [ + "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf", + "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", + "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b", + "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57", + "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b", + "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4", + "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07", + "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba", + "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9", + "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287", + "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513", + "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0", + "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782", + "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0", + "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92", + "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f", + "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2", + "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc", + "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1", + "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c", + "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86", + "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4", + "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c", + "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34", + "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b", + "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d", + "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c", + "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb", + "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7", + "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737", + "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3", + "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d", + "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358", + "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53", + "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78", + "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803", + "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a", + "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f", + "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174", + "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5" + ], + "index": "pypi", + "version": "==6.0" + }, "ruamel.yaml": { "hashes": [ "sha256:1a771fc92d3823682b7f0893ad56cb5a5c87c48e62b5399d6f42c8759a583b33", @@ -705,20 +729,20 @@ }, "tornado": { "hashes": [ - "sha256:1d54d13ab8414ed44de07efecb97d4ef7c39f7438cf5e976ccd356bebb1b5fca", - "sha256:20f638fd8cc85f3cbae3c732326e96addff0a15e22d80f049e00121651e82e72", - "sha256:5c87076709343557ef8032934ce5f637dbb552efa7b21d08e89ae7619ed0eb23", - "sha256:5f8c52d219d4995388119af7ccaa0bcec289535747620116a58d830e7c25d8a8", - "sha256:6fdfabffd8dfcb6cf887428849d30cf19a3ea34c2c248461e1f7d718ad30b66b", - "sha256:87dcafae3e884462f90c90ecc200defe5e580a7fbbb4365eda7c7c1eb809ebc9", - "sha256:9b630419bde84ec666bfd7ea0a4cb2a8a651c2d5cccdbdd1972a0c859dfc3c13", - "sha256:b8150f721c101abdef99073bf66d3903e292d851bee51910839831caba341a75", - "sha256:ba09ef14ca9893954244fd872798b4ccb2367c165946ce2dd7376aebdde8e3ac", - "sha256:d3a2f5999215a3a06a4fc218026cd84c61b8b2b40ac5296a6db1f1451ef04c1e", - "sha256:e5f923aa6a47e133d1cf87d60700889d7eae68988704e20c75fb2d65677a8e4b" + "sha256:4546003dc8b5733489139d3bff5fa6a0211be505faf819bd9970e7c2b32e8122", + "sha256:4d349846931557b7ec92f224b5d598b160e2ba26ae1812480b42e9622c884bf7", + "sha256:6164571f5b9f73143d1334df4584cb9ac86d20c461e17b6c189a19ead8bb93c1", + "sha256:6cfff1e9c15c79e106b8352269d201f8fc0815914a6260f3893ca18b724ea94b", + "sha256:720f53e6367b38190ae7fa398c25c086c69d88b3c6535bd6021a126b727fb5cd", + "sha256:912df5712024564e362ecce43c8d5862e14c78c8dd3846c9d889d44fbd7f4951", + "sha256:c37b6a384d54ce6a31168d40ab21ad2591ddaf34973075cc0cad154402ecd9e8", + "sha256:c659ab04d5aa477dbe44152c67d93f3ad3243b992d94f795ca1d5c73c37337ce", + "sha256:c9114a61a4588c09065b9996ae05462350d17160b92b9bf9a1e93689cc0424dc", + "sha256:d68f3192936ff2c4add04dc21a436a43b4408d466746b78bb2b9d0a53a18683f", + "sha256:d7b737e18f701de3e4a3b0824260b4d740e4d60607b8089bb80e80ffd464780e" ], - "markers": "python_version >= '3.7'", - "version": "==6.2" + "markers": "python_version >= '3.8'", + "version": "==6.3" }, "typing-extensions": { "hashes": [ @@ -808,42 +832,42 @@ "develop": { "attrs": { "hashes": [ - "sha256:29e95c7f6778868dbd49170f98f8818f78f3dc5e0e37c0b1f474e3561b240836", - "sha256:c9227bfc2f01993c03f68db37d1d15c9690188323c067c641f1a35ca58185f99" + "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04", + "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015" ], - "markers": "python_version >= '3.6'", - "version": "==22.2.0" + "markers": "python_version >= '3.7'", + "version": "==23.1.0" }, "black": { "hashes": [ - "sha256:0052dba51dec07ed029ed61b18183942043e00008ec65d5028814afaab9a22fd", - "sha256:0680d4380db3719ebcfb2613f34e86c8e6d15ffeabcf8ec59355c5e7b85bb555", - "sha256:121ca7f10b4a01fd99951234abdbd97728e1240be89fde18480ffac16503d481", - "sha256:162e37d49e93bd6eb6f1afc3e17a3d23a823042530c37c3c42eeeaf026f38468", - "sha256:2a951cc83ab535d248c89f300eccbd625e80ab880fbcfb5ac8afb5f01a258ac9", - "sha256:2bf649fda611c8550ca9d7592b69f0637218c2369b7744694c5e4902873b2f3a", - "sha256:382998821f58e5c8238d3166c492139573325287820963d2f7de4d518bd76958", - "sha256:49f7b39e30f326a34b5c9a4213213a6b221d7ae9d58ec70df1c4a307cf2a1580", - "sha256:57c18c5165c1dbe291d5306e53fb3988122890e57bd9b3dcb75f967f13411a26", - "sha256:7a0f701d314cfa0896b9001df70a530eb2472babb76086344e688829efd97d32", - "sha256:8178318cb74f98bc571eef19068f6ab5613b3e59d4f47771582f04e175570ed8", - "sha256:8b70eb40a78dfac24842458476135f9b99ab952dd3f2dab738c1881a9b38b753", - "sha256:9880d7d419bb7e709b37e28deb5e68a49227713b623c72b2b931028ea65f619b", - "sha256:9afd3f493666a0cd8f8df9a0200c6359ac53940cbde049dcb1a7eb6ee2dd7074", - "sha256:a29650759a6a0944e7cca036674655c2f0f63806ddecc45ed40b7b8aa314b651", - "sha256:a436e7881d33acaf2536c46a454bb964a50eff59b21b51c6ccf5a40601fbef24", - "sha256:a59db0a2094d2259c554676403fa2fac3473ccf1354c1c63eccf7ae65aac8ab6", - "sha256:a8471939da5e824b891b25751955be52ee7f8a30a916d570a5ba8e0f2eb2ecad", - "sha256:b0bd97bea8903f5a2ba7219257a44e3f1f9d00073d6cc1add68f0beec69692ac", - "sha256:b6a92a41ee34b883b359998f0c8e6eb8e99803aa8bf3123bf2b2e6fec505a221", - "sha256:bb460c8561c8c1bec7824ecbc3ce085eb50005883a6203dcfb0122e95797ee06", - "sha256:bfffba28dc52a58f04492181392ee380e95262af14ee01d4bc7bb1b1c6ca8d27", - "sha256:c1c476bc7b7d021321e7d93dc2cbd78ce103b84d5a4cf97ed535fbc0d6660648", - "sha256:c91dfc2c2a4e50df0026f88d2215e166616e0c80e86004d0003ece0488db2739", - "sha256:e6663f91b6feca5d06f2ccd49a10f254f9298cc1f7f49c46e498a0771b507104" + "sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5", + "sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915", + "sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326", + "sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940", + "sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b", + "sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30", + "sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c", + "sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c", + "sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab", + "sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27", + "sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2", + "sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961", + "sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9", + "sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb", + "sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70", + "sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331", + "sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2", + "sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266", + "sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d", + "sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6", + "sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b", + "sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925", + "sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8", + "sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4", + "sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3" ], "index": "pypi", - "version": "==23.1.0" + "version": "==23.3.0" }, "click": { "hashes": [ @@ -863,11 +887,11 @@ }, "flake8-bugbear": { "hashes": [ - "sha256:beb5c7efcd7ccc2039ef66a77bb8db925e7be3531ff1cb4d0b7030d0e2113d72", - "sha256:e3e7f74c8a49ad3794a7183353026dabd68c74030d5f46571f84c1fb0eb79363" + "sha256:8a218d13abd6904800970381057ce8e72cde8eea743240c4ef3ede4dd0bc9cfb", + "sha256:ea565bdb87b96b56dc499edd6cc3ba7f695373d902a5f56c989b74fad7c7719d" ], "index": "pypi", - "version": "==23.3.12" + "version": "==23.3.23" }, "isort": { "hashes": [ @@ -895,11 +919,11 @@ }, "packaging": { "hashes": [ - "sha256:714ac14496c3e68c99c29b00845f7a2b85f3bb6f1078fd9f72fd20f0570002b2", - "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97" + "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61", + "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f" ], "markers": "python_version >= '3.7'", - "version": "==23.0" + "version": "==23.1" }, "pathspec": { "hashes": [ @@ -911,11 +935,11 @@ }, "platformdirs": { "hashes": [ - "sha256:024996549ee88ec1a9aa99ff7f8fc819bb59e2c3477b410d90a16d32d6e707aa", - "sha256:e5986afb596e4bb5bde29a79ac9061aa955b94fca2399b7aaac4090860920dd8" + "sha256:d5b638ca397f25f979350ff789db335903d7ea010ab28903f57b27e1b16c2b08", + "sha256:ebe11c0d7a805086e99506aa331612429a72ca7cd52a1f0d277dc4adc20cb10e" ], "markers": "python_version >= '3.7'", - "version": "==3.1.1" + "version": "==3.2.0" }, "pycodestyle": { "hashes": [ diff --git a/src/test/regress/citus_tests/config.py b/src/test/regress/citus_tests/config.py index c5b7ce010..3dc47671b 100644 --- a/src/test/regress/citus_tests/config.py +++ b/src/test/regress/citus_tests/config.py @@ -15,6 +15,8 @@ WORKER2 = "worker2" REGULAR_USER_NAME = "regularuser" SUPER_USER_NAME = "postgres" +DATABASE_NAME = "postgres" + ARBITRARY_SCHEDULE_NAMES = [ "create_schedule", "sql_schedule", @@ -96,6 +98,7 @@ class CitusBaseClusterConfig(object, metaclass=NewInitCaller): self.temp_dir = CITUS_ARBITRARY_TEST_DIR self.worker_amount = 2 self.user = REGULAR_USER_NAME + self.dbname = DATABASE_NAME self.is_mx = True self.is_citus = True self.name = type(self).__name__ diff --git a/src/test/regress/citus_tests/query_generator/.gitignore b/src/test/regress/citus_tests/query_generator/.gitignore new file mode 100644 index 000000000..c397a501d --- /dev/null +++ b/src/test/regress/citus_tests/query_generator/.gitignore @@ -0,0 +1,2 @@ +__pycache__ +*-env diff --git a/src/test/regress/citus_tests/query_generator/README.md b/src/test/regress/citus_tests/query_generator/README.md new file mode 100644 index 000000000..cf4d54b0e --- /dev/null +++ b/src/test/regress/citus_tests/query_generator/README.md @@ -0,0 +1,223 @@ +## Goal of the Tool +Tool supports a simple syntax to be useful to generate queries with different join orders. Main motivation for me to create the tool was to compare results of the generated queries for different [Citus](https://github.com/citusdata/citus) tables and Postgres tables. That is why we support a basic syntax for now. It can be extended to support different queries. + +## Query Generator for Postgres +Tool generates SELECT queries, whose depth can be configured, with different join orders. It also generates DDLs required for query execution. +You can also tweak configuration parameters for data inserting command generation. + +## How to Run Citus Join Verification? +You can verify if Citus breaks any default PG join behaviour via `citus_compare_dist_local_joins.sh`. It creates +tables specified in config. Then, it runs generated queries on those tables and saves the results into `out/dist_queries.out`. +After running those queries for Citus tables, it creates PG tables with the same names as previous run, executes the same +queries, and saves the results into `out/local_queries.out`. In final step, it generates diff between local and distributed results. +You can see the contents of `out/local_dist_diffs` to see if there is any Citus unsupported query. + +1. Create a Citus local cluster with 2 workers by using [citus_dev](https://github.com/citusdata/tools/tree/develop/citus_dev) tool +(Note: make sure you do not configure psql via .psqlrc file as it would fail the test.) +```bash +citus_dev make testCluster --destroy +``` +2. Run the test, +```bash +cd src/test/regress/citus_tests/query_generator/bin +bash citus_compare_dist_local_joins.sh +``` +3. See the diff content in `src/test/regress/citus_tests/query_generator/out/local_dist_diffs` + +### Configuration +You can configure 3 different parts: + +- [DDL Configuration](#ddl-configuration) +- [Data Insertion Configuration](#data-insertion-configuration) +- [Query Configuration](#query-configuration) + +## DDL Configuration +Tool generates related ddl commands before generating queries. + +Schema for DDL configuration: +```yaml +ddlOutFile: +commonColName: +targetTables: + - Table: + name: + citusType: + maxAllowedUseOnQuery: + rowCount: + nullRate: + duplicateRate: + columns: + - Column: + name: + type: + distinctCopyCount: +``` + +Explanation: +```yaml +ddlOutFile: "file to write generated DDL commands" +commonColName: "name of the column that will be used as distribution column, filter column in restrictions and target column in selections" +targetTables: "array of tables that will be used in generated queries" + - Table: + name: "name prefix of table" + citusType: "citus type of table" + maxAllowedUseOnQuery: "limits how many times table can appear in query" + rowCount: "total # of rows that will be inserted into table" + nullRate: "percentage of null rows in rowCount that will be inserted into table" + duplicateRate: "percentage of duplicates in rowCount that will be inserted into table" + columns: "array of columns in table" + - Column: + name: "name of column" + type: "name of data type of column(only support 'int' now)" + distinctCopyCount: "how many tables with the same configuration we should create(only by changing full name, still using the same name prefix)" +``` + + +## Data Insertion Configuration +Tool generates data insertion commands if you want tables with filled data. You can configure total number of rows, what percentage of them should +be null and what percentage of them should be duplicated. For related configuration see Table schema at [DDL Configuration](#ddl-configuration). You +can also configure range of the randomly generated data. See `dataRange` at [Query Configuration](#query-configuration). + +## Query Configuration +After generation of ddls and data insertion commands, the tool generates queries. + +Schema for Query configuration: +```yaml +queryCount: +queryOutFile: +semiAntiJoin: +cartesianProduct: +limit: +orderby: +forceOrderbyWithLimit: +useAvgAtTopLevelTarget: +dataRange: + from: + to: +filterRange: + from: + to: +limitRange: + from: + to: +targetRteCount: +targetCteCount: +targetCteRteCount: +targetJoinTypes: +targetRteTypes: +targetRestrictOps: +``` + +Explanation: +```yaml +queryCount: "number of queries to generate" +queryOutFile: "file to write generated queries" +semiAntiJoin: "should we support semi joins (WHERE col IN (Subquery))" +cartesianProduct: "should we support cartesian joins" +limit: "should we support limit clause" +orderby: "should we support order by clause" +forceOrderbyWithLimit: "should we force order by when we use limit" +useAvgAtTopLevelTarget: "should we make top level query as select avg() from (subquery)" +dataRange: + from: "starting boundary for data generation" + to: "end boundary for data generation" +filterRange: + from: "starting boundary for restriction clause" + to: "end boundary for restriction clause" +limitRange: + from: "starting boundary for limit clause" + to: "end boundary for data limit clause" +targetRteCount: "limits how many rtes should exist in non-cte part of the query" +targetCteCount: "limits how many ctes should exist in query" +targetCteRteCount: "limits how many rtes should exist in cte part of the query" +targetJoinTypes: "supported join types" +targetRteTypes: "supported rte types" +targetRestrictOps: "supported restrict ops" +``` + +## Misc Configuration +Tool has some configuration options which does not suit above 3 parts. + +Schema for misc configuration: +```yaml +interactiveMode: +``` + +Explanation: +```yaml +interactiveMode: "when true, interactively prints generated ddls and queries. Otherwise, it writes them to configured files." +``` + +## Supported Operations +It uses `commonColName` for any kind of target selection required for any supported query clause. + +### Column Type Support +Tool currently supports only int data type, but plans to support other basic types. + +### Join Support +Tool supports following joins: +```yaml +targetJoinTypes: + - INNER + - LEFT + - RIGHT + - FULL +``` + +### Citus Table Support +Tool supports following citus table types: +```yaml +targetTables: + - Table: + ... + citusType: + ... +``` + +### Restrict Operation Support +Tool supports following restrict operations: +```yaml +targetRestrictOps: + - LT + - GT + - EQ +``` + +### Rte Support +Tool supports following rtes: +```yaml +targetRteTypes: + - RELATION + - SUBQUERY + - CTE + - VALUES +``` + +## How to Generate Queries? +You have 2 different options. + +- [Interactive Mode](#interactive-mode) +- [File Mode](#file-mode) + +### Interactive Mode +In this mode, you will be prompted to continue generating a query. When you hit to `Enter`, it will continue generating them. +You will need to hit to `x` to exit. + +1. Configure `interactiveMode: true` in config.yml, +2. Run the command shown below +```bash +cd src/test/regress/citus_tests/query_generator +python generate_queries.py +``` + +### File Mode +In this mode, generated ddls and queries will be written into the files configured in [config.yml](./config/config.yaml). + +1. Configure `interactiveMode: false`, +2. Configure `queryCount: `, +3. Configure `queryOutFile: ` and `ddlOutFile: ` +4. Run the command shown below +```bash +cd src/test/regress/citus_tests/query_generator +python generate_queries.py +``` diff --git a/src/test/regress/citus_tests/query_generator/bin/citus_compare_dist_local_joins.sh b/src/test/regress/citus_tests/query_generator/bin/citus_compare_dist_local_joins.sh new file mode 100644 index 000000000..f921bec0b --- /dev/null +++ b/src/test/regress/citus_tests/query_generator/bin/citus_compare_dist_local_joins.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +# make bash behave +set -euo pipefail + +psql_user=$1 +psql_db=$2 +psql_port=$3 + +runDDLs() +{ + # run ddls + psql -U "${psql_user}" -d "${psql_db}" -p "${psql_port}" -f "${out_folder}"/ddls.sql > /dev/null +} + +runUndistributeTables() +{ + undistribute_all_tables_command='SELECT undistribute_table(logicalrelid) FROM pg_dist_partition;' + # run undistribute all tables + psql -U "${psql_user}" -d "${psql_db}" -p "${psql_port}" -c "${undistribute_all_tables_command}" > /dev/null +} + +runQueries() +{ + out_filename=$1 + + # run dmls + # echo queries and comments for query tracing + psql -U "${psql_user}" -d "${psql_db}" -p "${psql_port}" \ + --echo-all \ + -f "${out_folder}"/queries.sql > "${out_filename}" 2>&1 +} + +showDiffs() +{ + pushd . && cd "${script_folder}" && python3 diff-checker.py && popd +} + +# run query generator and let it create output ddls and queries +script_folder=$(dirname "$0") +out_folder="${script_folder}"/../out +pushd . && cd "${script_folder}"/.. && python3 generate_queries.py && popd + +# remove result files if exists +rm -rf "${out_folder}"/dist_queries.out "${out_folder}"/local_queries.out + +# run ddls +runDDLs + +# runs dmls for distributed tables +runQueries "${out_folder}"/dist_queries.out + +# undistribute all dist tables +runUndistributeTables + +# runs the same dmls for pg local tables +runQueries "${out_folder}"/local_queries.out + +# see diffs in results +showDiffs diff --git a/src/test/regress/citus_tests/query_generator/bin/diff-checker.py b/src/test/regress/citus_tests/query_generator/bin/diff-checker.py new file mode 100644 index 000000000..5bd2898a9 --- /dev/null +++ b/src/test/regress/citus_tests/query_generator/bin/diff-checker.py @@ -0,0 +1,122 @@ +import os +import re +import subprocess +import sys + + +def createPatternForFailedQueryBlock(acceptableErrors): + totalAcceptableOrders = len(acceptableErrors) + + failedQueryBlockPattern = "-- queryId: [0-9]+\n.*\npsql:.*(?:" + for acceptableErrorIdx in range(totalAcceptableOrders): + failedQueryBlockPattern += acceptableErrors[acceptableErrorIdx] + if acceptableErrorIdx < totalAcceptableOrders - 1: + failedQueryBlockPattern += "|" + failedQueryBlockPattern += ")" + + return failedQueryBlockPattern + + +def findFailedQueriesFromFile(queryOutFile, acceptableErrors): + failedQueryIds = [] + outFileContent = "" + failedQueryBlockPattern = createPatternForFailedQueryBlock(acceptableErrors) + queryIdPattern = "queryId: ([0-9]+)" + with open(queryOutFile, "r") as f: + outFileContent = f.read() + failedQueryContents = re.findall(failedQueryBlockPattern, outFileContent) + failedQueryIds = [ + re.search(queryIdPattern, failedQueryContent)[1] + for failedQueryContent in failedQueryContents + ] + + return failedQueryIds + + +def removeFailedQueryOutputFromFile(outFile, failedQueryIds): + if len(failedQueryIds) == 0: + return + + distOutFileContentAsLines = [] + with open(outFile, "r") as f: + distOutFileContentAsLines = f.readlines() + + with open(outFile, "w") as f: + clear = False + nextIdx = 0 + nextQueryIdToDelete = failedQueryIds[nextIdx] + queryIdPattern = "queryId: ([0-9]+)" + + for line in distOutFileContentAsLines: + matched = re.search(queryIdPattern, line) + # founded line which contains query id + if matched: + # query id matches with the next failed query's id + if nextQueryIdToDelete == matched[1]: + # clear lines until we find succesfull query + clear = True + nextIdx += 1 + if nextIdx < len(failedQueryIds): + nextQueryIdToDelete = failedQueryIds[nextIdx] + else: + # we found successfull query + clear = False + + if not clear: + f.write(line) + return + + +def removeFailedQueryOutputFromFiles(distQueryOutFile, localQueryOutFile): + # remove the failed distributed query from both local and distributed query files to prevent diff for acceptable errors + # some of generated queries fails with below errors due to https://github.com/citusdata/citus/issues/6653, we skip those until we support them + acceptableErrors = [ + "ERROR: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns", + "ERROR: recursive complex joins are only supported when all distributed tables are co-located and joined on their distribution columns", + ] + failedDistQueryIds = findFailedQueriesFromFile(distQueryOutFile, acceptableErrors) + removeFailedQueryOutputFromFile(distQueryOutFile, failedDistQueryIds) + removeFailedQueryOutputFromFile(localQueryOutFile, failedDistQueryIds) + return + + +def showDiffs(distQueryOutFile, localQueryOutFile, diffFile): + exitCode = 1 + with open(diffFile, "w") as f: + diffCommand = "diff -u {} {}".format(localQueryOutFile, distQueryOutFile) + process = subprocess.Popen(diffCommand.split(), stdout=f, stderr=f, shell=False) + process.wait() + exitCode = process.returncode + + print("diff exit {}".format(exitCode)) + return exitCode + + +def exitIfAnyLocalQueryFailed(localQueryOutFile): + allErrors = ["ERROR:"] + failedLocalQueryIds = findFailedQueriesFromFile(localQueryOutFile, allErrors) + assert ( + len(failedLocalQueryIds) == 0 + ), """There might be an internal error related to query generator or + we might find a Postgres bug. Check local_queries.out to see the error.""" + return + + +if __name__ == "__main__": + scriptDirPath = os.path.dirname(os.path.abspath(__file__)) + outFolderPath = scriptDirPath + "/../out" + localQueryOutFile = "{}/local_queries.out".format(outFolderPath) + distQueryOutFile = "{}/dist_queries.out".format(outFolderPath) + diffFile = "{}/local_dist.diffs".format(outFolderPath) + + # exit if we have any error from local queries + exitIfAnyLocalQueryFailed(localQueryOutFile) + + # find failed queries from distQueryOutFile and then remove failed queries and their results from + # both distQueryOutFile and localQueryOutFile + removeFailedQueryOutputFromFiles(distQueryOutFile, localQueryOutFile) + + # show diffs in unified format + exitCode = showDiffs(distQueryOutFile, localQueryOutFile, diffFile) + + sys.exit(exitCode) diff --git a/src/test/regress/citus_tests/query_generator/bin/run_query_compare_test.py b/src/test/regress/citus_tests/query_generator/bin/run_query_compare_test.py new file mode 100755 index 000000000..9ab172ef7 --- /dev/null +++ b/src/test/regress/citus_tests/query_generator/bin/run_query_compare_test.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 + +"""query_gen_test +Usage: + run_query_compare_test --bindir= --pgxsdir= + +Options: + --bindir= PostgreSQL executable directory(ex: '~/.pgenv/pgsql-10.4/bin') + --pgxsdir= Path to the PGXS directory(ex: ~/.pgenv/src/postgresql-11.3) +""" + +import os +import subprocess +import sys + +from docopt import docopt + +# https://stackoverflow.com/questions/14132789/relative-imports-for-the-billionth-time/14132912#14132912 +sys.path.append( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +) + + +# ignore E402 because these imports require addition to path +import common # noqa: E402 + +import config as cfg # noqa: E402 + + +def run_test(config): + # start cluster + common.initialize_temp_dir(cfg.CITUS_ARBITRARY_TEST_DIR) + common.initialize_citus_cluster( + config.bindir, config.datadir, config.settings, config + ) + + # run test + scriptDirPath = os.path.dirname(os.path.abspath(__file__)) + testRunCommand = "bash {}/citus_compare_dist_local_joins.sh {} {} {}".format( + scriptDirPath, config.user, config.dbname, config.coordinator_port() + ) + process = subprocess.Popen( + testRunCommand.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + stdout, stderr = process.communicate() + + # stop cluster + common.stop_databases( + config.bindir, config.datadir, config.node_name_to_ports, config.name + ) + + print(stdout) + print(stderr) + print(process.returncode) + sys.exit(process.returncode) + + +if __name__ == "__main__": + citusClusterConfig = cfg.CitusSuperUserDefaultClusterConfig( + docopt(__doc__, version="run_query_compare_test") + ) + + run_test(citusClusterConfig) diff --git a/src/test/regress/citus_tests/query_generator/config/config.py b/src/test/regress/citus_tests/query_generator/config/config.py new file mode 100644 index 000000000..85def6b79 --- /dev/null +++ b/src/test/regress/citus_tests/query_generator/config/config.py @@ -0,0 +1,129 @@ +import copy + +import yaml +from config.config_parser import ( + parseJoinTypeArray, + parseRange, + parseRestrictOpArray, + parseRteTypeArray, + parseTableArray, +) +from node_defs import CitusType + + +class Config: + def __init__(self): + configObj = Config.parseConfigFile("config/config.yaml") + + self.targetTables = _distinctCopyTables( + parseTableArray(configObj["targetTables"]) + ) + self.targetJoinTypes = parseJoinTypeArray(configObj["targetJoinTypes"]) + self.targetRteTypes = parseRteTypeArray(configObj["targetRteTypes"]) + self.targetRestrictOps = parseRestrictOpArray(configObj["targetRestrictOps"]) + self.commonColName = configObj["commonColName"] + self.targetRteCount = configObj["targetRteCount"] + self.targetCteCount = configObj["targetCteCount"] + self.targetCteRteCount = configObj["targetCteRteCount"] + self.semiAntiJoin = configObj["semiAntiJoin"] + self.cartesianProduct = configObj["cartesianProduct"] + self.limit = configObj["limit"] + self.orderby = configObj["orderby"] + self.forceOrderbyWithLimit = configObj["forceOrderbyWithLimit"] + self.useAvgAtTopLevelTarget = configObj["useAvgAtTopLevelTarget"] + self.interactiveMode = configObj["interactiveMode"] + self.queryOutFile = configObj["queryOutFile"] + self.ddlOutFile = configObj["ddlOutFile"] + self.queryCount = configObj["queryCount"] + self.dataRange = parseRange(configObj["dataRange"]) + self.filterRange = parseRange(configObj["filterRange"]) + self.limitRange = parseRange(configObj["limitRange"]) + self._ensureRteLimitsAreSane() + # print(self) + + def __repr__(self): + rep = "targetRteCount: {}\n".format(self.targetRteCount) + rep += "targetCteCount: {}\n".format(self.targetCteCount) + rep += "targetCteRteCount: {}\n".format(self.targetCteRteCount) + + rep += "targetRteTypes:\n" + for rteType in self.targetRteTypes: + rep += "\t{}\n".format(rteType) + + rep += "targetJoinTypes:\n" + for joinType in self.targetJoinTypes: + rep += "\t{}\n".format(joinType) + + rep += "restrictOps:\n" + for restrictOp in self.targetRestrictOps: + rep += "\t{}\n".format(restrictOp) + + return rep + + def _ensureRteLimitsAreSane(self): + totalAllowedUseForAllTables = 0 + for table in self.targetTables: + totalAllowedUseForAllTables += table.maxAllowedUseOnQuery + assert ( + totalAllowedUseForAllTables >= self.targetRteCount + ), """targetRteCount cannot be greater than sum of maxAllowedUseOnQuery for all tables. + Set targetRteCount to a lower value or increase maxAllowedUseOnQuery for tables at config.yml.""" + + @staticmethod + def parseConfigFile(path): + try: + with open(path, "r") as configFile: + return yaml.load(configFile, yaml.Loader) + except IOError as e: + print(f"I/O error({0}): {1}".format(e.errno, e.strerror)) + raise BaseException("cannot parse config.yaml") + except Exception: + print("Unexpected error while parsing config.yml.") + + +_config = None + + +def resetConfig(): + global _config + _config = Config() + + +def getConfig(): + return _config + + +def getAllTableNames(): + """returns table names from target tables given at config""" + tables = getConfig().targetTables + tableNames = [table.name for table in tables] + return tableNames + + +def getMaxAllowedCountForTable(tableName): + tables = getConfig().targetTables + filtered = filter(lambda el: el.name == tableName, tables) + filtered = list(filtered) + assert len(filtered) == 1 + return filtered[0].maxAllowedUseOnQuery + + +def isTableDistributed(table): + return table.citusType == CitusType.DISTRIBUTED + + +def isTableReference(table): + return table.citusType == CitusType.REFERENCE + + +def _distinctCopyTables(tables): + distinctCopyTables = [] + for table in tables: + distinctCopyCount = table.distinctCopyCount + for tblIdx in range(1, distinctCopyCount): + distinctCopyTable = copy.deepcopy(table) + distinctCopyTable.name += str(tblIdx) + distinctCopyTables.append(distinctCopyTable) + table.name += "0" + tables.extend(distinctCopyTables) + return tables diff --git a/src/test/regress/citus_tests/query_generator/config/config.yaml b/src/test/regress/citus_tests/query_generator/config/config.yaml new file mode 100644 index 000000000..1920966ee --- /dev/null +++ b/src/test/regress/citus_tests/query_generator/config/config.yaml @@ -0,0 +1,65 @@ +interactiveMode: false +queryCount: 250 +queryOutFile: queries.sql +ddlOutFile: ddls.sql +semiAntiJoin: true +cartesianProduct: false +limit: true +orderby: true +forceOrderbyWithLimit: true +useAvgAtTopLevelTarget: true +dataRange: + from: 0 + to: 10 +filterRange: + from: 0 + to: 10 +limitRange: + from: 0 + to: 10 +targetRteCount: 40 +targetCteCount: 3 +targetCteRteCount: 2 + +commonColName: id + +targetTables: + - Table: + name: dist + citusType: DISTRIBUTED + maxAllowedUseOnQuery: 10 + rowCount: 10 + nullRate: 0.1 + duplicateRate: 0.1 + columns: + - Column: + name: id + type: int + distinctCopyCount: 2 + - Table: + name: ref + citusType: REFERENCE + maxAllowedUseOnQuery: 10 + rowCount: 10 + nullRate: 0.1 + duplicateRate: 0.1 + columns: + - Column: + name: id + type: int + distinctCopyCount: 2 + +targetJoinTypes: + - INNER + - LEFT + - RIGHT + - FULL + +targetRteTypes: + - RELATION + - SUBQUERY + - CTE + +targetRestrictOps: + - LT + - GT diff --git a/src/test/regress/citus_tests/query_generator/config/config_parser.py b/src/test/regress/citus_tests/query_generator/config/config_parser.py new file mode 100755 index 000000000..0b4f3837f --- /dev/null +++ b/src/test/regress/citus_tests/query_generator/config/config_parser.py @@ -0,0 +1,81 @@ +from node_defs import CitusType, Column, JoinType, RestrictOp, RTEType, Table + + +def parseJoinType(joinTypeText): + return JoinType[joinTypeText] + + +def parseJoinTypeArray(joinTypeTexts): + joinTypes = [] + for joinTypeText in joinTypeTexts: + joinType = parseJoinType(joinTypeText) + joinTypes.append(joinType) + return joinTypes + + +def parseRteType(rteTypeText): + return RTEType[rteTypeText] + + +def parseRteTypeArray(rteTypeTexts): + rteTypes = [] + for rteTypeText in rteTypeTexts: + rteType = parseRteType(rteTypeText) + rteTypes.append(rteType) + return rteTypes + + +def parseRestrictOp(restrictOpText): + return RestrictOp[restrictOpText] + + +def parseRestrictOpArray(restrictOpTexts): + restrictOps = [] + for restrictOpText in restrictOpTexts: + restrictOp = parseRestrictOp(restrictOpText) + restrictOps.append(restrictOp) + return restrictOps + + +def parseTable(targetTableDict): + name = targetTableDict["name"] + citusType = CitusType[targetTableDict["citusType"]] + maxAllowedUseOnQuery = targetTableDict["maxAllowedUseOnQuery"] + rowCount = targetTableDict["rowCount"] + nullRate = targetTableDict["nullRate"] + duplicateRate = targetTableDict["duplicateRate"] + columns = [] + for columnDict in targetTableDict["columns"]: + col = parseColumn(columnDict) + columns.append(col) + distinctCopyCount = targetTableDict["distinctCopyCount"] + return Table( + name, + citusType, + maxAllowedUseOnQuery, + rowCount, + nullRate, + duplicateRate, + columns, + distinctCopyCount, + ) + + +def parseTableArray(targetTableDicts): + tables = [] + for targetTableDict in targetTableDicts: + table = parseTable(targetTableDict["Table"]) + tables.append(table) + return tables + + +def parseColumn(targetColumnDict): + name = targetColumnDict["name"] + type = targetColumnDict["type"] + return Column(name, type) + + +def parseRange(rangeDict): + fromVal = rangeDict["from"] + toVal = rangeDict["to"] + return (fromVal, toVal) diff --git a/src/test/regress/citus_tests/query_generator/data_gen.py b/src/test/regress/citus_tests/query_generator/data_gen.py new file mode 100644 index 000000000..1d508acaf --- /dev/null +++ b/src/test/regress/citus_tests/query_generator/data_gen.py @@ -0,0 +1,81 @@ +from node_defs import CitusType + +from config.config import getConfig + + +def getTableData(): + dataGenerationSql = "" + + tableIdx = 1 + (fromVal, toVal) = getConfig().dataRange + tables = getConfig().targetTables + for table in tables: + # generate base rows + dataGenerationSql += _genOverlappingData(table.name, fromVal, table.rowCount) + dataGenerationSql += _genNonOverlappingData(table.name, toVal, tableIdx) + dataGenerationSql += "\n" + + # generate null rows + if not table.citusType == CitusType.DISTRIBUTED: + targetNullRows = int(table.rowCount * table.nullRate) + dataGenerationSql += _genNullData(table.name, targetNullRows) + dataGenerationSql += "\n" + + # generate duplicate rows + targetDuplicateRows = int(table.rowCount * table.duplicateRate) + dataGenerationSql += _genDupData(table.name, targetDuplicateRows) + dataGenerationSql += "\n\n" + tableIdx += 1 + return dataGenerationSql + + +def _genOverlappingData(tableName, startVal, rowCount): + """returns string to fill table with [startVal,startVal+rowCount] range of integers""" + dataGenerationSql = "" + dataGenerationSql += "INSERT INTO " + tableName + dataGenerationSql += ( + " SELECT i FROM generate_series(" + + str(startVal) + + "," + + str(startVal + rowCount) + + ") i;" + ) + return dataGenerationSql + + +def _genNullData(tableName, nullCount): + """returns string to fill table with NULLs""" + dataGenerationSql = "" + dataGenerationSql += "INSERT INTO " + tableName + dataGenerationSql += ( + " SELECT NULL FROM generate_series(0," + str(nullCount) + ") i;" + ) + return dataGenerationSql + + +def _genDupData(tableName, dupRowCount): + """returns string to fill table with duplicate integers which are fetched from given table""" + dataGenerationSql = "" + dataGenerationSql += "INSERT INTO " + tableName + dataGenerationSql += ( + " SELECT * FROM " + + tableName + + " ORDER BY " + + getConfig().commonColName + + " LIMIT " + + str(dupRowCount) + + ";" + ) + return dataGenerationSql + + +def _genNonOverlappingData(tableName, startVal, tableIdx): + """returns string to fill table with different integers for given table""" + startVal = startVal + tableIdx * 20 + endVal = startVal + 20 + dataGenerationSql = "" + dataGenerationSql += "INSERT INTO " + tableName + dataGenerationSql += ( + " SELECT i FROM generate_series(" + str(startVal) + "," + str(endVal) + ") i;" + ) + return dataGenerationSql diff --git a/src/test/regress/citus_tests/query_generator/ddl_gen.py b/src/test/regress/citus_tests/query_generator/ddl_gen.py new file mode 100755 index 000000000..b2f97f694 --- /dev/null +++ b/src/test/regress/citus_tests/query_generator/ddl_gen.py @@ -0,0 +1,48 @@ +from config.config import getConfig, isTableDistributed, isTableReference + + +def getTableDDLs(): + ddls = "" + tables = getConfig().targetTables + for table in tables: + ddls += _genTableDDL(table) + ddls += "\n" + return ddls + + +def _genTableDDL(table): + ddl = "" + ddl += "DROP TABLE IF EXISTS " + table.name + ";" + ddl += "\n" + + ddl += "CREATE TABLE " + table.name + "(" + for column in table.columns[:-1]: + ddl += _genColumnDDL(column) + ddl += ",\n" + if len(table.columns) > 0: + ddl += _genColumnDDL(table.columns[-1]) + ddl += ");\n" + + if isTableDistributed(table): + ddl += ( + "SELECT create_distributed_table(" + + "'" + + table.name + + "','" + + getConfig().commonColName + + "'" + + ");" + ) + ddl += "\n" + elif isTableReference(table): + ddl += "SELECT create_reference_table(" + "'" + table.name + "'" + ");" + ddl += "\n" + return ddl + + +def _genColumnDDL(column): + ddl = "" + ddl += column.name + ddl += " " + ddl += column.type + return ddl diff --git a/src/test/regress/citus_tests/query_generator/generate_queries.py b/src/test/regress/citus_tests/query_generator/generate_queries.py new file mode 100755 index 000000000..4f1f7967b --- /dev/null +++ b/src/test/regress/citus_tests/query_generator/generate_queries.py @@ -0,0 +1,65 @@ +import signal +import sys + +from data_gen import getTableData +from ddl_gen import getTableDDLs +from query_gen import newQuery + +from config.config import getConfig, resetConfig + + +def _signal_handler(sig, frame): + sys.exit(0) + + +def _interactiveMode(ddls, data): + print(ddls) + print(data) + + while True: + res = input("Press x to exit or Enter to generate more") + if res.lower() == "x": + print("Exit from query generation mode!") + sys.exit(0) + + query = newQuery() + print(query) + + resetConfig() + + +def _fileMode(ddls, data): + ddlFileName = "out/" + getConfig().ddlOutFile + with open(ddlFileName, "w") as ddlFile: + ddlFile.writelines([ddls, data]) + + queryCount = getConfig().queryCount + fileName = "out/" + getConfig().queryOutFile + with open(fileName, "w") as f: + # enable repartition joins due to https://github.com/citusdata/citus/issues/6865 + enableRepartitionJoinCommand = "SET citus.enable_repartition_joins TO on;\n" + queryLines = [enableRepartitionJoinCommand] + queryId = 1 + for _ in range(queryCount): + query = newQuery() + + queryLine = "-- queryId: " + str(queryId) + "\n" + queryLine += query + "\n\n" + + queryLines.append(queryLine) + queryId += 1 + f.writelines(queryLines) + + +if __name__ == "__main__": + signal.signal(signal.SIGINT, _signal_handler) + + resetConfig() + + ddls = getTableDDLs() + data = getTableData() + + if getConfig().interactiveMode: + _interactiveMode(ddls, data) + else: + _fileMode(ddls, data) diff --git a/src/test/regress/citus_tests/query_generator/node_defs.py b/src/test/regress/citus_tests/query_generator/node_defs.py new file mode 100755 index 000000000..b0db1da63 --- /dev/null +++ b/src/test/regress/citus_tests/query_generator/node_defs.py @@ -0,0 +1,55 @@ +from enum import Enum + + +class JoinType(Enum): + INNER = 1 + LEFT = 2 + RIGHT = 3 + FULL = 4 + + +class RTEType(Enum): + RELATION = 1 + SUBQUERY = 2 + CTE = 3 + VALUES = 4 + + +class RestrictOp(Enum): + LT = 1 + GT = 2 + EQ = 3 + + +class CitusType(Enum): + DISTRIBUTED = 1 + REFERENCE = 2 + POSTGRES = 3 + + +class Table: + def __init__( + self, + name, + citusType, + maxAllowedUseOnQuery, + rowCount, + nullRate, + duplicateRate, + columns, + distinctCopyCount, + ): + self.name = name + self.citusType = citusType + self.maxAllowedUseOnQuery = maxAllowedUseOnQuery + self.rowCount = rowCount + self.nullRate = nullRate + self.duplicateRate = duplicateRate + self.columns = columns + self.distinctCopyCount = distinctCopyCount + + +class Column: + def __init__(self, name, type): + self.name = name + self.type = type diff --git a/src/test/regress/citus_tests/query_generator/out/.gitignore b/src/test/regress/citus_tests/query_generator/out/.gitignore new file mode 100644 index 000000000..d6b7ef32c --- /dev/null +++ b/src/test/regress/citus_tests/query_generator/out/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/src/test/regress/citus_tests/query_generator/query_gen.py b/src/test/regress/citus_tests/query_generator/query_gen.py new file mode 100644 index 000000000..e25525d29 --- /dev/null +++ b/src/test/regress/citus_tests/query_generator/query_gen.py @@ -0,0 +1,434 @@ +import random + +from node_defs import RTEType +from random_selections import ( + randomJoinOp, + randomRestrictOp, + randomRteType, + shouldSelectThatBranch, +) + +from config.config import getAllTableNames, getConfig, getMaxAllowedCountForTable + +# query_gen.py generates a new query from grammar rules below. It randomly chooses allowed rules +# to generate a query. Here are some important notes about the query generation: +# +# - We enforce the max allowed # of usage for each table. It also enforces global total # of tables. +# - Table names, restriction types and any other selections are chosen between the values provided by +# configuration file (config.yml). +# - Entry point for the generator is newQuery and all other methods are internal methods. We pass a context +# object named GeneratorContext to all internal methods as parameter to perform checks and generations +# on the query via the context. +# - shouldSelectThatBranch() is useful utility method to randomly chose a grammar rule. Some of the rules +# are only selected if we allowed them in configuration file. +# - We enforce table limits separately if we are inside cte part of the query(see targetCteRteCount). +# We also enforce max # of ctes for a query. +# - commonColName from the config is used at select and where clauses. +# - useAvgAtTopLevelTarget is useful to return single row as query result. It is also useful to track Citus +# query bugs via run_query_compare_test.py. +# - '=' restriction is removed from the config by default to return values different than null most of the time. +# - 'RTE.VALUES' is also removed from the config for the same reason as above. +# - Filter range is selected as same with data range for the same reason as above. +# - aliasStack at GeneratorContext is useful to put correct table names into where clause. + +# grammar syntax +# +# ======Assumptions====== +# 1. Tables has common dist col +# 2. All operations execute on common column for all tables. +# +# TODO: RTE_FUNCTION, RTE_TABLEFUNC +# +# ====SYNTAX==== +# ===Nonterminals=== +# Query +# SelectExpr +# FromExpr +# RestrictExpr +# RteList +# Rte +# SubqueryRte +# RelationRte +# JoinList +# JoinOp +# Using +# RestrictList +# Restrict +# CteRte +# CteList +# Cte +# ValuesRte +# Limit +# OrderBy +# +# ===Terminals=== +# e 'SELECT' 'FROM' 'INNER JOIN' 'LEFT JOIN' 'RIGHT JOIN' 'FULL JOIN' 'WHERE' 'LIMIT' 'USING' 'WITH' +# 'ORDER BY' 'VALUES' 'IN' 'NOT' 'AS' '<' '>' '=' '*' ',' ';' '(' ')' +# +# ===Rules=== +# Start -> Query ';' || 'WITH' CteList Query ';' +# Query -> SelectExpr FromExpr [OrderBy] [Limit] || 'SELECT' 'avg(avgsub.DistColName)' 'FROM' SubqueryRte 'AS avgsub' +# SelectExpr -> 'SELECT' 'curAlias()' '.' DistColName +# FromExpr -> 'FROM' (Rte JoinList JoinOp Rte Using || RteList) ['WHERE' 'nextRandomAlias()' '.' DistColName RestrictExpr] +# RestrictExpr -> ('<' || '>' || '=') Int || ['NOT'] 'IN' SubqueryRte +# JoinList -> JoinOp Rte Using JoinList || e +# Using -> 'USING' '(' DistColName ')' +# RteList -> Rte [, RteList] || Rte +# Rte -> SubqueryRte 'AS' 'nextRandomAlias()' || RelationRte 'AS' 'nextRandomAlias()' || +# CteRte 'AS' 'nextRandomAlias()' || ValuesRte 'AS' 'nextRandomAlias()' +# SubqueryRte -> '(' Query ')' +# RelationRte -> 'nextRandomTableName()' +# CteRte -> 'randomCteName()' +# CteList -> Cte [',' CteList] || Cte +# Cte -> 'nextRandomAlias()' 'AS' '(' Query ')' +# ValuesRte -> '(' 'VALUES' '(' 'random()' ')' ')' +# JoinOp -> 'INNER JOIN' || 'LEFT JOIN' || 'RIGHT JOIN' || 'FULL JOIN' +# Limit -> 'LIMIT' 'random()' +# OrderBy -> 'ORDER BY' DistColName +# DistColName -> 'hardwired(get from config)' + + +class GeneratorContext: + """context to store some variables which should be available during generation""" + + def __init__(self): + # each level's last table is used in WHERE clause for the level + self.aliasStack = [] + # tracks if we are inside cte as we should not refer cte inside cte + self.insideCte = False + # total rtes in cte + non-cte parts + self.totalRteCount = 0 + # rte count in non-cte part to enforce non-cte rte limit + self.currentRteCount = 0 + # cte count to enforce cte limit + self.currentCteCount = 0 + # rte count in cte part to enforce rte limit in cte + self.currentCteRteCount = 0 + # rte counts per table to enforce rte limit per table + self.perTableRtes = {} + # tables which hit count limit + self.disallowedTables = set() + # useful to track usage avg only at first select + self.usedAvg = False + + def randomCteName(self): + """returns a randomly selected cte name""" + randCteRef = random.randint(0, self.currentCteCount - 1) + return " cte_" + str(randCteRef) + + def curAlias(self): + """returns current alias name to be used for the current table""" + return " table_" + str(self.totalRteCount) + + def curCteAlias(self): + """returns current alias name to be used for the current cte""" + return " cte_" + str(self.currentCteCount) + + def hasAnyCte(self): + """returns if context has any cte""" + return self.currentCteCount > 0 + + def canGenerateNewRte(self): + """checks if context exceeds allowed rte count""" + return self.currentRteCount < getConfig().targetRteCount + + def canGenerateNewCte(self): + """checks if context exceeds allowed cte count""" + return self.currentCteCount < getConfig().targetCteCount + + def canGenerateNewRteInsideCte(self): + """checks if context exceeds allowed rte count inside a cte""" + return self.currentCteRteCount < getConfig().targetCteRteCount + + def addAlias(self, alias): + """adds given alias to the stack""" + self.aliasStack.append(alias) + + def removeLastAlias(self): + """removes the latest added alias from the stack""" + return self.aliasStack.pop() + + def getRteNameEnforcingRteLimits(self): + """returns rteName by enforcing rte count limits for tables""" + # do not enforce per table rte limit if we are inside cte + if self.insideCte: + rteName = random.choice(getAllTableNames()) + return " " + rteName + " " + + while True: + # keep trying to find random table by eliminating the ones which hit table limit + allowedNames = set(getAllTableNames()) - self.disallowedTables + assert len(allowedNames) > 0 + rteName = random.choice(list(allowedNames)) + + # not yet added to rte count map, so we can allow the name + if rteName not in self.perTableRtes: + self.perTableRtes[rteName] = 0 + break + # limit is not exceeded, so we can allow the name + if self.perTableRtes[rteName] < getMaxAllowedCountForTable(rteName): + break + else: + self.disallowedTables.add(rteName) + + # increment rte count for the table name + self.perTableRtes[rteName] += 1 + return " " + rteName + " " + + +def newQuery(): + """returns generated query""" + genCtx = GeneratorContext() + return _start(genCtx) + + +def _start(genCtx): + # Query ';' || 'WITH' CteList Query ';' + query = "" + if not genCtx.canGenerateNewCte() or shouldSelectThatBranch(): + query += _genQuery(genCtx) + else: + genCtx.insideCte = True + query += " WITH " + query += _genCteList(genCtx) + genCtx.insideCte = False + query += _genQuery(genCtx) + query += ";" + return query + + +def _genQuery(genCtx): + # SelectExpr FromExpr [OrderBy] [Limit] || 'SELECT' 'avg(avgsub.DistColName)' 'FROM' SubqueryRte 'AS avgsub' + query = "" + if ( + getConfig().useAvgAtTopLevelTarget + and not genCtx.insideCte + and not genCtx.usedAvg + ): + genCtx.usedAvg = True + query += "SELECT " + query += "count(*), avg(avgsub." + getConfig().commonColName + ") FROM " + query += _genSubqueryRte(genCtx) + query += " AS avgsub" + else: + query += _genSelectExpr(genCtx) + query += _genFromExpr(genCtx) + choseOrderby = False + if getConfig().orderby and shouldSelectThatBranch(): + query += _genOrderBy(genCtx) + choseOrderby = True + if getConfig().limit and shouldSelectThatBranch(): + if not choseOrderby and getConfig().forceOrderbyWithLimit: + query += _genOrderBy(genCtx) + query += _genLimit(genCtx) + return query + + +def _genOrderBy(genCtx): + # 'ORDER BY' DistColName + query = "" + query += " ORDER BY " + query += getConfig().commonColName + " " + return query + + +def _genLimit(genCtx): + # 'LIMIT' 'random()' + query = "" + query += " LIMIT " + (fromVal, toVal) = getConfig().limitRange + query += str(random.randint(fromVal, toVal)) + return query + + +def _genSelectExpr(genCtx): + # 'SELECT' 'curAlias()' '.' DistColName + query = "" + query += " SELECT " + commonColName = getConfig().commonColName + query += genCtx.curAlias() + "." + commonColName + " " + + return query + + +def _genFromExpr(genCtx): + # 'FROM' (Rte JoinList JoinOp Rte Using || RteList) ['WHERE' 'nextRandomAlias()' '.' DistColName RestrictExpr] + query = "" + query += " FROM " + + if shouldSelectThatBranch(): + query += _genRte(genCtx) + query += _genJoinList(genCtx) + query += randomJoinOp() + query += _genRte(genCtx) + query += _genUsing(genCtx) + else: + query += _genRteList(genCtx) + + alias = genCtx.removeLastAlias() + if shouldSelectThatBranch(): + query += " WHERE " + query += alias + "." + getConfig().commonColName + query += _genRestrictExpr(genCtx) + return query + + +def _genRestrictExpr(genCtx): + # ('<' || '>' || '=') Int || ['NOT'] 'IN' '(' SubqueryRte ')' + query = "" + if ( + not getConfig().semiAntiJoin + or not genCtx.canGenerateNewRte() + or shouldSelectThatBranch() + ): + query += randomRestrictOp() + (fromVal, toVal) = getConfig().filterRange + query += str(random.randint(fromVal, toVal)) + else: + if shouldSelectThatBranch(): + query += " NOT" + query += " IN " + query += _genSubqueryRte(genCtx) + return query + + +def _genCteList(genCtx): + # Cte [',' CteList] || Cte + query = "" + + if shouldSelectThatBranch(): + query += _genCte(genCtx) + if not genCtx.canGenerateNewCte(): + return query + query += "," + query += _genCteList(genCtx) + else: + query += _genCte(genCtx) + return query + + +def _genCte(genCtx): + # 'nextRandomAlias()' 'AS' '(' Query ')' + query = "" + query += genCtx.curCteAlias() + genCtx.currentCteCount += 1 + query += " AS " + query += " ( " + query += _genQuery(genCtx) + query += " ) " + return query + + +def _genRteList(genCtx): + # RteList -> Rte [, RteList] || Rte + query = "" + if getConfig().cartesianProduct and shouldSelectThatBranch(): + query += _genRte(genCtx) + if not genCtx.canGenerateNewRte(): + return query + if genCtx.insideCte and not genCtx.canGenerateNewRteInsideCte(): + return query + query += "," + query += _genRteList(genCtx) + else: + query += _genRte(genCtx) + return query + + +def _genJoinList(genCtx): + # JoinOp Rte Using JoinList || e + query = "" + + if shouldSelectThatBranch(): + if not genCtx.canGenerateNewRte(): + return query + if genCtx.insideCte and not genCtx.canGenerateNewRteInsideCte(): + return query + query += randomJoinOp() + query += _genRte(genCtx) + query += _genUsing(genCtx) + query += _genJoinList(genCtx) + return query + + +def _genUsing(genCtx): + # 'USING' '(' DistColName ')' + query = "" + query += " USING (" + getConfig().commonColName + " ) " + return query + + +def _genRte(genCtx): + # SubqueryRte as 'nextRandomAlias()' || RelationRte as 'curAlias()' || + # CteRte as 'curAlias()' || ValuesRte 'AS' 'nextRandomAlias()' + alias = genCtx.curAlias() + modifiedAlias = None + + if genCtx.insideCte: + genCtx.currentCteRteCount += 1 + else: + genCtx.currentRteCount += 1 + genCtx.totalRteCount += 1 + + # donot dive into recursive subquery further if we hit into rte limit, replace it with relation rte + rteType = randomRteType() + if not genCtx.canGenerateNewRte(): + rteType = RTEType.RELATION + + # donot dive into recursive subquery further if we hit into rte in cte limit, replace it with relation rte + if genCtx.insideCte and not genCtx.canGenerateNewRteInsideCte(): + rteType = RTEType.RELATION + + # we cannot refer to cte if we are inside it or we donot have any cte + if (genCtx.insideCte or not genCtx.hasAnyCte()) and rteType == RTEType.CTE: + rteType = RTEType.RELATION + + query = "" + if rteType == RTEType.SUBQUERY: + query += _genSubqueryRte(genCtx) + elif rteType == RTEType.RELATION: + query += _genRelationRte(genCtx) + elif rteType == RTEType.CTE: + query += _genCteRte(genCtx) + elif rteType == RTEType.VALUES: + query += _genValuesRte(genCtx) + modifiedAlias = alias + "(" + getConfig().commonColName + ") " + else: + raise BaseException("unknown RTE type") + + query += " AS " + query += alias if not modifiedAlias else modifiedAlias + genCtx.addAlias(alias) + + return query + + +def _genSubqueryRte(genCtx): + # '(' Query ')' + query = "" + query += " ( " + query += _genQuery(genCtx) + query += " ) " + return query + + +def _genRelationRte(genCtx): + # 'randomAllowedTableName()' + query = "" + query += genCtx.getRteNameEnforcingRteLimits() + return query + + +def _genCteRte(genCtx): + # 'randomCteName()' + query = "" + query += genCtx.randomCteName() + return query + + +def _genValuesRte(genCtx): + # '( VALUES(random()) )' + query = "" + (fromVal, toVal) = getConfig().dataRange + query += " ( VALUES(" + str(random.randint(fromVal, toVal)) + " ) ) " + return query diff --git a/src/test/regress/citus_tests/query_generator/random_selections.py b/src/test/regress/citus_tests/query_generator/random_selections.py new file mode 100644 index 000000000..188107753 --- /dev/null +++ b/src/test/regress/citus_tests/query_generator/random_selections.py @@ -0,0 +1,39 @@ +import random + +from node_defs import RestrictOp + +from config.config import getConfig + + +def shouldSelectThatBranch(): + """returns 0 or 1 randomly""" + return random.randint(0, 1) + + +def randomRteType(): + """returns a randomly selected RteType given at config""" + rtes = getConfig().targetRteTypes + return random.choice(rtes) + + +def randomJoinOp(): + """returns a randomly selected JoinOp given at config""" + joinTypes = getConfig().targetJoinTypes + return " " + random.choice(joinTypes).name + " JOIN" + + +def randomRestrictOp(): + """returns a randomly selected RestrictOp given at config""" + restrictOps = getConfig().targetRestrictOps + restrictOp = random.choice(restrictOps) + opText = "" + if restrictOp == RestrictOp.LT: + opText = "<" + elif restrictOp == RestrictOp.GT: + opText = ">" + elif restrictOp == RestrictOp.EQ: + opText = "=" + else: + raise BaseException("Unknown restrict op") + + return " " + opText + " " From a5f4fece13ed7eec1a647dc4f7954dcd1d58e79e Mon Sep 17 00:00:00 2001 From: Jelte Fennema Date: Mon, 24 Apr 2023 15:54:32 +0200 Subject: [PATCH 008/118] Fix running PG upgrade tests with run_test.py (#6829) In #6814 we started using the Python test runner for upgrade tests in run_test.py, instead of the Perl based one. This had a problem though, not all tests in minimal_schedule can be run with the Python runner. This adds a separate minimal schedule for the pg_upgrade tests which doesn't include the tests that break with the Python runner. This PR also fixes various other issues that came up while testing the upgrade tests. --- src/test/regress/after_pg_upgrade_schedule | 8 +++++++- src/test/regress/citus_tests/run_test.py | 20 +++++++++++++++---- .../regress/expected/upgrade_basic_after.out | 18 ++++++++--------- src/test/regress/minimal_pg_upgrade_schedule | 1 + src/test/regress/sql/upgrade_basic_after.sql | 14 ++++++------- 5 files changed, 40 insertions(+), 21 deletions(-) create mode 100644 src/test/regress/minimal_pg_upgrade_schedule diff --git a/src/test/regress/after_pg_upgrade_schedule b/src/test/regress/after_pg_upgrade_schedule index 6b8c3973f..d02289510 100644 --- a/src/test/regress/after_pg_upgrade_schedule +++ b/src/test/regress/after_pg_upgrade_schedule @@ -1,4 +1,10 @@ -test: upgrade_basic_after upgrade_type_after upgrade_ref2ref_after upgrade_distributed_function_after upgrade_rebalance_strategy_after upgrade_list_citus_objects upgrade_autoconverted_after upgrade_citus_stat_activity upgrade_citus_locks upgrade_distributed_triggers_after +test: upgrade_basic_after upgrade_ref2ref_after upgrade_type_after upgrade_distributed_function_after upgrade_rebalance_strategy_after upgrade_list_citus_objects upgrade_autoconverted_after upgrade_citus_stat_activity upgrade_citus_locks + +# This test cannot be run with run_test.py currently due to its dependence on +# the specific PG versions that we use to run upgrade tests. For now we leave +# it out of the parallel line, so that flaky test detection can at least work +# for the other tests. +test: upgrade_distributed_triggers_after # This attempts dropping citus extension (and rollbacks), so please do # not run in parallel with any other tests. diff --git a/src/test/regress/citus_tests/run_test.py b/src/test/regress/citus_tests/run_test.py index ca969b9c1..fceadb3c0 100755 --- a/src/test/regress/citus_tests/run_test.py +++ b/src/test/regress/citus_tests/run_test.py @@ -15,7 +15,7 @@ from typing import Optional import common from common import REGRESS_DIR, capture, run -from config import ARBITRARY_SCHEDULE_NAMES, MASTER_VERSION, CitusDefaultClusterConfig +from config import ARBITRARY_SCHEDULE_NAMES, MASTER_VERSION, CitusBaseClusterConfig def main(): @@ -178,7 +178,7 @@ def run_schedule_with_python(schedule): "--bindir": bindir, } - config = CitusDefaultClusterConfig(args) + config = CitusBaseClusterConfig(args) common.initialize_temp_dir(config.temp_dir) common.initialize_citus_cluster( config.bindir, config.datadir, config.settings, config @@ -242,7 +242,7 @@ def default_base_schedule(test_schedule, args): return None if "pg_upgrade" in test_schedule: - return "minimal_schedule" + return "minimal_pg_upgrade_schedule" if test_schedule in ARBITRARY_SCHEDULE_NAMES: print(f"WARNING: Arbitrary config schedule ({test_schedule}) is not supported.") @@ -301,9 +301,21 @@ def test_dependencies(test_name, test_schedule, schedule_line, args): if schedule_line_is_upgrade_after(schedule_line): # upgrade_xxx_after tests always depend on upgrade_xxx_before + test_names = schedule_line.split()[1:] + before_tests = [] + # _after tests have implicit dependencies on _before tests + for test_name in test_names: + if "_after" in test_name: + before_tests.append(test_name.replace("_after", "_before")) + + # the upgrade_columnar_before renames the schema, on which other + # "after" tests depend. So we make sure to execute it always. + if "upgrade_columnar_before" not in before_tests: + before_tests.append("upgrade_columnar_before") + return TestDeps( default_base_schedule(test_schedule, args), - [test_name.replace("_after", "_before")], + before_tests, ) # before_ tests leave stuff around on purpose for the after tests. So they diff --git a/src/test/regress/expected/upgrade_basic_after.out b/src/test/regress/expected/upgrade_basic_after.out index ff118c593..e724b81d3 100644 --- a/src/test/regress/expected/upgrade_basic_after.out +++ b/src/test/regress/expected/upgrade_basic_after.out @@ -9,31 +9,31 @@ SELECT * FROM pg_indexes WHERE schemaname = 'upgrade_basic' and tablename NOT LI upgrade_basic | tp | tp_pkey | | CREATE UNIQUE INDEX tp_pkey ON upgrade_basic.tp USING btree (a) (3 rows) -SELECT nextval('pg_dist_shardid_seq') = MAX(shardid)+1 FROM pg_dist_shard; +SELECT nextval('pg_dist_shardid_seq') > MAX(shardid) FROM pg_dist_shard; ?column? --------------------------------------------------------------------- t (1 row) -SELECT nextval('pg_dist_placement_placementid_seq') = MAX(placementid)+1 FROM pg_dist_placement; +SELECT nextval('pg_dist_placement_placementid_seq') > MAX(placementid) FROM pg_dist_placement; ?column? --------------------------------------------------------------------- t (1 row) -SELECT nextval('pg_dist_groupid_seq') = MAX(groupid)+1 FROM pg_dist_node; +SELECT nextval('pg_dist_groupid_seq') > MAX(groupid) FROM pg_dist_node; ?column? --------------------------------------------------------------------- t (1 row) -SELECT nextval('pg_dist_node_nodeid_seq') = MAX(nodeid)+1 FROM pg_dist_node; +SELECT nextval('pg_dist_node_nodeid_seq') > MAX(nodeid) FROM pg_dist_node; ?column? --------------------------------------------------------------------- t (1 row) -SELECT nextval('pg_dist_colocationid_seq') = MAX(colocationid)+1 FROM pg_dist_colocation; +SELECT nextval('pg_dist_colocationid_seq') > MAX(colocationid) FROM pg_dist_colocation; ?column? --------------------------------------------------------------------- t @@ -45,7 +45,7 @@ SELECT nextval('pg_dist_colocationid_seq') = MAX(colocationid)+1 FROM pg_dist_co SELECT CASE WHEN MAX(operation_id) IS NULL THEN true - ELSE nextval('pg_dist_operationid_seq') = MAX(operation_id)+1 + ELSE nextval('pg_dist_operationid_seq') > MAX(operation_id) END AS check_operationid FROM pg_dist_cleanup; check_operationid @@ -56,7 +56,7 @@ SELECT SELECT CASE WHEN MAX(record_id) IS NULL THEN true - ELSE nextval('pg_dist_cleanup_recordid_seq') = MAX(record_id)+1 + ELSE nextval('pg_dist_cleanup_recordid_seq') > MAX(record_id) END AS check_recordid FROM pg_dist_cleanup; check_recordid @@ -93,8 +93,8 @@ SELECT sequence_name FROM information_schema.sequences 'pg_dist_groupid_seq', 'pg_dist_node_nodeid_seq', 'pg_dist_colocationid_seq', - 'pg_dist_operationid_seq', - 'pg_dist_cleanup_recordid_seq', + 'pg_dist_operationid_seq', + 'pg_dist_cleanup_recordid_seq', 'pg_dist_background_job_job_id_seq', 'pg_dist_background_task_task_id_seq', 'pg_dist_clock_logical_seq' diff --git a/src/test/regress/minimal_pg_upgrade_schedule b/src/test/regress/minimal_pg_upgrade_schedule new file mode 100644 index 000000000..64e5c982d --- /dev/null +++ b/src/test/regress/minimal_pg_upgrade_schedule @@ -0,0 +1 @@ +test: multi_test_helpers multi_test_helpers_superuser multi_test_catalog_views diff --git a/src/test/regress/sql/upgrade_basic_after.sql b/src/test/regress/sql/upgrade_basic_after.sql index 03c218a06..f2fc61769 100644 --- a/src/test/regress/sql/upgrade_basic_after.sql +++ b/src/test/regress/sql/upgrade_basic_after.sql @@ -3,24 +3,24 @@ BEGIN; -- We have the tablename filter to avoid adding an alternative output for when the coordinator is in metadata vs when not SELECT * FROM pg_indexes WHERE schemaname = 'upgrade_basic' and tablename NOT LIKE 'r_%' ORDER BY tablename; -SELECT nextval('pg_dist_shardid_seq') = MAX(shardid)+1 FROM pg_dist_shard; -SELECT nextval('pg_dist_placement_placementid_seq') = MAX(placementid)+1 FROM pg_dist_placement; -SELECT nextval('pg_dist_groupid_seq') = MAX(groupid)+1 FROM pg_dist_node; -SELECT nextval('pg_dist_node_nodeid_seq') = MAX(nodeid)+1 FROM pg_dist_node; -SELECT nextval('pg_dist_colocationid_seq') = MAX(colocationid)+1 FROM pg_dist_colocation; +SELECT nextval('pg_dist_shardid_seq') > MAX(shardid) FROM pg_dist_shard; +SELECT nextval('pg_dist_placement_placementid_seq') > MAX(placementid) FROM pg_dist_placement; +SELECT nextval('pg_dist_groupid_seq') > MAX(groupid) FROM pg_dist_node; +SELECT nextval('pg_dist_node_nodeid_seq') > MAX(nodeid) FROM pg_dist_node; +SELECT nextval('pg_dist_colocationid_seq') > MAX(colocationid) FROM pg_dist_colocation; -- while testing sequences on pg_dist_cleanup, they return null in pg upgrade schedule -- but return a valid value in citus upgrade schedule -- that's why we accept both NULL and MAX()+1 here SELECT CASE WHEN MAX(operation_id) IS NULL THEN true - ELSE nextval('pg_dist_operationid_seq') = MAX(operation_id)+1 + ELSE nextval('pg_dist_operationid_seq') > MAX(operation_id) END AS check_operationid FROM pg_dist_cleanup; SELECT CASE WHEN MAX(record_id) IS NULL THEN true - ELSE nextval('pg_dist_cleanup_recordid_seq') = MAX(record_id)+1 + ELSE nextval('pg_dist_cleanup_recordid_seq') > MAX(record_id) END AS check_recordid FROM pg_dist_cleanup; SELECT nextval('pg_dist_background_job_job_id_seq') > COALESCE(MAX(job_id), 0) FROM pg_dist_background_job; From da71b74f1d84e722f758a2a1051eb35f612ecab0 Mon Sep 17 00:00:00 2001 From: Hanefi Onaldi Date: Thu, 20 Apr 2023 03:18:10 +0300 Subject: [PATCH 009/118] Add changelog entries for 9.5.11 --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 428598d1a..5763e58e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +### citus v9.5.11 (April 20, 2023) ### + +* Fixes a crash that occurs when the aggregate that cannot be pushed-down + returns empty result from a worker (#5679) + +* Fixes memory leak issue with query results that returns single row (#6724) + +* Prevents alter table functions from dropping extensions (#5974) + ### citus v11.2.0 (January 30, 2023) ### * Adds support for outer joins with reference tables / complex subquery-CTEs From 61c7cc0a966fb51c7a66308cf0aa4fd5ebc0452d Mon Sep 17 00:00:00 2001 From: Hanefi Onaldi Date: Thu, 20 Apr 2023 03:11:44 +0300 Subject: [PATCH 010/118] Add changelog entries for 10.0.7 --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5763e58e8..51b2e6bc3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +### citus v10.0.7 (April 20, 2023) ### + +* Fixes a crash that occurs when the aggregate that cannot be pushed-down + returns empty result from a worker (#5679) + +* Fixes columnar freezing/wraparound bug (#5962) + +* Fixes memory leak issue with query results that returns single row (#6724) + +* Prevents alter table functions from dropping extensions (#5974) + ### citus v9.5.11 (April 20, 2023) ### * Fixes a crash that occurs when the aggregate that cannot be pushed-down From db77cb084b076e74ae4f0d6dffb879680f92e795 Mon Sep 17 00:00:00 2001 From: Hanefi Onaldi Date: Thu, 20 Apr 2023 03:10:10 +0300 Subject: [PATCH 011/118] Add changelog entries for 10.1.5 --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51b2e6bc3..ec40baf89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +### citus v10.1.5 (April 20, 2023) ### + +* Fixes a crash that occurs when the aggregate that cannot be pushed-down + returns empty result from a worker (#5679) + +* Fixes columnar freezing/wraparound bug (#5962) + +* Fixes memory leak issue with query results that returns single row (#6724) + +* Prevents alter table functions from dropping extensions (#5974) + ### citus v10.0.7 (April 20, 2023) ### * Fixes a crash that occurs when the aggregate that cannot be pushed-down From 65f957d345bc119aabc4bbdd12bbfbac7883dfa8 Mon Sep 17 00:00:00 2001 From: Hanefi Onaldi Date: Thu, 20 Apr 2023 03:06:38 +0300 Subject: [PATCH 012/118] Add changelog entries for 10.2.9 --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec40baf89..1b1197316 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +### citus v10.2.9 (April 20, 2023) ### + +* Correctly reports shard size in `citus_shards` view (#6748) + +* Fixes a bug in `ALTER EXTENSION citus UPDATE` (#6383) + +* Fixes a bug that breaks pg upgrades if the user has a columnar table (#6624) + +* Fixes a bug that prevents retaining columnar table options after a + table-rewrite (#6337) + +* Fixes memory leak issue with query results that returns single row (#6724) + +* Raises memory limits in columnar from 256MB to 1GB for reads and writes + (#6419) + ### citus v10.1.5 (April 20, 2023) ### * Fixes a crash that occurs when the aggregate that cannot be pushed-down From 214bc39a5ad035a3de5cc7fe9fbc37c456f71ccd Mon Sep 17 00:00:00 2001 From: Hanefi Onaldi Date: Thu, 20 Apr 2023 02:56:38 +0300 Subject: [PATCH 013/118] Add changelog entries for 11.0.8 --- CHANGELOG.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b1197316..bd6740ecd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,26 @@ +### citus v11.0.8 (April 20, 2023) ### + +* Correctly reports shard size in `citus_shards` view (#6748) + +* Fixes a bug that breaks pg upgrades if the user has a columnar table (#6624) + +* Fixes an unexpected foreign table error by disallowing to drop the + `table_name` option (#6669) + +* Fixes compilation warning on PG13 + OpenSSL 3.0 (#6038, #6502) + +* Fixes crash that happens when trying to replicate a reference table that is + actually dropped (#6595) + +* Fixes memory leak issue with query results that returns single row (#6724) + +* Fixes the modifiers for subscription and role creation (#6603) + +* Fixes two potential dangling pointer issues (#6504, #6507) + +* Makes sure to quote all identifiers used for logical replication to prevent + potential issues (#6604) + ### citus v10.2.9 (April 20, 2023) ### * Correctly reports shard size in `citus_shards` view (#6748) From c36adc8426f5155c5defc3aa3548a400ebedfd98 Mon Sep 17 00:00:00 2001 From: Hanefi Onaldi Date: Thu, 20 Apr 2023 02:08:57 +0300 Subject: [PATCH 014/118] Add changelog entries for 11.1.6 --- CHANGELOG.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd6740ecd..1bf9aea02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,37 @@ +### citus v11.1.6 (April 20, 2023) ### + +* Correctly reports shard size in `citus_shards` view (#6748) + +* Fixes a bug in shard copy operations (#6721) + +* Fixes a bug that breaks pg upgrades if the user has a columnar table (#6624) + +* Fixes a bug that causes background rebalancer to fail when a reference table + doesn't have a primary key (#6682) + +* Fixes a regression in allowed foreign keys on distributed tables (#6550) + +* Fixes a use-after-free bug in connection management (#6685) + +* Fixes an unexpected foreign table error by disallowing to drop the + `table_name` option (#6669) + +* Fixes an uninitialized memory access in shard split API (#6845) + +* Fixes compilation for PG13.10 and PG14.7 (#6711) + +* Fixes crash that happens when trying to replicate a reference table that is + actually dropped (#6595) + +* Fixes memory leak issue with query results that returns single row (#6724) + +* Fixes the modifiers for subscription and role creation (#6603) + +* Makes sure to quote all identifiers used for logical replication to prevent + potential issues (#6604) + +* Makes sure to skip foreign key validations at the end of shard moves (#6640) + ### citus v11.0.8 (April 20, 2023) ### * Correctly reports shard size in `citus_shards` view (#6748) From f7fd0dbae75dcb869aea9491e3a2d3ac319bd93f Mon Sep 17 00:00:00 2001 From: Hanefi Onaldi Date: Thu, 20 Apr 2023 01:49:17 +0300 Subject: [PATCH 015/118] Add changelog entries for 11.2.1 --- CHANGELOG.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bf9aea02..629bc8557 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,25 @@ +### citus v11.2.1 (April 20, 2023) ### + +* Correctly reports shard size in `citus_shards` view (#6748) + +* Fixes a bug in shard copy operations (#6721) + +* Fixes a bug with `INSERT .. SELECT` queries with identity columns (#6802) + +* Fixes an uninitialized memory access in shard split API (#6845) + +* Fixes compilation for PG13.10 and PG14.7 (#6711) + +* Fixes memory leak in `alter_distributed_table` (#6726) + +* Fixes memory leak issue with query results that returns single row (#6724) + +* Prevents using `alter_distributed_table` and `undistribute_table` UDFs when a + table has identity columns (#6738) + +* Prevents using identity columns on data types other than `bigint` on + distributed tables (#6738) + ### citus v11.1.6 (April 20, 2023) ### * Correctly reports shard size in `citus_shards` view (#6748) From 5fc5931506ac5552bc6b217f41cef9c2ab0371c5 Mon Sep 17 00:00:00 2001 From: Hanefi Onaldi Date: Wed, 26 Apr 2023 12:05:27 +0300 Subject: [PATCH 016/118] Skip some versions on changelog (#6882) We had 10.1.5, 10.0.7, and 9.5.11 in the changelog, but those versions are already used in enterprise repository. This commit skips those versions and uses 10.1.6, 10.0.8, and 9.5.12 instead to prevent clashes. --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 629bc8557..61ece27d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -93,7 +93,7 @@ * Raises memory limits in columnar from 256MB to 1GB for reads and writes (#6419) -### citus v10.1.5 (April 20, 2023) ### +### citus v10.1.6 (April 20, 2023) ### * Fixes a crash that occurs when the aggregate that cannot be pushed-down returns empty result from a worker (#5679) @@ -104,7 +104,7 @@ * Prevents alter table functions from dropping extensions (#5974) -### citus v10.0.7 (April 20, 2023) ### +### citus v10.0.8 (April 20, 2023) ### * Fixes a crash that occurs when the aggregate that cannot be pushed-down returns empty result from a worker (#5679) @@ -115,7 +115,7 @@ * Prevents alter table functions from dropping extensions (#5974) -### citus v9.5.11 (April 20, 2023) ### +### citus v9.5.12 (April 20, 2023) ### * Fixes a crash that occurs when the aggregate that cannot be pushed-down returns empty result from a worker (#5679) From a7fa1db69647ba21e6096c99fc8af00dcb24b803 Mon Sep 17 00:00:00 2001 From: aykut-bozkurt <51649454+aykut-bozkurt@users.noreply.github.com> Date: Thu, 27 Apr 2023 13:14:40 +0300 Subject: [PATCH 017/118] fix flaky test regex (#6890) There was a bug related to regex. We sometimes caught the wrong line when the test name is also included in comments. Example: We caught the wrong line as multi_metadata_sync is included in the comment before the test line. ``` # ---------- # multi_metadata_sync tests the propagation of mx-related metadata changes to metadata workers # multi_unsupported_worker_operations tests that unsupported operations error out on metadata workers # ---------- test: multi_metadata_sync ``` Solution: Restrict regex rule better. --- src/test/regress/citus_tests/run_test.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/regress/citus_tests/run_test.py b/src/test/regress/citus_tests/run_test.py index fceadb3c0..72ce2fb14 100755 --- a/src/test/regress/citus_tests/run_test.py +++ b/src/test/regress/citus_tests/run_test.py @@ -154,6 +154,8 @@ def run_python_test(test_name, args): def run_regress_test(test_name, args): original_schedule, schedule_line = find_test_schedule_and_line(test_name, args) + print(f"SCHEDULE: {original_schedule}") + print(f"SCHEDULE_LINE: {schedule_line}") dependencies = test_dependencies(test_name, original_schedule, schedule_line, args) @@ -287,7 +289,7 @@ def get_test_name(args): def find_test_schedule_and_line(test_name, args): for schedule_file_path in sorted(REGRESS_DIR.glob("*_schedule")): for schedule_line in open(schedule_file_path, "r"): - if re.search(r"\b" + test_name + r"\b", schedule_line): + if re.search(r"^test:.*\b" + test_name + r"\b", schedule_line): test_schedule = pathlib.Path(schedule_file_path).stem if args["use_whole_schedule_line"]: return test_schedule, schedule_line From 135aaf45ca4d6f6ab39d48813db1fb5ae647bf0c Mon Sep 17 00:00:00 2001 From: Hanefi Onaldi Date: Thu, 27 Apr 2023 16:01:04 +0300 Subject: [PATCH 018/118] Add missing entry for 10.0.8 (#6891) When creating tags for backport releases, I realized that I missed one changelog item. Adding it on the default branch in a commit. See #6885 for the relevant PR for the release branch. --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61ece27d1..4fdfae3ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -106,6 +106,9 @@ ### citus v10.0.8 (April 20, 2023) ### +* Fixes a bug that could break `DROP SCHEMA/EXTENSON` commands when there is a + columnar table (#5458) + * Fixes a crash that occurs when the aggregate that cannot be pushed-down returns empty result from a worker (#5679) From 8cb69cfd13ce6d1f06d7c190ca8b916ccf930fb6 Mon Sep 17 00:00:00 2001 From: aykut-bozkurt <51649454+aykut-bozkurt@users.noreply.github.com> Date: Fri, 28 Apr 2023 15:09:09 +0300 Subject: [PATCH 019/118] break sequence dependency during table creation (#6889) We need to break sequence dependency for a table while creating the table during non-transactional metadata sync to ensure idempotency of the creation of the table. **Problem:** When we send `SELECT pg_catalog.worker_drop_sequence_dependency(logicalrelid::regclass::text) FROM pg_dist_partition` to workers during the non-transactional sync, table might not be in `pg_dist_partition` at worker, and sequence dependency is not broken at the worker. **Solution:** We break sequence dependency via `SELECT pg_catalog.worker_drop_sequence_dependency(logicalrelid::regclass::text)` for each table while creating it at the workers. It is safe to send since the udf is a no-op when there is no sequence dependency. DESCRIPTION: Fixes a bug related to sequence idempotency at non-transactional sync. Fixes https://github.com/citusdata/citus/issues/6888. --- .../distributed/commands/alter_table.c | 11 +- .../distributed/commands/dependencies.c | 10 +- .../distributed/metadata/metadata_sync.c | 22 +- src/include/distributed/metadata_sync.h | 5 +- .../failure_mx_metadata_sync_multi_trans.out | 324 +++++++++++++++++- .../regress/expected/multi_metadata_sync.out | 23 +- .../expected/multi_metadata_sync_0.out | 23 +- .../failure_mx_metadata_sync_multi_trans.sql | 141 +++++++- 8 files changed, 522 insertions(+), 37 deletions(-) diff --git a/src/backend/distributed/commands/alter_table.c b/src/backend/distributed/commands/alter_table.c index 5ed82f760..9e03eb82f 100644 --- a/src/backend/distributed/commands/alter_table.c +++ b/src/backend/distributed/commands/alter_table.c @@ -1710,20 +1710,13 @@ ReplaceTable(Oid sourceId, Oid targetId, List *justBeforeDropCommands, } else if (ShouldSyncTableMetadata(sourceId)) { - char *qualifiedTableName = quote_qualified_identifier(schemaName, sourceName); - /* * We are converting a citus local table to a distributed/reference table, * so we should prevent dropping the sequence on the table. Otherwise, we'd * lose track of the previous changes in the sequence. */ - StringInfo command = makeStringInfo(); - - appendStringInfo(command, - "SELECT pg_catalog.worker_drop_sequence_dependency(%s);", - quote_literal_cstr(qualifiedTableName)); - - SendCommandToWorkersWithMetadata(command->data); + char *command = WorkerDropSequenceDependencyCommand(sourceId); + SendCommandToWorkersWithMetadata(command); } } diff --git a/src/backend/distributed/commands/dependencies.c b/src/backend/distributed/commands/dependencies.c index baa5082d7..0f736df7a 100644 --- a/src/backend/distributed/commands/dependencies.c +++ b/src/backend/distributed/commands/dependencies.c @@ -393,9 +393,17 @@ GetDependencyCreateDDLCommands(const ObjectAddress *dependency) tableDDLCommand)); } - /* we need to drop table, if exists, first to make table creation idempotent */ + /* + * We need to drop table, if exists, first to make table creation + * idempotent. Before dropping the table, we should also break + * dependencies with sequences since `drop cascade table` would also + * drop depended sequences. This is safe as we still record dependency + * with the sequence during table creation. + */ commandList = lcons(DropTableIfExistsCommand(relationId), commandList); + commandList = lcons(WorkerDropSequenceDependencyCommand(relationId), + commandList); } return commandList; diff --git a/src/backend/distributed/metadata/metadata_sync.c b/src/backend/distributed/metadata/metadata_sync.c index e3310c5c8..868617ce0 100644 --- a/src/backend/distributed/metadata/metadata_sync.c +++ b/src/backend/distributed/metadata/metadata_sync.c @@ -686,7 +686,7 @@ DropMetadataSnapshotOnNode(WorkerNode *workerNode) bool singleTransaction = true; List *dropMetadataCommandList = DetachPartitionCommandList(); dropMetadataCommandList = lappend(dropMetadataCommandList, - BREAK_CITUS_TABLE_SEQUENCE_DEPENDENCY_COMMAND); + BREAK_ALL_CITUS_TABLE_SEQUENCE_DEPENDENCY_COMMAND); dropMetadataCommandList = lappend(dropMetadataCommandList, WorkerDropAllShellTablesCommand(singleTransaction)); dropMetadataCommandList = list_concat(dropMetadataCommandList, @@ -4235,6 +4235,22 @@ WorkerDropAllShellTablesCommand(bool singleTransaction) } +/* + * WorkerDropSequenceDependencyCommand returns command to drop sequence dependencies for + * given table. + */ +char * +WorkerDropSequenceDependencyCommand(Oid relationId) +{ + char *qualifiedTableName = generate_qualified_relation_name(relationId); + StringInfo breakSequenceDepCommand = makeStringInfo(); + appendStringInfo(breakSequenceDepCommand, + BREAK_CITUS_TABLE_SEQUENCE_DEPENDENCY_COMMAND, + quote_literal_cstr(qualifiedTableName)); + return breakSequenceDepCommand->data; +} + + /* * PropagateNodeWideObjectsCommandList is called during node activation to * propagate any object that should be propagated for every node. These are @@ -4352,8 +4368,8 @@ SendNodeWideObjectsSyncCommands(MetadataSyncContext *context) void SendShellTableDeletionCommands(MetadataSyncContext *context) { - /* break all sequence deps for citus tables and remove all shell tables */ - char *breakSeqDepsCommand = BREAK_CITUS_TABLE_SEQUENCE_DEPENDENCY_COMMAND; + /* break all sequence deps for citus tables */ + char *breakSeqDepsCommand = BREAK_ALL_CITUS_TABLE_SEQUENCE_DEPENDENCY_COMMAND; SendOrCollectCommandListToActivatedNodes(context, list_make1(breakSeqDepsCommand)); /* remove shell tables */ diff --git a/src/include/distributed/metadata_sync.h b/src/include/distributed/metadata_sync.h index d5878ec71..227243cc1 100644 --- a/src/include/distributed/metadata_sync.h +++ b/src/include/distributed/metadata_sync.h @@ -156,6 +156,7 @@ extern void SendOrCollectCommandListToSingleNode(MetadataSyncContext *context, extern void ActivateNodeList(MetadataSyncContext *context); extern char * WorkerDropAllShellTablesCommand(bool singleTransaction); +extern char * WorkerDropSequenceDependencyCommand(Oid relationId); extern void SyncDistributedObjects(MetadataSyncContext *context); extern void SendNodeWideObjectsSyncCommands(MetadataSyncContext *context); @@ -180,8 +181,10 @@ extern void SendInterTableRelationshipCommands(MetadataSyncContext *context); #define REMOVE_ALL_CITUS_TABLES_COMMAND \ "SELECT worker_drop_distributed_table(logicalrelid::regclass::text) FROM pg_dist_partition" -#define BREAK_CITUS_TABLE_SEQUENCE_DEPENDENCY_COMMAND \ +#define BREAK_ALL_CITUS_TABLE_SEQUENCE_DEPENDENCY_COMMAND \ "SELECT pg_catalog.worker_drop_sequence_dependency(logicalrelid::regclass::text) FROM pg_dist_partition" +#define BREAK_CITUS_TABLE_SEQUENCE_DEPENDENCY_COMMAND \ + "SELECT pg_catalog.worker_drop_sequence_dependency(%s);" #define DISABLE_DDL_PROPAGATION "SET citus.enable_ddl_propagation TO 'off'" #define ENABLE_DDL_PROPAGATION "SET citus.enable_ddl_propagation TO 'on'" diff --git a/src/test/regress/expected/failure_mx_metadata_sync_multi_trans.out b/src/test/regress/expected/failure_mx_metadata_sync_multi_trans.out index 0c66b2548..541bce5c5 100644 --- a/src/test/regress/expected/failure_mx_metadata_sync_multi_trans.out +++ b/src/test/regress/expected/failure_mx_metadata_sync_multi_trans.out @@ -19,10 +19,30 @@ SET client_min_messages TO ERROR; -- Create roles CREATE ROLE foo1; CREATE ROLE foo2; +-- Create collation +CREATE COLLATION german_phonebook (provider = icu, locale = 'de-u-co-phonebk'); +-- Create type +CREATE TYPE pair_type AS (a int, b int); +-- Create function +CREATE FUNCTION one_as_result() RETURNS INT LANGUAGE SQL AS +$$ + SELECT 1; +$$; +-- Create text search dictionary +CREATE TEXT SEARCH DICTIONARY my_german_dict ( + template = snowball, + language = german, + stopwords = german +); +-- Create text search config +CREATE TEXT SEARCH CONFIGURATION my_ts_config ( parser = default ); +ALTER TEXT SEARCH CONFIGURATION my_ts_config ALTER MAPPING FOR asciiword WITH my_german_dict; -- Create sequence CREATE SEQUENCE seq; -- Create colocated distributed tables -CREATE TABLE dist1 (id int PRIMARY KEY default nextval('seq')); +CREATE TABLE dist1 (id int PRIMARY KEY default nextval('seq'), col int default (one_as_result()), myserial serial, phone text COLLATE german_phonebook, initials pair_type); +CREATE SEQUENCE seq_owned OWNED BY dist1.id; +CREATE INDEX dist1_search_phone_idx ON dist1 USING gin (to_tsvector('my_ts_config'::regconfig, (COALESCE(phone, ''::text))::text)); SELECT create_distributed_table('dist1', 'id'); create_distributed_table --------------------------------------------------------------------- @@ -52,6 +72,8 @@ CREATE TABLE loc1 (id int PRIMARY KEY); INSERT INTO loc1 SELECT i FROM generate_series(1,100) i; CREATE TABLE loc2 (id int REFERENCES loc1(id)); INSERT INTO loc2 SELECT i FROM generate_series(1,100) i; +-- Create publication +CREATE PUBLICATION pub_all; -- citus_set_coordinator_host with wrong port SELECT citus_set_coordinator_host('localhost', 9999); citus_set_coordinator_host @@ -168,8 +190,8 @@ SELECT citus.mitmproxy('conn.onQuery(query="INSERT INTO pg_dist_node").kill()'); SELECT citus_activate_node('localhost', :worker_2_proxy_port); ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open --- Failure to drop sequence -SELECT citus.mitmproxy('conn.onQuery(query="SELECT pg_catalog.worker_drop_sequence_dependency").cancel(' || :pid || ')'); +-- Failure to drop sequence dependency for all tables +SELECT citus.mitmproxy('conn.onQuery(query="SELECT pg_catalog.worker_drop_sequence_dependency.*FROM pg_dist_partition").cancel(' || :pid || ')'); mitmproxy --------------------------------------------------------------------- @@ -177,7 +199,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="SELECT pg_catalog.worker_drop_sequen SELECT citus_activate_node('localhost', :worker_2_proxy_port); ERROR: canceling statement due to user request -SELECT citus.mitmproxy('conn.onQuery(query="SELECT pg_catalog.worker_drop_sequence_dependency").kill()'); +SELECT citus.mitmproxy('conn.onQuery(query="SELECT pg_catalog.worker_drop_sequence_dependency.*FROM pg_dist_partition").kill()'); mitmproxy --------------------------------------------------------------------- @@ -321,7 +343,7 @@ SELECT citus.mitmproxy('conn.onQuery(query="ALTER DATABASE.*OWNER TO").kill()'); SELECT citus_activate_node('localhost', :worker_2_proxy_port); ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open --- Filure to create schema +-- Failure to create schema SELECT citus.mitmproxy('conn.onQuery(query="CREATE SCHEMA IF NOT EXISTS mx_metadata_sync_multi_trans AUTHORIZATION").cancel(' || :pid || ')'); mitmproxy --------------------------------------------------------------------- @@ -336,6 +358,108 @@ SELECT citus.mitmproxy('conn.onQuery(query="CREATE SCHEMA IF NOT EXISTS mx_metad (1 row) +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open +-- Failure to create collation +SELECT citus.mitmproxy('conn.onQuery(query="SELECT worker_create_or_replace_object.*CREATE COLLATION mx_metadata_sync_multi_trans.german_phonebook").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: canceling statement due to user request +SELECT citus.mitmproxy('conn.onQuery(query="SELECT worker_create_or_replace_object.*CREATE COLLATION mx_metadata_sync_multi_trans.german_phonebook").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open +-- Failure to create function +SELECT citus.mitmproxy('conn.onQuery(query="CREATE OR REPLACE FUNCTION mx_metadata_sync_multi_trans.one_as_result").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: canceling statement due to user request +SELECT citus.mitmproxy('conn.onQuery(query="CREATE OR REPLACE FUNCTION mx_metadata_sync_multi_trans.one_as_result").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open +-- Failure to create text search dictionary +SELECT citus.mitmproxy('conn.onQuery(query="SELECT worker_create_or_replace_object.*my_german_dict").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: canceling statement due to user request +SELECT citus.mitmproxy('conn.onQuery(query="SELECT worker_create_or_replace_object.*my_german_dict").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open +-- Failure to create text search config +SELECT citus.mitmproxy('conn.onQuery(query="SELECT worker_create_or_replace_object.*my_ts_config").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: canceling statement due to user request +SELECT citus.mitmproxy('conn.onQuery(query="SELECT worker_create_or_replace_object.*my_ts_config").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open +-- Failure to create type +SELECT citus.mitmproxy('conn.onQuery(query="SELECT worker_create_or_replace_object.*pair_type").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: canceling statement due to user request +SELECT citus.mitmproxy('conn.onQuery(query="SELECT worker_create_or_replace_object.*pair_type").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open +-- Failure to create publication +SELECT citus.mitmproxy('conn.onQuery(query="CREATE PUBLICATION.*pub_all").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: canceling statement due to user request +SELECT citus.mitmproxy('conn.onQuery(query="CREATE PUBLICATION.*pub_all").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + SELECT citus_activate_node('localhost', :worker_2_proxy_port); ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open -- Failure to create sequence @@ -353,6 +477,40 @@ SELECT citus.mitmproxy('conn.onQuery(query="SELECT worker_apply_sequence_command (1 row) +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open +-- Failure to drop sequence dependency for distributed table +SELECT citus.mitmproxy('conn.onQuery(query="SELECT pg_catalog.worker_drop_sequence_dependency.*mx_metadata_sync_multi_trans.dist1").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: canceling statement due to user request +SELECT citus.mitmproxy('conn.onQuery(query="SELECT pg_catalog.worker_drop_sequence_dependency.*mx_metadata_sync_multi_trans.dist1").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open +-- Failure to drop distributed table if exists +SELECT citus.mitmproxy('conn.onQuery(query="DROP TABLE IF EXISTS mx_metadata_sync_multi_trans.dist1").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: canceling statement due to user request +SELECT citus.mitmproxy('conn.onQuery(query="DROP TABLE IF EXISTS mx_metadata_sync_multi_trans.dist1").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + SELECT citus_activate_node('localhost', :worker_2_proxy_port); ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open -- Failure to create distributed table @@ -370,6 +528,40 @@ SELECT citus.mitmproxy('conn.onQuery(query="CREATE TABLE mx_metadata_sync_multi_ (1 row) +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open +-- Failure to record sequence dependency for table +SELECT citus.mitmproxy('conn.onQuery(query="SELECT pg_catalog.worker_record_sequence_dependency").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: canceling statement due to user request +SELECT citus.mitmproxy('conn.onQuery(query="SELECT pg_catalog.worker_record_sequence_dependency").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open +-- Failure to create index for table +SELECT citus.mitmproxy('conn.onQuery(query="CREATE INDEX dist1_search_phone_idx ON mx_metadata_sync_multi_trans.dist1 USING gin").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: canceling statement due to user request +SELECT citus.mitmproxy('conn.onQuery(query="CREATE INDEX dist1_search_phone_idx ON mx_metadata_sync_multi_trans.dist1 USING gin").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + SELECT citus_activate_node('localhost', :worker_2_proxy_port); ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open -- Failure to create reference table @@ -540,6 +732,125 @@ SELECT citus.mitmproxy('conn.onQuery(query="SELECT citus_internal_add_object_met (1 row) +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open +-- Failure to mark function as distributed +SELECT citus.mitmproxy('conn.onQuery(query="WITH distributed_object_data.*one_as_result").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: canceling statement due to user request +SELECT citus.mitmproxy('conn.onQuery(query="WITH distributed_object_data.*one_as_result").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open +-- Failure to mark collation as distributed +SELECT citus.mitmproxy('conn.onQuery(query="WITH distributed_object_data.*german_phonebook").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: canceling statement due to user request +SELECT citus.mitmproxy('conn.onQuery(query="WITH distributed_object_data.*german_phonebook").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open +-- Failure to mark text search dictionary as distributed +SELECT citus.mitmproxy('conn.onQuery(query="WITH distributed_object_data.*my_german_dict").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: canceling statement due to user request +SELECT citus.mitmproxy('conn.onQuery(query="WITH distributed_object_data.*my_german_dict").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open +-- Failure to mark text search configuration as distributed +SELECT citus.mitmproxy('conn.onQuery(query="WITH distributed_object_data.*my_ts_config").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: canceling statement due to user request +SELECT citus.mitmproxy('conn.onQuery(query="WITH distributed_object_data.*my_ts_config").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open +-- Failure to mark type as distributed +SELECT citus.mitmproxy('conn.onQuery(query="WITH distributed_object_data.*pair_type").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: canceling statement due to user request +SELECT citus.mitmproxy('conn.onQuery(query="WITH distributed_object_data.*pair_type").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open +-- Failure to mark sequence as distributed +SELECT citus.mitmproxy('conn.onQuery(query="WITH distributed_object_data.*seq_owned").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: canceling statement due to user request +SELECT citus.mitmproxy('conn.onQuery(query="WITH distributed_object_data.*seq_owned").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open +-- Failure to mark publication as distributed +SELECT citus.mitmproxy('conn.onQuery(query="WITH distributed_object_data.*pub_all").cancel(' || :pid || ')'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +ERROR: canceling statement due to user request +SELECT citus.mitmproxy('conn.onQuery(query="WITH distributed_object_data.*pub_all").kill()'); + mitmproxy +--------------------------------------------------------------------- + +(1 row) + SELECT citus_activate_node('localhost', :worker_2_proxy_port); ERROR: connection to the remote node localhost:xxxxx failed with the following error: connection not open -- Failure to set isactive to true @@ -680,9 +991,10 @@ SELECT citus.mitmproxy('conn.allow()'); (1 row) RESET citus.metadata_sync_mode; +DROP PUBLICATION pub_all; DROP SCHEMA dummy; DROP SCHEMA mx_metadata_sync_multi_trans CASCADE; -NOTICE: drop cascades to 10 other objects +NOTICE: drop cascades to 15 other objects DROP ROLE foo1; DROP ROLE foo2; SELECT citus_remove_node('localhost', :master_port); diff --git a/src/test/regress/expected/multi_metadata_sync.out b/src/test/regress/expected/multi_metadata_sync.out index 8c74045be..8c75b4fd4 100644 --- a/src/test/regress/expected/multi_metadata_sync.out +++ b/src/test/regress/expected/multi_metadata_sync.out @@ -149,6 +149,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; RESET ROLE SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') SELECT citus_internal_add_partition_metadata ('public.mx_test_table'::regclass, 'h', 'col_1', 2, 's') + SELECT pg_catalog.worker_drop_sequence_dependency('public.mx_test_table'); SELECT pg_catalog.worker_drop_sequence_dependency(logicalrelid::regclass::text) FROM pg_dist_partition SELECT pg_catalog.worker_record_sequence_dependency('public.mx_test_table_col_3_seq'::regclass,'public.mx_test_table'::regclass,'col_3') SELECT worker_apply_sequence_command ('CREATE SEQUENCE IF NOT EXISTS public.mx_test_table_col_3_seq AS bigint INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1 NO CYCLE','bigint') @@ -174,7 +175,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('table', ARRAY['public', 'mx_test_table']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310000, 0, 1, 100000), (1310001, 0, 2, 100001), (1310002, 0, 1, 100002), (1310003, 0, 2, 100003), (1310004, 0, 1, 100004), (1310005, 0, 2, 100005), (1310006, 0, 1, 100006), (1310007, 0, 2, 100007)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.mx_test_table'::regclass, 1310000, 't'::"char", '-2147483648', '-1610612737'), ('public.mx_test_table'::regclass, 1310001, 't'::"char", '-1610612736', '-1073741825'), ('public.mx_test_table'::regclass, 1310002, 't'::"char", '-1073741824', '-536870913'), ('public.mx_test_table'::regclass, 1310003, 't'::"char", '-536870912', '-1'), ('public.mx_test_table'::regclass, 1310004, 't'::"char", '0', '536870911'), ('public.mx_test_table'::regclass, 1310005, 't'::"char", '536870912', '1073741823'), ('public.mx_test_table'::regclass, 1310006, 't'::"char", '1073741824', '1610612735'), ('public.mx_test_table'::regclass, 1310007, 't'::"char", '1610612736', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; -(49 rows) +(50 rows) -- Show that CREATE INDEX commands are included in the activate node snapshot CREATE INDEX mx_index ON mx_test_table(col_2); @@ -206,6 +207,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; RESET ROLE SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') SELECT citus_internal_add_partition_metadata ('public.mx_test_table'::regclass, 'h', 'col_1', 2, 's') + SELECT pg_catalog.worker_drop_sequence_dependency('public.mx_test_table'); SELECT pg_catalog.worker_drop_sequence_dependency(logicalrelid::regclass::text) FROM pg_dist_partition SELECT pg_catalog.worker_record_sequence_dependency('public.mx_test_table_col_3_seq'::regclass,'public.mx_test_table'::regclass,'col_3') SELECT worker_apply_sequence_command ('CREATE SEQUENCE IF NOT EXISTS public.mx_test_table_col_3_seq AS bigint INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1 NO CYCLE','bigint') @@ -231,7 +233,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('table', ARRAY['public', 'mx_test_table']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310000, 0, 1, 100000), (1310001, 0, 2, 100001), (1310002, 0, 1, 100002), (1310003, 0, 2, 100003), (1310004, 0, 1, 100004), (1310005, 0, 2, 100005), (1310006, 0, 1, 100006), (1310007, 0, 2, 100007)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.mx_test_table'::regclass, 1310000, 't'::"char", '-2147483648', '-1610612737'), ('public.mx_test_table'::regclass, 1310001, 't'::"char", '-1610612736', '-1073741825'), ('public.mx_test_table'::regclass, 1310002, 't'::"char", '-1073741824', '-536870913'), ('public.mx_test_table'::regclass, 1310003, 't'::"char", '-536870912', '-1'), ('public.mx_test_table'::regclass, 1310004, 't'::"char", '0', '536870911'), ('public.mx_test_table'::regclass, 1310005, 't'::"char", '536870912', '1073741823'), ('public.mx_test_table'::regclass, 1310006, 't'::"char", '1073741824', '1610612735'), ('public.mx_test_table'::regclass, 1310007, 't'::"char", '1610612736', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; -(50 rows) +(51 rows) -- Show that schema changes are included in the activate node snapshot CREATE SCHEMA mx_testing_schema; @@ -265,6 +267,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; RESET ROLE SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') SELECT citus_internal_add_partition_metadata ('mx_testing_schema.mx_test_table'::regclass, 'h', 'col_1', 2, 's') + SELECT pg_catalog.worker_drop_sequence_dependency('mx_testing_schema.mx_test_table'); SELECT pg_catalog.worker_drop_sequence_dependency(logicalrelid::regclass::text) FROM pg_dist_partition SELECT pg_catalog.worker_record_sequence_dependency('mx_testing_schema.mx_test_table_col_3_seq'::regclass,'mx_testing_schema.mx_test_table'::regclass,'col_3') SELECT worker_apply_sequence_command ('CREATE SEQUENCE IF NOT EXISTS mx_testing_schema.mx_test_table_col_3_seq AS bigint INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1 NO CYCLE','bigint') @@ -291,7 +294,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('table', ARRAY['mx_testing_schema', 'mx_test_table']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310000, 0, 1, 100000), (1310001, 0, 2, 100001), (1310002, 0, 1, 100002), (1310003, 0, 2, 100003), (1310004, 0, 1, 100004), (1310005, 0, 2, 100005), (1310006, 0, 1, 100006), (1310007, 0, 2, 100007)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('mx_testing_schema.mx_test_table'::regclass, 1310000, 't'::"char", '-2147483648', '-1610612737'), ('mx_testing_schema.mx_test_table'::regclass, 1310001, 't'::"char", '-1610612736', '-1073741825'), ('mx_testing_schema.mx_test_table'::regclass, 1310002, 't'::"char", '-1073741824', '-536870913'), ('mx_testing_schema.mx_test_table'::regclass, 1310003, 't'::"char", '-536870912', '-1'), ('mx_testing_schema.mx_test_table'::regclass, 1310004, 't'::"char", '0', '536870911'), ('mx_testing_schema.mx_test_table'::regclass, 1310005, 't'::"char", '536870912', '1073741823'), ('mx_testing_schema.mx_test_table'::regclass, 1310006, 't'::"char", '1073741824', '1610612735'), ('mx_testing_schema.mx_test_table'::regclass, 1310007, 't'::"char", '1610612736', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; -(52 rows) +(53 rows) -- Show that append distributed tables are not included in the activate node snapshot CREATE TABLE non_mx_test_table (col_1 int, col_2 text); @@ -331,6 +334,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; RESET ROLE SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') SELECT citus_internal_add_partition_metadata ('mx_testing_schema.mx_test_table'::regclass, 'h', 'col_1', 2, 's') + SELECT pg_catalog.worker_drop_sequence_dependency('mx_testing_schema.mx_test_table'); SELECT pg_catalog.worker_drop_sequence_dependency(logicalrelid::regclass::text) FROM pg_dist_partition SELECT pg_catalog.worker_record_sequence_dependency('mx_testing_schema.mx_test_table_col_3_seq'::regclass,'mx_testing_schema.mx_test_table'::regclass,'col_3') SELECT worker_apply_sequence_command ('CREATE SEQUENCE IF NOT EXISTS mx_testing_schema.mx_test_table_col_3_seq AS bigint INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1 NO CYCLE','bigint') @@ -357,7 +361,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('table', ARRAY['mx_testing_schema', 'mx_test_table']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310000, 0, 1, 100000), (1310001, 0, 2, 100001), (1310002, 0, 1, 100002), (1310003, 0, 2, 100003), (1310004, 0, 1, 100004), (1310005, 0, 2, 100005), (1310006, 0, 1, 100006), (1310007, 0, 2, 100007)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('mx_testing_schema.mx_test_table'::regclass, 1310000, 't'::"char", '-2147483648', '-1610612737'), ('mx_testing_schema.mx_test_table'::regclass, 1310001, 't'::"char", '-1610612736', '-1073741825'), ('mx_testing_schema.mx_test_table'::regclass, 1310002, 't'::"char", '-1073741824', '-536870913'), ('mx_testing_schema.mx_test_table'::regclass, 1310003, 't'::"char", '-536870912', '-1'), ('mx_testing_schema.mx_test_table'::regclass, 1310004, 't'::"char", '0', '536870911'), ('mx_testing_schema.mx_test_table'::regclass, 1310005, 't'::"char", '536870912', '1073741823'), ('mx_testing_schema.mx_test_table'::regclass, 1310006, 't'::"char", '1073741824', '1610612735'), ('mx_testing_schema.mx_test_table'::regclass, 1310007, 't'::"char", '1610612736', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; -(52 rows) +(53 rows) -- Show that range distributed tables are not included in the activate node snapshot UPDATE pg_dist_partition SET partmethod='r' WHERE logicalrelid='non_mx_test_table'::regclass; @@ -390,6 +394,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; RESET ROLE SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') SELECT citus_internal_add_partition_metadata ('mx_testing_schema.mx_test_table'::regclass, 'h', 'col_1', 2, 's') + SELECT pg_catalog.worker_drop_sequence_dependency('mx_testing_schema.mx_test_table'); SELECT pg_catalog.worker_drop_sequence_dependency(logicalrelid::regclass::text) FROM pg_dist_partition SELECT pg_catalog.worker_record_sequence_dependency('mx_testing_schema.mx_test_table_col_3_seq'::regclass,'mx_testing_schema.mx_test_table'::regclass,'col_3') SELECT worker_apply_sequence_command ('CREATE SEQUENCE IF NOT EXISTS mx_testing_schema.mx_test_table_col_3_seq AS bigint INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1 NO CYCLE','bigint') @@ -416,7 +421,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('table', ARRAY['mx_testing_schema', 'mx_test_table']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310000, 0, 1, 100000), (1310001, 0, 2, 100001), (1310002, 0, 1, 100002), (1310003, 0, 2, 100003), (1310004, 0, 1, 100004), (1310005, 0, 2, 100005), (1310006, 0, 1, 100006), (1310007, 0, 2, 100007)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('mx_testing_schema.mx_test_table'::regclass, 1310000, 't'::"char", '-2147483648', '-1610612737'), ('mx_testing_schema.mx_test_table'::regclass, 1310001, 't'::"char", '-1610612736', '-1073741825'), ('mx_testing_schema.mx_test_table'::regclass, 1310002, 't'::"char", '-1073741824', '-536870913'), ('mx_testing_schema.mx_test_table'::regclass, 1310003, 't'::"char", '-536870912', '-1'), ('mx_testing_schema.mx_test_table'::regclass, 1310004, 't'::"char", '0', '536870911'), ('mx_testing_schema.mx_test_table'::regclass, 1310005, 't'::"char", '536870912', '1073741823'), ('mx_testing_schema.mx_test_table'::regclass, 1310006, 't'::"char", '1073741824', '1610612735'), ('mx_testing_schema.mx_test_table'::regclass, 1310007, 't'::"char", '1610612736', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; -(52 rows) +(53 rows) -- Test start_metadata_sync_to_node and citus_activate_node UDFs -- Ensure that hasmetadata=false for all nodes @@ -1943,6 +1948,12 @@ SELECT unnest(activate_node_snapshot()) order by 1; SELECT citus_internal_add_partition_metadata ('public.dist_table_1'::regclass, 'h', 'a', 10010, 's') SELECT citus_internal_add_partition_metadata ('public.mx_ref'::regclass, 'n', NULL, 10009, 't') SELECT citus_internal_add_partition_metadata ('public.test_table'::regclass, 'h', 'id', 10010, 's') + SELECT pg_catalog.worker_drop_sequence_dependency('mx_test_schema_1.mx_table_1'); + SELECT pg_catalog.worker_drop_sequence_dependency('mx_test_schema_2.mx_table_2'); + SELECT pg_catalog.worker_drop_sequence_dependency('mx_testing_schema.mx_test_table'); + SELECT pg_catalog.worker_drop_sequence_dependency('public.dist_table_1'); + SELECT pg_catalog.worker_drop_sequence_dependency('public.mx_ref'); + SELECT pg_catalog.worker_drop_sequence_dependency('public.test_table'); SELECT pg_catalog.worker_drop_sequence_dependency(logicalrelid::regclass::text) FROM pg_dist_partition SELECT pg_catalog.worker_record_sequence_dependency('mx_testing_schema.mx_test_table_col_3_seq'::regclass,'mx_testing_schema.mx_test_table'::regclass,'col_3') SELECT worker_apply_sequence_command ('CREATE SEQUENCE IF NOT EXISTS mx_testing_schema.mx_test_table_col_3_seq AS bigint INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1 NO CYCLE','bigint') @@ -1997,7 +2008,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.dist_table_1'::regclass, 1310074, 't'::"char", '-2147483648', '-1073741825'), ('public.dist_table_1'::regclass, 1310075, 't'::"char", '-1073741824', '-1'), ('public.dist_table_1'::regclass, 1310076, 't'::"char", '0', '1073741823'), ('public.dist_table_1'::regclass, 1310077, 't'::"char", '1073741824', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.mx_ref'::regclass, 1310073, 't'::"char", NULL, NULL)) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.test_table'::regclass, 1310083, 't'::"char", '-2147483648', '-1073741825'), ('public.test_table'::regclass, 1310084, 't'::"char", '-1073741824', '-1'), ('public.test_table'::regclass, 1310085, 't'::"char", '0', '1073741823'), ('public.test_table'::regclass, 1310086, 't'::"char", '1073741824', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; -(111 rows) +(117 rows) -- shouldn't work since test_table is MX ALTER TABLE test_table ADD COLUMN id3 bigserial; diff --git a/src/test/regress/expected/multi_metadata_sync_0.out b/src/test/regress/expected/multi_metadata_sync_0.out index 5fb08bbc3..07e00ca93 100644 --- a/src/test/regress/expected/multi_metadata_sync_0.out +++ b/src/test/regress/expected/multi_metadata_sync_0.out @@ -149,6 +149,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; RESET ROLE SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') SELECT citus_internal_add_partition_metadata ('public.mx_test_table'::regclass, 'h', 'col_1', 2, 's') + SELECT pg_catalog.worker_drop_sequence_dependency('public.mx_test_table'); SELECT pg_catalog.worker_drop_sequence_dependency(logicalrelid::regclass::text) FROM pg_dist_partition SELECT pg_catalog.worker_record_sequence_dependency('public.mx_test_table_col_3_seq'::regclass,'public.mx_test_table'::regclass,'col_3') SELECT worker_apply_sequence_command ('CREATE SEQUENCE IF NOT EXISTS public.mx_test_table_col_3_seq AS bigint INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1 NO CYCLE','bigint') @@ -174,7 +175,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('table', ARRAY['public', 'mx_test_table']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310000, 0, 1, 100000), (1310001, 0, 2, 100001), (1310002, 0, 1, 100002), (1310003, 0, 2, 100003), (1310004, 0, 1, 100004), (1310005, 0, 2, 100005), (1310006, 0, 1, 100006), (1310007, 0, 2, 100007)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.mx_test_table'::regclass, 1310000, 't'::"char", '-2147483648', '-1610612737'), ('public.mx_test_table'::regclass, 1310001, 't'::"char", '-1610612736', '-1073741825'), ('public.mx_test_table'::regclass, 1310002, 't'::"char", '-1073741824', '-536870913'), ('public.mx_test_table'::regclass, 1310003, 't'::"char", '-536870912', '-1'), ('public.mx_test_table'::regclass, 1310004, 't'::"char", '0', '536870911'), ('public.mx_test_table'::regclass, 1310005, 't'::"char", '536870912', '1073741823'), ('public.mx_test_table'::regclass, 1310006, 't'::"char", '1073741824', '1610612735'), ('public.mx_test_table'::regclass, 1310007, 't'::"char", '1610612736', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; -(49 rows) +(50 rows) -- Show that CREATE INDEX commands are included in the activate node snapshot CREATE INDEX mx_index ON mx_test_table(col_2); @@ -206,6 +207,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; RESET ROLE SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') SELECT citus_internal_add_partition_metadata ('public.mx_test_table'::regclass, 'h', 'col_1', 2, 's') + SELECT pg_catalog.worker_drop_sequence_dependency('public.mx_test_table'); SELECT pg_catalog.worker_drop_sequence_dependency(logicalrelid::regclass::text) FROM pg_dist_partition SELECT pg_catalog.worker_record_sequence_dependency('public.mx_test_table_col_3_seq'::regclass,'public.mx_test_table'::regclass,'col_3') SELECT worker_apply_sequence_command ('CREATE SEQUENCE IF NOT EXISTS public.mx_test_table_col_3_seq AS bigint INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1 NO CYCLE','bigint') @@ -231,7 +233,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('table', ARRAY['public', 'mx_test_table']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310000, 0, 1, 100000), (1310001, 0, 2, 100001), (1310002, 0, 1, 100002), (1310003, 0, 2, 100003), (1310004, 0, 1, 100004), (1310005, 0, 2, 100005), (1310006, 0, 1, 100006), (1310007, 0, 2, 100007)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.mx_test_table'::regclass, 1310000, 't'::"char", '-2147483648', '-1610612737'), ('public.mx_test_table'::regclass, 1310001, 't'::"char", '-1610612736', '-1073741825'), ('public.mx_test_table'::regclass, 1310002, 't'::"char", '-1073741824', '-536870913'), ('public.mx_test_table'::regclass, 1310003, 't'::"char", '-536870912', '-1'), ('public.mx_test_table'::regclass, 1310004, 't'::"char", '0', '536870911'), ('public.mx_test_table'::regclass, 1310005, 't'::"char", '536870912', '1073741823'), ('public.mx_test_table'::regclass, 1310006, 't'::"char", '1073741824', '1610612735'), ('public.mx_test_table'::regclass, 1310007, 't'::"char", '1610612736', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; -(50 rows) +(51 rows) -- Show that schema changes are included in the activate node snapshot CREATE SCHEMA mx_testing_schema; @@ -265,6 +267,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; RESET ROLE SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') SELECT citus_internal_add_partition_metadata ('mx_testing_schema.mx_test_table'::regclass, 'h', 'col_1', 2, 's') + SELECT pg_catalog.worker_drop_sequence_dependency('mx_testing_schema.mx_test_table'); SELECT pg_catalog.worker_drop_sequence_dependency(logicalrelid::regclass::text) FROM pg_dist_partition SELECT pg_catalog.worker_record_sequence_dependency('mx_testing_schema.mx_test_table_col_3_seq'::regclass,'mx_testing_schema.mx_test_table'::regclass,'col_3') SELECT worker_apply_sequence_command ('CREATE SEQUENCE IF NOT EXISTS mx_testing_schema.mx_test_table_col_3_seq AS bigint INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1 NO CYCLE','bigint') @@ -291,7 +294,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('table', ARRAY['mx_testing_schema', 'mx_test_table']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310000, 0, 1, 100000), (1310001, 0, 2, 100001), (1310002, 0, 1, 100002), (1310003, 0, 2, 100003), (1310004, 0, 1, 100004), (1310005, 0, 2, 100005), (1310006, 0, 1, 100006), (1310007, 0, 2, 100007)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('mx_testing_schema.mx_test_table'::regclass, 1310000, 't'::"char", '-2147483648', '-1610612737'), ('mx_testing_schema.mx_test_table'::regclass, 1310001, 't'::"char", '-1610612736', '-1073741825'), ('mx_testing_schema.mx_test_table'::regclass, 1310002, 't'::"char", '-1073741824', '-536870913'), ('mx_testing_schema.mx_test_table'::regclass, 1310003, 't'::"char", '-536870912', '-1'), ('mx_testing_schema.mx_test_table'::regclass, 1310004, 't'::"char", '0', '536870911'), ('mx_testing_schema.mx_test_table'::regclass, 1310005, 't'::"char", '536870912', '1073741823'), ('mx_testing_schema.mx_test_table'::regclass, 1310006, 't'::"char", '1073741824', '1610612735'), ('mx_testing_schema.mx_test_table'::regclass, 1310007, 't'::"char", '1610612736', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; -(52 rows) +(53 rows) -- Show that append distributed tables are not included in the activate node snapshot CREATE TABLE non_mx_test_table (col_1 int, col_2 text); @@ -331,6 +334,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; RESET ROLE SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') SELECT citus_internal_add_partition_metadata ('mx_testing_schema.mx_test_table'::regclass, 'h', 'col_1', 2, 's') + SELECT pg_catalog.worker_drop_sequence_dependency('mx_testing_schema.mx_test_table'); SELECT pg_catalog.worker_drop_sequence_dependency(logicalrelid::regclass::text) FROM pg_dist_partition SELECT pg_catalog.worker_record_sequence_dependency('mx_testing_schema.mx_test_table_col_3_seq'::regclass,'mx_testing_schema.mx_test_table'::regclass,'col_3') SELECT worker_apply_sequence_command ('CREATE SEQUENCE IF NOT EXISTS mx_testing_schema.mx_test_table_col_3_seq AS bigint INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1 NO CYCLE','bigint') @@ -357,7 +361,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('table', ARRAY['mx_testing_schema', 'mx_test_table']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310000, 0, 1, 100000), (1310001, 0, 2, 100001), (1310002, 0, 1, 100002), (1310003, 0, 2, 100003), (1310004, 0, 1, 100004), (1310005, 0, 2, 100005), (1310006, 0, 1, 100006), (1310007, 0, 2, 100007)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('mx_testing_schema.mx_test_table'::regclass, 1310000, 't'::"char", '-2147483648', '-1610612737'), ('mx_testing_schema.mx_test_table'::regclass, 1310001, 't'::"char", '-1610612736', '-1073741825'), ('mx_testing_schema.mx_test_table'::regclass, 1310002, 't'::"char", '-1073741824', '-536870913'), ('mx_testing_schema.mx_test_table'::regclass, 1310003, 't'::"char", '-536870912', '-1'), ('mx_testing_schema.mx_test_table'::regclass, 1310004, 't'::"char", '0', '536870911'), ('mx_testing_schema.mx_test_table'::regclass, 1310005, 't'::"char", '536870912', '1073741823'), ('mx_testing_schema.mx_test_table'::regclass, 1310006, 't'::"char", '1073741824', '1610612735'), ('mx_testing_schema.mx_test_table'::regclass, 1310007, 't'::"char", '1610612736', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; -(52 rows) +(53 rows) -- Show that range distributed tables are not included in the activate node snapshot UPDATE pg_dist_partition SET partmethod='r' WHERE logicalrelid='non_mx_test_table'::regclass; @@ -390,6 +394,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; RESET ROLE SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') SELECT citus_internal_add_partition_metadata ('mx_testing_schema.mx_test_table'::regclass, 'h', 'col_1', 2, 's') + SELECT pg_catalog.worker_drop_sequence_dependency('mx_testing_schema.mx_test_table'); SELECT pg_catalog.worker_drop_sequence_dependency(logicalrelid::regclass::text) FROM pg_dist_partition SELECT pg_catalog.worker_record_sequence_dependency('mx_testing_schema.mx_test_table_col_3_seq'::regclass,'mx_testing_schema.mx_test_table'::regclass,'col_3') SELECT worker_apply_sequence_command ('CREATE SEQUENCE IF NOT EXISTS mx_testing_schema.mx_test_table_col_3_seq AS bigint INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1 NO CYCLE','bigint') @@ -416,7 +421,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('table', ARRAY['mx_testing_schema', 'mx_test_table']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310000, 0, 1, 100000), (1310001, 0, 2, 100001), (1310002, 0, 1, 100002), (1310003, 0, 2, 100003), (1310004, 0, 1, 100004), (1310005, 0, 2, 100005), (1310006, 0, 1, 100006), (1310007, 0, 2, 100007)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('mx_testing_schema.mx_test_table'::regclass, 1310000, 't'::"char", '-2147483648', '-1610612737'), ('mx_testing_schema.mx_test_table'::regclass, 1310001, 't'::"char", '-1610612736', '-1073741825'), ('mx_testing_schema.mx_test_table'::regclass, 1310002, 't'::"char", '-1073741824', '-536870913'), ('mx_testing_schema.mx_test_table'::regclass, 1310003, 't'::"char", '-536870912', '-1'), ('mx_testing_schema.mx_test_table'::regclass, 1310004, 't'::"char", '0', '536870911'), ('mx_testing_schema.mx_test_table'::regclass, 1310005, 't'::"char", '536870912', '1073741823'), ('mx_testing_schema.mx_test_table'::regclass, 1310006, 't'::"char", '1073741824', '1610612735'), ('mx_testing_schema.mx_test_table'::regclass, 1310007, 't'::"char", '1610612736', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; -(52 rows) +(53 rows) -- Test start_metadata_sync_to_node and citus_activate_node UDFs -- Ensure that hasmetadata=false for all nodes @@ -1943,6 +1948,12 @@ SELECT unnest(activate_node_snapshot()) order by 1; SELECT citus_internal_add_partition_metadata ('public.dist_table_1'::regclass, 'h', 'a', 10010, 's') SELECT citus_internal_add_partition_metadata ('public.mx_ref'::regclass, 'n', NULL, 10009, 't') SELECT citus_internal_add_partition_metadata ('public.test_table'::regclass, 'h', 'id', 10010, 's') + SELECT pg_catalog.worker_drop_sequence_dependency('mx_test_schema_1.mx_table_1'); + SELECT pg_catalog.worker_drop_sequence_dependency('mx_test_schema_2.mx_table_2'); + SELECT pg_catalog.worker_drop_sequence_dependency('mx_testing_schema.mx_test_table'); + SELECT pg_catalog.worker_drop_sequence_dependency('public.dist_table_1'); + SELECT pg_catalog.worker_drop_sequence_dependency('public.mx_ref'); + SELECT pg_catalog.worker_drop_sequence_dependency('public.test_table'); SELECT pg_catalog.worker_drop_sequence_dependency(logicalrelid::regclass::text) FROM pg_dist_partition SELECT pg_catalog.worker_record_sequence_dependency('mx_testing_schema.mx_test_table_col_3_seq'::regclass,'mx_testing_schema.mx_test_table'::regclass,'col_3') SELECT worker_apply_sequence_command ('CREATE SEQUENCE IF NOT EXISTS mx_testing_schema.mx_test_table_col_3_seq AS bigint INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1 NO CYCLE','bigint') @@ -1997,7 +2008,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.dist_table_1'::regclass, 1310074, 't'::"char", '-2147483648', '-1073741825'), ('public.dist_table_1'::regclass, 1310075, 't'::"char", '-1073741824', '-1'), ('public.dist_table_1'::regclass, 1310076, 't'::"char", '0', '1073741823'), ('public.dist_table_1'::regclass, 1310077, 't'::"char", '1073741824', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.mx_ref'::regclass, 1310073, 't'::"char", NULL, NULL)) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.test_table'::regclass, 1310083, 't'::"char", '-2147483648', '-1073741825'), ('public.test_table'::regclass, 1310084, 't'::"char", '-1073741824', '-1'), ('public.test_table'::regclass, 1310085, 't'::"char", '0', '1073741823'), ('public.test_table'::regclass, 1310086, 't'::"char", '1073741824', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; -(111 rows) +(117 rows) -- shouldn't work since test_table is MX ALTER TABLE test_table ADD COLUMN id3 bigserial; diff --git a/src/test/regress/sql/failure_mx_metadata_sync_multi_trans.sql b/src/test/regress/sql/failure_mx_metadata_sync_multi_trans.sql index ec05167fc..c0e575c14 100644 --- a/src/test/regress/sql/failure_mx_metadata_sync_multi_trans.sql +++ b/src/test/regress/sql/failure_mx_metadata_sync_multi_trans.sql @@ -18,11 +18,36 @@ SET client_min_messages TO ERROR; CREATE ROLE foo1; CREATE ROLE foo2; +-- Create collation +CREATE COLLATION german_phonebook (provider = icu, locale = 'de-u-co-phonebk'); + +-- Create type +CREATE TYPE pair_type AS (a int, b int); + +-- Create function +CREATE FUNCTION one_as_result() RETURNS INT LANGUAGE SQL AS +$$ + SELECT 1; +$$; + +-- Create text search dictionary +CREATE TEXT SEARCH DICTIONARY my_german_dict ( + template = snowball, + language = german, + stopwords = german +); + +-- Create text search config +CREATE TEXT SEARCH CONFIGURATION my_ts_config ( parser = default ); +ALTER TEXT SEARCH CONFIGURATION my_ts_config ALTER MAPPING FOR asciiword WITH my_german_dict; + -- Create sequence CREATE SEQUENCE seq; -- Create colocated distributed tables -CREATE TABLE dist1 (id int PRIMARY KEY default nextval('seq')); +CREATE TABLE dist1 (id int PRIMARY KEY default nextval('seq'), col int default (one_as_result()), myserial serial, phone text COLLATE german_phonebook, initials pair_type); +CREATE SEQUENCE seq_owned OWNED BY dist1.id; +CREATE INDEX dist1_search_phone_idx ON dist1 USING gin (to_tsvector('my_ts_config'::regconfig, (COALESCE(phone, ''::text))::text)); SELECT create_distributed_table('dist1', 'id'); INSERT INTO dist1 SELECT i FROM generate_series(1,100) i; @@ -42,6 +67,9 @@ INSERT INTO loc1 SELECT i FROM generate_series(1,100) i; CREATE TABLE loc2 (id int REFERENCES loc1(id)); INSERT INTO loc2 SELECT i FROM generate_series(1,100) i; +-- Create publication +CREATE PUBLICATION pub_all; + -- citus_set_coordinator_host with wrong port SELECT citus_set_coordinator_host('localhost', 9999); -- citus_set_coordinator_host with correct port @@ -88,10 +116,10 @@ SELECT citus_activate_node('localhost', :worker_2_proxy_port); SELECT citus.mitmproxy('conn.onQuery(query="INSERT INTO pg_dist_node").kill()'); SELECT citus_activate_node('localhost', :worker_2_proxy_port); --- Failure to drop sequence -SELECT citus.mitmproxy('conn.onQuery(query="SELECT pg_catalog.worker_drop_sequence_dependency").cancel(' || :pid || ')'); +-- Failure to drop sequence dependency for all tables +SELECT citus.mitmproxy('conn.onQuery(query="SELECT pg_catalog.worker_drop_sequence_dependency.*FROM pg_dist_partition").cancel(' || :pid || ')'); SELECT citus_activate_node('localhost', :worker_2_proxy_port); -SELECT citus.mitmproxy('conn.onQuery(query="SELECT pg_catalog.worker_drop_sequence_dependency").kill()'); +SELECT citus.mitmproxy('conn.onQuery(query="SELECT pg_catalog.worker_drop_sequence_dependency.*FROM pg_dist_partition").kill()'); SELECT citus_activate_node('localhost', :worker_2_proxy_port); -- Failure to drop shell table @@ -142,24 +170,84 @@ SELECT citus_activate_node('localhost', :worker_2_proxy_port); SELECT citus.mitmproxy('conn.onQuery(query="ALTER DATABASE.*OWNER TO").kill()'); SELECT citus_activate_node('localhost', :worker_2_proxy_port); --- Filure to create schema +-- Failure to create schema SELECT citus.mitmproxy('conn.onQuery(query="CREATE SCHEMA IF NOT EXISTS mx_metadata_sync_multi_trans AUTHORIZATION").cancel(' || :pid || ')'); SELECT citus_activate_node('localhost', :worker_2_proxy_port); SELECT citus.mitmproxy('conn.onQuery(query="CREATE SCHEMA IF NOT EXISTS mx_metadata_sync_multi_trans AUTHORIZATION").kill()'); SELECT citus_activate_node('localhost', :worker_2_proxy_port); +-- Failure to create collation +SELECT citus.mitmproxy('conn.onQuery(query="SELECT worker_create_or_replace_object.*CREATE COLLATION mx_metadata_sync_multi_trans.german_phonebook").cancel(' || :pid || ')'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +SELECT citus.mitmproxy('conn.onQuery(query="SELECT worker_create_or_replace_object.*CREATE COLLATION mx_metadata_sync_multi_trans.german_phonebook").kill()'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); + +-- Failure to create function +SELECT citus.mitmproxy('conn.onQuery(query="CREATE OR REPLACE FUNCTION mx_metadata_sync_multi_trans.one_as_result").cancel(' || :pid || ')'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +SELECT citus.mitmproxy('conn.onQuery(query="CREATE OR REPLACE FUNCTION mx_metadata_sync_multi_trans.one_as_result").kill()'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); + +-- Failure to create text search dictionary +SELECT citus.mitmproxy('conn.onQuery(query="SELECT worker_create_or_replace_object.*my_german_dict").cancel(' || :pid || ')'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +SELECT citus.mitmproxy('conn.onQuery(query="SELECT worker_create_or_replace_object.*my_german_dict").kill()'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); + +-- Failure to create text search config +SELECT citus.mitmproxy('conn.onQuery(query="SELECT worker_create_or_replace_object.*my_ts_config").cancel(' || :pid || ')'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +SELECT citus.mitmproxy('conn.onQuery(query="SELECT worker_create_or_replace_object.*my_ts_config").kill()'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); + +-- Failure to create type +SELECT citus.mitmproxy('conn.onQuery(query="SELECT worker_create_or_replace_object.*pair_type").cancel(' || :pid || ')'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +SELECT citus.mitmproxy('conn.onQuery(query="SELECT worker_create_or_replace_object.*pair_type").kill()'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); + +-- Failure to create publication +SELECT citus.mitmproxy('conn.onQuery(query="CREATE PUBLICATION.*pub_all").cancel(' || :pid || ')'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +SELECT citus.mitmproxy('conn.onQuery(query="CREATE PUBLICATION.*pub_all").kill()'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); + -- Failure to create sequence SELECT citus.mitmproxy('conn.onQuery(query="SELECT worker_apply_sequence_command").cancel(' || :pid || ')'); SELECT citus_activate_node('localhost', :worker_2_proxy_port); SELECT citus.mitmproxy('conn.onQuery(query="SELECT worker_apply_sequence_command").kill()'); SELECT citus_activate_node('localhost', :worker_2_proxy_port); +-- Failure to drop sequence dependency for distributed table +SELECT citus.mitmproxy('conn.onQuery(query="SELECT pg_catalog.worker_drop_sequence_dependency.*mx_metadata_sync_multi_trans.dist1").cancel(' || :pid || ')'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +SELECT citus.mitmproxy('conn.onQuery(query="SELECT pg_catalog.worker_drop_sequence_dependency.*mx_metadata_sync_multi_trans.dist1").kill()'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); + +-- Failure to drop distributed table if exists +SELECT citus.mitmproxy('conn.onQuery(query="DROP TABLE IF EXISTS mx_metadata_sync_multi_trans.dist1").cancel(' || :pid || ')'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +SELECT citus.mitmproxy('conn.onQuery(query="DROP TABLE IF EXISTS mx_metadata_sync_multi_trans.dist1").kill()'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); + -- Failure to create distributed table SELECT citus.mitmproxy('conn.onQuery(query="CREATE TABLE mx_metadata_sync_multi_trans.dist1").cancel(' || :pid || ')'); SELECT citus_activate_node('localhost', :worker_2_proxy_port); SELECT citus.mitmproxy('conn.onQuery(query="CREATE TABLE mx_metadata_sync_multi_trans.dist1").kill()'); SELECT citus_activate_node('localhost', :worker_2_proxy_port); +-- Failure to record sequence dependency for table +SELECT citus.mitmproxy('conn.onQuery(query="SELECT pg_catalog.worker_record_sequence_dependency").cancel(' || :pid || ')'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +SELECT citus.mitmproxy('conn.onQuery(query="SELECT pg_catalog.worker_record_sequence_dependency").kill()'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); + +-- Failure to create index for table +SELECT citus.mitmproxy('conn.onQuery(query="CREATE INDEX dist1_search_phone_idx ON mx_metadata_sync_multi_trans.dist1 USING gin").cancel(' || :pid || ')'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +SELECT citus.mitmproxy('conn.onQuery(query="CREATE INDEX dist1_search_phone_idx ON mx_metadata_sync_multi_trans.dist1 USING gin").kill()'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); + -- Failure to create reference table SELECT citus.mitmproxy('conn.onQuery(query="CREATE TABLE mx_metadata_sync_multi_trans.ref").cancel(' || :pid || ')'); SELECT citus_activate_node('localhost', :worker_2_proxy_port); @@ -220,6 +308,48 @@ SELECT citus_activate_node('localhost', :worker_2_proxy_port); SELECT citus.mitmproxy('conn.onQuery(query="SELECT citus_internal_add_object_metadata").kill()'); SELECT citus_activate_node('localhost', :worker_2_proxy_port); +-- Failure to mark function as distributed +SELECT citus.mitmproxy('conn.onQuery(query="WITH distributed_object_data.*one_as_result").cancel(' || :pid || ')'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +SELECT citus.mitmproxy('conn.onQuery(query="WITH distributed_object_data.*one_as_result").kill()'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); + +-- Failure to mark collation as distributed +SELECT citus.mitmproxy('conn.onQuery(query="WITH distributed_object_data.*german_phonebook").cancel(' || :pid || ')'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +SELECT citus.mitmproxy('conn.onQuery(query="WITH distributed_object_data.*german_phonebook").kill()'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); + +-- Failure to mark text search dictionary as distributed +SELECT citus.mitmproxy('conn.onQuery(query="WITH distributed_object_data.*my_german_dict").cancel(' || :pid || ')'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +SELECT citus.mitmproxy('conn.onQuery(query="WITH distributed_object_data.*my_german_dict").kill()'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); + +-- Failure to mark text search configuration as distributed +SELECT citus.mitmproxy('conn.onQuery(query="WITH distributed_object_data.*my_ts_config").cancel(' || :pid || ')'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +SELECT citus.mitmproxy('conn.onQuery(query="WITH distributed_object_data.*my_ts_config").kill()'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); + +-- Failure to mark type as distributed +SELECT citus.mitmproxy('conn.onQuery(query="WITH distributed_object_data.*pair_type").cancel(' || :pid || ')'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +SELECT citus.mitmproxy('conn.onQuery(query="WITH distributed_object_data.*pair_type").kill()'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); + +-- Failure to mark sequence as distributed +SELECT citus.mitmproxy('conn.onQuery(query="WITH distributed_object_data.*seq_owned").cancel(' || :pid || ')'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +SELECT citus.mitmproxy('conn.onQuery(query="WITH distributed_object_data.*seq_owned").kill()'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); + +-- Failure to mark publication as distributed +SELECT citus.mitmproxy('conn.onQuery(query="WITH distributed_object_data.*pub_all").cancel(' || :pid || ')'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); +SELECT citus.mitmproxy('conn.onQuery(query="WITH distributed_object_data.*pub_all").kill()'); +SELECT citus_activate_node('localhost', :worker_2_proxy_port); + -- Failure to set isactive to true SELECT citus.mitmproxy('conn.onQuery(query="UPDATE pg_dist_node SET isactive = TRUE").cancel(' || :pid || ')'); SELECT citus_activate_node('localhost', :worker_2_proxy_port); @@ -277,6 +407,7 @@ SELECT * FROM pg_dist_node ORDER BY nodeport; SELECT citus.mitmproxy('conn.allow()'); RESET citus.metadata_sync_mode; +DROP PUBLICATION pub_all; DROP SCHEMA dummy; DROP SCHEMA mx_metadata_sync_multi_trans CASCADE; DROP ROLE foo1; From 59ccf364dfd820c5645c112bcb5828bd7df1e541 Mon Sep 17 00:00:00 2001 From: Ahmet Gedemenli Date: Mon, 1 May 2023 13:21:08 +0300 Subject: [PATCH 020/118] Ignore nodes not allowed for shards, when planning rebalance steps (#6887) We are handling colocation groups with shard group count less than the worker node count, using a method different than the usual rebalancer. See #6739 While making the decision of using this method or not, we should've ignored the nodes that are marked `shouldhaveshards = false`. This PR excludes those nodes when making the decision. Adds a test such that: coordinator: [] worker 1: [1_1, 1_2] worker 2: [2_1, 2_2] (rebalance) coordinator: [] worker 1: [1_1, 2_1] worker 2: [1_2, 2_2] If we take the coordinator into account, the rebalancer considers the first state as balanced and does nothing (because shard_count < worker_count) But with this pr, we ignore the coordinator because it's shouldhaveshards = false So the rebalancer distributes each colocation group to both workers Also, fixes an unrelated flaky test in the same file --- .../distributed/operations/shard_rebalancer.c | 13 ++- .../regress/expected/shard_rebalancer.out | 110 +++++++++++++++++- src/test/regress/sql/shard_rebalancer.sql | 57 +++++++++ 3 files changed, 177 insertions(+), 3 deletions(-) diff --git a/src/backend/distributed/operations/shard_rebalancer.c b/src/backend/distributed/operations/shard_rebalancer.c index b5ec9b7ba..082e0a8b5 100644 --- a/src/backend/distributed/operations/shard_rebalancer.c +++ b/src/backend/distributed/operations/shard_rebalancer.c @@ -515,6 +515,16 @@ GetRebalanceSteps(RebalanceOptions *options) /* sort the lists to make the function more deterministic */ List *activeWorkerList = SortedActiveWorkers(); + int shardAllowedNodeCount = 0; + WorkerNode *workerNode = NULL; + foreach_ptr(workerNode, activeWorkerList) + { + if (workerNode->shouldHaveShards) + { + shardAllowedNodeCount++; + } + } + List *activeShardPlacementListList = NIL; List *unbalancedShards = NIL; @@ -532,8 +542,7 @@ GetRebalanceSteps(RebalanceOptions *options) shardPlacementList, options->workerNode); } - if (list_length(activeShardPlacementListForRelation) >= list_length( - activeWorkerList)) + if (list_length(activeShardPlacementListForRelation) >= shardAllowedNodeCount) { activeShardPlacementListList = lappend(activeShardPlacementListList, activeShardPlacementListForRelation); diff --git a/src/test/regress/expected/shard_rebalancer.out b/src/test/regress/expected/shard_rebalancer.out index 1dea3b442..23f1f7373 100644 --- a/src/test/regress/expected/shard_rebalancer.out +++ b/src/test/regress/expected/shard_rebalancer.out @@ -20,13 +20,14 @@ SELECT create_distributed_table('dist_table_test', 'a'); CREATE TABLE postgres_table_test(a int primary key); -- make sure that all rebalance operations works fine when -- reference tables are replicated to the coordinator +SET client_min_messages TO ERROR; SELECT 1 FROM master_add_node('localhost', :master_port, groupId=>0); -NOTICE: localhost:xxxxx is the coordinator and already contains metadata, skipping syncing the metadata ?column? --------------------------------------------------------------------- 1 (1 row) +RESET client_min_messages; -- should just be noops even if we add the coordinator to the pg_dist_node SELECT rebalance_table_shards('dist_table_test'); rebalance_table_shards @@ -2713,6 +2714,113 @@ SELECT sh.logicalrelid, pl.nodeport (5 rows) DROP TABLE single_shard_colocation_1a, single_shard_colocation_1b, single_shard_colocation_1c, single_shard_colocation_2a, single_shard_colocation_2b CASCADE; +-- test the same with coordinator shouldhaveshards = false and shard_count = 2 +-- so that the shard allowed node count would be 2 when rebalancing +-- for such cases, we only count the nodes that are allowed for shard placements +UPDATE pg_dist_node SET shouldhaveshards=false WHERE nodeport = :master_port; +create table two_shard_colocation_1a (a int primary key); +create table two_shard_colocation_1b (a int primary key); +SET citus.shard_replication_factor = 1; +select create_distributed_table('two_shard_colocation_1a','a', colocate_with => 'none', shard_count => 2); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +select create_distributed_table('two_shard_colocation_1b','a',colocate_with=>'two_shard_colocation_1a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +create table two_shard_colocation_2a (a int primary key); +create table two_shard_colocation_2b (a int primary key); +select create_distributed_table('two_shard_colocation_2a','a', colocate_with => 'none', shard_count => 2); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +select create_distributed_table('two_shard_colocation_2b','a',colocate_with=>'two_shard_colocation_2a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- move shards of colocation group 1 to worker1 +SELECT citus_move_shard_placement(sh.shardid, 'localhost', :worker_2_port, 'localhost', :worker_1_port) + FROM pg_dist_shard sh JOIN pg_dist_shard_placement pl ON sh.shardid = pl.shardid + WHERE sh.logicalrelid = 'two_shard_colocation_1a'::regclass + AND pl.nodeport = :worker_2_port + LIMIT 1; + citus_move_shard_placement +--------------------------------------------------------------------- + +(1 row) + +-- move shards of colocation group 2 to worker2 +SELECT citus_move_shard_placement(sh.shardid, 'localhost', :worker_1_port, 'localhost', :worker_2_port) + FROM pg_dist_shard sh JOIN pg_dist_shard_placement pl ON sh.shardid = pl.shardid + WHERE sh.logicalrelid = 'two_shard_colocation_2a'::regclass + AND pl.nodeport = :worker_1_port + LIMIT 1; + citus_move_shard_placement +--------------------------------------------------------------------- + +(1 row) + +-- current state: +-- coordinator: [] +-- worker 1: [1_1, 1_2] +-- worker 2: [2_1, 2_2] +SELECT sh.logicalrelid, pl.nodeport + FROM pg_dist_shard sh JOIN pg_dist_shard_placement pl ON sh.shardid = pl.shardid + WHERE sh.logicalrelid::text IN ('two_shard_colocation_1a', 'two_shard_colocation_1b', 'two_shard_colocation_2a', 'two_shard_colocation_2b') + ORDER BY sh.logicalrelid, pl.nodeport; + logicalrelid | nodeport +--------------------------------------------------------------------- + two_shard_colocation_1a | 57637 + two_shard_colocation_1a | 57637 + two_shard_colocation_1b | 57637 + two_shard_colocation_1b | 57637 + two_shard_colocation_2a | 57638 + two_shard_colocation_2a | 57638 + two_shard_colocation_2b | 57638 + two_shard_colocation_2b | 57638 +(8 rows) + +-- If we take the coordinator into account, the rebalancer considers this as balanced and does nothing (shard_count < worker_count) +-- but because the coordinator is not allowed for shards, rebalancer will distribute each colocation group to both workers +select rebalance_table_shards(shard_transfer_mode:='block_writes'); +NOTICE: Moving shard xxxxx from localhost:xxxxx to localhost:xxxxx ... +NOTICE: Moving shard xxxxx from localhost:xxxxx to localhost:xxxxx ... + rebalance_table_shards +--------------------------------------------------------------------- + +(1 row) + +-- final state: +-- coordinator: [] +-- worker 1: [1_1, 2_1] +-- worker 2: [1_2, 2_2] +SELECT sh.logicalrelid, pl.nodeport + FROM pg_dist_shard sh JOIN pg_dist_shard_placement pl ON sh.shardid = pl.shardid + WHERE sh.logicalrelid::text IN ('two_shard_colocation_1a', 'two_shard_colocation_1b', 'two_shard_colocation_2a', 'two_shard_colocation_2b') + ORDER BY sh.logicalrelid, pl.nodeport; + logicalrelid | nodeport +--------------------------------------------------------------------- + two_shard_colocation_1a | 57637 + two_shard_colocation_1a | 57638 + two_shard_colocation_1b | 57637 + two_shard_colocation_1b | 57638 + two_shard_colocation_2a | 57637 + two_shard_colocation_2a | 57638 + two_shard_colocation_2b | 57637 + two_shard_colocation_2b | 57638 +(8 rows) + +-- cleanup +DROP TABLE two_shard_colocation_1a, two_shard_colocation_1b, two_shard_colocation_2a, two_shard_colocation_2b CASCADE; -- verify we detect if one of the tables do not have a replica identity or primary key -- and error out in case of shard transfer mode = auto SELECT 1 FROM citus_remove_node('localhost', :worker_2_port); diff --git a/src/test/regress/sql/shard_rebalancer.sql b/src/test/regress/sql/shard_rebalancer.sql index da4259f5b..f524212bf 100644 --- a/src/test/regress/sql/shard_rebalancer.sql +++ b/src/test/regress/sql/shard_rebalancer.sql @@ -13,7 +13,9 @@ CREATE TABLE postgres_table_test(a int primary key); -- make sure that all rebalance operations works fine when -- reference tables are replicated to the coordinator +SET client_min_messages TO ERROR; SELECT 1 FROM master_add_node('localhost', :master_port, groupId=>0); +RESET client_min_messages; -- should just be noops even if we add the coordinator to the pg_dist_node SELECT rebalance_table_shards('dist_table_test'); @@ -1497,6 +1499,61 @@ SELECT sh.logicalrelid, pl.nodeport DROP TABLE single_shard_colocation_1a, single_shard_colocation_1b, single_shard_colocation_1c, single_shard_colocation_2a, single_shard_colocation_2b CASCADE; +-- test the same with coordinator shouldhaveshards = false and shard_count = 2 +-- so that the shard allowed node count would be 2 when rebalancing +-- for such cases, we only count the nodes that are allowed for shard placements +UPDATE pg_dist_node SET shouldhaveshards=false WHERE nodeport = :master_port; + +create table two_shard_colocation_1a (a int primary key); +create table two_shard_colocation_1b (a int primary key); +SET citus.shard_replication_factor = 1; + +select create_distributed_table('two_shard_colocation_1a','a', colocate_with => 'none', shard_count => 2); +select create_distributed_table('two_shard_colocation_1b','a',colocate_with=>'two_shard_colocation_1a'); + +create table two_shard_colocation_2a (a int primary key); +create table two_shard_colocation_2b (a int primary key); +select create_distributed_table('two_shard_colocation_2a','a', colocate_with => 'none', shard_count => 2); +select create_distributed_table('two_shard_colocation_2b','a',colocate_with=>'two_shard_colocation_2a'); + +-- move shards of colocation group 1 to worker1 +SELECT citus_move_shard_placement(sh.shardid, 'localhost', :worker_2_port, 'localhost', :worker_1_port) + FROM pg_dist_shard sh JOIN pg_dist_shard_placement pl ON sh.shardid = pl.shardid + WHERE sh.logicalrelid = 'two_shard_colocation_1a'::regclass + AND pl.nodeport = :worker_2_port + LIMIT 1; +-- move shards of colocation group 2 to worker2 +SELECT citus_move_shard_placement(sh.shardid, 'localhost', :worker_1_port, 'localhost', :worker_2_port) + FROM pg_dist_shard sh JOIN pg_dist_shard_placement pl ON sh.shardid = pl.shardid + WHERE sh.logicalrelid = 'two_shard_colocation_2a'::regclass + AND pl.nodeport = :worker_1_port + LIMIT 1; + +-- current state: +-- coordinator: [] +-- worker 1: [1_1, 1_2] +-- worker 2: [2_1, 2_2] +SELECT sh.logicalrelid, pl.nodeport + FROM pg_dist_shard sh JOIN pg_dist_shard_placement pl ON sh.shardid = pl.shardid + WHERE sh.logicalrelid::text IN ('two_shard_colocation_1a', 'two_shard_colocation_1b', 'two_shard_colocation_2a', 'two_shard_colocation_2b') + ORDER BY sh.logicalrelid, pl.nodeport; + +-- If we take the coordinator into account, the rebalancer considers this as balanced and does nothing (shard_count < worker_count) +-- but because the coordinator is not allowed for shards, rebalancer will distribute each colocation group to both workers +select rebalance_table_shards(shard_transfer_mode:='block_writes'); + +-- final state: +-- coordinator: [] +-- worker 1: [1_1, 2_1] +-- worker 2: [1_2, 2_2] +SELECT sh.logicalrelid, pl.nodeport + FROM pg_dist_shard sh JOIN pg_dist_shard_placement pl ON sh.shardid = pl.shardid + WHERE sh.logicalrelid::text IN ('two_shard_colocation_1a', 'two_shard_colocation_1b', 'two_shard_colocation_2a', 'two_shard_colocation_2b') + ORDER BY sh.logicalrelid, pl.nodeport; + +-- cleanup +DROP TABLE two_shard_colocation_1a, two_shard_colocation_1b, two_shard_colocation_2a, two_shard_colocation_2b CASCADE; + -- verify we detect if one of the tables do not have a replica identity or primary key -- and error out in case of shard transfer mode = auto SELECT 1 FROM citus_remove_node('localhost', :worker_2_port); From 934430003ebeaad681ee4c03c0803242fa329032 Mon Sep 17 00:00:00 2001 From: Hanefi Onaldi Date: Tue, 2 May 2023 11:29:24 +0300 Subject: [PATCH 021/118] Changelog entries for 11.3.0 (#6856) In this release, I tried something different. I experimented with adding the PR number and title to the changelog right before each changelog entry. This way, it is easier to track where a particular changelog entry comes from. After reviews are over, I plan to remove those lines with PR numbers and titles. I went through all the PRs that are merged after 11.2.0 release and came up with a list of PRs that may need help with changelog entries. You can see details on PRs grouped in several sections below. ## PRs with missing entries The following PRs below do not have a changelog entry. If you think that this is a mistake, please share it in this PR along with a suggestion on what the changelog item should be. PR #6846 : fix 3 flaky tests in failure schedule PR #6844 : Add CPU usage to citus_stat_tenants PR #6833 : Fix citus_stat_tenants period updating bug PR #6787 : Add more tests for ddl coverage PR #6842 : Add build-cdc-* temporary directories to .gitignore PR #6841 : Add build-cdc-* temporary directories to .gitignore PR #6840 : Bump Citus to 12.0devel PR #6824 : Fixes flakiness in multi_metadata_sync test PR #6811 : Backport identity column improvements to v11.2 PR #6830 : In run_test.py actually return worker_count PR #6825 : Fixes flakiness in multi_cluster_management test PR #6816 : Refactor run_test.py PR #6817 : Explicitly disallow local rels when inserting into dist table PR #6821 : Rename citus stats tenants PR #6822 : Add some more tests for initial sql support PR #6819 : Fix flakyness in citus_split_shard_by_split_points_deferred_drop PR #6814 : Make python-regress based tests runnable with run_test.py PR #6813 : Fix flaky multi_mx_schema_support test PR #6720 : Convert columnar tap tests to pytest PR #6812 : Revoke statistics permissions from public and grant them to pg_monitor PR #6769 : Citus stats tenants guc PR #6807 : Fix the incorrect (constant) value passed to pointer-to-bool parameter, pass a NULL as the value is not used PR #6797 : Attribute local queries and cached plans on local execution PR #6796 : Parse the annotation string correctly PR #6762 : Add logs to citus_stats_tenants PR #6773 : Add initial sql support for distributed tables that don't have a shard key PR #6792 : Disentangle MERGE planning code from the modify-planning code path PR #6761 : Citus stats tenants collector view PR #6791 : Make 8 more tests runnable multiple times via run_test.py PR #6786 : Refactor some of the planning code to accommodate a new planning path for MERGE SQL PR #6789 : Rename AllRelations.. functions to AllDistributedRelations.. PR #6788 : Actually skip arbitrary_configs_router & nested_execution for AllNullDistKeyDefaultConfig PR #6783 : Add a config for arbitrary config tests where all the tables are null-shard-key tables PR #6784 : Fix attach partition: citus local to null distributed PR #6782 : Add an arbitrary config test heavily based on multi_router_planner_fast_path.sql PR #6781 : Decide what to do with router planner error at one place PR #6778 : Support partitioning for dist tables with null dist keys PR #6766 : fix pip lock file PR #6764 : Make workerCount configurable for regression tests PR #6745 : Add support for creating distributed tables with a null shard key PR #6696 : This implements MERGE phase-III PR #6767 : Add pytest depedencies to Pipfile PR #6760 : Decide core distribution params in CreateCitusTable PR #6759 : Add multi_create_fdw into minimal_schedule PR #6743 : Replace CITUS_TABLE_WITH_NO_DIST_KEY checks with HasDistributionKey() PR #6751 : Stabilize single_node.sql and others that report illegal node removal PR #6742 : Refactor CreateDistributedTable() PR #6747 : Remove unused lock functions PR #6744 : Fix multiple output version arbitrary config tests PR #6741 : Stabilize single node tests PR #6740 : Fix string eval bug in migration files check PR #6736 : Make run_test.py and create_test.py importable without errors PR #6734 : Don't blanket ignore flake8 E402 error PR #6737 : Fixes bookworm packaging pipeline problem PR #6735 : Fix run_test.py on python 3.9 PR #6733 : MERGE: In deparser, add missing check for RETURNING clause. PR #6714 : Remove auto_explain workaround in citus explain hook for ALTER TABLE PR #6719 : Fix flaky test PR #6718 : Add more powerfull dependency tracking to run_test.py PR #6710 : Install non-vulnerable cryptography package PR #6711 : Support compilation and run tests on latest PG versions PR #6700 : Add auto-formatting and linting to our python code PR #6707 : Allow multi_insert_select to run repeatably PR #6708 : Fix flakyness in failure_create_distributed_table_non_empty PR #6698 : Miscellaneous cleanup PR #6704 : Update README for 11.2 PR #6703 : Fix dubious ownership error from git PR #6690 : Bump Citus to 11.3devel ## Too long changelog entries The following PRs have changelog entries that are too long to fit in a single line. I'd expect authors to supply at changelog entries in `DESCRIPTION:` lines that are at most 78 characters. If you want to supply multi-line changelog items, you can have multiple lines that start with `DESCRIPTION:` instead. PR #6837 : fixes update propagation bug when `citus_set_coordinator_host` is called more than once PR #6738 : Identity column implementation refactorings PR #6756 : Schedule parallel shard moves in background rebalancer by removing task dependencies between shard moves across colocation groups. PR #6793 : Add a GUC to disallow planning the queries that reference non-colocated tables via router planner PR #6726 : fix memory leak during altering distributed table with a lot of partition and shards PR #6722 : fix memory leak during distribution of a table with a lot of partitions PR #6693 : prevent memory leak during ConvertTable with a lot of partitions ## Empty changelog entries. The following PR had an empty `DESCRIPTION:` line. This generates an empty changelog line that needs to be removed manually. Please either provide a short entry, or remove `DESCRIPTION:` line completely. PR #6810 : Make CDC decoder an independent extension PR #6827 : Makefile changes to build CDC in builddir for pgoutput and wal2json. --------- Co-authored-by: Onur Tirtir --- CHANGELOG.md | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fdfae3ae..c72e64cbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,71 @@ +### citus v11.3.0 (May 2, 2023) ### + +* Introduces CDC implementation for Citus using logical replication + (#6623, #6810, #6827) + +* Adds support for `MERGE` command on co-located distributed tables joined on + distribution column (#6696, #6733) + +* Adds the view `citus_stats_tenants` that monitor statistics on tenant usages + (#6725) + +* Adds the GUC `citus.max_background_task_executors_per_node` to control number + of background task executors involving a node (#6771) + +* Allows parallel shard moves in background rebalancer (#6756) + +* Introduces the GUC `citus.metadata_sync_mode` that introduces nontransactional + mode for metadata sync (#6728, #6889) + +* Propagates CREATE/ALTER/DROP PUBLICATION statements for distributed tables + (#6776) + +* Adds the GUC `citus.enable_non_colocated_router_query_pushdown` to ensure + generating a consistent distributed plan for the queries that reference + non-colocated distributed tables when set to "false" (#6793) + +* Checks if all moves are able to be done via logical replication for rebalancer + (#6754) + +* Correctly reports shard size in `citus_shards` view (#6748) + +* Fixes a bug in shard copy operations (#6721) + +* Fixes a bug that prevents enforcing identity column restrictions on worker + nodes (#6738) + +* Fixes a bug with `INSERT .. SELECT` queries with identity columns (#6802) + +* Fixes an issue that caused some queries with custom aggregates to fail (#6805) + +* Fixes an issue when `citus_set_coordinator_host` is called more than once + (#6837) + +* Fixes an uninitialized memory access in shard split API (#6845) + +* Fixes memory leak and max allocation block errors during metadata syncing + (#6728) + +* Fixes memory leak in `undistribute_table` (#6693) + +* Fixes memory leak in `alter_distributed_table` (#6726) + +* Fixes memory leak in `create_distributed_table` (#6722) + +* Fixes memory leak issue with query results that returns single row (#6724) + +* Improves rebalancer when shard groups have placement count less than worker + count (#6739) + +* Makes sure to stop maintenance daemon when dropping a database even without + Citus extension (#6688) + +* Prevents using `alter_distributed_table` and `undistribute_table` UDFs when a + table has identity columns (#6738) + +* Prevents using identity columns on data types other than `bigint` on + distributed tables (#6738) + ### citus v11.2.1 (April 20, 2023) ### * Correctly reports shard size in `citus_shards` view (#6748) From efd41e8ea55cf613fc1a0255a034ce13ceb554d3 Mon Sep 17 00:00:00 2001 From: Hanefi Onaldi Date: Tue, 2 May 2023 11:58:32 +0300 Subject: [PATCH 022/118] Bump columnar to 11.3 (#6898) When working on changelog, Marco suggested in https://github.com/citusdata/citus/pull/6856#pullrequestreview-1386601215 that we should bump columnar version to 11.3 as well. This PR aims to contain all the necessary changes to allow upgrades to and downgrades from 11.3.0 for columnar. Note that updating citus extension version does not affect columnar as the two extension versions are not really coupled. The same changes will also be applied to the release branch in https://github.com/citusdata/citus/pull/6897 --- src/backend/columnar/citus_columnar.control | 2 +- src/backend/columnar/sql/citus_columnar--11.2-1--11.3-1.sql | 1 + .../sql/downgrades/citus_columnar--11.3-1--11.2-1.sql | 1 + .../expected/multi_fix_partition_shard_index_names.out | 4 ++-- 4 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 src/backend/columnar/sql/citus_columnar--11.2-1--11.3-1.sql create mode 100644 src/backend/columnar/sql/downgrades/citus_columnar--11.3-1--11.2-1.sql diff --git a/src/backend/columnar/citus_columnar.control b/src/backend/columnar/citus_columnar.control index d60a71bad..d8a54923c 100644 --- a/src/backend/columnar/citus_columnar.control +++ b/src/backend/columnar/citus_columnar.control @@ -1,6 +1,6 @@ # Columnar extension comment = 'Citus Columnar extension' -default_version = '11.2-1' +default_version = '11.3-1' module_pathname = '$libdir/citus_columnar' relocatable = false schema = pg_catalog diff --git a/src/backend/columnar/sql/citus_columnar--11.2-1--11.3-1.sql b/src/backend/columnar/sql/citus_columnar--11.2-1--11.3-1.sql new file mode 100644 index 000000000..18fa4963c --- /dev/null +++ b/src/backend/columnar/sql/citus_columnar--11.2-1--11.3-1.sql @@ -0,0 +1 @@ +-- citus_columnar--11.2-1--11.3-1 diff --git a/src/backend/columnar/sql/downgrades/citus_columnar--11.3-1--11.2-1.sql b/src/backend/columnar/sql/downgrades/citus_columnar--11.3-1--11.2-1.sql new file mode 100644 index 000000000..50f6a2011 --- /dev/null +++ b/src/backend/columnar/sql/downgrades/citus_columnar--11.3-1--11.2-1.sql @@ -0,0 +1 @@ +-- citus_columnar--11.3-1--11.2-1 diff --git a/src/test/regress/expected/multi_fix_partition_shard_index_names.out b/src/test/regress/expected/multi_fix_partition_shard_index_names.out index fea9c67a5..ef47efed4 100644 --- a/src/test/regress/expected/multi_fix_partition_shard_index_names.out +++ b/src/test/regress/expected/multi_fix_partition_shard_index_names.out @@ -650,7 +650,7 @@ NOTICE: issuing BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;SELECT assign_ DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing SET citus.enable_ddl_propagation TO 'off' DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing CREATE EXTENSION IF NOT EXISTS citus_columnar WITH SCHEMA pg_catalog VERSION "11.2-1"; +NOTICE: issuing CREATE EXTENSION IF NOT EXISTS citus_columnar WITH SCHEMA pg_catalog VERSION "11.3-1"; DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing COMMIT DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx @@ -658,7 +658,7 @@ NOTICE: issuing BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;SELECT assign_ DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing SET citus.enable_ddl_propagation TO 'off' DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing CREATE EXTENSION IF NOT EXISTS citus_columnar WITH SCHEMA pg_catalog VERSION "11.2-1"; +NOTICE: issuing CREATE EXTENSION IF NOT EXISTS citus_columnar WITH SCHEMA pg_catalog VERSION "11.3-1"; DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing COMMIT DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx From e444dd4f3f349e62d411e0d4bcc71e9c17eed0d0 Mon Sep 17 00:00:00 2001 From: Teja Mupparti Date: Sat, 25 Mar 2023 15:54:02 -0700 Subject: [PATCH 023/118] MERGE: Support reference table as source with local table as target --- .../distributed/metadata/node_metadata.c | 4 +- .../distributed/planner/merge_planner.c | 278 +++++++++--------- .../planner/multi_router_planner.c | 21 +- src/include/distributed/merge_planner.h | 7 +- .../distributed/multi_router_planner.h | 1 + .../distributed/pg_dist_node_metadata.h | 2 + src/test/regress/expected/merge.out | 141 ++++++++- src/test/regress/expected/pg15.out | 7 +- src/test/regress/sql/merge.sql | 101 +++++++ src/test/regress/sql/pg15.sql | 4 +- 10 files changed, 410 insertions(+), 156 deletions(-) diff --git a/src/backend/distributed/metadata/node_metadata.c b/src/backend/distributed/metadata/node_metadata.c index 1c0314a49..8586ac934 100644 --- a/src/backend/distributed/metadata/node_metadata.c +++ b/src/backend/distributed/metadata/node_metadata.c @@ -36,6 +36,7 @@ #include "distributed/multi_join_order.h" #include "distributed/multi_router_planner.h" #include "distributed/pg_dist_node.h" +#include "distributed/pg_dist_node_metadata.h" #include "distributed/reference_table_utils.h" #include "distributed/remote_commands.h" #include "distributed/resource_lock.h" @@ -119,7 +120,6 @@ static char * NodeMetadataSyncedUpdateCommand(uint32 nodeId, bool metadataSynced static void ErrorIfCoordinatorMetadataSetFalse(WorkerNode *workerNode, Datum value, char *field); static WorkerNode * SetShouldHaveShards(WorkerNode *workerNode, bool shouldHaveShards); -static int FindCoordinatorNodeId(void); static WorkerNode * FindNodeAnyClusterByNodeId(uint32 nodeId); static void ErrorIfAnyNodeNotExist(List *nodeList); static void UpdateLocalGroupIdsViaMetadataContext(MetadataSyncContext *context); @@ -1800,7 +1800,7 @@ FindNodeWithNodeId(int nodeId, bool missingOk) /* * FindCoordinatorNodeId returns the node id of the coordinator node */ -static int +int FindCoordinatorNodeId() { bool includeNodesFromOtherClusters = false; diff --git a/src/backend/distributed/planner/merge_planner.c b/src/backend/distributed/planner/merge_planner.c index c67095624..5b39aeba6 100644 --- a/src/backend/distributed/planner/merge_planner.c +++ b/src/backend/distributed/planner/merge_planner.c @@ -22,29 +22,27 @@ #include "distributed/merge_planner.h" #include "distributed/multi_logical_optimizer.h" #include "distributed/multi_router_planner.h" +#include "distributed/pg_dist_node_metadata.h" #include "distributed/pg_version_constants.h" #include "distributed/query_pushdown_planning.h" #if PG_VERSION_NUM >= PG_VERSION_15 -static DeferredErrorMessage * CheckIfRTETypeIsUnsupported(Query *parse, - RangeTblEntry *rangeTableEntry); static DeferredErrorMessage * ErrorIfDistTablesNotColocated(Query *parse, List * distTablesList, PlannerRestrictionContext * plannerRestrictionContext); -static DeferredErrorMessage * ErrorIfMergeHasUnsupportedTables(Query *parse, +static DeferredErrorMessage * ErrorIfMergeHasUnsupportedTables(Oid targetRelationId, + Query *parse, List *rangeTableList, PlannerRestrictionContext * restrictionContext); static bool IsDistributionColumnInMergeSource(Expr *columnExpression, Query *query, bool skipOuterVars); -static DeferredErrorMessage * InsertDistributionColumnMatchesSource(Query *query, - RangeTblEntry * - resultRte); - +static DeferredErrorMessage * InsertDistributionColumnMatchesSource(Oid targetRelationId, + Query *query); static DeferredErrorMessage * MergeQualAndTargetListFunctionsSupported(Oid resultRelationId, FromExpr *joinTree, @@ -65,12 +63,15 @@ CreateMergePlan(Query *originalQuery, Query *query, { DistributedPlan *distributedPlan = CitusMakeNode(DistributedPlan); bool multiShardQuery = false; + Oid targetRelationId = ModifyQueryResultRelationId(originalQuery); Assert(originalQuery->commandType == CMD_MERGE); + Assert(OidIsValid(targetRelationId)); + distributedPlan->targetRelationId = targetRelationId; distributedPlan->modLevel = RowModifyLevelForQuery(query); - - distributedPlan->planningError = MergeQuerySupported(originalQuery, + distributedPlan->planningError = MergeQuerySupported(targetRelationId, + originalQuery, multiShardQuery, plannerRestrictionContext); @@ -94,8 +95,6 @@ CreateMergePlan(Query *originalQuery, Query *query, /* MERGE doesn't support RETURNING clause */ distributedPlan->expectResults = false; - distributedPlan->targetRelationId = ResultRelationOidForQuery(query); - distributedPlan->fastPathRouterPlan = plannerRestrictionContext->fastPathRestrictionContext->fastPathRouterQuery; @@ -111,7 +110,7 @@ CreateMergePlan(Query *originalQuery, Query *query, * - Checks target-lists and functions-in-quals in TargetlistAndFunctionsSupported */ DeferredErrorMessage * -MergeQuerySupported(Query *originalQuery, bool multiShardQuery, +MergeQuerySupported(Oid resultRelationId, Query *originalQuery, bool multiShardQuery, PlannerRestrictionContext *plannerRestrictionContext) { /* function is void for pre-15 versions of Postgres */ @@ -138,7 +137,6 @@ MergeQuerySupported(Query *originalQuery, bool multiShardQuery, } List *rangeTableList = ExtractRangeTableEntryList(originalQuery); - RangeTblEntry *resultRte = ExtractResultRelationRTE(originalQuery); /* * Fast path queries cannot have merge command, and we prevent the remaining here. @@ -149,7 +147,8 @@ MergeQuerySupported(Query *originalQuery, bool multiShardQuery, * ErrorIfDistTablesNotColocated for details. */ DeferredErrorMessage *deferredError = - ErrorIfMergeHasUnsupportedTables(originalQuery, + ErrorIfMergeHasUnsupportedTables(resultRelationId, + originalQuery, rangeTableList, plannerRestrictionContext); if (deferredError) @@ -158,7 +157,6 @@ MergeQuerySupported(Query *originalQuery, bool multiShardQuery, RaiseDeferredError(deferredError, ERROR); } - Oid resultRelationId = resultRte->relid; deferredError = MergeQualAndTargetListFunctionsSupported(resultRelationId, originalQuery->jointree, originalQuery->jointree-> @@ -191,7 +189,7 @@ MergeQuerySupported(Query *originalQuery, bool multiShardQuery, } deferredError = - InsertDistributionColumnMatchesSource(originalQuery, resultRte); + InsertDistributionColumnMatchesSource(resultRelationId, originalQuery); if (deferredError) { /* MERGE's unsupported scenario, raise the exception */ @@ -222,32 +220,6 @@ MergeQuerySupported(Query *originalQuery, bool multiShardQuery, } -/* - * IsMergeAllowedOnRelation takes a relation entry and checks if MERGE command is - * permitted on special relations, such as materialized view, returns true only if - * it's a "source" relation. - */ -bool -IsMergeAllowedOnRelation(Query *parse, RangeTblEntry *rte) -{ - if (!IsMergeQuery(parse)) - { - return false; - } - - /* Fetch the MERGE target relation */ - RangeTblEntry *targetRte = rt_fetch(parse->resultRelation, parse->rtable); - - /* Is it a target relation? */ - if (targetRte->relid == rte->relid) - { - return false; - } - - return true; -} - - #if PG_VERSION_NUM >= PG_VERSION_15 /* @@ -283,70 +255,6 @@ ErrorIfDistTablesNotColocated(Query *parse, List *distTablesList, } -/* - * ErrorIfRTETypeIsUnsupported Checks for types of tables that are not supported, such - * as, reference tables, append-distributed tables and materialized view as target relation. - * Routine returns NULL for the supported types, error message for everything else. - */ -static DeferredErrorMessage * -CheckIfRTETypeIsUnsupported(Query *parse, RangeTblEntry *rangeTableEntry) -{ - if (rangeTableEntry->relkind == RELKIND_MATVIEW || - rangeTableEntry->relkind == RELKIND_FOREIGN_TABLE) - { - /* Materialized view or Foreign table as target is not allowed */ - if (IsMergeAllowedOnRelation(parse, rangeTableEntry)) - { - /* Non target relation is ok */ - return NULL; - } - else - { - /* Usually we don't reach this exception as the Postgres parser catches it */ - StringInfo errorMessage = makeStringInfo(); - appendStringInfo(errorMessage, "MERGE command is not allowed on " - "relation type(relkind:%c)", - rangeTableEntry->relkind); - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - errorMessage->data, NULL, NULL); - } - } - - if (rangeTableEntry->relkind != RELKIND_RELATION && - rangeTableEntry->relkind != RELKIND_PARTITIONED_TABLE) - { - StringInfo errorMessage = makeStringInfo(); - appendStringInfo(errorMessage, "Unexpected table type(relkind:%c) " - "in MERGE command", rangeTableEntry->relkind); - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - errorMessage->data, NULL, NULL); - } - - Assert(rangeTableEntry->relid != 0); - - /* Reference tables are not supported yet */ - if (IsCitusTableType(rangeTableEntry->relid, REFERENCE_TABLE)) - { - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - "MERGE command is not supported on reference " - "tables yet", NULL, NULL); - } - - /* Append/Range tables are not supported */ - if (IsCitusTableType(rangeTableEntry->relid, APPEND_DISTRIBUTED) || - IsCitusTableType(rangeTableEntry->relid, RANGE_DISTRIBUTED)) - { - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - "For MERGE command, all the distributed tables " - "must be colocated, for append/range distribution, " - "colocation is not supported", NULL, - "Consider using hash distribution instead"); - } - - return NULL; -} - - /* * ErrorIfMergeHasUnsupportedTables checks if all the tables(target, source or any CTE * present) in the MERGE command are local i.e. a combination of Citus local and Non-Citus @@ -355,11 +263,12 @@ CheckIfRTETypeIsUnsupported(Query *parse, RangeTblEntry *rangeTableEntry) * for all other combinations. */ static DeferredErrorMessage * -ErrorIfMergeHasUnsupportedTables(Query *parse, List *rangeTableList, +ErrorIfMergeHasUnsupportedTables(Oid targetRelationId, Query *parse, List *rangeTableList, PlannerRestrictionContext *restrictionContext) { List *distTablesList = NIL; bool foundLocalTables = false; + bool foundReferenceTables = false; RangeTblEntry *rangeTableEntry = NULL; foreach_ptr(rangeTableEntry, rangeTableList) @@ -410,18 +319,48 @@ ErrorIfMergeHasUnsupportedTables(Query *parse, List *rangeTableList, } /* RTE Relation can be of various types, check them now */ - - /* skip the regular views as they are replaced with subqueries */ - if (rangeTableEntry->relkind == RELKIND_VIEW) + switch (rangeTableEntry->relkind) { - continue; - } + /* skip the regular views as they are replaced with subqueries */ + case RELKIND_VIEW: + { + continue; + } - DeferredErrorMessage *errorMessage = - CheckIfRTETypeIsUnsupported(parse, rangeTableEntry); - if (errorMessage) - { - return errorMessage; + case RELKIND_MATVIEW: + case RELKIND_FOREIGN_TABLE: + { + /* These two cases as a target is not allowed */ + if (relationId == targetRelationId) + { + /* Usually we don't reach this exception as the Postgres parser catches it */ + StringInfo errorMessage = makeStringInfo(); + appendStringInfo(errorMessage, "MERGE command is not allowed on " + "relation type(relkind:%c)", + rangeTableEntry->relkind); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + errorMessage->data, NULL, NULL); + } + break; + } + + case RELKIND_RELATION: + case RELKIND_PARTITIONED_TABLE: + { + /* Check for citus/postgres table types */ + Assert(OidIsValid(relationId)); + break; + } + + default: + { + StringInfo errorMessage = makeStringInfo(); + appendStringInfo(errorMessage, "Unexpected table type(relkind:%c) " + "in MERGE command", + rangeTableEntry->relkind); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + errorMessage->data, NULL, NULL); + } } /* @@ -430,28 +369,63 @@ ErrorIfMergeHasUnsupportedTables(Query *parse, List *rangeTableList, */ if (IsCitusTableType(relationId, DISTRIBUTED_TABLE)) { - distTablesList = lappend(distTablesList, rangeTableEntry); - continue; - } + /* Append/Range distributed tables are not supported */ + if (IsCitusTableType(relationId, APPEND_DISTRIBUTED) || + IsCitusTableType(relationId, RANGE_DISTRIBUTED)) + { + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "For MERGE command, all the distributed tables " + "must be colocated, for append/range distribution, " + "colocation is not supported", NULL, + "Consider using hash distribution instead"); + } - /* Regular Postgres tables and Citus local tables are allowed */ - if (!IsCitusTable(relationId) || - IsCitusTableType(relationId, CITUS_LOCAL_TABLE)) + distTablesList = lappend(distTablesList, rangeTableEntry); + } + else if (IsCitusTableType(relationId, REFERENCE_TABLE)) { + /* Reference table as a target is not allowed */ + if (relationId == targetRelationId) + { + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "Reference table as target " + "is not allowed in " + "MERGE command", NULL, NULL); + } + + foundReferenceTables = true; + } + else if (IsCitusTableType(relationId, CITUS_LOCAL_TABLE)) + { + /* Citus local tables */ + foundLocalTables = true; + } + else if (!IsCitusTable(relationId)) + { + /* Regular Postgres table */ foundLocalTables = true; - continue; } /* Any other Citus table type missing ? */ } - /* Ensure all tables are indeed local */ - if (foundLocalTables && list_length(distTablesList) == 0) + /* Ensure all tables are indeed local (or a combination of reference and local) */ + if (list_length(distTablesList) == 0) { - /* All the tables are local, supported */ + /* + * All the tables are local/reference, supported as long as + * coordinator is in the metadata. + */ + if (FindCoordinatorNodeId() == -1) + { + elog(ERROR, "Coordinator node is not in the metadata. TODO better meesage"); + } + + /* All the tables are local/reference, supported */ return NULL; } - else if (foundLocalTables && list_length(distTablesList) > 0) + + if (foundLocalTables) { return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, "MERGE command is not supported with " @@ -459,6 +433,17 @@ ErrorIfMergeHasUnsupportedTables(Query *parse, List *rangeTableList, NULL, NULL); } + if (foundReferenceTables) + { + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "MERGE command is not supported with " + "combination of distributed/reference yet", + NULL, + "If target is distributed, source " + "must be distributed and co-located"); + } + + /* Ensure all distributed tables are indeed co-located */ return ErrorIfDistTablesNotColocated(parse, distTablesList, @@ -515,11 +500,11 @@ IsDistributionColumnInMergeSource(Expr *columnExpression, Query *query, bool * prevent such mishaps, we disallow such inserts here. */ static DeferredErrorMessage * -InsertDistributionColumnMatchesSource(Query *query, RangeTblEntry *resultRte) +InsertDistributionColumnMatchesSource(Oid targetRelationId, Query *query) { Assert(IsMergeQuery(query)); - if (!IsCitusTableType(resultRte->relid, DISTRIBUTED_TABLE)) + if (!IsCitusTableType(targetRelationId, DISTRIBUTED_TABLE)) { return NULL; } @@ -549,7 +534,7 @@ InsertDistributionColumnMatchesSource(Query *query, RangeTblEntry *resultRte) } Assert(action->commandType == CMD_INSERT); - Var *targetKey = PartitionColumn(resultRte->relid, 1); + Var *targetKey = PartitionColumn(targetRelationId, 1); TargetEntry *targetEntry = NULL; foreach_ptr(targetEntry, action->targetList) @@ -736,3 +721,34 @@ MergeQualAndTargetListFunctionsSupported(Oid resultRelationId, FromExpr *joinTre #endif + + +/* + * IsLocalTableModification returns true if the table modified is a Postgres table. + * We do not support recursive planning for MERGE yet, so we could have a join + * between local and Citus tables. Only allow local tables when it is the target table. + */ +bool +IsLocalTableModification(Oid targetRelationId, Query *query, uint64 shardId, + RTEListProperties *rteProperties) +{ + /* No-op for SELECT command */ + if (!IsModifyCommand(query)) + { + return false; + } + + /* For MERGE, we have to check only the target relation */ + if (IsMergeQuery(query) && !IsCitusTable(targetRelationId)) + { + /* Postgres table */ + return true; + } + + if (shardId == INVALID_SHARD_ID && ContainsOnlyLocalTables(rteProperties)) + { + return true; + } + + return false; +} diff --git a/src/backend/distributed/planner/multi_router_planner.c b/src/backend/distributed/planner/multi_router_planner.c index 94691bab9..a95be74f8 100644 --- a/src/backend/distributed/planner/multi_router_planner.c +++ b/src/backend/distributed/planner/multi_router_planner.c @@ -2246,10 +2246,8 @@ SelectsFromDistributedTable(List *rangeTableList, Query *query) } -static bool ContainsOnlyLocalTables(RTEListProperties *rteProperties); - /* - * RouterQuery runs router pruning logic for SELECT, UPDATE and DELETE queries. + * RouterQuery runs router pruning logic for SELECT, UPDATE, DELETE, and MERGE queries. * If there are shards present and query is routable, all RTEs have been updated * to point to the relevant shards in the originalQuery. Also, placementList is * filled with the list of worker nodes that has all the required shard placements @@ -2282,6 +2280,7 @@ PlanRouterQuery(Query *originalQuery, DeferredErrorMessage *planningError = NULL; bool shardsPresent = false; CmdType commandType = originalQuery->commandType; + Oid targetRelationId = InvalidOid; bool fastPathRouterQuery = plannerRestrictionContext->fastPathRestrictionContext->fastPathRouterQuery; @@ -2350,7 +2349,8 @@ PlanRouterQuery(Query *originalQuery, if (IsMergeQuery(originalQuery)) { - planningError = MergeQuerySupported(originalQuery, + targetRelationId = ModifyQueryResultRelationId(originalQuery); + planningError = MergeQuerySupported(targetRelationId, originalQuery, isMultiShardQuery, plannerRestrictionContext); } @@ -2403,13 +2403,14 @@ PlanRouterQuery(Query *originalQuery, /* both Postgres tables and materialized tables are locally avaliable */ RTEListProperties *rteProperties = GetRTEListPropertiesForQuery(originalQuery); - if (shardId == INVALID_SHARD_ID && ContainsOnlyLocalTables(rteProperties)) + + if (isLocalTableModification) { - if (commandType != CMD_SELECT) - { - *isLocalTableModification = true; - } + *isLocalTableModification = + IsLocalTableModification(targetRelationId, originalQuery, shardId, + rteProperties); } + bool hasPostgresLocalRelation = rteProperties->hasPostgresLocalTable || rteProperties->hasMaterializedView; List *taskPlacementList = @@ -2447,7 +2448,7 @@ PlanRouterQuery(Query *originalQuery, * ContainsOnlyLocalTables returns true if there is only * local tables and not any distributed or reference table. */ -static bool +bool ContainsOnlyLocalTables(RTEListProperties *rteProperties) { return !rteProperties->hasDistributedTable && !rteProperties->hasReferenceTable; diff --git a/src/include/distributed/merge_planner.h b/src/include/distributed/merge_planner.h index 158f26861..b4ec1852f 100644 --- a/src/include/distributed/merge_planner.h +++ b/src/include/distributed/merge_planner.h @@ -19,13 +19,16 @@ #include "distributed/errormessage.h" #include "distributed/multi_physical_planner.h" -extern bool IsMergeAllowedOnRelation(Query *parse, RangeTblEntry *rte); -extern DeferredErrorMessage * MergeQuerySupported(Query *originalQuery, +extern DeferredErrorMessage * MergeQuerySupported(Oid resultRelationId, + Query *originalQuery, bool multiShardQuery, PlannerRestrictionContext * plannerRestrictionContext); extern DistributedPlan * CreateMergePlan(Query *originalQuery, Query *query, PlannerRestrictionContext * plannerRestrictionContext); +extern bool IsLocalTableModification(Oid targetRelationId, Query *query, + uint64 shardId, + RTEListProperties *rteProperties); #endif /* MERGE_PLANNER_H */ diff --git a/src/include/distributed/multi_router_planner.h b/src/include/distributed/multi_router_planner.h index 200c498ef..a255fd520 100644 --- a/src/include/distributed/multi_router_planner.h +++ b/src/include/distributed/multi_router_planner.h @@ -117,5 +117,6 @@ extern bool HasDangerousJoinUsing(List *rtableList, Node *jtnode); extern Job * RouterJob(Query *originalQuery, PlannerRestrictionContext *plannerRestrictionContext, DeferredErrorMessage **planningError); +extern bool ContainsOnlyLocalTables(RTEListProperties *rteProperties); #endif /* MULTI_ROUTER_PLANNER_H */ diff --git a/src/include/distributed/pg_dist_node_metadata.h b/src/include/distributed/pg_dist_node_metadata.h index f2bdc4801..00ccb9788 100644 --- a/src/include/distributed/pg_dist_node_metadata.h +++ b/src/include/distributed/pg_dist_node_metadata.h @@ -19,4 +19,6 @@ #define Natts_pg_dist_node_metadata 1 #define Anum_pg_dist_node_metadata_metadata 1 +extern int FindCoordinatorNodeId(void); + #endif /* PG_DIST_NODE_METADATA_H */ diff --git a/src/test/regress/expected/merge.out b/src/test/regress/expected/merge.out index 412667037..2196d966d 100644 --- a/src/test/regress/expected/merge.out +++ b/src/test/regress/expected/merge.out @@ -2416,9 +2416,134 @@ SELECT * FROM target_set ORDER BY 1, 2; 2 | (2 rows) +-- +-- Reference as a source +-- +CREATE TABLE reftarget_local(t1 int, t2 int); +CREATE TABLE refsource_ref(s1 int, s2 int); +INSERT INTO reftarget_local VALUES(1, 0); +INSERT INTO reftarget_local VALUES(3, 100); +INSERT INTO refsource_ref VALUES(1, 1); +INSERT INTO refsource_ref VALUES(2, 2); +INSERT INTO refsource_ref VALUES(3, 3); +MERGE INTO reftarget_local +USING (SELECT * FROM refsource_ref UNION SELECT * FROM refsource_ref) AS foo ON reftarget_local.t1 = foo.s1 +WHEN MATCHED AND reftarget_local.t2 = 100 THEN + DELETE +WHEN MATCHED THEN + UPDATE SET t2 = t2 + 100 +WHEN NOT MATCHED THEN + INSERT VALUES(foo.s1); +DROP TABLE IF EXISTS pg_result; +SELECT * INTO pg_result FROM reftarget_local ORDER BY 1, 2; +-- Make source table as reference (target is Postgres) +TRUNCATE reftarget_local; +TRUNCATE refsource_ref; +INSERT INTO reftarget_local VALUES(1, 0); +INSERT INTO reftarget_local VALUES(3, 100); +INSERT INTO refsource_ref VALUES(1, 1); +INSERT INTO refsource_ref VALUES(2, 2); +INSERT INTO refsource_ref VALUES(3, 3); +SELECT create_reference_table('refsource_ref'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_schema.refsource_ref$$) + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +MERGE INTO reftarget_local +USING (SELECT * FROM refsource_ref UNION SELECT * FROM refsource_ref) AS foo ON reftarget_local.t1 = foo.s1 +WHEN MATCHED AND reftarget_local.t2 = 100 THEN + DELETE +WHEN MATCHED THEN + UPDATE SET t2 = t2 + 100 +WHEN NOT MATCHED THEN + INSERT VALUES(foo.s1); +SELECT * INTO pg_ref FROM reftarget_local ORDER BY 1, 2; +-- Should be equal +SELECT c.*, p.* +FROM pg_ref c, pg_result p +WHERE c.t1 = p.t1 +ORDER BY 1,2; + t1 | t2 | t1 | t2 +--------------------------------------------------------------------- + 1 | 100 | 1 | 100 + 2 | | 2 | +(2 rows) + +-- Must return zero rows +SELECT count(*) +FROM pg_result FULL OUTER JOIN pg_ref ON pg_result.t1 = pg_ref.t1 +WHERE pg_result.t1 IS NULL OR pg_ref.t1 IS NULL; + count +--------------------------------------------------------------------- + 0 +(1 row) + +-- Now make both Citus tables, reference as source, local as target +TRUNCATE reftarget_local; +TRUNCATE refsource_ref; +INSERT INTO reftarget_local VALUES(1, 0); +INSERT INTO reftarget_local VALUES(3, 100); +INSERT INTO refsource_ref VALUES(1, 1); +INSERT INTO refsource_ref VALUES(2, 2); +INSERT INTO refsource_ref VALUES(3, 3); +SELECT citus_add_local_table_to_metadata('reftarget_local'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +MERGE INTO reftarget_local +USING (SELECT * FROM refsource_ref UNION SELECT * FROM refsource_ref) AS foo ON reftarget_local.t1 = foo.s1 +WHEN MATCHED AND reftarget_local.t2 = 100 THEN + DELETE +WHEN MATCHED THEN + UPDATE SET t2 = t2 + 100 +WHEN NOT MATCHED THEN + INSERT VALUES(foo.s1); +SELECT * INTO local_ref FROM reftarget_local ORDER BY 1, 2; +-- Should be equal +SELECT c.*, p.* +FROM local_ref c, pg_result p +WHERE c.t1 = p.t1 +ORDER BY 1,2; + t1 | t2 | t1 | t2 +--------------------------------------------------------------------- + 1 | 100 | 1 | 100 + 2 | | 2 | +(2 rows) + +-- Must return zero rows +SELECT count(*) +FROM pg_result FULL OUTER JOIN local_ref ON pg_result.t1 = local_ref.t1 +WHERE pg_result.t1 IS NULL OR local_ref.t1 IS NULL; + count +--------------------------------------------------------------------- + 0 +(1 row) + -- -- Error and Unsupported scenarios -- +-- Reference as a target and local as source +MERGE INTO refsource_ref +USING (SELECT * FROM reftarget_local UNION SELECT * FROM reftarget_local) AS foo ON refsource_ref.s1 = foo.t1 +WHEN MATCHED THEN + UPDATE SET s2 = s2 + 100 +WHEN NOT MATCHED THEN + INSERT VALUES(foo.t1); +ERROR: Reference table as target is not allowed in MERGE command +-- Reference as a source and distributed as target +MERGE INTO target_set t +USING refsource_ref AS s ON t.t1 = s.s1 +WHEN MATCHED THEN + DO NOTHING; +ERROR: MERGE command is not supported with combination of distributed/reference yet +HINT: If target is distributed, source must be distributed and co-located MERGE INTO target_set USING source_set AS foo ON target_set.t1 = foo.s1 WHEN MATCHED THEN @@ -2735,7 +2860,7 @@ MERGE INTO t1 UPDATE SET val = t1.val + 1 WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s1.id, s1.val); -ERROR: MERGE command is not supported on reference tables yet +ERROR: Reference table as target is not allowed in MERGE command -- -- Postgres + Citus-Distributed table -- @@ -3113,9 +3238,8 @@ CONTEXT: SQL statement "SELECT citus_drop_all_shards(v_obj.objid, v_obj.schema_ PL/pgSQL function citus_drop_trigger() line XX at PERFORM DROP FUNCTION merge_when_and_write(); DROP SCHEMA merge_schema CASCADE; -NOTICE: drop cascades to 84 other objects +NOTICE: drop cascades to 90 other objects DETAIL: drop cascades to function insert_data() -drop cascades to table pg_result drop cascades to table local_local drop cascades to table target drop cascades to table source @@ -3188,10 +3312,17 @@ drop cascades to table source_serial drop cascades to table target_serial drop cascades to table target_set drop cascades to table source_set +drop cascades to table reftarget_local_4000113 +drop cascades to table refsource_ref +drop cascades to table pg_result +drop cascades to table refsource_ref_4000112 +drop cascades to table pg_ref +drop cascades to table reftarget_local +drop cascades to table local_ref drop cascades to function add_s(integer,integer) drop cascades to table pg -drop cascades to table t1_4000131 -drop cascades to table s1_4000132 +drop cascades to table t1_4000133 +drop cascades to table s1_4000134 drop cascades to table t1 drop cascades to table s1 drop cascades to table dist_colocated diff --git a/src/test/regress/expected/pg15.out b/src/test/regress/expected/pg15.out index 7fc102dbb..4d1040a7e 100644 --- a/src/test/regress/expected/pg15.out +++ b/src/test/regress/expected/pg15.out @@ -306,7 +306,7 @@ SELECT citus_add_local_table_to_metadata('tbl2'); MERGE INTO tbl1 USING tbl2 ON (true) WHEN MATCHED THEN DELETE; --- one table is reference, the other local, not supported +-- source table is reference, the target is local, supported SELECT create_reference_table('tbl2'); create_reference_table --------------------------------------------------------------------- @@ -315,8 +315,7 @@ SELECT create_reference_table('tbl2'); MERGE INTO tbl1 USING tbl2 ON (true) WHEN MATCHED THEN DELETE; -ERROR: MERGE command is not supported on reference tables yet --- now, both are reference, still not supported +-- now, both are reference, not supported SELECT create_reference_table('tbl1'); create_reference_table --------------------------------------------------------------------- @@ -325,7 +324,7 @@ SELECT create_reference_table('tbl1'); MERGE INTO tbl1 USING tbl2 ON (true) WHEN MATCHED THEN DELETE; -ERROR: MERGE command is not supported on reference tables yet +ERROR: Reference table as target is not allowed in MERGE command -- now, both distributed, not works SELECT undistribute_table('tbl1'); NOTICE: creating a new table for pg15.tbl1 diff --git a/src/test/regress/sql/merge.sql b/src/test/regress/sql/merge.sql index d663491ae..5b9190516 100644 --- a/src/test/regress/sql/merge.sql +++ b/src/test/regress/sql/merge.sql @@ -1536,10 +1536,111 @@ WHEN NOT MATCHED THEN INSERT VALUES(foo.s1); SELECT * FROM target_set ORDER BY 1, 2; +-- +-- Reference as a source +-- +CREATE TABLE reftarget_local(t1 int, t2 int); +CREATE TABLE refsource_ref(s1 int, s2 int); + +INSERT INTO reftarget_local VALUES(1, 0); +INSERT INTO reftarget_local VALUES(3, 100); +INSERT INTO refsource_ref VALUES(1, 1); +INSERT INTO refsource_ref VALUES(2, 2); +INSERT INTO refsource_ref VALUES(3, 3); + +MERGE INTO reftarget_local +USING (SELECT * FROM refsource_ref UNION SELECT * FROM refsource_ref) AS foo ON reftarget_local.t1 = foo.s1 +WHEN MATCHED AND reftarget_local.t2 = 100 THEN + DELETE +WHEN MATCHED THEN + UPDATE SET t2 = t2 + 100 +WHEN NOT MATCHED THEN + INSERT VALUES(foo.s1); + +DROP TABLE IF EXISTS pg_result; +SELECT * INTO pg_result FROM reftarget_local ORDER BY 1, 2; + +-- Make source table as reference (target is Postgres) +TRUNCATE reftarget_local; +TRUNCATE refsource_ref; +INSERT INTO reftarget_local VALUES(1, 0); +INSERT INTO reftarget_local VALUES(3, 100); +INSERT INTO refsource_ref VALUES(1, 1); +INSERT INTO refsource_ref VALUES(2, 2); +INSERT INTO refsource_ref VALUES(3, 3); +SELECT create_reference_table('refsource_ref'); + +MERGE INTO reftarget_local +USING (SELECT * FROM refsource_ref UNION SELECT * FROM refsource_ref) AS foo ON reftarget_local.t1 = foo.s1 +WHEN MATCHED AND reftarget_local.t2 = 100 THEN + DELETE +WHEN MATCHED THEN + UPDATE SET t2 = t2 + 100 +WHEN NOT MATCHED THEN + INSERT VALUES(foo.s1); +SELECT * INTO pg_ref FROM reftarget_local ORDER BY 1, 2; + +-- Should be equal +SELECT c.*, p.* +FROM pg_ref c, pg_result p +WHERE c.t1 = p.t1 +ORDER BY 1,2; + +-- Must return zero rows +SELECT count(*) +FROM pg_result FULL OUTER JOIN pg_ref ON pg_result.t1 = pg_ref.t1 +WHERE pg_result.t1 IS NULL OR pg_ref.t1 IS NULL; + +-- Now make both Citus tables, reference as source, local as target +TRUNCATE reftarget_local; +TRUNCATE refsource_ref; +INSERT INTO reftarget_local VALUES(1, 0); +INSERT INTO reftarget_local VALUES(3, 100); +INSERT INTO refsource_ref VALUES(1, 1); +INSERT INTO refsource_ref VALUES(2, 2); +INSERT INTO refsource_ref VALUES(3, 3); + +SELECT citus_add_local_table_to_metadata('reftarget_local'); + +MERGE INTO reftarget_local +USING (SELECT * FROM refsource_ref UNION SELECT * FROM refsource_ref) AS foo ON reftarget_local.t1 = foo.s1 +WHEN MATCHED AND reftarget_local.t2 = 100 THEN + DELETE +WHEN MATCHED THEN + UPDATE SET t2 = t2 + 100 +WHEN NOT MATCHED THEN + INSERT VALUES(foo.s1); +SELECT * INTO local_ref FROM reftarget_local ORDER BY 1, 2; + +-- Should be equal +SELECT c.*, p.* +FROM local_ref c, pg_result p +WHERE c.t1 = p.t1 +ORDER BY 1,2; + +-- Must return zero rows +SELECT count(*) +FROM pg_result FULL OUTER JOIN local_ref ON pg_result.t1 = local_ref.t1 +WHERE pg_result.t1 IS NULL OR local_ref.t1 IS NULL; + -- -- Error and Unsupported scenarios -- +-- Reference as a target and local as source +MERGE INTO refsource_ref +USING (SELECT * FROM reftarget_local UNION SELECT * FROM reftarget_local) AS foo ON refsource_ref.s1 = foo.t1 +WHEN MATCHED THEN + UPDATE SET s2 = s2 + 100 +WHEN NOT MATCHED THEN + INSERT VALUES(foo.t1); + +-- Reference as a source and distributed as target +MERGE INTO target_set t +USING refsource_ref AS s ON t.t1 = s.s1 +WHEN MATCHED THEN + DO NOTHING; + MERGE INTO target_set USING source_set AS foo ON target_set.t1 = foo.s1 WHEN MATCHED THEN diff --git a/src/test/regress/sql/pg15.sql b/src/test/regress/sql/pg15.sql index ac8062c65..b82b0d745 100644 --- a/src/test/regress/sql/pg15.sql +++ b/src/test/regress/sql/pg15.sql @@ -198,13 +198,13 @@ SELECT citus_add_local_table_to_metadata('tbl2'); MERGE INTO tbl1 USING tbl2 ON (true) WHEN MATCHED THEN DELETE; --- one table is reference, the other local, not supported +-- source table is reference, the target is local, supported SELECT create_reference_table('tbl2'); MERGE INTO tbl1 USING tbl2 ON (true) WHEN MATCHED THEN DELETE; --- now, both are reference, still not supported +-- now, both are reference, not supported SELECT create_reference_table('tbl1'); MERGE INTO tbl1 USING tbl2 ON (true) From 2d005ac77794157d7cc1da8a9cd191f00502cc64 Mon Sep 17 00:00:00 2001 From: aykut-bozkurt <51649454+aykut-bozkurt@users.noreply.github.com> Date: Wed, 3 May 2023 15:54:11 +0300 Subject: [PATCH 024/118] Query Generator Seed (#6883) - Give seed number as argument to query generator to reproduce a previous run. - Expose the difference between results, if any, as artifact on CI. --- .circleci/config.yml | 3 ++ src/test/regress/Makefile | 2 +- .../citus_tests/query_generator/README.md | 4 ++- .../bin/citus_compare_dist_local_joins.sh | 3 +- .../bin/run_query_compare_test.py | 19 ++++++----- .../query_generator/config/config.py | 5 ++- .../citus_tests/query_generator/data_gen.py | 1 + .../query_generator/generate_queries.py | 33 +++++++++++++++++-- .../query_generator/random_selections.py | 6 ++++ 9 files changed, 62 insertions(+), 14 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1f97c5635..1a73a4b96 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -474,6 +474,9 @@ jobs: - store_artifacts: name: 'Save dmls' path: src/test/regress/citus_tests/query_generator/out/queries.sql + - store_artifacts: + name: 'Save diffs' + path: src/test/regress/citus_tests/query_generator/out/local_dist.diffs - stack_trace - coverage: flags: 'test_<< parameters.pg_major >>,querygen' diff --git a/src/test/regress/Makefile b/src/test/regress/Makefile index 04c38033d..51d6fe86a 100644 --- a/src/test/regress/Makefile +++ b/src/test/regress/Makefile @@ -264,7 +264,7 @@ check-pytest: pytest -n auto check-query-generator: all - ${query_generator_check} --bindir=$(bindir) --pgxsdir=$(pgxsdir) + ${query_generator_check} --bindir=$(bindir) --pgxsdir=$(pgxsdir) --seed=$(seed) check-citus-upgrade: all $(citus_upgrade_check) \ diff --git a/src/test/regress/citus_tests/query_generator/README.md b/src/test/regress/citus_tests/query_generator/README.md index cf4d54b0e..b35a96c0a 100644 --- a/src/test/regress/citus_tests/query_generator/README.md +++ b/src/test/regress/citus_tests/query_generator/README.md @@ -20,10 +20,12 @@ citus_dev make testCluster --destroy 2. Run the test, ```bash cd src/test/regress/citus_tests/query_generator/bin -bash citus_compare_dist_local_joins.sh +bash citus_compare_dist_local_joins.sh Optional: ``` 3. See the diff content in `src/test/regress/citus_tests/query_generator/out/local_dist_diffs` +Note: `seed` can be used to reproduce a run of Citus test by generating the same queries and results via the given seed. + ### Configuration You can configure 3 different parts: diff --git a/src/test/regress/citus_tests/query_generator/bin/citus_compare_dist_local_joins.sh b/src/test/regress/citus_tests/query_generator/bin/citus_compare_dist_local_joins.sh index f921bec0b..c5a97b80c 100644 --- a/src/test/regress/citus_tests/query_generator/bin/citus_compare_dist_local_joins.sh +++ b/src/test/regress/citus_tests/query_generator/bin/citus_compare_dist_local_joins.sh @@ -6,6 +6,7 @@ set -euo pipefail psql_user=$1 psql_db=$2 psql_port=$3 +seed=${4:-""} runDDLs() { @@ -39,7 +40,7 @@ showDiffs() # run query generator and let it create output ddls and queries script_folder=$(dirname "$0") out_folder="${script_folder}"/../out -pushd . && cd "${script_folder}"/.. && python3 generate_queries.py && popd +pushd . && cd "${script_folder}"/.. && python3 generate_queries.py --seed="${seed}" && popd # remove result files if exists rm -rf "${out_folder}"/dist_queries.out "${out_folder}"/local_queries.out diff --git a/src/test/regress/citus_tests/query_generator/bin/run_query_compare_test.py b/src/test/regress/citus_tests/query_generator/bin/run_query_compare_test.py index 9ab172ef7..dc2f8808c 100755 --- a/src/test/regress/citus_tests/query_generator/bin/run_query_compare_test.py +++ b/src/test/regress/citus_tests/query_generator/bin/run_query_compare_test.py @@ -2,11 +2,12 @@ """query_gen_test Usage: - run_query_compare_test --bindir= --pgxsdir= + run_query_compare_test --bindir= --pgxsdir= --seed= Options: --bindir= PostgreSQL executable directory(ex: '~/.pgenv/pgsql-10.4/bin') --pgxsdir= Path to the PGXS directory(ex: ~/.pgenv/src/postgresql-11.3) + --seed= Seed number used by the query generator.(ex: 123) """ import os @@ -27,7 +28,7 @@ import common # noqa: E402 import config as cfg # noqa: E402 -def run_test(config): +def run_test(config, seed): # start cluster common.initialize_temp_dir(cfg.CITUS_ARBITRARY_TEST_DIR) common.initialize_citus_cluster( @@ -36,8 +37,8 @@ def run_test(config): # run test scriptDirPath = os.path.dirname(os.path.abspath(__file__)) - testRunCommand = "bash {}/citus_compare_dist_local_joins.sh {} {} {}".format( - scriptDirPath, config.user, config.dbname, config.coordinator_port() + testRunCommand = "bash {}/citus_compare_dist_local_joins.sh {} {} {} {}".format( + scriptDirPath, config.user, config.dbname, config.coordinator_port(), seed ) process = subprocess.Popen( testRunCommand.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE @@ -56,8 +57,10 @@ def run_test(config): if __name__ == "__main__": - citusClusterConfig = cfg.CitusSuperUserDefaultClusterConfig( - docopt(__doc__, version="run_query_compare_test") - ) + arguments = docopt(__doc__, version="run_query_compare_test") + citusClusterConfig = cfg.CitusSuperUserDefaultClusterConfig(arguments) - run_test(citusClusterConfig) + seed = "" + if "--seed" in arguments and arguments["--seed"] != "": + seed = arguments["--seed"] + run_test(citusClusterConfig, seed) diff --git a/src/test/regress/citus_tests/query_generator/config/config.py b/src/test/regress/citus_tests/query_generator/config/config.py index 85def6b79..fec93ef19 100644 --- a/src/test/regress/citus_tests/query_generator/config/config.py +++ b/src/test/regress/citus_tests/query_generator/config/config.py @@ -1,4 +1,5 @@ import copy +import os import yaml from config.config_parser import ( @@ -13,7 +14,9 @@ from node_defs import CitusType class Config: def __init__(self): - configObj = Config.parseConfigFile("config/config.yaml") + configObj = Config.parseConfigFile( + f"{os.path.dirname(os.path.abspath(__file__))}/config.yaml" + ) self.targetTables = _distinctCopyTables( parseTableArray(configObj["targetTables"]) diff --git a/src/test/regress/citus_tests/query_generator/data_gen.py b/src/test/regress/citus_tests/query_generator/data_gen.py index 1d508acaf..96f7a1366 100644 --- a/src/test/regress/citus_tests/query_generator/data_gen.py +++ b/src/test/regress/citus_tests/query_generator/data_gen.py @@ -12,6 +12,7 @@ def getTableData(): for table in tables: # generate base rows dataGenerationSql += _genOverlappingData(table.name, fromVal, table.rowCount) + dataGenerationSql += "\n" dataGenerationSql += _genNonOverlappingData(table.name, toVal, tableIdx) dataGenerationSql += "\n" diff --git a/src/test/regress/citus_tests/query_generator/generate_queries.py b/src/test/regress/citus_tests/query_generator/generate_queries.py index 4f1f7967b..dd63b17ec 100755 --- a/src/test/regress/citus_tests/query_generator/generate_queries.py +++ b/src/test/regress/citus_tests/query_generator/generate_queries.py @@ -1,9 +1,23 @@ +#!/usr/bin/env python3 + +"""generate_queries +Usage: + generate_queries --seed= + +Options: + --seed= Seed number used by the query generator.(ex: 123) +""" + +import os +import random import signal import sys from data_gen import getTableData from ddl_gen import getTableDDLs +from docopt import docopt from query_gen import newQuery +from random_selections import currentMilliSecs from config.config import getConfig, resetConfig @@ -29,12 +43,16 @@ def _interactiveMode(ddls, data): def _fileMode(ddls, data): - ddlFileName = "out/" + getConfig().ddlOutFile + ddlFileName = ( + f"{os.path.dirname(os.path.abspath(__file__))}/out/{getConfig().ddlOutFile}" + ) with open(ddlFileName, "w") as ddlFile: ddlFile.writelines([ddls, data]) queryCount = getConfig().queryCount - fileName = "out/" + getConfig().queryOutFile + fileName = ( + f"{os.path.dirname(os.path.abspath(__file__))}/out/{getConfig().queryOutFile}" + ) with open(fileName, "w") as f: # enable repartition joins due to https://github.com/citusdata/citus/issues/6865 enableRepartitionJoinCommand = "SET citus.enable_repartition_joins TO on;\n" @@ -54,6 +72,17 @@ def _fileMode(ddls, data): if __name__ == "__main__": signal.signal(signal.SIGINT, _signal_handler) + arguments = docopt(__doc__, version="generate_queries") + seed = -1 + if "--seed" in arguments and arguments["--seed"] != "": + seed = int(arguments["--seed"]) + else: + seed = currentMilliSecs() + assert seed > 0 + + random.seed(seed) + print(f"---SEED: {seed} ---") + resetConfig() ddls = getTableDDLs() diff --git a/src/test/regress/citus_tests/query_generator/random_selections.py b/src/test/regress/citus_tests/query_generator/random_selections.py index 188107753..ee32c620a 100644 --- a/src/test/regress/citus_tests/query_generator/random_selections.py +++ b/src/test/regress/citus_tests/query_generator/random_selections.py @@ -1,4 +1,5 @@ import random +import time from node_defs import RestrictOp @@ -10,6 +11,11 @@ def shouldSelectThatBranch(): return random.randint(0, 1) +def currentMilliSecs(): + """returns total milliseconds since epoch""" + return round(time.time() * 1000) + + def randomRteType(): """returns a randomly selected RteType given at config""" rtes = getConfig().targetRteTypes From fa467e05e79cca5d07a4ae359f825d0c8ac17b6f Mon Sep 17 00:00:00 2001 From: Onur Tirtir Date: Fri, 17 Mar 2023 14:17:36 +0300 Subject: [PATCH 025/118] Add support for creating distributed tables with a null shard key (#6745) With this PR, we allow creating distributed tables with without specifying a shard key via create_distributed_table(). Here are the the important details about those tables: * Specifying `shard_count` is not allowed because it is assumed to be 1. * We mostly call such tables as "null shard-key" table in code / comments. * To avoid doing a breaking layout change in create_distributed_table(); instead of throwing an error, it will inform the user that `distribution_type` param is ignored unless it's explicitly set to NULL or 'h'. * `colocate_with` param allows colocating such null shard-key tables to each other. * We define this table type, i.e., NULL_SHARD_KEY_TABLE, as a subclass of DISTRIBUTED_TABLE because we mostly want to treat them as distributed tables in terms of SQL / DDL / operation support. * Metadata for such tables look like: - distribution method => DISTRIBUTE_BY_NONE - replication model => REPLICATION_MODEL_STREAMING - colocation id => **!=** INVALID_COLOCATION_ID (distinguishes from Citus local tables) * We assign colocation groups for such tables to different nodes in a round-robin fashion based on the modulo of "colocation id". Note that this PR doesn't care about DDL (except CREATE TABLE) / SQL / operation (i.e., Citus UDFs) support for such tables but adds a preliminary API. --- .../commands/create_distributed_table.c | 195 ++- .../distributed/commands/foreign_constraint.c | 12 +- src/backend/distributed/commands/multi_copy.c | 1 + src/backend/distributed/commands/truncate.c | 3 +- .../distributed/metadata/metadata_cache.c | 27 +- .../distributed/metadata/metadata_sync.c | 11 +- .../distributed/operations/create_shards.c | 88 +- .../distributed/operations/stage_protocol.c | 3 +- .../planner/insert_select_planner.c | 7 + .../planner/multi_physical_planner.c | 2 +- .../planner/multi_router_planner.c | 10 +- .../transaction/relation_access_tracking.c | 8 +- .../distributed/utils/colocation_utils.c | 16 +- .../distributed/utils/distribution_column.c | 2 +- .../distributed/utils/shardinterval_utils.c | 7 +- .../distributed/coordinator_protocol.h | 1 + src/include/distributed/metadata_cache.h | 3 + src/test/regress/citus_tests/run_test.py | 1 + .../regress/expected/create_null_dist_key.out | 1432 +++++++++++++++++ .../expected/multi_colocation_utils.out | 4 +- src/test/regress/expected/multi_extension.out | 27 + src/test/regress/expected/single_node.out | 57 +- src/test/regress/expected/single_node_0.out | 57 +- src/test/regress/multi_1_schedule | 1 + src/test/regress/sql/create_null_dist_key.sql | 962 +++++++++++ src/test/regress/sql/multi_extension.sql | 14 + src/test/regress/sql/single_node.sql | 37 +- 27 files changed, 2911 insertions(+), 77 deletions(-) create mode 100644 src/test/regress/expected/create_null_dist_key.out create mode 100644 src/test/regress/sql/create_null_dist_key.sql diff --git a/src/backend/distributed/commands/create_distributed_table.c b/src/backend/distributed/commands/create_distributed_table.c index 7e907c8a8..12bfcf9a5 100644 --- a/src/backend/distributed/commands/create_distributed_table.c +++ b/src/backend/distributed/commands/create_distributed_table.c @@ -134,6 +134,7 @@ static List * HashSplitPointsForShardList(List *shardList); static List * HashSplitPointsForShardCount(int shardCount); static List * WorkerNodesForShardList(List *shardList); static List * RoundRobinWorkerNodeList(List *workerNodeList, int listLength); +static void CreateNullShardKeyDistTable(Oid relationId, char *colocateWithTableName); static CitusTableParams DecideCitusTableParams(CitusTableType tableType, DistributedTableParams * distributedTableParams); @@ -141,6 +142,8 @@ static void CreateCitusTable(Oid relationId, CitusTableType tableType, DistributedTableParams *distributedTableParams); static void CreateHashDistributedTableShards(Oid relationId, int shardCount, Oid colocatedTableId, bool localTableEmpty); +static void CreateNullShardKeyDistTableShard(Oid relationId, Oid colocatedTableId, + uint32 colocationId); static uint32 ColocationIdForNewTable(Oid relationId, CitusTableType tableType, DistributedTableParams *distributedTableParams, Var *distributionColumn); @@ -216,51 +219,86 @@ create_distributed_table(PG_FUNCTION_ARGS) { CheckCitusVersion(ERROR); - if (PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2) || PG_ARGISNULL(3)) + if (PG_ARGISNULL(0) || PG_ARGISNULL(3)) { PG_RETURN_VOID(); } Oid relationId = PG_GETARG_OID(0); - text *distributionColumnText = PG_GETARG_TEXT_P(1); + text *distributionColumnText = PG_ARGISNULL(1) ? NULL : PG_GETARG_TEXT_P(1); Oid distributionMethodOid = PG_GETARG_OID(2); text *colocateWithTableNameText = PG_GETARG_TEXT_P(3); char *colocateWithTableName = text_to_cstring(colocateWithTableNameText); bool shardCountIsStrict = false; - int shardCount = ShardCount; - if (!PG_ARGISNULL(4)) + if (distributionColumnText) { - if (pg_strncasecmp(colocateWithTableName, "default", NAMEDATALEN) != 0 && - pg_strncasecmp(colocateWithTableName, "none", NAMEDATALEN) != 0) + if (PG_ARGISNULL(2)) { - ereport(ERROR, (errmsg("Cannot use colocate_with with a table " - "and shard_count at the same time"))); + PG_RETURN_VOID(); } - shardCount = PG_GETARG_INT32(4); + int shardCount = ShardCount; + if (!PG_ARGISNULL(4)) + { + if (!IsColocateWithDefault(colocateWithTableName) && + !IsColocateWithNone(colocateWithTableName)) + { + ereport(ERROR, (errmsg("Cannot use colocate_with with a table " + "and shard_count at the same time"))); + } - /* - * if shard_count parameter is given than we have to - * make sure table has that many shards - */ - shardCountIsStrict = true; + shardCount = PG_GETARG_INT32(4); + + /* + * If shard_count parameter is given, then we have to + * make sure table has that many shards. + */ + shardCountIsStrict = true; + } + + char *distributionColumnName = text_to_cstring(distributionColumnText); + Assert(distributionColumnName != NULL); + + char distributionMethod = LookupDistributionMethod(distributionMethodOid); + + if (shardCount < 1 || shardCount > MAX_SHARD_COUNT) + { + ereport(ERROR, (errmsg("%d is outside the valid range for " + "parameter \"shard_count\" (1 .. %d)", + shardCount, MAX_SHARD_COUNT))); + } + + CreateDistributedTable(relationId, distributionColumnName, distributionMethod, + shardCount, shardCountIsStrict, colocateWithTableName); } - - char *distributionColumnName = text_to_cstring(distributionColumnText); - Assert(distributionColumnName != NULL); - - char distributionMethod = LookupDistributionMethod(distributionMethodOid); - - if (shardCount < 1 || shardCount > MAX_SHARD_COUNT) + else { - ereport(ERROR, (errmsg("%d is outside the valid range for " - "parameter \"shard_count\" (1 .. %d)", - shardCount, MAX_SHARD_COUNT))); - } + if (!PG_ARGISNULL(4)) + { + ereport(ERROR, (errmsg("shard_count can't be specified when the " + "distribution column is null because in " + "that case it's automatically set to 1"))); + } - CreateDistributedTable(relationId, distributionColumnName, distributionMethod, - shardCount, shardCountIsStrict, colocateWithTableName); + if (!PG_ARGISNULL(2) && + LookupDistributionMethod(PG_GETARG_OID(2)) != DISTRIBUTE_BY_HASH) + { + /* + * As we do for shard_count parameter, we could throw an error if + * distribution_type is not NULL when creating a null-shard-key table. + * However, this requires changing the default value of distribution_type + * parameter to NULL and this would mean a breaking change for most + * users because they're mostly using this API to create sharded + * tables. For this reason, here we instead do nothing if the distribution + * method is DISTRIBUTE_BY_HASH. + */ + ereport(ERROR, (errmsg("distribution_type can't be specified " + "when the distribution column is null "))); + } + + CreateNullShardKeyDistTable(relationId, colocateWithTableName); + } PG_RETURN_VOID(); } @@ -276,11 +314,18 @@ create_distributed_table_concurrently(PG_FUNCTION_ARGS) { CheckCitusVersion(ERROR); - if (PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2) || PG_ARGISNULL(3)) + if (PG_ARGISNULL(0) || PG_ARGISNULL(2) || PG_ARGISNULL(3)) { PG_RETURN_VOID(); } + if (PG_ARGISNULL(1)) + { + ereport(ERROR, (errmsg("cannot use create_distributed_table_concurrently " + "to create a distributed table with a null shard " + "key, consider using create_distributed_table()"))); + } + Oid relationId = PG_GETARG_OID(0); text *distributionColumnText = PG_GETARG_TEXT_P(1); char *distributionColumnName = text_to_cstring(distributionColumnText); @@ -982,6 +1027,23 @@ CreateReferenceTable(Oid relationId) } +/* + * CreateNullShardKeyDistTable is a wrapper around CreateCitusTable that creates a + * single shard distributed table that doesn't have a shard key. + */ +static void +CreateNullShardKeyDistTable(Oid relationId, char *colocateWithTableName) +{ + DistributedTableParams distributedTableParams = { + .colocateWithTableName = colocateWithTableName, + .shardCount = 1, + .shardCountIsStrict = true, + .distributionColumnName = NULL + }; + CreateCitusTable(relationId, NULL_KEY_DISTRIBUTED_TABLE, &distributedTableParams); +} + + /* * CreateCitusTable is the internal method that creates a Citus table in * given configuration. @@ -1000,7 +1062,8 @@ CreateCitusTable(Oid relationId, CitusTableType tableType, DistributedTableParams *distributedTableParams) { if ((tableType == HASH_DISTRIBUTED || tableType == APPEND_DISTRIBUTED || - tableType == RANGE_DISTRIBUTED) != (distributedTableParams != NULL)) + tableType == RANGE_DISTRIBUTED || tableType == NULL_KEY_DISTRIBUTED_TABLE) != + (distributedTableParams != NULL)) { ereport(ERROR, (errmsg("distributed table params must be provided " "when creating a distributed table and must " @@ -1078,7 +1141,7 @@ CreateCitusTable(Oid relationId, CitusTableType tableType, PropagatePrerequisiteObjectsForDistributedTable(relationId); Var *distributionColumn = NULL; - if (distributedTableParams) + if (distributedTableParams && distributedTableParams->distributionColumnName) { distributionColumn = BuildDistributionKeyFromColumnName(relationId, distributedTableParams-> @@ -1150,6 +1213,11 @@ CreateCitusTable(Oid relationId, CitusTableType tableType, { CreateReferenceTableShard(relationId); } + else if (tableType == NULL_KEY_DISTRIBUTED_TABLE) + { + CreateNullShardKeyDistTableShard(relationId, colocatedTableId, + colocationId); + } if (ShouldSyncTableMetadata(relationId)) { @@ -1204,7 +1272,8 @@ CreateCitusTable(Oid relationId, CitusTableType tableType, } /* copy over data for hash distributed and reference tables */ - if (tableType == HASH_DISTRIBUTED || tableType == REFERENCE_TABLE) + if (tableType == HASH_DISTRIBUTED || tableType == NULL_KEY_DISTRIBUTED_TABLE || + tableType == REFERENCE_TABLE) { if (RegularTable(relationId)) { @@ -1268,6 +1337,13 @@ DecideCitusTableParams(CitusTableType tableType, break; } + case NULL_KEY_DISTRIBUTED_TABLE: + { + citusTableParams.distributionMethod = DISTRIBUTE_BY_NONE; + citusTableParams.replicationModel = REPLICATION_MODEL_STREAMING; + break; + } + case REFERENCE_TABLE: { citusTableParams.distributionMethod = DISTRIBUTE_BY_NONE; @@ -1630,6 +1706,41 @@ CreateHashDistributedTableShards(Oid relationId, int shardCount, } +/* + * CreateHashDistributedTableShards creates the shard of given null-shard-key + * distributed table. + */ +static void +CreateNullShardKeyDistTableShard(Oid relationId, Oid colocatedTableId, + uint32 colocationId) +{ + if (colocatedTableId != InvalidOid) + { + /* + * We currently allow concurrent distribution of colocated tables (which + * we probably should not be allowing because of foreign keys / + * partitioning etc). + * + * We also prevent concurrent shard moves / copy / splits) while creating + * a colocated table. + */ + AcquirePlacementColocationLock(colocatedTableId, ShareLock, + "colocate distributed table"); + + /* + * We don't need to force using exclusive connections because we're anyway + * creating a single shard. + */ + bool useExclusiveConnection = false; + CreateColocatedShards(relationId, colocatedTableId, useExclusiveConnection); + } + else + { + CreateNullKeyShardWithRoundRobinPolicy(relationId, colocationId); + } +} + + /* * ColocationIdForNewTable returns a colocation id for given table * according to given configuration. If there is no such configuration, it @@ -1662,8 +1773,8 @@ ColocationIdForNewTable(Oid relationId, CitusTableType tableType, { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot distribute relation"), - errdetail("Currently, colocate_with option is only supported " - "for hash distributed tables."))); + errdetail("Currently, colocate_with option is not supported " + "for append / range distributed tables."))); } return colocationId; @@ -1679,10 +1790,11 @@ ColocationIdForNewTable(Oid relationId, CitusTableType tableType, * can be sure that there will no modifications on the colocation table * until this transaction is committed. */ - Assert(citusTableParams.distributionMethod == DISTRIBUTE_BY_HASH); - Oid distributionColumnType = distributionColumn->vartype; - Oid distributionColumnCollation = get_typcollation(distributionColumnType); + Oid distributionColumnType = + distributionColumn ? distributionColumn->vartype : InvalidOid; + Oid distributionColumnCollation = + distributionColumn ? get_typcollation(distributionColumnType) : InvalidOid; /* get an advisory lock to serialize concurrent default group creations */ if (IsColocateWithDefault(distributedTableParams->colocateWithTableName)) @@ -1871,8 +1983,15 @@ EnsureRelationCanBeDistributed(Oid relationId, Var *distributionColumn, */ if (PartitionedTableNoLock(relationId)) { - /* distributing partitioned tables in only supported for hash-distribution */ - if (distributionMethod != DISTRIBUTE_BY_HASH) + /* + * Distributing partitioned tables is only supported for hash-distribution + * or null-shard-key tables. + */ + bool isNullShardKeyTable = + distributionMethod == DISTRIBUTE_BY_NONE && + replicationModel == REPLICATION_MODEL_STREAMING && + colocationId != INVALID_COLOCATION_ID; + if (distributionMethod != DISTRIBUTE_BY_HASH && !isNullShardKeyTable) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("distributing partitioned tables in only supported " diff --git a/src/backend/distributed/commands/foreign_constraint.c b/src/backend/distributed/commands/foreign_constraint.c index 6f12db13f..0b2c5573e 100644 --- a/src/backend/distributed/commands/foreign_constraint.c +++ b/src/backend/distributed/commands/foreign_constraint.c @@ -303,6 +303,11 @@ ErrorIfUnsupportedForeignConstraintExists(Relation relation, char referencingDis /* * Foreign keys from citus local tables or reference tables to distributed * tables are not supported. + * + * We could support foreign keys from references tables to null-shard-key + * tables but this doesn't seem useful a lot. However, if we decide supporting + * this, then we need to expand relation access tracking check for the null-shard-key + * tables too. */ if (referencingIsCitusLocalOrRefTable && !referencedIsCitusLocalOrRefTable) { @@ -361,7 +366,12 @@ ErrorIfUnsupportedForeignConstraintExists(Relation relation, char referencingDis * if tables are hash-distributed and colocated, we need to make sure that * the distribution key is included in foreign constraint. */ - if (!referencedIsCitusLocalOrRefTable && !foreignConstraintOnDistKey) + bool referencedIsNullShardKeyTable = + IsNullShardKeyTableByDistParams(referencedDistMethod, + referencedReplicationModel, + referencedColocationId); + if (!referencedIsCitusLocalOrRefTable && !referencedIsNullShardKeyTable && + !foreignConstraintOnDistKey) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot create foreign key constraint"), diff --git a/src/backend/distributed/commands/multi_copy.c b/src/backend/distributed/commands/multi_copy.c index 6e3d19b68..fdb9552ef 100644 --- a/src/backend/distributed/commands/multi_copy.c +++ b/src/backend/distributed/commands/multi_copy.c @@ -2146,6 +2146,7 @@ CitusCopyDestReceiverStartup(DestReceiver *dest, int operation, } if (IsCitusTableTypeCacheEntry(cacheEntry, DISTRIBUTED_TABLE) && + !IsCitusTableTypeCacheEntry(cacheEntry, NULL_KEY_DISTRIBUTED_TABLE) && copyDest->partitionColumnIndex == INVALID_PARTITION_COLUMN_INDEX) { ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), diff --git a/src/backend/distributed/commands/truncate.c b/src/backend/distributed/commands/truncate.c index 52f769a11..70fee6bd5 100644 --- a/src/backend/distributed/commands/truncate.c +++ b/src/backend/distributed/commands/truncate.c @@ -324,7 +324,8 @@ ExecuteTruncateStmtSequentialIfNecessary(TruncateStmt *command) { Oid relationId = RangeVarGetRelid(rangeVar, NoLock, failOK); - if (IsCitusTable(relationId) && !HasDistributionKey(relationId) && + if ((IsCitusTableType(relationId, REFERENCE_TABLE) || + IsCitusTableType(relationId, CITUS_LOCAL_TABLE)) && TableReferenced(relationId)) { char *relationName = get_rel_name(relationId); diff --git a/src/backend/distributed/metadata/metadata_cache.c b/src/backend/distributed/metadata/metadata_cache.c index 1e73eef6b..4eab2aeed 100644 --- a/src/backend/distributed/metadata/metadata_cache.c +++ b/src/backend/distributed/metadata/metadata_cache.c @@ -508,11 +508,21 @@ IsCitusTableTypeInternal(char partitionMethod, char replicationModel, return partitionMethod == DISTRIBUTE_BY_RANGE; } + case NULL_KEY_DISTRIBUTED_TABLE: + { + return partitionMethod == DISTRIBUTE_BY_NONE && + replicationModel != REPLICATION_MODEL_2PC && + colocationId != INVALID_COLOCATION_ID; + } + case DISTRIBUTED_TABLE: { return partitionMethod == DISTRIBUTE_BY_HASH || partitionMethod == DISTRIBUTE_BY_RANGE || - partitionMethod == DISTRIBUTE_BY_APPEND; + partitionMethod == DISTRIBUTE_BY_APPEND || + (partitionMethod == DISTRIBUTE_BY_NONE && + replicationModel != REPLICATION_MODEL_2PC && + colocationId != INVALID_COLOCATION_ID); } case STRICTLY_PARTITIONED_DISTRIBUTED_TABLE: @@ -815,6 +825,21 @@ IsCitusLocalTableByDistParams(char partitionMethod, char replicationModel, } +/* + * IsNullShardKeyTableByDistParams returns true if given partitionMethod, + * replicationModel and colocationId would identify a distributed table that + * has a null shard key. + */ +bool +IsNullShardKeyTableByDistParams(char partitionMethod, char replicationModel, + uint32 colocationId) +{ + return partitionMethod == DISTRIBUTE_BY_NONE && + replicationModel != REPLICATION_MODEL_2PC && + colocationId != INVALID_COLOCATION_ID; +} + + /* * CitusTableList returns a list that includes all the valid distributed table * cache entries. diff --git a/src/backend/distributed/metadata/metadata_sync.c b/src/backend/distributed/metadata/metadata_sync.c index 868617ce0..f9c80942a 100644 --- a/src/backend/distributed/metadata/metadata_sync.c +++ b/src/backend/distributed/metadata/metadata_sync.c @@ -515,7 +515,7 @@ ShouldSyncUserCommandForObject(ObjectAddress objectAddress) /* * ShouldSyncTableMetadata checks if the metadata of a distributed table should be * propagated to metadata workers, i.e. the table is a hash distributed table or - * reference/citus local table. + * a Citus table that doesn't have shard key. */ bool ShouldSyncTableMetadata(Oid relationId) @@ -537,10 +537,11 @@ ShouldSyncTableMetadata(Oid relationId) /* - * ShouldSyncTableMetadataViaCatalog checks if the metadata of a distributed table should - * be propagated to metadata workers, i.e. the table is an MX table or reference table. + * ShouldSyncTableMetadataViaCatalog checks if the metadata of a Citus table should + * be propagated to metadata workers, i.e. the table is an MX table or Citus table + * that doesn't have shard key. * Tables with streaming replication model (which means RF=1) and hash distribution are - * considered as MX tables while tables with none distribution are reference tables. + * considered as MX tables. * * ShouldSyncTableMetadataViaCatalog does not use the CitusTableCache and instead reads * from catalog tables directly. @@ -1080,7 +1081,7 @@ EnsureObjectMetadataIsSane(int distributionArgumentIndex, int colocationId) /* * DistributionCreateCommands generates a commands that can be - * executed to replicate the metadata for a distributed table. + * executed to replicate the metadata for a Citus table. */ char * DistributionCreateCommand(CitusTableCacheEntry *cacheEntry) diff --git a/src/backend/distributed/operations/create_shards.c b/src/backend/distributed/operations/create_shards.c index 3edab94e9..1a2ce5f1c 100644 --- a/src/backend/distributed/operations/create_shards.c +++ b/src/backend/distributed/operations/create_shards.c @@ -217,9 +217,9 @@ CreateColocatedShards(Oid targetRelationId, Oid sourceRelationId, bool List *insertedShardPlacements = NIL; List *insertedShardIds = NIL; - /* make sure that tables are hash partitioned */ - CheckHashPartitionedTable(targetRelationId); - CheckHashPartitionedTable(sourceRelationId); + CitusTableCacheEntry *targetCacheEntry = GetCitusTableCacheEntry(targetRelationId); + Assert(targetCacheEntry->partitionMethod == DISTRIBUTE_BY_HASH || + targetCacheEntry->partitionMethod == DISTRIBUTE_BY_NONE); /* * In contrast to append/range partitioned tables it makes more sense to @@ -259,10 +259,20 @@ CreateColocatedShards(Oid targetRelationId, Oid sourceRelationId, bool *newShardIdPtr = GetNextShardId(); insertedShardIds = lappend(insertedShardIds, newShardIdPtr); - int32 shardMinValue = DatumGetInt32(sourceShardInterval->minValue); - int32 shardMaxValue = DatumGetInt32(sourceShardInterval->maxValue); - text *shardMinValueText = IntegerToText(shardMinValue); - text *shardMaxValueText = IntegerToText(shardMaxValue); + text *shardMinValueText = NULL; + text *shardMaxValueText = NULL; + if (targetCacheEntry->partitionMethod == DISTRIBUTE_BY_NONE) + { + Assert(list_length(sourceShardIntervalList) == 1); + } + else + { + int32 shardMinValue = DatumGetInt32(sourceShardInterval->minValue); + int32 shardMaxValue = DatumGetInt32(sourceShardInterval->maxValue); + shardMinValueText = IntegerToText(shardMinValue); + shardMaxValueText = IntegerToText(shardMaxValue); + } + List *sourceShardPlacementList = ShardPlacementListSortedByWorker( sourceShardId); @@ -362,6 +372,70 @@ CreateReferenceTableShard(Oid distributedTableId) } +/* + * CreateNullKeyShardWithRoundRobinPolicy creates a single shard for the given + * distributedTableId. The created shard does not have min/max values. + * Unlike CreateReferenceTableShard, the shard is _not_ replicated to + * all nodes but would have a single placement like Citus local tables. + * However, this placement doesn't necessarily need to be placed on + * coordinator. This is determined based on modulo of the colocation + * id that given table has been associated to. + */ +void +CreateNullKeyShardWithRoundRobinPolicy(Oid relationId, uint32 colocationId) +{ + EnsureTableOwner(relationId); + + /* we plan to add shards: get an exclusive lock on relation oid */ + LockRelationOid(relationId, ExclusiveLock); + + /* + * Load and sort the worker node list for deterministic placement. + * + * Also take a RowShareLock on pg_dist_node to disallow concurrent + * node list changes that require an exclusive lock. + */ + List *workerNodeList = DistributedTablePlacementNodeList(RowShareLock); + workerNodeList = SortList(workerNodeList, CompareWorkerNodes); + + int32 workerNodeCount = list_length(workerNodeList); + if (workerNodeCount == 0) + { + ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("couldn't find any worker nodes"), + errhint("Add more worker nodes"))); + } + + char shardStorageType = ShardStorageType(relationId); + text *minHashTokenText = NULL; + text *maxHashTokenText = NULL; + uint64 shardId = GetNextShardId(); + InsertShardRow(relationId, shardId, shardStorageType, + minHashTokenText, maxHashTokenText); + + /* determine the node index based on colocation id */ + int roundRobinNodeIdx = colocationId % workerNodeCount; + + int replicationFactor = 1; + List *insertedShardPlacements = InsertShardPlacementRows( + relationId, + shardId, + workerNodeList, + roundRobinNodeIdx, + replicationFactor); + + /* + * We don't need to force using exclusive connections because we're anyway + * creating a single shard. + */ + bool useExclusiveConnection = false; + + bool colocatedShard = false; + CreateShardsOnWorkers(relationId, insertedShardPlacements, + useExclusiveConnection, colocatedShard); +} + + /* * CheckHashPartitionedTable looks up the partition information for the given * tableId and checks if the table is hash partitioned. If not, the function diff --git a/src/backend/distributed/operations/stage_protocol.c b/src/backend/distributed/operations/stage_protocol.c index ce9fe3f31..c1031ffdf 100644 --- a/src/backend/distributed/operations/stage_protocol.c +++ b/src/backend/distributed/operations/stage_protocol.c @@ -521,7 +521,8 @@ RelationShardListForShardCreate(ShardInterval *shardInterval) relationShard->shardId = shardInterval->shardId; List *relationShardList = list_make1(relationShard); - if (IsCitusTableTypeCacheEntry(cacheEntry, HASH_DISTRIBUTED) && + if ((IsCitusTableTypeCacheEntry(cacheEntry, HASH_DISTRIBUTED) || + IsCitusTableTypeCacheEntry(cacheEntry, NULL_KEY_DISTRIBUTED_TABLE)) && cacheEntry->colocationId != INVALID_COLOCATION_ID) { shardIndex = ShardIndex(shardInterval); diff --git a/src/backend/distributed/planner/insert_select_planner.c b/src/backend/distributed/planner/insert_select_planner.c index 2eab62fc3..53fe58cdb 100644 --- a/src/backend/distributed/planner/insert_select_planner.c +++ b/src/backend/distributed/planner/insert_select_planner.c @@ -715,6 +715,13 @@ DistributedInsertSelectSupported(Query *queryTree, RangeTblEntry *insertRte, NULL, NULL); } } + else if (IsCitusTableType(targetRelationId, NULL_KEY_DISTRIBUTED_TABLE)) + { + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "distributed INSERT ... SELECT cannot target a distributed " + "table with a null shard key", + NULL, NULL); + } else { /* diff --git a/src/backend/distributed/planner/multi_physical_planner.c b/src/backend/distributed/planner/multi_physical_planner.c index f488a1cd5..b210da7d7 100644 --- a/src/backend/distributed/planner/multi_physical_planner.c +++ b/src/backend/distributed/planner/multi_physical_planner.c @@ -2487,7 +2487,7 @@ QueryPushdownTaskCreate(Query *originalQuery, int shardIndex, /* non-distributed tables have only one shard */ shardInterval = cacheEntry->sortedShardIntervalArray[0]; - /* only use reference table as anchor shard if none exists yet */ + /* use as anchor shard only if we couldn't find any yet */ if (anchorShardId == INVALID_SHARD_ID) { anchorShardId = shardInterval->shardId; diff --git a/src/backend/distributed/planner/multi_router_planner.c b/src/backend/distributed/planner/multi_router_planner.c index a95be74f8..97c2cecf6 100644 --- a/src/backend/distributed/planner/multi_router_planner.c +++ b/src/backend/distributed/planner/multi_router_planner.c @@ -2684,7 +2684,7 @@ TargetShardIntervalForFastPathQuery(Query *query, bool *isMultiShardQuery, if (!HasDistributionKey(relationId)) { - /* we don't need to do shard pruning for non-distributed tables */ + /* we don't need to do shard pruning for single shard tables */ return list_make1(LoadShardIntervalList(relationId)); } @@ -2974,7 +2974,7 @@ BuildRoutesForInsert(Query *query, DeferredErrorMessage **planningError) Assert(query->commandType == CMD_INSERT); - /* reference tables and citus local tables can only have one shard */ + /* tables that don't have distribution column can only have one shard */ if (!HasDistributionKeyCacheEntry(cacheEntry)) { List *shardIntervalList = LoadShardIntervalList(distributedTableId); @@ -2992,6 +2992,12 @@ BuildRoutesForInsert(Query *query, DeferredErrorMessage **planningError) ereport(ERROR, (errmsg("local table cannot have %d shards", shardCount))); } + else if (IsCitusTableTypeCacheEntry(cacheEntry, NULL_KEY_DISTRIBUTED_TABLE)) + { + ereport(ERROR, (errmsg("distributed tables having a null shard key " + "cannot have %d shards", + shardCount))); + } } ShardInterval *shardInterval = linitial(shardIntervalList); diff --git a/src/backend/distributed/transaction/relation_access_tracking.c b/src/backend/distributed/transaction/relation_access_tracking.c index 2ecbba5b7..b0af4e476 100644 --- a/src/backend/distributed/transaction/relation_access_tracking.c +++ b/src/backend/distributed/transaction/relation_access_tracking.c @@ -195,7 +195,7 @@ RecordRelationAccessIfNonDistTable(Oid relationId, ShardPlacementAccessType acce * recursively calling RecordRelationAccessBase(), so becareful about * removing this check. */ - if (IsCitusTable(relationId) && HasDistributionKey(relationId)) + if (IsCitusTableType(relationId, DISTRIBUTED_TABLE)) { return; } @@ -732,7 +732,7 @@ CheckConflictingRelationAccesses(Oid relationId, ShardPlacementAccessType access CitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId); - if (HasDistributionKeyCacheEntry(cacheEntry) || + if (IsCitusTableTypeCacheEntry(cacheEntry, DISTRIBUTED_TABLE) || cacheEntry->referencingRelationsViaForeignKey == NIL) { return; @@ -931,7 +931,7 @@ HoldsConflictingLockWithReferencedRelations(Oid relationId, ShardPlacementAccess * We're only interested in foreign keys to reference tables and citus * local tables. */ - if (IsCitusTable(referencedRelation) && HasDistributionKey(referencedRelation)) + if (IsCitusTableType(referencedRelation, DISTRIBUTED_TABLE)) { continue; } @@ -993,7 +993,7 @@ HoldsConflictingLockWithReferencingRelations(Oid relationId, ShardPlacementAcces CitusTableCacheEntry *cacheEntry = GetCitusTableCacheEntry(relationId); bool holdsConflictingLocks = false; - Assert(!HasDistributionKeyCacheEntry(cacheEntry)); + Assert(!IsCitusTableTypeCacheEntry(cacheEntry, DISTRIBUTED_TABLE)); Oid referencingRelation = InvalidOid; foreach_oid(referencingRelation, cacheEntry->referencingRelationsViaForeignKey) diff --git a/src/backend/distributed/utils/colocation_utils.c b/src/backend/distributed/utils/colocation_utils.c index 985d4c38e..9cc5df8f9 100644 --- a/src/backend/distributed/utils/colocation_utils.c +++ b/src/backend/distributed/utils/colocation_utils.c @@ -1384,17 +1384,19 @@ EnsureTableCanBeColocatedWith(Oid relationId, char replicationModel, Oid distributionColumnType, Oid sourceRelationId) { CitusTableCacheEntry *sourceTableEntry = GetCitusTableCacheEntry(sourceRelationId); - char sourceReplicationModel = sourceTableEntry->replicationModel; - Var *sourceDistributionColumn = DistPartitionKeyOrError(sourceRelationId); - if (!IsCitusTableTypeCacheEntry(sourceTableEntry, HASH_DISTRIBUTED)) + if (IsCitusTableTypeCacheEntry(sourceTableEntry, APPEND_DISTRIBUTED) || + IsCitusTableTypeCacheEntry(sourceTableEntry, RANGE_DISTRIBUTED) || + IsCitusTableTypeCacheEntry(sourceTableEntry, CITUS_LOCAL_TABLE)) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot distribute relation"), - errdetail("Currently, colocate_with option is only supported " - "for hash distributed tables."))); + errdetail("Currently, colocate_with option is not supported " + "with append / range distributed tables and local " + "tables added to metadata."))); } + char sourceReplicationModel = sourceTableEntry->replicationModel; if (sourceReplicationModel != replicationModel) { char *relationName = get_rel_name(relationId); @@ -1406,7 +1408,9 @@ EnsureTableCanBeColocatedWith(Oid relationId, char replicationModel, sourceRelationName, relationName))); } - Oid sourceDistributionColumnType = sourceDistributionColumn->vartype; + Var *sourceDistributionColumn = DistPartitionKey(sourceRelationId); + Oid sourceDistributionColumnType = !sourceDistributionColumn ? InvalidOid : + sourceDistributionColumn->vartype; if (sourceDistributionColumnType != distributionColumnType) { char *relationName = get_rel_name(relationId); diff --git a/src/backend/distributed/utils/distribution_column.c b/src/backend/distributed/utils/distribution_column.c index 7fbab98eb..1f5ac9ec5 100644 --- a/src/backend/distributed/utils/distribution_column.c +++ b/src/backend/distributed/utils/distribution_column.c @@ -135,7 +135,7 @@ BuildDistributionKeyFromColumnName(Oid relationId, char *columnName, LOCKMODE lo char *tableName = get_rel_name(relationId); - /* short circuit for reference tables */ + /* short circuit for reference tables and null-shard key tables */ if (columnName == NULL) { return NULL; diff --git a/src/backend/distributed/utils/shardinterval_utils.c b/src/backend/distributed/utils/shardinterval_utils.c index 12635f9f4..6c18e201e 100644 --- a/src/backend/distributed/utils/shardinterval_utils.c +++ b/src/backend/distributed/utils/shardinterval_utils.c @@ -206,7 +206,7 @@ CompareRelationShards(const void *leftElement, const void *rightElement) * * For hash partitioned tables, it calculates hash value of a number in its * range (e.g. min value) and finds which shard should contain the hashed - * value. For reference tables and citus local tables, it simply returns 0. + * value. For the tables that don't have a shard key, it simply returns 0. * For the other table types, the function errors out. */ int @@ -231,12 +231,11 @@ ShardIndex(ShardInterval *shardInterval) "tables that are added to citus metadata"))); } - /* short-circuit for reference tables */ + /* short-circuit for the tables that don't have a distribution key */ if (!HasDistributionKeyCacheEntry(cacheEntry)) { /* - * Reference tables and citus local tables have only a single shard, - * so the index is fixed to 0. + * Such tables have only a single shard, so the index is fixed to 0. */ shardIndex = 0; diff --git a/src/include/distributed/coordinator_protocol.h b/src/include/distributed/coordinator_protocol.h index ad8329a6c..351efb790 100644 --- a/src/include/distributed/coordinator_protocol.h +++ b/src/include/distributed/coordinator_protocol.h @@ -262,6 +262,7 @@ extern void CreateShardsWithRoundRobinPolicy(Oid distributedTableId, int32 shard extern void CreateColocatedShards(Oid targetRelationId, Oid sourceRelationId, bool useExclusiveConnections); extern void CreateReferenceTableShard(Oid distributedTableId); +extern void CreateNullKeyShardWithRoundRobinPolicy(Oid relationId, uint32 colocationId); extern List * WorkerCreateShardCommandList(Oid relationId, int shardIndex, uint64 shardId, List *ddlCommandList, List *foreignConstraintCommandList); diff --git a/src/include/distributed/metadata_cache.h b/src/include/distributed/metadata_cache.h index c23a047ec..6f29f9d63 100644 --- a/src/include/distributed/metadata_cache.h +++ b/src/include/distributed/metadata_cache.h @@ -123,6 +123,7 @@ typedef enum HASH_DISTRIBUTED, APPEND_DISTRIBUTED, RANGE_DISTRIBUTED, + NULL_KEY_DISTRIBUTED_TABLE, /* hash, range or append distributed table */ DISTRIBUTED_TABLE, @@ -157,6 +158,8 @@ extern uint32 ColocationIdViaCatalog(Oid relationId); bool IsReferenceTableByDistParams(char partitionMethod, char replicationModel); extern bool IsCitusLocalTableByDistParams(char partitionMethod, char replicationModel, uint32 colocationId); +extern bool IsNullShardKeyTableByDistParams(char partitionMethod, char replicationModel, + uint32 colocationId); extern List * CitusTableList(void); extern ShardInterval * LoadShardInterval(uint64 shardId); extern bool ShardExists(uint64 shardId); diff --git a/src/test/regress/citus_tests/run_test.py b/src/test/regress/citus_tests/run_test.py index 72ce2fb14..cda9fc0ae 100755 --- a/src/test/regress/citus_tests/run_test.py +++ b/src/test/regress/citus_tests/run_test.py @@ -128,6 +128,7 @@ DEPS = { "multi_mx_copy_data": TestDeps(None, ["multi_mx_create_table"]), "multi_mx_schema_support": TestDeps(None, ["multi_mx_copy_data"]), "multi_simple_queries": TestDeps("base_schedule"), + "create_null_dist_key": TestDeps("minimal_schedule"), } diff --git a/src/test/regress/expected/create_null_dist_key.out b/src/test/regress/expected/create_null_dist_key.out new file mode 100644 index 000000000..e24ff1e91 --- /dev/null +++ b/src/test/regress/expected/create_null_dist_key.out @@ -0,0 +1,1432 @@ +CREATE SCHEMA create_null_dist_key; +SET search_path TO create_null_dist_key; +SET citus.next_shard_id TO 1720000; +SET citus.shard_count TO 32; +SET citus.shard_replication_factor TO 1; +SELECT 1 FROM citus_remove_node('localhost', :worker_1_port); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT 1 FROM citus_remove_node('localhost', :worker_2_port); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +CREATE TABLE add_node_test(a int, "b" text); +-- add a node before creating the null-shard-key table +SELECT 1 FROM citus_add_node('localhost', :worker_1_port); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT create_distributed_table('add_node_test', null, colocate_with=>'none', distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- add a node after creating the null-shard-key table +SELECT 1 FROM citus_add_node('localhost', :worker_2_port); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +-- make sure that table is created on the worker nodes added before/after create_distributed_table +SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=1 FROM pg_class WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + relname='add_node_test' +$$); + result +--------------------------------------------------------------------- + t + t +(2 rows) + +-- and check the metadata tables +SELECT result FROM run_command_on_workers($$ + SELECT (partmethod, partkey, repmodel, autoconverted) FROM pg_dist_partition + WHERE logicalrelid = 'create_null_dist_key.add_node_test'::regclass +$$); + result +--------------------------------------------------------------------- + (n,,s,f) + (n,,s,f) +(2 rows) + +SELECT result FROM run_command_on_workers($$ + SELECT (shardstorage, shardminvalue, shardmaxvalue) FROM pg_dist_shard + WHERE logicalrelid = 'create_null_dist_key.add_node_test'::regclass +$$); + result +--------------------------------------------------------------------- + (t,,) + (t,,) +(2 rows) + +SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=1 FROM pg_dist_placement + WHERE shardid = ( + SELECT shardid FROM pg_dist_shard + WHERE logicalrelid = 'create_null_dist_key.add_node_test'::regclass + ); +$$); + result +--------------------------------------------------------------------- + t + t +(2 rows) + +SELECT result FROM run_command_on_workers($$ + SELECT (shardcount, replicationfactor, distributioncolumntype, distributioncolumncollation) FROM pg_dist_colocation + WHERE colocationid = ( + SELECT colocationid FROM pg_dist_partition + WHERE logicalrelid = 'create_null_dist_key.add_node_test'::regclass + ); +$$); + result +--------------------------------------------------------------------- + (1,1,0,0) + (1,1,0,0) +(2 rows) + +SET client_min_messages TO WARNING; +SELECT 1 FROM citus_add_node('localhost', :master_port, groupid => 0); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +SET client_min_messages TO NOTICE; +CREATE TABLE invalid_configs_1(a int primary key); +SELECT create_distributed_table('invalid_configs_1', null, shard_count=>2); +ERROR: shard_count can't be specified when the distribution column is null because in that case it's automatically set to 1 +SELECT create_distributed_table('invalid_configs_1', null, shard_count=>1); +ERROR: shard_count can't be specified when the distribution column is null because in that case it's automatically set to 1 +CREATE TABLE nullkey_c1_t1(a int, b int); +CREATE TABLE nullkey_c1_t2(a int, b int); +CREATE TABLE nullkey_c1_t3(a int, b int); +SELECT create_distributed_table('nullkey_c1_t1', null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT colocationid AS nullkey_c1_t1_colocation_id FROM pg_dist_partition WHERE logicalrelid = 'create_null_dist_key.nullkey_c1_t1'::regclass \gset +BEGIN; + DROP TABLE nullkey_c1_t1; + -- make sure that we delete the colocation group after dropping the last table that belongs to it + SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = :'nullkey_c1_t1_colocation_id'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +ROLLBACK; +SELECT create_distributed_table('nullkey_c1_t2', null, colocate_with=>'nullkey_c1_t1'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('nullkey_c1_t3', null, colocate_with=>'nullkey_c1_t1', distribution_type=>'append'); +ERROR: distribution_type can't be specified when the distribution column is null +SELECT create_distributed_table('nullkey_c1_t3', null, colocate_with=>'nullkey_c1_t1'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE nullkey_c2_t1(a int, b int); +CREATE TABLE nullkey_c2_t2(a int, b int); +CREATE TABLE nullkey_c2_t3(a int, b int); +-- create_distributed_table_concurrently is not yet supported yet +SELECT create_distributed_table_concurrently('nullkey_c2_t1', null); +ERROR: cannot use create_distributed_table_concurrently to create a distributed table with a null shard key, consider using create_distributed_table() +SELECT create_distributed_table_concurrently('nullkey_c2_t1', null, colocate_with=>'none'); +ERROR: cannot use create_distributed_table_concurrently to create a distributed table with a null shard key, consider using create_distributed_table() +SELECT create_distributed_table('nullkey_c2_t1', null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('nullkey_c2_t2', null, colocate_with=>'nullkey_c2_t1', distribution_type=>'hash'); -- distribution_type is ignored anyway + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('nullkey_c2_t3', null, colocate_with=>'nullkey_c2_t2', distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- check the metadata for the colocated tables whose names start with nullkey_c1_ +SELECT logicalrelid, partmethod, partkey, repmodel, autoconverted FROM pg_dist_partition +WHERE logicalrelid IN ( + SELECT oid FROM pg_class + WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + relname LIKE 'nullkey_c1_%' +) +ORDER BY 1; + logicalrelid | partmethod | partkey | repmodel | autoconverted +--------------------------------------------------------------------- + nullkey_c1_t1 | n | | s | f + nullkey_c1_t2 | n | | s | f + nullkey_c1_t3 | n | | s | f +(3 rows) + +-- make sure that all those 3 tables belong to same colocation group +SELECT COUNT(*) FROM pg_dist_partition +WHERE logicalrelid IN ( + SELECT oid FROM pg_class + WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + relname LIKE 'nullkey_c1_%' +) +GROUP BY colocationid; + count +--------------------------------------------------------------------- + 3 +(1 row) + +SELECT logicalrelid, shardstorage, shardminvalue, shardmaxvalue FROM pg_dist_shard +WHERE logicalrelid IN ( + SELECT oid FROM pg_class + WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + relname LIKE 'nullkey_c1_%' +) +ORDER BY 1; + logicalrelid | shardstorage | shardminvalue | shardmaxvalue +--------------------------------------------------------------------- + nullkey_c1_t1 | t | | + nullkey_c1_t2 | t | | + nullkey_c1_t3 | t | | +(3 rows) + +-- make sure that all those 3 shards are created on the same node group +SELECT COUNT(*) FROM pg_dist_placement +WHERE shardid IN ( + SELECT shardid FROM pg_dist_shard + WHERE logicalrelid IN ( + SELECT oid FROM pg_class + WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + relname LIKE 'nullkey_c1_%' + ) +) +GROUP BY groupid; + count +--------------------------------------------------------------------- + 3 +(1 row) + +-- check the metadata for the colocated tables whose names start with nullkey_c2_ +SELECT logicalrelid, partmethod, partkey, repmodel, autoconverted FROM pg_dist_partition +WHERE logicalrelid IN ( + SELECT oid FROM pg_class + WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + relname LIKE 'nullkey_c2_%' +) +ORDER BY 1; + logicalrelid | partmethod | partkey | repmodel | autoconverted +--------------------------------------------------------------------- + nullkey_c2_t1 | n | | s | f + nullkey_c2_t2 | n | | s | f + nullkey_c2_t3 | n | | s | f +(3 rows) + +-- make sure that all those 3 tables belong to same colocation group +SELECT COUNT(*) FROM pg_dist_partition +WHERE logicalrelid IN ( + SELECT oid FROM pg_class + WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + relname LIKE 'nullkey_c2_%' +) +GROUP BY colocationid; + count +--------------------------------------------------------------------- + 3 +(1 row) + +SELECT logicalrelid, shardstorage, shardminvalue, shardmaxvalue FROM pg_dist_shard +WHERE logicalrelid IN ( + SELECT oid FROM pg_class + WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + relname LIKE 'nullkey_c2_%' +) +ORDER BY 1; + logicalrelid | shardstorage | shardminvalue | shardmaxvalue +--------------------------------------------------------------------- + nullkey_c2_t1 | t | | + nullkey_c2_t2 | t | | + nullkey_c2_t3 | t | | +(3 rows) + +-- make sure that all those 3 shards created on the same node group +SELECT COUNT(*) FROM pg_dist_placement +WHERE shardid IN ( + SELECT shardid FROM pg_dist_shard + WHERE logicalrelid IN ( + SELECT oid FROM pg_class + WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + relname LIKE 'nullkey_c2_%' + ) +) +GROUP BY groupid; + count +--------------------------------------------------------------------- + 3 +(1 row) + +-- Make sure that the colocated tables whose names start with nullkey_c1_ +-- belong to a different colocation group than the ones whose names start +-- with nullkey_c2_. +-- +-- It's ok to only compare nullkey_c1_t1 and nullkey_c2_t1 because we already +-- verified that null_dist_key.nullkey_c1_t1 is colocated with the other two +-- and null_dist_key.nullkey_c2_t1 is colocated with the other two. +SELECT +( + SELECT colocationid FROM pg_dist_partition + WHERE logicalrelid = 'create_null_dist_key.nullkey_c1_t1'::regclass +) +!= +( + SELECT colocationid FROM pg_dist_partition + WHERE logicalrelid = 'create_null_dist_key.nullkey_c2_t1'::regclass +); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- Since we determine node for the placement based on the module of colocation id, +-- we don't expect those two colocation groups to get assigned to same node. +SELECT +( + SELECT groupid FROM pg_dist_placement + WHERE shardid = ( + SELECT shardid FROM pg_dist_shard + WHERE logicalrelid = 'create_null_dist_key.nullkey_c1_t1'::regclass + ) +) +!= +( + SELECT groupid FROM pg_dist_placement + WHERE shardid = ( + SELECT shardid FROM pg_dist_shard + WHERE logicalrelid = 'create_null_dist_key.nullkey_c2_t1'::regclass + ) +); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- It's ok to only check nullkey_c1_t1 and nullkey_c2_t1 because we already +-- verified that null_dist_key.nullkey_c1_t1 is colocated with the other two +-- and null_dist_key.nullkey_c2_t1 is colocated with the other two. +SELECT shardcount, replicationfactor, distributioncolumntype, distributioncolumncollation FROM pg_dist_colocation +WHERE colocationid = ( + SELECT colocationid FROM pg_dist_partition + WHERE logicalrelid = 'create_null_dist_key.nullkey_c1_t1'::regclass +); + shardcount | replicationfactor | distributioncolumntype | distributioncolumncollation +--------------------------------------------------------------------- + 1 | 1 | 0 | 0 +(1 row) + +SELECT shardcount, replicationfactor, distributioncolumntype, distributioncolumncollation FROM pg_dist_colocation +WHERE colocationid = ( + SELECT colocationid FROM pg_dist_partition + WHERE logicalrelid = 'create_null_dist_key.nullkey_c2_t1'::regclass +); + shardcount | replicationfactor | distributioncolumntype | distributioncolumncollation +--------------------------------------------------------------------- + 1 | 1 | 0 | 0 +(1 row) + +CREATE TABLE round_robin_test_c1(a int, b int); +SELECT create_distributed_table('round_robin_test_c1', null, colocate_with=>'none', distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +\c - - - :master_port +SET search_path TO create_null_dist_key; +SET citus.next_shard_id TO 1730000; +SET citus.shard_count TO 32; +SET citus.shard_replication_factor TO 1; +SET client_min_messages TO NOTICE; +CREATE TABLE round_robin_test_c2(a int, b int); +SELECT create_distributed_table('round_robin_test_c2', null, colocate_with=>'none', distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- Since we determine node for the placement based on the module of colocation id, +-- we don't expect those two colocation groups to get assigned to same node even +-- after reconnecting to the coordinator. +SELECT +( + SELECT groupid FROM pg_dist_placement + WHERE shardid = ( + SELECT shardid FROM pg_dist_shard + WHERE logicalrelid = 'create_null_dist_key.round_robin_test_c1'::regclass + ) +) +!= +( + SELECT groupid FROM pg_dist_placement + WHERE shardid = ( + SELECT shardid FROM pg_dist_shard + WHERE logicalrelid = 'create_null_dist_key.round_robin_test_c2'::regclass + ) +); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +CREATE TABLE distributed_table(a int, b int); +-- cannot colocate a sharded table with null shard key table +SELECT create_distributed_table('distributed_table', 'a', colocate_with=>'nullkey_c1_t1'); +ERROR: cannot colocate tables nullkey_c1_t1 and distributed_table +DETAIL: Distribution column types don't match for nullkey_c1_t1 and distributed_table. +CREATE TABLE reference_table(a int, b int); +CREATE TABLE local(a int, b int); +SELECT create_reference_table('reference_table'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('distributed_table', 'a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- cannot colocate null shard key tables with other table types +CREATE TABLE cannot_colocate_with_other_types (a int, b int); +SELECT create_distributed_table('cannot_colocate_with_other_types', null, colocate_with=>'reference_table'); +ERROR: cannot colocate tables reference_table and cannot_colocate_with_other_types +DETAIL: Replication models don't match for reference_table and cannot_colocate_with_other_types. +SELECT create_distributed_table('cannot_colocate_with_other_types', null, colocate_with=>'distributed_table'); +ERROR: cannot colocate tables distributed_table and cannot_colocate_with_other_types +DETAIL: Distribution column types don't match for distributed_table and cannot_colocate_with_other_types. +SELECT create_distributed_table('cannot_colocate_with_other_types', null, colocate_with=>'local'); -- postgres local +ERROR: relation local is not distributed +SELECT citus_add_local_table_to_metadata('local'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +-- cannot colocate null shard key tables with citus local tables +SELECT create_distributed_table('cannot_colocate_with_other_types', null, colocate_with=>'local'); -- citus local +ERROR: cannot distribute relation +DETAIL: Currently, colocate_with option is not supported with append / range distributed tables and local tables added to metadata. +SET client_min_messages TO WARNING; +-- can't create such a distributed table from another Citus table, except Citus local tables +SELECT create_distributed_table('reference_table', null, colocate_with=>'none'); +ERROR: table "reference_table" is already distributed +SELECT create_distributed_table('distributed_table', null, colocate_with=>'none'); +ERROR: table "distributed_table" is already distributed +SELECT create_distributed_table('local', null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +BEGIN; + -- creating a null-shard-key table from a temporary table is not supported + CREATE TEMPORARY TABLE temp_table (a int); + SELECT create_distributed_table('temp_table', null, colocate_with=>'none', distribution_type=>null); +ERROR: cannot distribute a temporary table +ROLLBACK; +-- creating a null-shard-key table from a catalog table is not supported +SELECT create_distributed_table('pg_catalog.pg_index', NULL, distribution_type=>null); +ERROR: cannot create a citus table from a catalog table +-- creating a null-shard-key table from an unlogged table is supported +CREATE UNLOGGED TABLE unlogged_table (a int); +SELECT create_distributed_table('unlogged_table', null, colocate_with=>'none', distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- creating a null-shard-key table from a foreign table is not supported +CREATE FOREIGN TABLE foreign_table ( + id bigint not null, + full_name text not null default '' +) SERVER fake_fdw_server OPTIONS (encoding 'utf-8', compression 'true', table_name 'foreign_table'); +SELECT create_distributed_table('foreign_table', null, colocate_with=>'none', distribution_type=>null); +ERROR: foreign tables cannot be distributed +HINT: Can add foreign table "foreign_table" to metadata by running: SELECT citus_add_local_table_to_metadata($$create_null_dist_key.foreign_table$$); +-- create a null dist key table that has no tuples +CREATE TABLE null_dist_key_table_1 (a int primary key); +SELECT create_distributed_table('null_dist_key_table_1', null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- create a null dist key table that has some tuples +CREATE TABLE null_dist_key_table_2(a int primary key); +INSERT INTO null_dist_key_table_2 VALUES(1); +SELECT create_distributed_table('null_dist_key_table_2', null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT * FROM null_dist_key_table_2 ORDER BY a; + a +--------------------------------------------------------------------- + 1 +(1 row) + +DROP TABLE null_dist_key_table_1, null_dist_key_table_2; +-- create indexes before creating the null dist key tables +-- .. for an initially empty table +CREATE TABLE null_dist_key_table_1(a int); +CREATE INDEX null_dist_key_table_1_idx ON null_dist_key_table_1(a); +SELECT create_distributed_table('null_dist_key_table_1', null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- .. and for another table having data in it before creating null dist key table +CREATE TABLE null_dist_key_table_2(a int); +INSERT INTO null_dist_key_table_2 VALUES(1); +CREATE INDEX null_dist_key_table_2_idx ON null_dist_key_table_2(a); +SELECT create_distributed_table('null_dist_key_table_2', null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT * FROM null_dist_key_table_2 ORDER BY a; + a +--------------------------------------------------------------------- + 1 +(1 row) + +-- show that we do not support inheritance relationships +CREATE TABLE parent_table (a int, b text); +CREATE TABLE child_table () INHERITS (parent_table); +-- both of below should error out +SELECT create_distributed_table('parent_table', null, colocate_with=>'none'); +ERROR: parent_table is not a regular, foreign or partitioned table +SELECT create_distributed_table('child_table', null, colocate_with=>'none'); +ERROR: child_table is not a regular, foreign or partitioned table +-- show that we support policies +BEGIN; + CREATE TABLE null_dist_key_table_3 (table_user text); + ALTER TABLE null_dist_key_table_3 ENABLE ROW LEVEL SECURITY; + CREATE ROLE table_users; + CREATE POLICY table_policy ON null_dist_key_table_3 TO table_users + USING (table_user = current_user); + SELECT create_distributed_table('null_dist_key_table_3', null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +ROLLBACK; +-- drop them for next tests +DROP TABLE null_dist_key_table_1, null_dist_key_table_2, distributed_table; +-- tests for object names that should be escaped properly +CREATE SCHEMA "NULL_!_dist_key"; +CREATE TABLE "NULL_!_dist_key"."my_TABLE.1!?!"(id int, "Second_Id" int); +SELECT create_distributed_table('"NULL_!_dist_key"."my_TABLE.1!?!"', null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- drop the table before creating it when the search path is set +SET search_path to "NULL_!_dist_key" ; +DROP TABLE "my_TABLE.1!?!"; +CREATE TYPE int_jsonb_type AS (key int, value jsonb); +CREATE DOMAIN age_with_default AS int CHECK (value >= 0) DEFAULT 0; +CREATE TYPE yes_no_enum AS ENUM ('yes', 'no'); +CREATE EXTENSION btree_gist; +CREATE SEQUENCE my_seq_1 START WITH 10; +CREATE TABLE "Table?!.1Table"( + id int PRIMARY KEY, + "Second_Id" int, + "local_Type" int_jsonb_type, + "jsondata" jsonb NOT NULL, + name text, + price numeric CHECK (price > 0), + age_with_default_col age_with_default, + yes_no_enum_col yes_no_enum, + seq_col_1 bigserial, + seq_col_2 int DEFAULT nextval('my_seq_1'), + generated_column int GENERATED ALWAYS AS (seq_col_1 * seq_col_2 + 4) STORED, + UNIQUE (id, price), + EXCLUDE USING GIST (name WITH =)); +-- create some objects before create_distributed_table +CREATE INDEX "my!Index1" ON "Table?!.1Table"(id) WITH ( fillfactor = 80 ) WHERE id > 10; +CREATE INDEX text_index ON "Table?!.1Table"(name); +CREATE UNIQUE INDEX uniqueIndex ON "Table?!.1Table" (id); +CREATE STATISTICS stats_1 ON id, price FROM "Table?!.1Table"; +CREATE TEXT SEARCH CONFIGURATION text_search_cfg (parser = default); +CREATE INDEX text_search_index ON "Table?!.1Table" +USING gin (to_tsvector('text_search_cfg'::regconfig, (COALESCE(name, ''::character varying))::text)); +-- ingest some data before create_distributed_table +INSERT INTO "Table?!.1Table" VALUES (1, 1, (1, row_to_json(row(1,1)))::int_jsonb_type, row_to_json(row(1,1), true)), + (2, 1, (2, row_to_json(row(2,2)))::int_jsonb_type, row_to_json(row(2,2), 'false')); +-- create a replica identity before create_distributed_table +ALTER TABLE "Table?!.1Table" REPLICA IDENTITY USING INDEX uniqueIndex; +SELECT create_distributed_table('"Table?!.1Table"', null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO "Table?!.1Table" VALUES (10, 15, (150, row_to_json(row(4,8)))::int_jsonb_type, '{}', 'text_1', 10, 27, 'yes', 60, 70); +INSERT INTO "Table?!.1Table" VALUES (5, 5, (5, row_to_json(row(5,5)))::int_jsonb_type, row_to_json(row(5,5), true)); +-- tuples that are supposed to violate different data type / check constraints +INSERT INTO "Table?!.1Table"(id, jsondata, name) VALUES (101, '{"a": 1}', 'text_1'); +ERROR: conflicting key value violates exclusion constraint "Table?!.1Table_name_excl_1730043" +DETAIL: Key (name)=(text_1) conflicts with existing key (name)=(text_1). +CONTEXT: while executing command on localhost:xxxxx +INSERT INTO "Table?!.1Table"(id, jsondata, price) VALUES (101, '{"a": 1}', -1); +ERROR: new row for relation "Table?!.1Table_1730043" violates check constraint "Table?!.1Table_price_check" +DETAIL: Failing row contains (101, null, null, {"a": 1}, null, -1, 0, null, 5, 14, 74). +CONTEXT: while executing command on localhost:xxxxx +INSERT INTO "Table?!.1Table"(id, jsondata, age_with_default_col) VALUES (101, '{"a": 1}', -1); +ERROR: value for domain age_with_default violates check constraint "age_with_default_check" +INSERT INTO "Table?!.1Table"(id, jsondata, yes_no_enum_col) VALUES (101, '{"a": 1}', 'what?'); +ERROR: invalid input value for enum yes_no_enum: "what?" +SELECT * FROM "Table?!.1Table" ORDER BY id; + id | Second_Id | local_Type | jsondata | name | price | age_with_default_col | yes_no_enum_col | seq_col_1 | seq_col_2 | generated_column +--------------------------------------------------------------------- + 1 | 1 | (1,"{""f1"": 1, ""f2"": 1}") | {"f1": 1, "f2": 1} | | | 0 | | 1 | 10 | 14 + 2 | 1 | (2,"{""f1"": 2, ""f2"": 2}") | {"f1": 2, "f2": 2} | | | 0 | | 2 | 11 | 26 + 5 | 5 | (5,"{""f1"": 5, ""f2"": 5}") | {"f1": 5, "f2": 5} | | | 0 | | 3 | 12 | 40 + 10 | 15 | (150,"{""f1"": 4, ""f2"": 8}") | {} | text_1 | 10 | 27 | yes | 60 | 70 | 4204 +(4 rows) + +SET search_path TO create_null_dist_key; +-- create a partitioned table with some columns that +-- are going to be dropped within the tests +CREATE TABLE sensors( + col_to_drop_1 text, + measureid integer, + eventdatetime date, + measure_data jsonb, +PRIMARY KEY (measureid, eventdatetime, measure_data)) +PARTITION BY RANGE(eventdatetime); +-- drop column even before attaching any partitions +ALTER TABLE sensors DROP COLUMN col_to_drop_1; +CREATE TABLE sensors_2000 PARTITION OF sensors FOR VALUES FROM ('2000-01-01') TO ('2001-01-01'); +-- cannot distribute child table without distributing the parent +SELECT create_distributed_table('sensors_2000', NULL, distribution_type=>null); +ERROR: cannot distribute relation "sensors_2000" which is partition of "sensors" +DETAIL: Citus does not support distributing partitions if their parent is not distributed table. +HINT: Distribute the partitioned table "sensors" instead. +SELECT create_distributed_table('sensors', NULL, distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE multi_level_partitioning_parent( + measureid integer, + eventdatetime date, + measure_data jsonb) +PARTITION BY RANGE(eventdatetime); +CREATE TABLE multi_level_partitioning_level_1( + measureid integer, + eventdatetime date, + measure_data jsonb) +PARTITION BY RANGE(eventdatetime); +ALTER TABLE multi_level_partitioning_parent ATTACH PARTITION multi_level_partitioning_level_1 +FOR VALUES FROM ('2000-01-01') TO ('2001-01-01'); +CREATE TABLE multi_level_partitioning_level_2 PARTITION OF multi_level_partitioning_level_1 +FOR VALUES FROM ('2000-01-01') TO ('2000-06-06'); +-- multi-level partitioning is not supported +SELECT create_distributed_table('multi_level_partitioning_parent', NULL, distribution_type=>null); +ERROR: distributing multi-level partitioned tables is not supported +DETAIL: Relation "multi_level_partitioning_level_1" is partitioned table itself and it is also partition of relation "multi_level_partitioning_parent". +CREATE FUNCTION normalize_generate_always_as_error(query text) RETURNS void AS $$ +BEGIN + EXECUTE query; + EXCEPTION WHEN OTHERS THEN + IF SQLERRM LIKE 'cannot insert into column %' OR + SQLERRM LIKE 'cannot insert a non-DEFAULT value into column %' + THEN + RAISE 'cannot insert a non-DEFAULT value into column'; + ELSE + RAISE 'unknown error'; + END IF; +END; +$$LANGUAGE plpgsql; +CREATE TABLE identity_test ( + a int GENERATED BY DEFAULT AS IDENTITY (START WITH 10 INCREMENT BY 10), + b bigint GENERATED ALWAYS AS IDENTITY (START WITH 100 INCREMENT BY 100), + c bigint GENERATED BY DEFAULT AS IDENTITY (START WITH 1000 INCREMENT BY 1000) +); +SELECT create_distributed_table('identity_test', NULL, distribution_type=>null); +ERROR: cannot complete operation on create_null_dist_key.identity_test with smallint/int identity column +HINT: Use bigint identity column instead. +DROP TABLE identity_test; +-- Above failed because we don't support using a data type other than BIGINT +-- for identity columns, so drop the table and create a new one with BIGINT +-- identity columns. +CREATE TABLE identity_test ( + a bigint GENERATED BY DEFAULT AS IDENTITY (START WITH 10 INCREMENT BY 10), + b bigint GENERATED ALWAYS AS IDENTITY (START WITH 100 INCREMENT BY 100), + c bigint GENERATED BY DEFAULT AS IDENTITY (START WITH 1000 INCREMENT BY 1000) +); +SELECT create_distributed_table('identity_test', NULL, distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO identity_test (a) VALUES (5); +SELECT normalize_generate_always_as_error($$INSERT INTO identity_test (b) VALUES (5)$$); -- fails due to missing OVERRIDING SYSTEM VALUE +ERROR: cannot insert a non-DEFAULT value into column +CONTEXT: PL/pgSQL function normalize_generate_always_as_error(text) line XX at RAISE +INSERT INTO identity_test (b) OVERRIDING SYSTEM VALUE VALUES (5); +INSERT INTO identity_test (c) VALUES (5); +SELECT result, success FROM run_command_on_workers($$ + INSERT INTO create_null_dist_key.identity_test (a) VALUES (6) +$$); + result | success +--------------------------------------------------------------------- + INSERT 0 1 | t + INSERT 0 1 | t +(2 rows) + +SELECT result, success FROM run_command_on_workers($$ + SELECT create_null_dist_key.normalize_generate_always_as_error('INSERT INTO create_null_dist_key.identity_test (b) VALUES (1)') +$$); + result | success +--------------------------------------------------------------------- + ERROR: cannot insert a non-DEFAULT value into column | f + ERROR: cannot insert a non-DEFAULT value into column | f +(2 rows) + +-- This should fail due to missing OVERRIDING SYSTEM VALUE. +SELECT result, success FROM run_command_on_workers($$ + SELECT create_null_dist_key.normalize_generate_always_as_error('INSERT INTO create_null_dist_key.identity_test (a, b) VALUES (1, 1)') +$$); + result | success +--------------------------------------------------------------------- + ERROR: cannot insert a non-DEFAULT value into column | f + ERROR: cannot insert a non-DEFAULT value into column | f +(2 rows) + +SELECT result, success FROM run_command_on_workers($$ + INSERT INTO create_null_dist_key.identity_test (a, b) OVERRIDING SYSTEM VALUE VALUES (7, 7) +$$); + result | success +--------------------------------------------------------------------- + INSERT 0 1 | t + INSERT 0 1 | t +(2 rows) + +SELECT result, success FROM run_command_on_workers($$ + INSERT INTO create_null_dist_key.identity_test (c, a) OVERRIDING SYSTEM VALUE VALUES (8, 8) +$$); + result | success +--------------------------------------------------------------------- + INSERT 0 1 | t + INSERT 0 1 | t +(2 rows) + +-- test foreign keys +CREATE TABLE referenced_table(a int UNIQUE, b int); +CREATE TABLE referencing_table(a int, b int, + FOREIGN KEY (a) REFERENCES referenced_table(a)); +-- to a colocated null dist key table +BEGIN; + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'referenced_table'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + + INSERT INTO referenced_table VALUES (1, 1); + INSERT INTO referencing_table VALUES (1, 2); + -- fails + INSERT INTO referencing_table VALUES (2, 2); +ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730049" +DETAIL: Key (a)=(2) is not present in table "referenced_table_xxxxxxx". +CONTEXT: while executing command on localhost:xxxxx +ROLLBACK; +-- to a non-colocated null dist key table +BEGIN; + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'none'); +ERROR: cannot create foreign key constraint since relations are not colocated or not referencing a reference table +DETAIL: A distributed table can only have foreign keys if it is referencing another colocated hash distributed table or a reference table +ROLLBACK; +-- to a sharded table +BEGIN; + SELECT create_distributed_table('referenced_table', 'a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null); +ERROR: cannot create foreign key constraint since relations are not colocated or not referencing a reference table +DETAIL: A distributed table can only have foreign keys if it is referencing another colocated hash distributed table or a reference table +ROLLBACK; +-- to a reference table +BEGIN; + SELECT create_reference_table('referenced_table'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + + INSERT INTO referenced_table VALUES (1, 1); + INSERT INTO referencing_table VALUES (1, 2); + -- fails + INSERT INTO referencing_table VALUES (2, 2); +ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730085" +DETAIL: Key (a)=(2) is not present in table "referenced_table_xxxxxxx". +CONTEXT: while executing command on localhost:xxxxx +ROLLBACK; +-- to a citus local table +BEGIN; + SELECT citus_add_local_table_to_metadata('referenced_table', cascade_via_foreign_keys=>true); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null); +ERROR: cannot create foreign key constraint since relations are not colocated or not referencing a reference table +DETAIL: A distributed table can only have foreign keys if it is referencing another colocated hash distributed table or a reference table +ROLLBACK; +-- to a postgres table +SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null); +ERROR: referenced table "referenced_table" must be a distributed table or a reference table +DETAIL: To enforce foreign keys, the referencing and referenced rows need to be stored on the same node. +HINT: You could use SELECT create_reference_table('referenced_table') to replicate the referenced table to all nodes or consider dropping the foreign key +-- from a reference table +BEGIN; + SELECT create_reference_table('referencing_table'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); +ERROR: cannot create foreign key constraint since foreign keys from reference tables and local tables to distributed tables are not supported +DETAIL: Reference tables and local tables can only have foreign keys to reference tables and local tables +ROLLBACK; +BEGIN; + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + + SELECT create_reference_table('referencing_table'); +ERROR: cannot create foreign key constraint since foreign keys from reference tables and local tables to distributed tables are not supported +DETAIL: Reference tables and local tables can only have foreign keys to reference tables and local tables +ROLLBACK; +-- from a sharded table +BEGIN; + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + + SELECT create_distributed_table('referencing_table', 'a'); +ERROR: cannot create foreign key constraint since relations are not colocated or not referencing a reference table +DETAIL: A distributed table can only have foreign keys if it is referencing another colocated hash distributed table or a reference table +ROLLBACK; +-- from a citus local table +BEGIN; + SELECT citus_add_local_table_to_metadata('referencing_table', cascade_via_foreign_keys=>true); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); +ERROR: cannot create foreign key constraint since foreign keys from reference tables and local tables to distributed tables are not supported +DETAIL: Reference tables and local tables can only have foreign keys to reference tables and local tables +ROLLBACK; +BEGIN; + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + + SELECT citus_add_local_table_to_metadata('referencing_table', cascade_via_foreign_keys=>true); +ERROR: cannot create foreign key constraint since foreign keys from reference tables and local tables to distributed tables are not supported +DETAIL: Reference tables and local tables can only have foreign keys to reference tables and local tables +ROLLBACK; +-- from a postgres table (only useful to preserve legacy behavior) +BEGIN; + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +ROLLBACK; +-- make sure that we enforce the foreign key constraint when inserting from workers too +SELECT create_reference_table('referenced_table'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO referenced_table VALUES (1, 1); +-- ok +SELECT result, success FROM run_command_on_workers($$ + INSERT INTO create_null_dist_key.referencing_table VALUES (1, 2) +$$); + result | success +--------------------------------------------------------------------- + INSERT 0 1 | t + INSERT 0 1 | t +(2 rows) + +-- fails +SELECT result, success FROM run_command_on_workers($$ + INSERT INTO create_null_dist_key.referencing_table VALUES (2, 2) +$$); + result | success +--------------------------------------------------------------------- + ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730102" | f + ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730102" | f +(2 rows) + +DROP TABLE referencing_table, referenced_table; +CREATE TABLE self_fkey_test(a int UNIQUE, b int, + FOREIGN KEY (b) REFERENCES self_fkey_test(a), + FOREIGN KEY (a) REFERENCES self_fkey_test(a)); +SELECT create_distributed_table('self_fkey_test', NULL, distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO self_fkey_test VALUES (1, 1); -- ok +INSERT INTO self_fkey_test VALUES (2, 3); -- fails +ERROR: insert or update on table "self_fkey_test_1730103" violates foreign key constraint "self_fkey_test_b_fkey_1730103" +DETAIL: Key (b)=(3) is not present in table "self_fkey_test_1730103". +CONTEXT: while executing command on localhost:xxxxx +-- similar foreign key tests but this time create the referencing table later on +-- referencing table is a null shard key table +-- to a colocated null dist key table +BEGIN; + CREATE TABLE referenced_table(a int UNIQUE, b int); + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + + CREATE TABLE referencing_table(a int, b int, FOREIGN KEY (a) REFERENCES referenced_table(a)); + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'referenced_table'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + + INSERT INTO referenced_table VALUES (1, 1); + INSERT INTO referencing_table VALUES (1, 2); + -- fails + INSERT INTO referencing_table VALUES (2, 2); +ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730105" +DETAIL: Key (a)=(2) is not present in table "referenced_table_xxxxxxx". +CONTEXT: while executing command on localhost:xxxxx +ROLLBACK; +BEGIN; + CREATE TABLE referenced_table(a int, b int, UNIQUE(b, a)); + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + + CREATE TABLE referencing_table(a int, b int, FOREIGN KEY (a, b) REFERENCES referenced_table(b, a)); + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'referenced_table'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + + INSERT INTO referenced_table VALUES (1, 2); + INSERT INTO referencing_table VALUES (2, 1); + -- fails + INSERT INTO referencing_table VALUES (1, 2); +ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_b_fkey_1730107" +DETAIL: Key (a, b)=(1, 2) is not present in table "referenced_table_xxxxxxx". +CONTEXT: while executing command on localhost:xxxxx +ROLLBACK; +BEGIN; + CREATE TABLE referenced_table(a int UNIQUE, b int); + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + + CREATE TABLE referencing_table(a int, b int, FOREIGN KEY (a) REFERENCES referenced_table(a) ON UPDATE SET NULL); + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'referenced_table'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + + INSERT INTO referenced_table VALUES (1, 1); + INSERT INTO referencing_table VALUES (1, 2); + UPDATE referenced_table SET a = 5; + SELECT * FROM referencing_table; + a | b +--------------------------------------------------------------------- + | 2 +(1 row) + +ROLLBACK; +BEGIN; + CREATE TABLE referenced_table(a int UNIQUE, b int); + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + + CREATE TABLE referencing_table(a serial, b int, FOREIGN KEY (a) REFERENCES referenced_table(a) ON UPDATE SET DEFAULT); + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'referenced_table'); +ERROR: cannot create foreign key constraint since Citus does not support ON DELETE / UPDATE SET DEFAULT actions on the columns that default to sequences +ROLLBACK; +-- to a non-colocated null dist key table +BEGIN; + CREATE TABLE referenced_table(a int UNIQUE, b int); + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + + CREATE TABLE referencing_table(a int, b int, FOREIGN KEY (a) REFERENCES referenced_table(a)); + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'none'); +ERROR: cannot create foreign key constraint since relations are not colocated or not referencing a reference table +DETAIL: A distributed table can only have foreign keys if it is referencing another colocated hash distributed table or a reference table +ROLLBACK; +-- to a sharded table +BEGIN; + CREATE TABLE referenced_table(a int UNIQUE, b int); + SELECT create_distributed_table('referenced_table', 'a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + + CREATE TABLE referencing_table(a int, b int, FOREIGN KEY (a) REFERENCES referenced_table(a)); + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'none'); +ERROR: cannot create foreign key constraint since relations are not colocated or not referencing a reference table +DETAIL: A distributed table can only have foreign keys if it is referencing another colocated hash distributed table or a reference table +ROLLBACK; +-- to a reference table +BEGIN; + CREATE TABLE referenced_table(a int UNIQUE, b int); + SELECT create_reference_table('referenced_table'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + + CREATE TABLE referencing_table(a int, b int, FOREIGN KEY (a) REFERENCES referenced_table(a)); + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + + INSERT INTO referenced_table VALUES (1, 1); + INSERT INTO referencing_table VALUES (1, 2); + -- fails + INSERT INTO referencing_table VALUES (2, 2); +ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730146" +DETAIL: Key (a)=(2) is not present in table "referenced_table_xxxxxxx". +CONTEXT: while executing command on localhost:xxxxx +ROLLBACK; +BEGIN; + CREATE TABLE referenced_table(a int UNIQUE, b int); + SELECT create_reference_table('referenced_table'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + + CREATE TABLE referencing_table(a int, b int, FOREIGN KEY (a) REFERENCES referenced_table(a) ON DELETE CASCADE); + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + + INSERT INTO referenced_table VALUES (1, 1); + INSERT INTO referencing_table VALUES (1, 2); + DELETE FROM referenced_table CASCADE; + SELECT * FROM referencing_table; + a | b +--------------------------------------------------------------------- +(0 rows) + +ROLLBACK; +-- to a citus local table +BEGIN; + CREATE TABLE referenced_table(a int UNIQUE, b int); + SELECT citus_add_local_table_to_metadata('referenced_table'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + + CREATE TABLE referencing_table(a int, b int, FOREIGN KEY (a) REFERENCES referenced_table(a)); + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'none'); +ERROR: cannot create foreign key constraint since relations are not colocated or not referencing a reference table +DETAIL: A distributed table can only have foreign keys if it is referencing another colocated hash distributed table or a reference table +ROLLBACK; +-- to a postgres table +BEGIN; + CREATE TABLE referenced_table(a int UNIQUE, b int); + CREATE TABLE referencing_table(a int, b int, FOREIGN KEY (a) REFERENCES referenced_table(a)); + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'none'); +ERROR: referenced table "referenced_table" must be a distributed table or a reference table +DETAIL: To enforce foreign keys, the referencing and referenced rows need to be stored on the same node. +HINT: You could use SELECT create_reference_table('referenced_table') to replicate the referenced table to all nodes or consider dropping the foreign key +ROLLBACK; +-- referenced table is a null shard key table +-- from a sharded table +BEGIN; + CREATE TABLE referenced_table(a int UNIQUE, b int); + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + + CREATE TABLE referencing_table(a int, b int, FOREIGN KEY (a) REFERENCES referenced_table(a)); + SELECT create_distributed_table('referencing_table', 'a'); +ERROR: cannot create foreign key constraint since relations are not colocated or not referencing a reference table +DETAIL: A distributed table can only have foreign keys if it is referencing another colocated hash distributed table or a reference table +ROLLBACK; +-- from a reference table +BEGIN; + CREATE TABLE referenced_table(a int UNIQUE, b int); + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + + CREATE TABLE referencing_table(a int, b int, FOREIGN KEY (a) REFERENCES referenced_table(a)); + SELECT create_reference_table('referencing_table'); +ERROR: cannot create foreign key constraint since foreign keys from reference tables and local tables to distributed tables are not supported +DETAIL: Reference tables and local tables can only have foreign keys to reference tables and local tables +ROLLBACK; +-- from a citus local table +BEGIN; + CREATE TABLE referenced_table(a int UNIQUE, b int); + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + + CREATE TABLE referencing_table(a int, b int, FOREIGN KEY (a) REFERENCES referenced_table(a)); + SELECT citus_add_local_table_to_metadata('referencing_table', cascade_via_foreign_keys=>true); +ERROR: cannot create foreign key constraint since foreign keys from reference tables and local tables to distributed tables are not supported +DETAIL: Reference tables and local tables can only have foreign keys to reference tables and local tables +ROLLBACK; +-- from a postgres table (only useful to preserve legacy behavior) +BEGIN; + CREATE TABLE referenced_table(a int UNIQUE, b int); + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + + CREATE TABLE referencing_table(a int, b int, FOREIGN KEY (a) REFERENCES referenced_table(a)); +ROLLBACK; +-- Test whether we switch to sequential execution to enforce foreign +-- key restrictions. +CREATE TABLE referenced_table(id int PRIMARY KEY, value_1 int); +SELECT create_reference_table('referenced_table'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE referencing_table(id int PRIMARY KEY, value_1 int, CONSTRAINT fkey FOREIGN KEY(value_1) REFERENCES referenced_table(id) ON UPDATE CASCADE); +SELECT create_distributed_table('referencing_table', null, colocate_with=>'none', distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SET client_min_messages TO DEBUG1; +BEGIN; + -- Switches to sequential execution because referenced_table is a reference table + -- and referenced by a null-shard-key distributed table. + -- + -- Given that we cannot do parallel access on null-shard-key, this is not useful. + -- However, this is already what we're doing for, e.g., a foreign key from a + -- reference table to another reference table. + TRUNCATE referenced_table CASCADE; +DEBUG: switching to sequential query execution mode +DETAIL: Table "referenced_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed tables due to foreign keys. Any parallel modification to those hash distributed tables in the same transaction can only be executed in sequential query execution mode +NOTICE: truncate cascades to table "referencing_table" +DEBUG: truncate cascades to table "referencing_table_xxxxxxx" +DETAIL: from localhost:xxxxx + SELECT COUNT(*) FROM referencing_table; + count +--------------------------------------------------------------------- + 0 +(1 row) + +COMMIT; +BEGIN; + SELECT COUNT(*) FROM referencing_table; + count +--------------------------------------------------------------------- + 0 +(1 row) + + -- Doesn't fail because the SELECT didn't perform parallel execution. + TRUNCATE referenced_table CASCADE; +DEBUG: switching to sequential query execution mode +DETAIL: Table "referenced_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed tables due to foreign keys. Any parallel modification to those hash distributed tables in the same transaction can only be executed in sequential query execution mode +NOTICE: truncate cascades to table "referencing_table" +DEBUG: truncate cascades to table "referencing_table_xxxxxxx" +DETAIL: from localhost:xxxxx +COMMIT; +BEGIN; + UPDATE referencing_table SET value_1 = 15; + -- Doesn't fail because the UPDATE didn't perform parallel execution. + TRUNCATE referenced_table CASCADE; +DEBUG: switching to sequential query execution mode +DETAIL: Table "referenced_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed tables due to foreign keys. Any parallel modification to those hash distributed tables in the same transaction can only be executed in sequential query execution mode +NOTICE: truncate cascades to table "referencing_table" +DEBUG: truncate cascades to table "referencing_table_xxxxxxx" +DETAIL: from localhost:xxxxx +COMMIT; +BEGIN; + SELECT COUNT(*) FROM referenced_table; + count +--------------------------------------------------------------------- + 0 +(1 row) + + -- doesn't switch to sequential execution + ALTER TABLE referencing_table ADD COLUMN X INT; +ROLLBACK; +BEGIN; + -- Switches to sequential execution because referenced_table is a reference table + -- and referenced by a null-shard-key distributed table. + -- + -- Given that we cannot do parallel access on null-shard-key, this is not useful. + -- However, this is already what we're doing for, e.g., a foreign key from a + -- reference table to another reference table. + UPDATE referenced_table SET id = 101 WHERE id = 99; +DEBUG: switching to sequential query execution mode +DETAIL: Table "referenced_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed tables due to foreign keys. Any parallel modification to those hash distributed tables in the same transaction can only be executed in sequential query execution mode + UPDATE referencing_table SET value_1 = 15; +ROLLBACK; +BEGIN; + UPDATE referencing_table SET value_1 = 15; + -- Doesn't fail because prior UPDATE didn't perform parallel execution. + UPDATE referenced_table SET id = 101 WHERE id = 99; +DEBUG: switching to sequential query execution mode +DETAIL: Table "referenced_table" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed tables due to foreign keys. Any parallel modification to those hash distributed tables in the same transaction can only be executed in sequential query execution mode +ROLLBACK; +SET client_min_messages TO WARNING; +DROP TABLE referenced_table, referencing_table; +-- Test whether we unnecessarily switch to sequential execution +-- when the referenced relation is a null-shard-key table. +CREATE TABLE referenced_table(id int PRIMARY KEY, value_1 int); +SELECT create_distributed_table('referenced_table', null, colocate_with=>'none', distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE referencing_table(id int PRIMARY KEY, value_1 int, CONSTRAINT fkey FOREIGN KEY(value_1) REFERENCES referenced_table(id) ON UPDATE CASCADE); +SELECT create_distributed_table('referencing_table', null, colocate_with=>'referenced_table', distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SET client_min_messages TO DEBUG1; +BEGIN; + SELECT COUNT(*) FROM referenced_table; + count +--------------------------------------------------------------------- + 0 +(1 row) + + -- Doesn't switch to sequential execution because the referenced_table is + -- a null-shard-key distributed table. + ALTER TABLE referencing_table ADD COLUMN X INT; +ROLLBACK; +BEGIN; + -- Doesn't switch to sequential execution because the referenced_table is + -- a null-shard-key distributed table. + TRUNCATE referenced_table CASCADE; +NOTICE: truncate cascades to table "referencing_table" +DEBUG: truncate cascades to table "referencing_table_xxxxxxx" +DETAIL: from localhost:xxxxx + SELECT COUNT(*) FROM referencing_table; + count +--------------------------------------------------------------------- + 0 +(1 row) + +COMMIT; +SET client_min_messages TO WARNING; +CREATE FUNCTION increment_value() RETURNS trigger AS $increment_value$ +BEGIN + NEW.value := NEW.value+1; + RETURN NEW; +END; +$increment_value$ LANGUAGE plpgsql; +CREATE TABLE trigger_table_1 (value int); +CREATE TRIGGER trigger_1 +BEFORE INSERT ON trigger_table_1 +FOR EACH ROW EXECUTE FUNCTION increment_value(); +SELECT create_distributed_table('trigger_table_1', NULL, distribution_type=>null); +ERROR: cannot distribute relation "trigger_table_1" because it has triggers +HINT: Consider dropping all the triggers on "trigger_table_1" and retry. +SET citus.enable_unsafe_triggers TO ON; +SELECT create_distributed_table('trigger_table_1', NULL, distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO trigger_table_1 VALUES(1), (2); +SELECT * FROM trigger_table_1 ORDER BY value; + value +--------------------------------------------------------------------- + 2 + 3 +(2 rows) + +CREATE FUNCTION insert_some() RETURNS trigger AS $insert_some$ +BEGIN + RAISE NOTICE 'inserted some rows'; + RETURN NEW; +END; +$insert_some$ LANGUAGE plpgsql; +CREATE TABLE trigger_table_2 (value int); +CREATE TRIGGER trigger_2 +AFTER INSERT ON trigger_table_2 +FOR EACH STATEMENT EXECUTE FUNCTION insert_some(); +ALTER TABLE trigger_table_2 DISABLE TRIGGER trigger_2; +SELECT create_distributed_table('trigger_table_2', NULL, distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SET client_min_messages TO NOTICE; +INSERT INTO trigger_table_2 VALUES(3), (4); +SET client_min_messages TO WARNING; +SELECT * FROM trigger_table_2 ORDER BY value; + value +--------------------------------------------------------------------- + 3 + 4 +(2 rows) + +CREATE FUNCTION combine_old_new_val() RETURNS trigger AS $combine_old_new_val$ +BEGIN + NEW.value = NEW.value * 10 + OLD.value; + RETURN NEW; +END; +$combine_old_new_val$ LANGUAGE plpgsql; +CREATE FUNCTION notice_truncate() RETURNS trigger AS $notice_truncate$ +BEGIN + RAISE NOTICE 'notice_truncate()'; + RETURN NEW; +END; +$notice_truncate$ LANGUAGE plpgsql; +CREATE TABLE trigger_table_3 (value int); +CREATE TRIGGER trigger_3 +BEFORE UPDATE ON trigger_table_3 +FOR EACH ROW EXECUTE FUNCTION combine_old_new_val(); +CREATE TRIGGER trigger_4 +AFTER TRUNCATE ON trigger_table_3 +FOR EACH STATEMENT EXECUTE FUNCTION notice_truncate(); +INSERT INTO trigger_table_3 VALUES(3), (4); +SELECT create_distributed_table('trigger_table_3', NULL, distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +UPDATE trigger_table_3 SET value = 5; +SELECT * FROM trigger_table_3 ORDER BY value; + value +--------------------------------------------------------------------- + 53 + 54 +(2 rows) + +SET client_min_messages TO NOTICE; +TRUNCATE trigger_table_3; +NOTICE: notice_truncate() +CONTEXT: PL/pgSQL function notice_truncate() line XX at RAISE +SET client_min_messages TO WARNING; +-- try a few simple queries at least to make sure that we don't crash +BEGIN; + INSERT INTO nullkey_c1_t1 SELECT * FROM nullkey_c2_t1; +ROLLBACK; +-- cleanup at exit +SET client_min_messages TO ERROR; +DROP SCHEMA create_null_dist_key, "NULL_!_dist_key" CASCADE; diff --git a/src/test/regress/expected/multi_colocation_utils.out b/src/test/regress/expected/multi_colocation_utils.out index 93596a1b4..219327dc1 100644 --- a/src/test/regress/expected/multi_colocation_utils.out +++ b/src/test/regress/expected/multi_colocation_utils.out @@ -612,10 +612,10 @@ CREATE TABLE table_postgresql( id int ); CREATE TABLE table_failing ( id int ); SELECT create_distributed_table('table_failing', 'id', colocate_with => 'table_append'); ERROR: cannot distribute relation -DETAIL: Currently, colocate_with option is only supported for hash distributed tables. +DETAIL: Currently, colocate_with option is not supported with append / range distributed tables and local tables added to metadata. SELECT create_distributed_table('table_failing', 'id', 'append', 'table1_groupE'); ERROR: cannot distribute relation -DETAIL: Currently, colocate_with option is only supported for hash distributed tables. +DETAIL: Currently, colocate_with option is not supported for append / range distributed tables. SELECT create_distributed_table('table_failing', 'id', colocate_with => 'table_postgresql'); ERROR: relation table_postgresql is not distributed SELECT create_distributed_table('table_failing', 'id', colocate_with => 'no_table'); diff --git a/src/test/regress/expected/multi_extension.out b/src/test/regress/expected/multi_extension.out index 0dd52e1ca..56de93802 100644 --- a/src/test/regress/expected/multi_extension.out +++ b/src/test/regress/expected/multi_extension.out @@ -1735,6 +1735,33 @@ HINT: To remove the local data, run: SELECT truncate_local_data_after_distribut DROP TABLE test; TRUNCATE pg_dist_node; +-- confirm that we can create a null shard key table on an empty node +CREATE TABLE test (x int, y int); +INSERT INTO test VALUES (1,2); +SET citus.shard_replication_factor TO 1; +SELECT create_distributed_table('test', null, colocate_with=>'none', distribution_type=>null); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$public.test$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- and make sure that we can't remove the coordinator due to "test" +SELECT citus_remove_node('localhost', :master_port); +ERROR: cannot remove or disable the node localhost:xxxxx because because it contains the only shard placement for shard xxxxx +DETAIL: One of the table(s) that prevents the operation complete successfully is public.test +HINT: To proceed, either drop the tables or use undistribute_table() function to convert them to local tables +DROP TABLE test; +-- and now we should be able to remove the coordinator +SELECT citus_remove_node('localhost', :master_port); + citus_remove_node +--------------------------------------------------------------------- + +(1 row) + -- confirm that we can create a reference table on an empty node CREATE TABLE test (x int, y int); INSERT INTO test VALUES (1,2); diff --git a/src/test/regress/expected/single_node.out b/src/test/regress/expected/single_node.out index 7f152decd..ecb652931 100644 --- a/src/test/regress/expected/single_node.out +++ b/src/test/regress/expected/single_node.out @@ -101,8 +101,63 @@ SELECT pg_reload_conf(); t (1 row) +CREATE TABLE single_node_nullkey_c1(a int, b int); +SELECT create_distributed_table('single_node_nullkey_c1', null, colocate_with=>'none', distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE single_node_nullkey_c2(a int, b int); +SELECT create_distributed_table('single_node_nullkey_c2', null, colocate_with=>'none', distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- created on different colocation groups .. +SELECT +( + SELECT colocationid FROM pg_dist_partition + WHERE logicalrelid = 'single_node.single_node_nullkey_c1'::regclass +) +!= +( + SELECT colocationid FROM pg_dist_partition + WHERE logicalrelid = 'single_node.single_node_nullkey_c2'::regclass +); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- .. but both are associated to coordinator +SELECT groupid = 0 FROM pg_dist_placement +WHERE shardid = ( + SELECT shardid FROM pg_dist_shard + WHERE logicalrelid = 'single_node.single_node_nullkey_c1'::regclass +); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SELECT groupid = 0 FROM pg_dist_placement +WHERE shardid = ( + SELECT shardid FROM pg_dist_shard + WHERE logicalrelid = 'single_node.single_node_nullkey_c2'::regclass +); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- try creating a null-shard-key distributed table from a shard relation +SELECT shardid AS round_robin_test_c1_shard_id FROM pg_dist_shard WHERE logicalrelid = 'single_node.single_node_nullkey_c1'::regclass \gset +SELECT create_distributed_table('single_node_nullkey_c1_' || :round_robin_test_c1_shard_id , null, colocate_with=>'none', distribution_type=>null); +ERROR: relation "single_node_nullkey_c1_90630532" is a shard relation SET client_min_messages TO WARNING; -DROP TABLE failover_to_local; +DROP TABLE failover_to_local, single_node_nullkey_c1, single_node_nullkey_c2; RESET client_min_messages; -- so that we don't have to update rest of the test output SET citus.next_shard_id TO 90630500; diff --git a/src/test/regress/expected/single_node_0.out b/src/test/regress/expected/single_node_0.out index a21cdd28a..86b4982e6 100644 --- a/src/test/regress/expected/single_node_0.out +++ b/src/test/regress/expected/single_node_0.out @@ -101,8 +101,63 @@ SELECT pg_reload_conf(); t (1 row) +CREATE TABLE single_node_nullkey_c1(a int, b int); +SELECT create_distributed_table('single_node_nullkey_c1', null, colocate_with=>'none', distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE single_node_nullkey_c2(a int, b int); +SELECT create_distributed_table('single_node_nullkey_c2', null, colocate_with=>'none', distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- created on different colocation groups .. +SELECT +( + SELECT colocationid FROM pg_dist_partition + WHERE logicalrelid = 'single_node.single_node_nullkey_c1'::regclass +) +!= +( + SELECT colocationid FROM pg_dist_partition + WHERE logicalrelid = 'single_node.single_node_nullkey_c2'::regclass +); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- .. but both are associated to coordinator +SELECT groupid = 0 FROM pg_dist_placement +WHERE shardid = ( + SELECT shardid FROM pg_dist_shard + WHERE logicalrelid = 'single_node.single_node_nullkey_c1'::regclass +); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SELECT groupid = 0 FROM pg_dist_placement +WHERE shardid = ( + SELECT shardid FROM pg_dist_shard + WHERE logicalrelid = 'single_node.single_node_nullkey_c2'::regclass +); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- try creating a null-shard-key distributed table from a shard relation +SELECT shardid AS round_robin_test_c1_shard_id FROM pg_dist_shard WHERE logicalrelid = 'single_node.single_node_nullkey_c1'::regclass \gset +SELECT create_distributed_table('single_node_nullkey_c1_' || :round_robin_test_c1_shard_id , null, colocate_with=>'none', distribution_type=>null); +ERROR: relation "single_node_nullkey_c1_90630532" is a shard relation SET client_min_messages TO WARNING; -DROP TABLE failover_to_local; +DROP TABLE failover_to_local, single_node_nullkey_c1, single_node_nullkey_c2; RESET client_min_messages; -- so that we don't have to update rest of the test output SET citus.next_shard_id TO 90630500; diff --git a/src/test/regress/multi_1_schedule b/src/test/regress/multi_1_schedule index cefb1777f..a673a71d0 100644 --- a/src/test/regress/multi_1_schedule +++ b/src/test/regress/multi_1_schedule @@ -32,6 +32,7 @@ test: escape_extension_name test: ref_citus_local_fkeys test: alter_database_owner test: distributed_triggers +test: create_null_dist_key test: multi_test_catalog_views test: multi_table_ddl diff --git a/src/test/regress/sql/create_null_dist_key.sql b/src/test/regress/sql/create_null_dist_key.sql new file mode 100644 index 000000000..b03cdde4d --- /dev/null +++ b/src/test/regress/sql/create_null_dist_key.sql @@ -0,0 +1,962 @@ +CREATE SCHEMA create_null_dist_key; +SET search_path TO create_null_dist_key; + +SET citus.next_shard_id TO 1720000; +SET citus.shard_count TO 32; +SET citus.shard_replication_factor TO 1; + +SELECT 1 FROM citus_remove_node('localhost', :worker_1_port); +SELECT 1 FROM citus_remove_node('localhost', :worker_2_port); + +CREATE TABLE add_node_test(a int, "b" text); + +-- add a node before creating the null-shard-key table +SELECT 1 FROM citus_add_node('localhost', :worker_1_port); + +SELECT create_distributed_table('add_node_test', null, colocate_with=>'none', distribution_type=>null); + +-- add a node after creating the null-shard-key table +SELECT 1 FROM citus_add_node('localhost', :worker_2_port); + +-- make sure that table is created on the worker nodes added before/after create_distributed_table +SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=1 FROM pg_class WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + relname='add_node_test' +$$); + +-- and check the metadata tables + +SELECT result FROM run_command_on_workers($$ + SELECT (partmethod, partkey, repmodel, autoconverted) FROM pg_dist_partition + WHERE logicalrelid = 'create_null_dist_key.add_node_test'::regclass +$$); + +SELECT result FROM run_command_on_workers($$ + SELECT (shardstorage, shardminvalue, shardmaxvalue) FROM pg_dist_shard + WHERE logicalrelid = 'create_null_dist_key.add_node_test'::regclass +$$); + +SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=1 FROM pg_dist_placement + WHERE shardid = ( + SELECT shardid FROM pg_dist_shard + WHERE logicalrelid = 'create_null_dist_key.add_node_test'::regclass + ); +$$); + +SELECT result FROM run_command_on_workers($$ + SELECT (shardcount, replicationfactor, distributioncolumntype, distributioncolumncollation) FROM pg_dist_colocation + WHERE colocationid = ( + SELECT colocationid FROM pg_dist_partition + WHERE logicalrelid = 'create_null_dist_key.add_node_test'::regclass + ); +$$); + +SET client_min_messages TO WARNING; +SELECT 1 FROM citus_add_node('localhost', :master_port, groupid => 0); + +SET client_min_messages TO NOTICE; + +CREATE TABLE invalid_configs_1(a int primary key); +SELECT create_distributed_table('invalid_configs_1', null, shard_count=>2); +SELECT create_distributed_table('invalid_configs_1', null, shard_count=>1); + +CREATE TABLE nullkey_c1_t1(a int, b int); +CREATE TABLE nullkey_c1_t2(a int, b int); +CREATE TABLE nullkey_c1_t3(a int, b int); +SELECT create_distributed_table('nullkey_c1_t1', null, colocate_with=>'none'); + +SELECT colocationid AS nullkey_c1_t1_colocation_id FROM pg_dist_partition WHERE logicalrelid = 'create_null_dist_key.nullkey_c1_t1'::regclass \gset + +BEGIN; + DROP TABLE nullkey_c1_t1; + -- make sure that we delete the colocation group after dropping the last table that belongs to it + SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = :'nullkey_c1_t1_colocation_id'; +ROLLBACK; + +SELECT create_distributed_table('nullkey_c1_t2', null, colocate_with=>'nullkey_c1_t1'); +SELECT create_distributed_table('nullkey_c1_t3', null, colocate_with=>'nullkey_c1_t1', distribution_type=>'append'); +SELECT create_distributed_table('nullkey_c1_t3', null, colocate_with=>'nullkey_c1_t1'); + +CREATE TABLE nullkey_c2_t1(a int, b int); +CREATE TABLE nullkey_c2_t2(a int, b int); +CREATE TABLE nullkey_c2_t3(a int, b int); + +-- create_distributed_table_concurrently is not yet supported yet +SELECT create_distributed_table_concurrently('nullkey_c2_t1', null); +SELECT create_distributed_table_concurrently('nullkey_c2_t1', null, colocate_with=>'none'); + +SELECT create_distributed_table('nullkey_c2_t1', null, colocate_with=>'none'); +SELECT create_distributed_table('nullkey_c2_t2', null, colocate_with=>'nullkey_c2_t1', distribution_type=>'hash'); -- distribution_type is ignored anyway +SELECT create_distributed_table('nullkey_c2_t3', null, colocate_with=>'nullkey_c2_t2', distribution_type=>null); + +-- check the metadata for the colocated tables whose names start with nullkey_c1_ + +SELECT logicalrelid, partmethod, partkey, repmodel, autoconverted FROM pg_dist_partition +WHERE logicalrelid IN ( + SELECT oid FROM pg_class + WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + relname LIKE 'nullkey_c1_%' +) +ORDER BY 1; + +-- make sure that all those 3 tables belong to same colocation group +SELECT COUNT(*) FROM pg_dist_partition +WHERE logicalrelid IN ( + SELECT oid FROM pg_class + WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + relname LIKE 'nullkey_c1_%' +) +GROUP BY colocationid; + +SELECT logicalrelid, shardstorage, shardminvalue, shardmaxvalue FROM pg_dist_shard +WHERE logicalrelid IN ( + SELECT oid FROM pg_class + WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + relname LIKE 'nullkey_c1_%' +) +ORDER BY 1; + +-- make sure that all those 3 shards are created on the same node group +SELECT COUNT(*) FROM pg_dist_placement +WHERE shardid IN ( + SELECT shardid FROM pg_dist_shard + WHERE logicalrelid IN ( + SELECT oid FROM pg_class + WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + relname LIKE 'nullkey_c1_%' + ) +) +GROUP BY groupid; + +-- check the metadata for the colocated tables whose names start with nullkey_c2_ + +SELECT logicalrelid, partmethod, partkey, repmodel, autoconverted FROM pg_dist_partition +WHERE logicalrelid IN ( + SELECT oid FROM pg_class + WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + relname LIKE 'nullkey_c2_%' +) +ORDER BY 1; + +-- make sure that all those 3 tables belong to same colocation group +SELECT COUNT(*) FROM pg_dist_partition +WHERE logicalrelid IN ( + SELECT oid FROM pg_class + WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + relname LIKE 'nullkey_c2_%' +) +GROUP BY colocationid; + +SELECT logicalrelid, shardstorage, shardminvalue, shardmaxvalue FROM pg_dist_shard +WHERE logicalrelid IN ( + SELECT oid FROM pg_class + WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + relname LIKE 'nullkey_c2_%' +) +ORDER BY 1; + +-- make sure that all those 3 shards created on the same node group +SELECT COUNT(*) FROM pg_dist_placement +WHERE shardid IN ( + SELECT shardid FROM pg_dist_shard + WHERE logicalrelid IN ( + SELECT oid FROM pg_class + WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + relname LIKE 'nullkey_c2_%' + ) +) +GROUP BY groupid; + +-- Make sure that the colocated tables whose names start with nullkey_c1_ +-- belong to a different colocation group than the ones whose names start +-- with nullkey_c2_. +-- +-- It's ok to only compare nullkey_c1_t1 and nullkey_c2_t1 because we already +-- verified that null_dist_key.nullkey_c1_t1 is colocated with the other two +-- and null_dist_key.nullkey_c2_t1 is colocated with the other two. +SELECT +( + SELECT colocationid FROM pg_dist_partition + WHERE logicalrelid = 'create_null_dist_key.nullkey_c1_t1'::regclass +) +!= +( + SELECT colocationid FROM pg_dist_partition + WHERE logicalrelid = 'create_null_dist_key.nullkey_c2_t1'::regclass +); + +-- Since we determine node for the placement based on the module of colocation id, +-- we don't expect those two colocation groups to get assigned to same node. +SELECT +( + SELECT groupid FROM pg_dist_placement + WHERE shardid = ( + SELECT shardid FROM pg_dist_shard + WHERE logicalrelid = 'create_null_dist_key.nullkey_c1_t1'::regclass + ) +) +!= +( + SELECT groupid FROM pg_dist_placement + WHERE shardid = ( + SELECT shardid FROM pg_dist_shard + WHERE logicalrelid = 'create_null_dist_key.nullkey_c2_t1'::regclass + ) +); + +-- It's ok to only check nullkey_c1_t1 and nullkey_c2_t1 because we already +-- verified that null_dist_key.nullkey_c1_t1 is colocated with the other two +-- and null_dist_key.nullkey_c2_t1 is colocated with the other two. +SELECT shardcount, replicationfactor, distributioncolumntype, distributioncolumncollation FROM pg_dist_colocation +WHERE colocationid = ( + SELECT colocationid FROM pg_dist_partition + WHERE logicalrelid = 'create_null_dist_key.nullkey_c1_t1'::regclass +); + +SELECT shardcount, replicationfactor, distributioncolumntype, distributioncolumncollation FROM pg_dist_colocation +WHERE colocationid = ( + SELECT colocationid FROM pg_dist_partition + WHERE logicalrelid = 'create_null_dist_key.nullkey_c2_t1'::regclass +); + +CREATE TABLE round_robin_test_c1(a int, b int); +SELECT create_distributed_table('round_robin_test_c1', null, colocate_with=>'none', distribution_type=>null); + +\c - - - :master_port +SET search_path TO create_null_dist_key; +SET citus.next_shard_id TO 1730000; +SET citus.shard_count TO 32; +SET citus.shard_replication_factor TO 1; +SET client_min_messages TO NOTICE; + +CREATE TABLE round_robin_test_c2(a int, b int); +SELECT create_distributed_table('round_robin_test_c2', null, colocate_with=>'none', distribution_type=>null); + +-- Since we determine node for the placement based on the module of colocation id, +-- we don't expect those two colocation groups to get assigned to same node even +-- after reconnecting to the coordinator. +SELECT +( + SELECT groupid FROM pg_dist_placement + WHERE shardid = ( + SELECT shardid FROM pg_dist_shard + WHERE logicalrelid = 'create_null_dist_key.round_robin_test_c1'::regclass + ) +) +!= +( + SELECT groupid FROM pg_dist_placement + WHERE shardid = ( + SELECT shardid FROM pg_dist_shard + WHERE logicalrelid = 'create_null_dist_key.round_robin_test_c2'::regclass + ) +); + +CREATE TABLE distributed_table(a int, b int); + +-- cannot colocate a sharded table with null shard key table +SELECT create_distributed_table('distributed_table', 'a', colocate_with=>'nullkey_c1_t1'); + +CREATE TABLE reference_table(a int, b int); +CREATE TABLE local(a int, b int); +SELECT create_reference_table('reference_table'); +SELECT create_distributed_table('distributed_table', 'a'); + +-- cannot colocate null shard key tables with other table types +CREATE TABLE cannot_colocate_with_other_types (a int, b int); +SELECT create_distributed_table('cannot_colocate_with_other_types', null, colocate_with=>'reference_table'); +SELECT create_distributed_table('cannot_colocate_with_other_types', null, colocate_with=>'distributed_table'); +SELECT create_distributed_table('cannot_colocate_with_other_types', null, colocate_with=>'local'); -- postgres local + +SELECT citus_add_local_table_to_metadata('local'); + +-- cannot colocate null shard key tables with citus local tables +SELECT create_distributed_table('cannot_colocate_with_other_types', null, colocate_with=>'local'); -- citus local + +SET client_min_messages TO WARNING; + +-- can't create such a distributed table from another Citus table, except Citus local tables +SELECT create_distributed_table('reference_table', null, colocate_with=>'none'); +SELECT create_distributed_table('distributed_table', null, colocate_with=>'none'); +SELECT create_distributed_table('local', null, colocate_with=>'none'); + +BEGIN; + -- creating a null-shard-key table from a temporary table is not supported + CREATE TEMPORARY TABLE temp_table (a int); + SELECT create_distributed_table('temp_table', null, colocate_with=>'none', distribution_type=>null); +ROLLBACK; + +-- creating a null-shard-key table from a catalog table is not supported +SELECT create_distributed_table('pg_catalog.pg_index', NULL, distribution_type=>null); + +-- creating a null-shard-key table from an unlogged table is supported +CREATE UNLOGGED TABLE unlogged_table (a int); +SELECT create_distributed_table('unlogged_table', null, colocate_with=>'none', distribution_type=>null); + +-- creating a null-shard-key table from a foreign table is not supported +CREATE FOREIGN TABLE foreign_table ( + id bigint not null, + full_name text not null default '' +) SERVER fake_fdw_server OPTIONS (encoding 'utf-8', compression 'true', table_name 'foreign_table'); +SELECT create_distributed_table('foreign_table', null, colocate_with=>'none', distribution_type=>null); + +-- create a null dist key table that has no tuples +CREATE TABLE null_dist_key_table_1 (a int primary key); +SELECT create_distributed_table('null_dist_key_table_1', null, colocate_with=>'none'); + +-- create a null dist key table that has some tuples +CREATE TABLE null_dist_key_table_2(a int primary key); +INSERT INTO null_dist_key_table_2 VALUES(1); +SELECT create_distributed_table('null_dist_key_table_2', null, colocate_with=>'none'); + +SELECT * FROM null_dist_key_table_2 ORDER BY a; + +DROP TABLE null_dist_key_table_1, null_dist_key_table_2; + +-- create indexes before creating the null dist key tables + +-- .. for an initially empty table +CREATE TABLE null_dist_key_table_1(a int); +CREATE INDEX null_dist_key_table_1_idx ON null_dist_key_table_1(a); +SELECT create_distributed_table('null_dist_key_table_1', null, colocate_with=>'none'); + +-- .. and for another table having data in it before creating null dist key table +CREATE TABLE null_dist_key_table_2(a int); +INSERT INTO null_dist_key_table_2 VALUES(1); +CREATE INDEX null_dist_key_table_2_idx ON null_dist_key_table_2(a); +SELECT create_distributed_table('null_dist_key_table_2', null, colocate_with=>'none'); + +SELECT * FROM null_dist_key_table_2 ORDER BY a; + +-- show that we do not support inheritance relationships +CREATE TABLE parent_table (a int, b text); +CREATE TABLE child_table () INHERITS (parent_table); + +-- both of below should error out +SELECT create_distributed_table('parent_table', null, colocate_with=>'none'); +SELECT create_distributed_table('child_table', null, colocate_with=>'none'); + +-- show that we support policies +BEGIN; + CREATE TABLE null_dist_key_table_3 (table_user text); + + ALTER TABLE null_dist_key_table_3 ENABLE ROW LEVEL SECURITY; + + CREATE ROLE table_users; + CREATE POLICY table_policy ON null_dist_key_table_3 TO table_users + USING (table_user = current_user); + + SELECT create_distributed_table('null_dist_key_table_3', null, colocate_with=>'none'); +ROLLBACK; + +-- drop them for next tests +DROP TABLE null_dist_key_table_1, null_dist_key_table_2, distributed_table; + +-- tests for object names that should be escaped properly + +CREATE SCHEMA "NULL_!_dist_key"; + +CREATE TABLE "NULL_!_dist_key"."my_TABLE.1!?!"(id int, "Second_Id" int); +SELECT create_distributed_table('"NULL_!_dist_key"."my_TABLE.1!?!"', null, colocate_with=>'none'); + +-- drop the table before creating it when the search path is set +SET search_path to "NULL_!_dist_key" ; +DROP TABLE "my_TABLE.1!?!"; + +CREATE TYPE int_jsonb_type AS (key int, value jsonb); +CREATE DOMAIN age_with_default AS int CHECK (value >= 0) DEFAULT 0; +CREATE TYPE yes_no_enum AS ENUM ('yes', 'no'); +CREATE EXTENSION btree_gist; + +CREATE SEQUENCE my_seq_1 START WITH 10; + +CREATE TABLE "Table?!.1Table"( + id int PRIMARY KEY, + "Second_Id" int, + "local_Type" int_jsonb_type, + "jsondata" jsonb NOT NULL, + name text, + price numeric CHECK (price > 0), + age_with_default_col age_with_default, + yes_no_enum_col yes_no_enum, + seq_col_1 bigserial, + seq_col_2 int DEFAULT nextval('my_seq_1'), + generated_column int GENERATED ALWAYS AS (seq_col_1 * seq_col_2 + 4) STORED, + UNIQUE (id, price), + EXCLUDE USING GIST (name WITH =)); + +-- create some objects before create_distributed_table +CREATE INDEX "my!Index1" ON "Table?!.1Table"(id) WITH ( fillfactor = 80 ) WHERE id > 10; +CREATE INDEX text_index ON "Table?!.1Table"(name); +CREATE UNIQUE INDEX uniqueIndex ON "Table?!.1Table" (id); +CREATE STATISTICS stats_1 ON id, price FROM "Table?!.1Table"; + +CREATE TEXT SEARCH CONFIGURATION text_search_cfg (parser = default); +CREATE INDEX text_search_index ON "Table?!.1Table" +USING gin (to_tsvector('text_search_cfg'::regconfig, (COALESCE(name, ''::character varying))::text)); + +-- ingest some data before create_distributed_table +INSERT INTO "Table?!.1Table" VALUES (1, 1, (1, row_to_json(row(1,1)))::int_jsonb_type, row_to_json(row(1,1), true)), + (2, 1, (2, row_to_json(row(2,2)))::int_jsonb_type, row_to_json(row(2,2), 'false')); + +-- create a replica identity before create_distributed_table +ALTER TABLE "Table?!.1Table" REPLICA IDENTITY USING INDEX uniqueIndex; + +SELECT create_distributed_table('"Table?!.1Table"', null, colocate_with=>'none'); + +INSERT INTO "Table?!.1Table" VALUES (10, 15, (150, row_to_json(row(4,8)))::int_jsonb_type, '{}', 'text_1', 10, 27, 'yes', 60, 70); +INSERT INTO "Table?!.1Table" VALUES (5, 5, (5, row_to_json(row(5,5)))::int_jsonb_type, row_to_json(row(5,5), true)); + +-- tuples that are supposed to violate different data type / check constraints +INSERT INTO "Table?!.1Table"(id, jsondata, name) VALUES (101, '{"a": 1}', 'text_1'); +INSERT INTO "Table?!.1Table"(id, jsondata, price) VALUES (101, '{"a": 1}', -1); +INSERT INTO "Table?!.1Table"(id, jsondata, age_with_default_col) VALUES (101, '{"a": 1}', -1); +INSERT INTO "Table?!.1Table"(id, jsondata, yes_no_enum_col) VALUES (101, '{"a": 1}', 'what?'); + +SELECT * FROM "Table?!.1Table" ORDER BY id; + +SET search_path TO create_null_dist_key; + +-- create a partitioned table with some columns that +-- are going to be dropped within the tests +CREATE TABLE sensors( + col_to_drop_1 text, + measureid integer, + eventdatetime date, + measure_data jsonb, +PRIMARY KEY (measureid, eventdatetime, measure_data)) +PARTITION BY RANGE(eventdatetime); + +-- drop column even before attaching any partitions +ALTER TABLE sensors DROP COLUMN col_to_drop_1; + +CREATE TABLE sensors_2000 PARTITION OF sensors FOR VALUES FROM ('2000-01-01') TO ('2001-01-01'); + +-- cannot distribute child table without distributing the parent +SELECT create_distributed_table('sensors_2000', NULL, distribution_type=>null); + +SELECT create_distributed_table('sensors', NULL, distribution_type=>null); + +CREATE TABLE multi_level_partitioning_parent( + measureid integer, + eventdatetime date, + measure_data jsonb) +PARTITION BY RANGE(eventdatetime); + +CREATE TABLE multi_level_partitioning_level_1( + measureid integer, + eventdatetime date, + measure_data jsonb) +PARTITION BY RANGE(eventdatetime); + +ALTER TABLE multi_level_partitioning_parent ATTACH PARTITION multi_level_partitioning_level_1 +FOR VALUES FROM ('2000-01-01') TO ('2001-01-01'); + +CREATE TABLE multi_level_partitioning_level_2 PARTITION OF multi_level_partitioning_level_1 +FOR VALUES FROM ('2000-01-01') TO ('2000-06-06'); + +-- multi-level partitioning is not supported +SELECT create_distributed_table('multi_level_partitioning_parent', NULL, distribution_type=>null); + +CREATE FUNCTION normalize_generate_always_as_error(query text) RETURNS void AS $$ +BEGIN + EXECUTE query; + EXCEPTION WHEN OTHERS THEN + IF SQLERRM LIKE 'cannot insert into column %' OR + SQLERRM LIKE 'cannot insert a non-DEFAULT value into column %' + THEN + RAISE 'cannot insert a non-DEFAULT value into column'; + ELSE + RAISE 'unknown error'; + END IF; +END; +$$LANGUAGE plpgsql; + +CREATE TABLE identity_test ( + a int GENERATED BY DEFAULT AS IDENTITY (START WITH 10 INCREMENT BY 10), + b bigint GENERATED ALWAYS AS IDENTITY (START WITH 100 INCREMENT BY 100), + c bigint GENERATED BY DEFAULT AS IDENTITY (START WITH 1000 INCREMENT BY 1000) +); + +SELECT create_distributed_table('identity_test', NULL, distribution_type=>null); + +DROP TABLE identity_test; + +-- Above failed because we don't support using a data type other than BIGINT +-- for identity columns, so drop the table and create a new one with BIGINT +-- identity columns. +CREATE TABLE identity_test ( + a bigint GENERATED BY DEFAULT AS IDENTITY (START WITH 10 INCREMENT BY 10), + b bigint GENERATED ALWAYS AS IDENTITY (START WITH 100 INCREMENT BY 100), + c bigint GENERATED BY DEFAULT AS IDENTITY (START WITH 1000 INCREMENT BY 1000) +); + +SELECT create_distributed_table('identity_test', NULL, distribution_type=>null); + +INSERT INTO identity_test (a) VALUES (5); + +SELECT normalize_generate_always_as_error($$INSERT INTO identity_test (b) VALUES (5)$$); -- fails due to missing OVERRIDING SYSTEM VALUE +INSERT INTO identity_test (b) OVERRIDING SYSTEM VALUE VALUES (5); + +INSERT INTO identity_test (c) VALUES (5); + +SELECT result, success FROM run_command_on_workers($$ + INSERT INTO create_null_dist_key.identity_test (a) VALUES (6) +$$); + +SELECT result, success FROM run_command_on_workers($$ + SELECT create_null_dist_key.normalize_generate_always_as_error('INSERT INTO create_null_dist_key.identity_test (b) VALUES (1)') +$$); + +-- This should fail due to missing OVERRIDING SYSTEM VALUE. +SELECT result, success FROM run_command_on_workers($$ + SELECT create_null_dist_key.normalize_generate_always_as_error('INSERT INTO create_null_dist_key.identity_test (a, b) VALUES (1, 1)') +$$); + +SELECT result, success FROM run_command_on_workers($$ + INSERT INTO create_null_dist_key.identity_test (a, b) OVERRIDING SYSTEM VALUE VALUES (7, 7) +$$); + +SELECT result, success FROM run_command_on_workers($$ + INSERT INTO create_null_dist_key.identity_test (c, a) OVERRIDING SYSTEM VALUE VALUES (8, 8) +$$); + +-- test foreign keys + +CREATE TABLE referenced_table(a int UNIQUE, b int); +CREATE TABLE referencing_table(a int, b int, + FOREIGN KEY (a) REFERENCES referenced_table(a)); + +-- to a colocated null dist key table +BEGIN; + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'referenced_table'); + + INSERT INTO referenced_table VALUES (1, 1); + INSERT INTO referencing_table VALUES (1, 2); + + -- fails + INSERT INTO referencing_table VALUES (2, 2); +ROLLBACK; + +-- to a non-colocated null dist key table +BEGIN; + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'none'); +ROLLBACK; + +-- to a sharded table +BEGIN; + SELECT create_distributed_table('referenced_table', 'a'); + + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null); +ROLLBACK; + +-- to a reference table +BEGIN; + SELECT create_reference_table('referenced_table'); + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null); + + INSERT INTO referenced_table VALUES (1, 1); + INSERT INTO referencing_table VALUES (1, 2); + + -- fails + INSERT INTO referencing_table VALUES (2, 2); +ROLLBACK; + +-- to a citus local table +BEGIN; + SELECT citus_add_local_table_to_metadata('referenced_table', cascade_via_foreign_keys=>true); + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null); +ROLLBACK; + +-- to a postgres table +SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null); + +-- from a reference table +BEGIN; + SELECT create_reference_table('referencing_table'); + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); +ROLLBACK; + +BEGIN; + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); + SELECT create_reference_table('referencing_table'); +ROLLBACK; + +-- from a sharded table +BEGIN; + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); + SELECT create_distributed_table('referencing_table', 'a'); +ROLLBACK; + +-- from a citus local table +BEGIN; + SELECT citus_add_local_table_to_metadata('referencing_table', cascade_via_foreign_keys=>true); + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); +ROLLBACK; + +BEGIN; + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); + SELECT citus_add_local_table_to_metadata('referencing_table', cascade_via_foreign_keys=>true); +ROLLBACK; + +-- from a postgres table (only useful to preserve legacy behavior) +BEGIN; + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); +ROLLBACK; + +-- make sure that we enforce the foreign key constraint when inserting from workers too +SELECT create_reference_table('referenced_table'); +SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null); +INSERT INTO referenced_table VALUES (1, 1); +-- ok +SELECT result, success FROM run_command_on_workers($$ + INSERT INTO create_null_dist_key.referencing_table VALUES (1, 2) +$$); +-- fails +SELECT result, success FROM run_command_on_workers($$ + INSERT INTO create_null_dist_key.referencing_table VALUES (2, 2) +$$); + +DROP TABLE referencing_table, referenced_table; + +CREATE TABLE self_fkey_test(a int UNIQUE, b int, + FOREIGN KEY (b) REFERENCES self_fkey_test(a), + FOREIGN KEY (a) REFERENCES self_fkey_test(a)); +SELECT create_distributed_table('self_fkey_test', NULL, distribution_type=>null); + +INSERT INTO self_fkey_test VALUES (1, 1); -- ok +INSERT INTO self_fkey_test VALUES (2, 3); -- fails + +-- similar foreign key tests but this time create the referencing table later on + +-- referencing table is a null shard key table + +-- to a colocated null dist key table +BEGIN; + CREATE TABLE referenced_table(a int UNIQUE, b int); + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); + + CREATE TABLE referencing_table(a int, b int, FOREIGN KEY (a) REFERENCES referenced_table(a)); + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'referenced_table'); + + INSERT INTO referenced_table VALUES (1, 1); + INSERT INTO referencing_table VALUES (1, 2); + + -- fails + INSERT INTO referencing_table VALUES (2, 2); +ROLLBACK; + +BEGIN; + CREATE TABLE referenced_table(a int, b int, UNIQUE(b, a)); + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); + + CREATE TABLE referencing_table(a int, b int, FOREIGN KEY (a, b) REFERENCES referenced_table(b, a)); + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'referenced_table'); + + INSERT INTO referenced_table VALUES (1, 2); + INSERT INTO referencing_table VALUES (2, 1); + + -- fails + INSERT INTO referencing_table VALUES (1, 2); +ROLLBACK; + +BEGIN; + CREATE TABLE referenced_table(a int UNIQUE, b int); + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); + + CREATE TABLE referencing_table(a int, b int, FOREIGN KEY (a) REFERENCES referenced_table(a) ON UPDATE SET NULL); + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'referenced_table'); + + INSERT INTO referenced_table VALUES (1, 1); + INSERT INTO referencing_table VALUES (1, 2); + + UPDATE referenced_table SET a = 5; + SELECT * FROM referencing_table; +ROLLBACK; + +BEGIN; + CREATE TABLE referenced_table(a int UNIQUE, b int); + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); + + CREATE TABLE referencing_table(a serial, b int, FOREIGN KEY (a) REFERENCES referenced_table(a) ON UPDATE SET DEFAULT); + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'referenced_table'); +ROLLBACK; + +-- to a non-colocated null dist key table +BEGIN; + CREATE TABLE referenced_table(a int UNIQUE, b int); + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); + + CREATE TABLE referencing_table(a int, b int, FOREIGN KEY (a) REFERENCES referenced_table(a)); + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'none'); +ROLLBACK; + +-- to a sharded table +BEGIN; + CREATE TABLE referenced_table(a int UNIQUE, b int); + SELECT create_distributed_table('referenced_table', 'a'); + + CREATE TABLE referencing_table(a int, b int, FOREIGN KEY (a) REFERENCES referenced_table(a)); + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'none'); +ROLLBACK; + +-- to a reference table +BEGIN; + CREATE TABLE referenced_table(a int UNIQUE, b int); + SELECT create_reference_table('referenced_table'); + + CREATE TABLE referencing_table(a int, b int, FOREIGN KEY (a) REFERENCES referenced_table(a)); + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'none'); + + INSERT INTO referenced_table VALUES (1, 1); + INSERT INTO referencing_table VALUES (1, 2); + + -- fails + INSERT INTO referencing_table VALUES (2, 2); +ROLLBACK; + +BEGIN; + CREATE TABLE referenced_table(a int UNIQUE, b int); + SELECT create_reference_table('referenced_table'); + + CREATE TABLE referencing_table(a int, b int, FOREIGN KEY (a) REFERENCES referenced_table(a) ON DELETE CASCADE); + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'none'); + + INSERT INTO referenced_table VALUES (1, 1); + INSERT INTO referencing_table VALUES (1, 2); + + DELETE FROM referenced_table CASCADE; + SELECT * FROM referencing_table; +ROLLBACK; + +-- to a citus local table +BEGIN; + CREATE TABLE referenced_table(a int UNIQUE, b int); + SELECT citus_add_local_table_to_metadata('referenced_table'); + + CREATE TABLE referencing_table(a int, b int, FOREIGN KEY (a) REFERENCES referenced_table(a)); + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'none'); +ROLLBACK; + +-- to a postgres table +BEGIN; + CREATE TABLE referenced_table(a int UNIQUE, b int); + CREATE TABLE referencing_table(a int, b int, FOREIGN KEY (a) REFERENCES referenced_table(a)); + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'none'); +ROLLBACK; + +-- referenced table is a null shard key table + +-- from a sharded table +BEGIN; + CREATE TABLE referenced_table(a int UNIQUE, b int); + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null, colocate_with=>'none'); + + CREATE TABLE referencing_table(a int, b int, FOREIGN KEY (a) REFERENCES referenced_table(a)); + SELECT create_distributed_table('referencing_table', 'a'); +ROLLBACK; + +-- from a reference table +BEGIN; + CREATE TABLE referenced_table(a int UNIQUE, b int); + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null, colocate_with=>'none'); + + CREATE TABLE referencing_table(a int, b int, FOREIGN KEY (a) REFERENCES referenced_table(a)); + SELECT create_reference_table('referencing_table'); +ROLLBACK; + +-- from a citus local table +BEGIN; + CREATE TABLE referenced_table(a int UNIQUE, b int); + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null, colocate_with=>'none'); + + CREATE TABLE referencing_table(a int, b int, FOREIGN KEY (a) REFERENCES referenced_table(a)); + SELECT citus_add_local_table_to_metadata('referencing_table', cascade_via_foreign_keys=>true); +ROLLBACK; + +-- from a postgres table (only useful to preserve legacy behavior) +BEGIN; + CREATE TABLE referenced_table(a int UNIQUE, b int); + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null, colocate_with=>'none'); + + CREATE TABLE referencing_table(a int, b int, FOREIGN KEY (a) REFERENCES referenced_table(a)); +ROLLBACK; + +-- Test whether we switch to sequential execution to enforce foreign +-- key restrictions. + +CREATE TABLE referenced_table(id int PRIMARY KEY, value_1 int); +SELECT create_reference_table('referenced_table'); + +CREATE TABLE referencing_table(id int PRIMARY KEY, value_1 int, CONSTRAINT fkey FOREIGN KEY(value_1) REFERENCES referenced_table(id) ON UPDATE CASCADE); +SELECT create_distributed_table('referencing_table', null, colocate_with=>'none', distribution_type=>null); + +SET client_min_messages TO DEBUG1; + +BEGIN; + -- Switches to sequential execution because referenced_table is a reference table + -- and referenced by a null-shard-key distributed table. + -- + -- Given that we cannot do parallel access on null-shard-key, this is not useful. + -- However, this is already what we're doing for, e.g., a foreign key from a + -- reference table to another reference table. + TRUNCATE referenced_table CASCADE; + SELECT COUNT(*) FROM referencing_table; +COMMIT; + +BEGIN; + SELECT COUNT(*) FROM referencing_table; + -- Doesn't fail because the SELECT didn't perform parallel execution. + TRUNCATE referenced_table CASCADE; +COMMIT; + +BEGIN; + UPDATE referencing_table SET value_1 = 15; + -- Doesn't fail because the UPDATE didn't perform parallel execution. + TRUNCATE referenced_table CASCADE; +COMMIT; + +BEGIN; + SELECT COUNT(*) FROM referenced_table; + -- doesn't switch to sequential execution + ALTER TABLE referencing_table ADD COLUMN X INT; +ROLLBACK; + +BEGIN; + -- Switches to sequential execution because referenced_table is a reference table + -- and referenced by a null-shard-key distributed table. + -- + -- Given that we cannot do parallel access on null-shard-key, this is not useful. + -- However, this is already what we're doing for, e.g., a foreign key from a + -- reference table to another reference table. + UPDATE referenced_table SET id = 101 WHERE id = 99; + UPDATE referencing_table SET value_1 = 15; +ROLLBACK; + +BEGIN; + UPDATE referencing_table SET value_1 = 15; + -- Doesn't fail because prior UPDATE didn't perform parallel execution. + UPDATE referenced_table SET id = 101 WHERE id = 99; +ROLLBACK; + +SET client_min_messages TO WARNING; + +DROP TABLE referenced_table, referencing_table; + +-- Test whether we unnecessarily switch to sequential execution +-- when the referenced relation is a null-shard-key table. + +CREATE TABLE referenced_table(id int PRIMARY KEY, value_1 int); +SELECT create_distributed_table('referenced_table', null, colocate_with=>'none', distribution_type=>null); + +CREATE TABLE referencing_table(id int PRIMARY KEY, value_1 int, CONSTRAINT fkey FOREIGN KEY(value_1) REFERENCES referenced_table(id) ON UPDATE CASCADE); +SELECT create_distributed_table('referencing_table', null, colocate_with=>'referenced_table', distribution_type=>null); + +SET client_min_messages TO DEBUG1; + +BEGIN; + SELECT COUNT(*) FROM referenced_table; + -- Doesn't switch to sequential execution because the referenced_table is + -- a null-shard-key distributed table. + ALTER TABLE referencing_table ADD COLUMN X INT; +ROLLBACK; + +BEGIN; + -- Doesn't switch to sequential execution because the referenced_table is + -- a null-shard-key distributed table. + TRUNCATE referenced_table CASCADE; + SELECT COUNT(*) FROM referencing_table; +COMMIT; + +SET client_min_messages TO WARNING; + +CREATE FUNCTION increment_value() RETURNS trigger AS $increment_value$ +BEGIN + NEW.value := NEW.value+1; + RETURN NEW; +END; +$increment_value$ LANGUAGE plpgsql; + +CREATE TABLE trigger_table_1 (value int); + +CREATE TRIGGER trigger_1 +BEFORE INSERT ON trigger_table_1 +FOR EACH ROW EXECUTE FUNCTION increment_value(); + +SELECT create_distributed_table('trigger_table_1', NULL, distribution_type=>null); + +SET citus.enable_unsafe_triggers TO ON; +SELECT create_distributed_table('trigger_table_1', NULL, distribution_type=>null); + +INSERT INTO trigger_table_1 VALUES(1), (2); +SELECT * FROM trigger_table_1 ORDER BY value; + +CREATE FUNCTION insert_some() RETURNS trigger AS $insert_some$ +BEGIN + RAISE NOTICE 'inserted some rows'; + RETURN NEW; +END; +$insert_some$ LANGUAGE plpgsql; + +CREATE TABLE trigger_table_2 (value int); + +CREATE TRIGGER trigger_2 +AFTER INSERT ON trigger_table_2 +FOR EACH STATEMENT EXECUTE FUNCTION insert_some(); + +ALTER TABLE trigger_table_2 DISABLE TRIGGER trigger_2; + +SELECT create_distributed_table('trigger_table_2', NULL, distribution_type=>null); + +SET client_min_messages TO NOTICE; +INSERT INTO trigger_table_2 VALUES(3), (4); +SET client_min_messages TO WARNING; + +SELECT * FROM trigger_table_2 ORDER BY value; + +CREATE FUNCTION combine_old_new_val() RETURNS trigger AS $combine_old_new_val$ +BEGIN + NEW.value = NEW.value * 10 + OLD.value; + RETURN NEW; +END; +$combine_old_new_val$ LANGUAGE plpgsql; + +CREATE FUNCTION notice_truncate() RETURNS trigger AS $notice_truncate$ +BEGIN + RAISE NOTICE 'notice_truncate()'; + RETURN NEW; +END; +$notice_truncate$ LANGUAGE plpgsql; + +CREATE TABLE trigger_table_3 (value int); + +CREATE TRIGGER trigger_3 +BEFORE UPDATE ON trigger_table_3 +FOR EACH ROW EXECUTE FUNCTION combine_old_new_val(); + +CREATE TRIGGER trigger_4 +AFTER TRUNCATE ON trigger_table_3 +FOR EACH STATEMENT EXECUTE FUNCTION notice_truncate(); + +INSERT INTO trigger_table_3 VALUES(3), (4); + +SELECT create_distributed_table('trigger_table_3', NULL, distribution_type=>null); + +UPDATE trigger_table_3 SET value = 5; +SELECT * FROM trigger_table_3 ORDER BY value; + +SET client_min_messages TO NOTICE; +TRUNCATE trigger_table_3; +SET client_min_messages TO WARNING; + +-- try a few simple queries at least to make sure that we don't crash +BEGIN; + INSERT INTO nullkey_c1_t1 SELECT * FROM nullkey_c2_t1; +ROLLBACK; + +-- cleanup at exit +SET client_min_messages TO ERROR; +DROP SCHEMA create_null_dist_key, "NULL_!_dist_key" CASCADE; diff --git a/src/test/regress/sql/multi_extension.sql b/src/test/regress/sql/multi_extension.sql index cbad97524..50b821b0c 100644 --- a/src/test/regress/sql/multi_extension.sql +++ b/src/test/regress/sql/multi_extension.sql @@ -904,6 +904,20 @@ SELECT create_distributed_table('test','x'); DROP TABLE test; TRUNCATE pg_dist_node; +-- confirm that we can create a null shard key table on an empty node +CREATE TABLE test (x int, y int); +INSERT INTO test VALUES (1,2); +SET citus.shard_replication_factor TO 1; +SELECT create_distributed_table('test', null, colocate_with=>'none', distribution_type=>null); + +-- and make sure that we can't remove the coordinator due to "test" +SELECT citus_remove_node('localhost', :master_port); + +DROP TABLE test; + +-- and now we should be able to remove the coordinator +SELECT citus_remove_node('localhost', :master_port); + -- confirm that we can create a reference table on an empty node CREATE TABLE test (x int, y int); INSERT INTO test VALUES (1,2); diff --git a/src/test/regress/sql/single_node.sql b/src/test/regress/sql/single_node.sql index 3419025af..8c612c1bb 100644 --- a/src/test/regress/sql/single_node.sql +++ b/src/test/regress/sql/single_node.sql @@ -63,8 +63,43 @@ ALTER SYSTEM RESET citus.local_shared_pool_size; ALTER SYSTEM RESET citus.max_cached_conns_per_worker; SELECT pg_reload_conf(); +CREATE TABLE single_node_nullkey_c1(a int, b int); +SELECT create_distributed_table('single_node_nullkey_c1', null, colocate_with=>'none', distribution_type=>null); + +CREATE TABLE single_node_nullkey_c2(a int, b int); +SELECT create_distributed_table('single_node_nullkey_c2', null, colocate_with=>'none', distribution_type=>null); + +-- created on different colocation groups .. +SELECT +( + SELECT colocationid FROM pg_dist_partition + WHERE logicalrelid = 'single_node.single_node_nullkey_c1'::regclass +) +!= +( + SELECT colocationid FROM pg_dist_partition + WHERE logicalrelid = 'single_node.single_node_nullkey_c2'::regclass +); + +-- .. but both are associated to coordinator +SELECT groupid = 0 FROM pg_dist_placement +WHERE shardid = ( + SELECT shardid FROM pg_dist_shard + WHERE logicalrelid = 'single_node.single_node_nullkey_c1'::regclass +); + +SELECT groupid = 0 FROM pg_dist_placement +WHERE shardid = ( + SELECT shardid FROM pg_dist_shard + WHERE logicalrelid = 'single_node.single_node_nullkey_c2'::regclass +); + +-- try creating a null-shard-key distributed table from a shard relation +SELECT shardid AS round_robin_test_c1_shard_id FROM pg_dist_shard WHERE logicalrelid = 'single_node.single_node_nullkey_c1'::regclass \gset +SELECT create_distributed_table('single_node_nullkey_c1_' || :round_robin_test_c1_shard_id , null, colocate_with=>'none', distribution_type=>null); + SET client_min_messages TO WARNING; -DROP TABLE failover_to_local; +DROP TABLE failover_to_local, single_node_nullkey_c1, single_node_nullkey_c2; RESET client_min_messages; -- so that we don't have to update rest of the test output From cdf54ff4b1ead8b4a77dc995f0ddd2695a4c2ff1 Mon Sep 17 00:00:00 2001 From: Ahmet Gedemenli Date: Tue, 21 Mar 2023 12:24:16 +0300 Subject: [PATCH 026/118] Add DDL support null-shard-key tables(#6778/#6784/#6787/#6859) Add tests for ddl coverage: * indexes * partitioned tables + indexes with long names * triggers * foreign keys * statistics * grant & revoke statements * truncate & vacuum * create/test/drop view that depends on a dist table with no shard key * policy & rls test * alter table add/drop/alter_type column (using sequences/different data types/identity columns) * alter table add constraint (not null, check, exclusion constraint) * alter table add column with a default value / set default / drop default * alter table set option (autovacuum) * indexes / constraints without names * multiple subcommands Adds support for * Creating new partitions after distributing (with null key) the parent table * Attaching partitions to a distributed table with null distribution key (and automatically distribute the new partition with null key as well) * Detaching partitions from it --- .../commands/create_distributed_table.c | 3 +- src/backend/distributed/commands/table.c | 35 +- .../distributed/planner/multi_join_order.c | 2 +- src/include/distributed/metadata_utility.h | 1 + .../expected/alter_table_null_dist_key.out | 154 +++++++ .../regress/expected/create_null_dist_key.out | 410 +++++++++++++++++- src/test/regress/multi_1_schedule | 2 +- .../regress/sql/alter_table_null_dist_key.sql | 98 +++++ src/test/regress/sql/create_null_dist_key.sql | 280 +++++++++++- 9 files changed, 956 insertions(+), 29 deletions(-) create mode 100644 src/test/regress/expected/alter_table_null_dist_key.out create mode 100644 src/test/regress/sql/alter_table_null_dist_key.sql diff --git a/src/backend/distributed/commands/create_distributed_table.c b/src/backend/distributed/commands/create_distributed_table.c index 12bfcf9a5..544d8f04e 100644 --- a/src/backend/distributed/commands/create_distributed_table.c +++ b/src/backend/distributed/commands/create_distributed_table.c @@ -134,7 +134,6 @@ static List * HashSplitPointsForShardList(List *shardList); static List * HashSplitPointsForShardCount(int shardCount); static List * WorkerNodesForShardList(List *shardList); static List * RoundRobinWorkerNodeList(List *workerNodeList, int listLength); -static void CreateNullShardKeyDistTable(Oid relationId, char *colocateWithTableName); static CitusTableParams DecideCitusTableParams(CitusTableType tableType, DistributedTableParams * distributedTableParams); @@ -1031,7 +1030,7 @@ CreateReferenceTable(Oid relationId) * CreateNullShardKeyDistTable is a wrapper around CreateCitusTable that creates a * single shard distributed table that doesn't have a shard key. */ -static void +void CreateNullShardKeyDistTable(Oid relationId, char *colocateWithTableName) { DistributedTableParams distributedTableParams = { diff --git a/src/backend/distributed/commands/table.c b/src/backend/distributed/commands/table.c index 6d5fcda3f..af3439ab5 100644 --- a/src/backend/distributed/commands/table.c +++ b/src/backend/distributed/commands/table.c @@ -384,6 +384,11 @@ PostprocessCreateTableStmtPartitionOf(CreateStmt *createStatement, const */ if (IsCitusTable(parentRelationId)) { + /* + * We can create Citus local tables and distributed tables with null shard keys + * right away, without switching to sequential mode, because they are going to + * have only one shard. + */ if (IsCitusTableType(parentRelationId, CITUS_LOCAL_TABLE)) { CreateCitusLocalTablePartitionOf(createStatement, relationId, @@ -391,11 +396,18 @@ PostprocessCreateTableStmtPartitionOf(CreateStmt *createStatement, const return; } + char *parentRelationName = generate_qualified_relation_name(parentRelationId); + + if (IsCitusTableType(parentRelationId, NULL_KEY_DISTRIBUTED_TABLE)) + { + CreateNullShardKeyDistTable(relationId, parentRelationName); + return; + } + Var *parentDistributionColumn = DistPartitionKeyOrError(parentRelationId); char *distributionColumnName = ColumnToColumnName(parentRelationId, (Node *) parentDistributionColumn); char parentDistributionMethod = DISTRIBUTE_BY_HASH; - char *parentRelationName = generate_qualified_relation_name(parentRelationId); SwitchToSequentialAndLocalExecutionIfPartitionNameTooLong(parentRelationId, relationId); @@ -589,19 +601,32 @@ PreprocessAttachCitusPartitionToCitusTable(Oid parentCitusRelationId, Oid /* * DistributePartitionUsingParent takes a parent and a partition relation and - * distributes the partition, using the same distribution column as the parent. - * It creates a *hash* distributed table by default, as partitioned tables can only be - * distributed by hash. + * distributes the partition, using the same distribution column as the parent, if the + * parent has a distribution column. It creates a *hash* distributed table by default, as + * partitioned tables can only be distributed by hash, unless it's null key distributed. + * + * If the parent has no distribution key, we distribute the partition with null key too. */ static void DistributePartitionUsingParent(Oid parentCitusRelationId, Oid partitionRelationId) { + char *parentRelationName = generate_qualified_relation_name(parentCitusRelationId); + + if (!HasDistributionKey(parentCitusRelationId)) + { + /* + * If the parent is null key distributed, we should distribute the partition + * with null distribution key as well. + */ + CreateNullShardKeyDistTable(partitionRelationId, parentRelationName); + return; + } + Var *distributionColumn = DistPartitionKeyOrError(parentCitusRelationId); char *distributionColumnName = ColumnToColumnName(parentCitusRelationId, (Node *) distributionColumn); char distributionMethod = DISTRIBUTE_BY_HASH; - char *parentRelationName = generate_qualified_relation_name(parentCitusRelationId); SwitchToSequentialAndLocalExecutionIfPartitionNameTooLong( parentCitusRelationId, partitionRelationId); diff --git a/src/backend/distributed/planner/multi_join_order.c b/src/backend/distributed/planner/multi_join_order.c index b1195c664..0fff79ed8 100644 --- a/src/backend/distributed/planner/multi_join_order.c +++ b/src/backend/distributed/planner/multi_join_order.c @@ -1404,7 +1404,7 @@ DistPartitionKeyOrError(Oid relationId) if (partitionKey == NULL) { ereport(ERROR, (errmsg( - "no distribution column found for relation %d, because it is a reference table", + "no distribution column found for relation %d", relationId))); } diff --git a/src/include/distributed/metadata_utility.h b/src/include/distributed/metadata_utility.h index e27f3df22..fe404acf8 100644 --- a/src/include/distributed/metadata_utility.h +++ b/src/include/distributed/metadata_utility.h @@ -326,6 +326,7 @@ extern void DeletePartitionRow(Oid distributedRelationId); extern void DeleteShardRow(uint64 shardId); extern void UpdatePlacementGroupId(uint64 placementId, int groupId); extern void DeleteShardPlacementRow(uint64 placementId); +extern void CreateNullShardKeyDistTable(Oid relationId, char *colocateWithTableName); extern void CreateDistributedTable(Oid relationId, char *distributionColumnName, char distributionMethod, int shardCount, bool shardCountIsStrict, char *colocateWithTableName); diff --git a/src/test/regress/expected/alter_table_null_dist_key.out b/src/test/regress/expected/alter_table_null_dist_key.out new file mode 100644 index 000000000..1812c33cb --- /dev/null +++ b/src/test/regress/expected/alter_table_null_dist_key.out @@ -0,0 +1,154 @@ +CREATE SCHEMA alter_null_dist_key; +SET search_path TO alter_null_dist_key; +SET citus.next_shard_id TO 1720000; +SET citus.shard_count TO 32; +SET citus.shard_replication_factor TO 1; +CREATE SEQUENCE dist_seq; +CREATE TABLE null_dist_table(a bigint DEFAULT nextval('dist_seq') UNIQUE, "b" text, c bigint GENERATED BY DEFAULT AS IDENTITY); +INSERT INTO null_dist_table("b") VALUES ('test'); +SELECT create_distributed_table('null_dist_table', null, colocate_with=>'none', distribution_type=>null); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$alter_null_dist_key.null_dist_table$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- add column +ALTER TABLE null_dist_table ADD COLUMN d bigint DEFAULT 2; +SELECT * FROM null_dist_table ORDER BY c; + a | b | c | d +--------------------------------------------------------------------- + 1 | test | 1 | 2 +(1 row) + +-- alter default, set to 3 +ALTER TABLE null_dist_table ALTER COLUMN d SET DEFAULT 3; +INSERT INTO null_dist_table("b") VALUES ('test'); +SELECT * FROM null_dist_table ORDER BY c; + a | b | c | d +--------------------------------------------------------------------- + 1 | test | 1 | 2 + 2 | test | 2 | 3 +(2 rows) + +-- drop default, see null +ALTER TABLE null_dist_table ALTER COLUMN d DROP DEFAULT; +INSERT INTO null_dist_table("b") VALUES ('test'); +SELECT * FROM null_dist_table ORDER BY c; + a | b | c | d +--------------------------------------------------------------------- + 1 | test | 1 | 2 + 2 | test | 2 | 3 + 3 | test | 3 | +(3 rows) + +-- cleanup the rows that were added to test the default behavior +DELETE FROM null_dist_table WHERE "b" = 'test' AND a > 1; +-- alter column type +ALTER TABLE null_dist_table ALTER COLUMN d TYPE text; +UPDATE null_dist_table SET d = 'this is a text' WHERE d = '2'; +SELECT * FROM null_dist_table ORDER BY c; + a | b | c | d +--------------------------------------------------------------------- + 1 | test | 1 | this is a text +(1 row) + +-- drop seq column +ALTER TABLE null_dist_table DROP COLUMN a; +SELECT * FROM null_dist_table ORDER BY c; + b | c | d +--------------------------------------------------------------------- + test | 1 | this is a text +(1 row) + +-- add not null constraint +ALTER TABLE null_dist_table ALTER COLUMN b SET NOT NULL; +-- not null constraint violation, error out +INSERT INTO null_dist_table VALUES (NULL, 2, 'test'); +ERROR: null value in column "b" violates not-null constraint +DETAIL: Failing row contains (null, 2, test). +CONTEXT: while executing command on localhost:xxxxx +-- drop not null constraint and try again +ALTER TABLE null_dist_table ALTER COLUMN b DROP NOT NULL; +INSERT INTO null_dist_table VALUES (NULL, 3, 'test'); +SELECT * FROM null_dist_table ORDER BY c; + b | c | d +--------------------------------------------------------------------- + test | 1 | this is a text + | 3 | test +(2 rows) + +-- add exclusion constraint +ALTER TABLE null_dist_table ADD CONSTRAINT exc_b EXCLUDE USING btree (b with =); +-- rename the exclusion constraint, errors out +ALTER TABLE null_dist_table RENAME CONSTRAINT exc_b TO exc_b_1; +ERROR: renaming constraints belonging to distributed tables is currently unsupported +-- create exclusion constraint without a name +ALTER TABLE null_dist_table ADD EXCLUDE USING btree (b with =); +-- test setting autovacuum option +ALTER TABLE null_dist_table SET (autovacuum_enabled = false); +-- test multiple subcommands +ALTER TABLE null_dist_table ADD COLUMN int_column1 INTEGER, + DROP COLUMN d; +SELECT * FROM null_dist_table ORDER BY c; + b | c | int_column1 +--------------------------------------------------------------------- + test | 1 | + | 3 | +(2 rows) + +-- test policy and row level security +CREATE TABLE null_dist_key_with_policy (table_user text); +INSERT INTO null_dist_key_with_policy VALUES ('user_1'); +SELECT create_distributed_table('null_dist_key_with_policy', null); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$alter_null_dist_key.null_dist_key_with_policy$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- enable rls +ALTER TABLE null_dist_key_with_policy ENABLE ROW LEVEL SECURITY; +-- user_1 will be allowed to see the inserted row +CREATE ROLE user_1 WITH LOGIN; +GRANT ALL ON SCHEMA alter_null_dist_key TO user_1; +GRANT ALL ON TABLE alter_null_dist_key.null_dist_key_with_policy TO user_1; +CREATE POLICY table_policy ON null_dist_key_with_policy TO user_1 + USING (table_user = current_user); +-- user_2 will not be allowed to see the inserted row +CREATE ROLE user_2 WITH LOGIN; +GRANT ALL ON SCHEMA alter_null_dist_key TO user_2; +GRANT ALL ON TABLE alter_null_dist_key.null_dist_key_with_policy TO user_2; +CREATE POLICY table_policy_1 ON null_dist_key_with_policy TO user_2 + USING (table_user = current_user); +\c - user_1 - +SELECT * FROM alter_null_dist_key.null_dist_key_with_policy; + table_user +--------------------------------------------------------------------- + user_1 +(1 row) + +\c - user_2 - +SELECT * FROM alter_null_dist_key.null_dist_key_with_policy; + table_user +--------------------------------------------------------------------- +(0 rows) + +-- postgres will always be allowed to see the row as a superuser +\c - postgres - +SELECT * FROM alter_null_dist_key.null_dist_key_with_policy; + table_user +--------------------------------------------------------------------- + user_1 +(1 row) + +-- cleanup +SET client_min_messages TO ERROR; +DROP SCHEMA alter_null_dist_key CASCADE; +DROP ROLE user_1, user_2; diff --git a/src/test/regress/expected/create_null_dist_key.out b/src/test/regress/expected/create_null_dist_key.out index e24ff1e91..af6e66f62 100644 --- a/src/test/regress/expected/create_null_dist_key.out +++ b/src/test/regress/expected/create_null_dist_key.out @@ -497,7 +497,8 @@ SELECT * FROM null_dist_key_table_2 ORDER BY a; DROP TABLE null_dist_key_table_1, null_dist_key_table_2; -- create indexes before creating the null dist key tables -- .. for an initially empty table -CREATE TABLE null_dist_key_table_1(a int); +CREATE TABLE null_dist_key_table_1(a int, b int); +CREATE STATISTICS s1 (dependencies) ON a, b FROM null_dist_key_table_1; CREATE INDEX null_dist_key_table_1_idx ON null_dist_key_table_1(a); SELECT create_distributed_table('null_dist_key_table_1', null, colocate_with=>'none'); create_distributed_table @@ -505,6 +506,7 @@ SELECT create_distributed_table('null_dist_key_table_1', null, colocate_with=>'n (1 row) +CREATE STATISTICS s2 (dependencies) ON a, b FROM null_dist_key_table_1; -- .. and for another table having data in it before creating null dist key table CREATE TABLE null_dist_key_table_2(a int); INSERT INTO null_dist_key_table_2 VALUES(1); @@ -515,6 +517,11 @@ SELECT create_distributed_table('null_dist_key_table_2', null, colocate_with=>'n (1 row) +-- test create index concurrently, then reindex +CREATE INDEX CONCURRENTLY ind_conc ON null_dist_key_table_2(a); +REINDEX INDEX ind_conc; +REINDEX INDEX CONCURRENTLY ind_conc; +DROP INDEX ind_conc; SELECT * FROM null_dist_key_table_2 ORDER BY a; a --------------------------------------------------------------------- @@ -536,15 +543,23 @@ BEGIN; CREATE ROLE table_users; CREATE POLICY table_policy ON null_dist_key_table_3 TO table_users USING (table_user = current_user); + GRANT ALL ON TABLE null_dist_key_table_3 TO table_users; + ALTER TABLE null_dist_key_table_3 OWNER TO table_users; SELECT create_distributed_table('null_dist_key_table_3', null, colocate_with=>'none'); create_distributed_table --------------------------------------------------------------------- (1 row) + REVOKE ALL ON TABLE null_dist_key_table_3 FROM table_users; + ALTER TABLE null_dist_key_table_3 OWNER TO postgres; + GRANT ALL ON TABLE null_dist_key_table_3 TO table_users; ROLLBACK; +ALTER STATISTICS s2 SET STATISTICS 46; +ALTER TABLE null_dist_key_table_1 SET SCHEMA public; +DROP STATISTICS s1, s2; -- drop them for next tests -DROP TABLE null_dist_key_table_1, null_dist_key_table_2, distributed_table; +DROP TABLE public.null_dist_key_table_1, null_dist_key_table_2, distributed_table; -- tests for object names that should be escaped properly CREATE SCHEMA "NULL_!_dist_key"; CREATE TABLE "NULL_!_dist_key"."my_TABLE.1!?!"(id int, "Second_Id" int); @@ -643,6 +658,348 @@ SELECT create_distributed_table('sensors', NULL, distribution_type=>null); (1 row) +-- verify we can create new partitions after distributing the parent table +CREATE TABLE sensors_2001 PARTITION OF sensors FOR VALUES FROM ('2001-01-01') TO ('2002-01-01'); +-- verify we can attach to a null dist key table +CREATE TABLE sensors_2002 (measureid integer, eventdatetime date, measure_data jsonb, PRIMARY KEY (measureid, eventdatetime, measure_data)); +ALTER TABLE sensors ATTACH PARTITION sensors_2002 FOR VALUES FROM ('2002-01-01') TO ('2003-01-01'); +-- verify we can detach from a null dist key table +ALTER TABLE sensors DETACH PARTITION sensors_2001; +-- error out when attaching a noncolocated partition +CREATE TABLE sensors_2003 (measureid integer, eventdatetime date, measure_data jsonb, PRIMARY KEY (measureid, eventdatetime, measure_data)); +SELECT create_distributed_table('sensors_2003', NULL, distribution_type=>null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +ALTER TABLE sensors ATTACH PARTITION sensors_2003 FOR VALUES FROM ('2003-01-01') TO ('2004-01-01'); +ERROR: distributed tables cannot have non-colocated distributed tables as a partition +DROP TABLE sensors_2003; +-- verify we can attach after distributing, if the parent and partition are colocated +CREATE TABLE sensors_2004 (measureid integer, eventdatetime date, measure_data jsonb, PRIMARY KEY (measureid, eventdatetime, measure_data)); +SELECT create_distributed_table('sensors_2004', NULL, distribution_type=>null, colocate_with=>'sensors'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +ALTER TABLE sensors ATTACH PARTITION sensors_2004 FOR VALUES FROM ('2004-01-01') TO ('2005-01-01'); +-- verify we can attach a citus local table +CREATE TABLE sensors_2005 (measureid integer, eventdatetime date, measure_data jsonb, PRIMARY KEY (measureid, eventdatetime, measure_data)); +SELECT citus_add_local_table_to_metadata('sensors_2005'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +ALTER TABLE sensors ATTACH PARTITION sensors_2005 FOR VALUES FROM ('2005-01-01') TO ('2006-01-01'); +-- check metadata +-- check all partitions and the parent on pg_dist_partition +SELECT logicalrelid::text FROM pg_dist_partition WHERE logicalrelid::text IN ('sensors', 'sensors_2000', 'sensors_2001', 'sensors_2002', 'sensors_2004', 'sensors_2005') ORDER BY logicalrelid::text; + logicalrelid +--------------------------------------------------------------------- + sensors + sensors_2000 + sensors_2001 + sensors_2002 + sensors_2004 + sensors_2005 +(6 rows) + +-- verify they are all colocated +SELECT COUNT(DISTINCT(colocationid)) FROM pg_dist_partition WHERE logicalrelid::text IN ('sensors', 'sensors_2000', 'sensors_2001', 'sensors_2002', 'sensors_2004', 'sensors_2005'); + count +--------------------------------------------------------------------- + 1 +(1 row) + +-- verify all partitions are placed on the same node +SELECT COUNT(DISTINCT(groupid)) FROM pg_dist_placement WHERE shardid IN + (SELECT shardid FROM pg_dist_shard WHERE logicalrelid::text IN ('sensors', 'sensors_2000', 'sensors_2001', 'sensors_2002', 'sensors_2004', 'sensors_2005')); + count +--------------------------------------------------------------------- + 1 +(1 row) + +-- verify the shard of sensors_2000 is attached to the parent shard, on the worker node +SELECT COUNT(*) FROM run_command_on_workers($$ + SELECT relpartbound FROM pg_class WHERE relname LIKE 'sensors_2000_1______';$$) + WHERE length(result) > 0; + count +--------------------------------------------------------------------- + 1 +(1 row) + +-- verify the shard of sensors_2001 is detached from the parent shard, on the worker node +SELECT COUNT(*) FROM run_command_on_workers($$ + SELECT relpartbound FROM pg_class WHERE relname LIKE 'sensors_2001_1______';$$) + WHERE length(result) > 0; + count +--------------------------------------------------------------------- + 0 +(1 row) + +-- verify the shard of sensors_2002 is attached to the parent shard, on the worker node +SELECT COUNT(*) FROM run_command_on_workers($$ + SELECT relpartbound FROM pg_class WHERE relname LIKE 'sensors_2002_1______';$$) + WHERE length(result) > 0; + count +--------------------------------------------------------------------- + 1 +(1 row) + +-- create a partitioned citus local table and verify we error out when attaching a partition with null dist key +CREATE TABLE partitioned_citus_local_tbl( + measureid integer, + eventdatetime date, + measure_data jsonb, +PRIMARY KEY (measureid, eventdatetime, measure_data)) +PARTITION BY RANGE(eventdatetime); +SELECT citus_add_local_table_to_metadata('partitioned_citus_local_tbl'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE partition_with_null_key (measureid integer, eventdatetime date, measure_data jsonb, PRIMARY KEY (measureid, eventdatetime, measure_data)); +SELECT create_distributed_table('partition_with_null_key', NULL, distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +ALTER TABLE partitioned_citus_local_tbl ATTACH PARTITION partition_with_null_key FOR VALUES FROM ('2004-01-01') TO ('2005-01-01'); +ERROR: non-distributed partitioned tables cannot have distributed partitions +-- test partitioned tables + indexes with long names +CREATE TABLE "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"( + id int PRIMARY KEY, + "TeNANt_Id" int, + "jsondata" jsonb NOT NULL, + name text, + price numeric CHECK (price > 0), + serial_data bigserial, UNIQUE (id, price)) + PARTITION BY LIST(id); +CREATE TABLE "NULL_!_dist_key"."partition1_nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" + PARTITION OF "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" + FOR VALUES IN (1); +CREATE TABLE "NULL_!_dist_key"."partition2_nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" + PARTITION OF "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" + FOR VALUES IN (2); +CREATE TABLE "NULL_!_dist_key"."partition100_nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" + PARTITION OF "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" + FOR VALUES IN (100); +-- create some objects before create_distributed_table +CREATE INDEX "my!Index1New" ON "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"(id) WITH ( fillfactor = 80 ) WHERE id > 10; +CREATE UNIQUE INDEX uniqueIndexNew ON "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" (id); +-- ingest some data before create_distributed_table +set client_min_messages to ERROR; +INSERT INTO "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" VALUES (1, 1, row_to_json(row(1,1), true)), + (2, 1, row_to_json(row(2,2), 'false')); +reset client_min_messages; +-- create a replica identity before create_distributed_table +ALTER TABLE "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" REPLICA IDENTITY USING INDEX uniqueIndexNew; +NOTICE: identifier "nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" will be truncated to "nullKeyTable.1!?!9012345678901234567890123456789012345678901234" +-- test triggers +SET client_min_messages TO ERROR; +CREATE FUNCTION insert_id_100() RETURNS trigger AS $insert_100$ +BEGIN + INSERT INTO "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" VALUES (100, 1, row_to_json(row(1,1), true)); + RETURN NEW; +END; +$insert_100$ LANGUAGE plpgsql; +CREATE TABLE null_key_table_with_trigger(a INT); +SELECT create_distributed_table('null_key_table_with_trigger', null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- try to add a trigger after distributing the table, fails +CREATE TRIGGER insert_100_trigger + AFTER UPDATE ON null_key_table_with_trigger + FOR EACH STATEMENT EXECUTE FUNCTION insert_id_100(); +ERROR: triggers are not supported on distributed tables +-- now try to distribute a table that already has a trigger on it +CREATE TRIGGER insert_100_trigger + AFTER UPDATE ON "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" + FOR EACH STATEMENT EXECUTE FUNCTION insert_id_100(); +-- error out because of the trigger +SELECT create_distributed_table('"NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"', null); +ERROR: cannot distribute relation "nullKeyTable.1!?!9012345678901234567890123456789012345678901234" because it has triggers +HINT: Consider dropping all the triggers on "nullKeyTable.1!?!9012345678901234567890123456789012345678901234" and retry. +SET citus.enable_unsafe_triggers TO ON; +RESET client_min_messages; +-- this shouldn't give any syntax errors +SELECT create_distributed_table('"NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"', null); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$"NULL_!_dist_key"."partition1_nullKeyTable.1!?!90123456789012345678901234567890123"$$) +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$"NULL_!_dist_key"."partition2_nullKeyTable.1!?!90123456789012345678901234567890123"$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- now we can add triggers on distributed tables, because we set the GUC to on +CREATE TRIGGER insert_100_trigger_2 + AFTER UPDATE ON null_key_table_with_trigger + FOR EACH STATEMENT EXECUTE FUNCTION insert_id_100(); +SET client_min_messages TO ERROR; +UPDATE "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" SET "TeNANt_Id"="TeNANt_Id"+1; +-- we should see one row with id = 100 +SELECT COUNT(*) FROM "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" WHERE id = 100; + count +--------------------------------------------------------------------- + 1 +(1 row) + +-- create some objects after create_distributed_table +CREATE INDEX "my!Index2New" ON "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"(id) WITH ( fillfactor = 90 ) WHERE id < 20; +CREATE UNIQUE INDEX uniqueIndex2New ON "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"(id); +-- error out for already existing, because of the unique index +INSERT INTO "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" VALUES (1, 1, row_to_json(row(1,1), true)); +ERROR: duplicate key value violates unique constraint "partition1_nullKeyTable.1!?!901234567890123456_bf4a8ac1_1730056" +DETAIL: Key (id)=(X) already exists. +CONTEXT: while executing command on localhost:xxxxx +-- verify all 4 shard indexes are created on the same node +SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*) FROM pg_indexes WHERE indexname LIKE '%my!Index_New_1%' OR indexname LIKE '%uniqueindex%new_1%';$$) + ORDER BY nodeport; + result +--------------------------------------------------------------------- + 4 + 0 +(2 rows) + +-- foreign key to a ref table +CREATE TABLE dummy_reference_table (a INT PRIMARY KEY); +SELECT create_reference_table('dummy_reference_table'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +TRUNCATE "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"; +ALTER TABLE "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" + ADD CONSTRAINT fkey_to_dummy_ref FOREIGN KEY (id) REFERENCES dummy_reference_table(a); +BEGIN; -- try to add the same fkey, reversed + ALTER TABLE dummy_reference_table + ADD CONSTRAINT fkey_to_dummy_ref FOREIGN KEY (a) REFERENCES "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"(id); +ERROR: cannot create foreign key constraint since foreign keys from reference tables and local tables to distributed tables are not supported +DETAIL: Reference tables and local tables can only have foreign keys to reference tables and local tables +ROLLBACK; +-- errors out because of foreign key violation +INSERT INTO "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" VALUES (100, 1, row_to_json(row(1,1), true)); +ERROR: insert or update on table "partition100_nullKeyTable.1!?!9012345678901234_0aba0bf3_1730058" violates foreign key constraint "fkey_to_dummy_ref_1730055" +DETAIL: Key (id)=(X) is not present in table "dummy_reference_table_1730059". +CONTEXT: while executing command on localhost:xxxxx +-- now inserts successfully +INSERT INTO dummy_reference_table VALUES (100); +INSERT INTO "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" VALUES (100, 1, row_to_json(row(1,1), true)); +DELETE FROM "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" WHERE id = 100; +-- foreign key to a local table, errors out +CREATE TABLE local_table_for_fkey (a INT PRIMARY KEY); +ALTER TABLE "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" + ADD CONSTRAINT fkey_to_dummy_local FOREIGN KEY (id) REFERENCES local_table_for_fkey(a); +ERROR: referenced table "local_table_for_fkey" must be a distributed table or a reference table +DETAIL: To enforce foreign keys, the referencing and referenced rows need to be stored on the same node. +HINT: You could use SELECT create_reference_table('local_table_for_fkey') to replicate the referenced table to all nodes or consider dropping the foreign key +-- Normally, we support foreign keys from Postgres tables to distributed +-- tables assuming that the user will soon distribute the local table too +-- anyway. However, this is not the case for null-shard-key tables before +-- we improve SQL support. +ALTER TABLE local_table_for_fkey + ADD CONSTRAINT fkey_from_dummy_local FOREIGN KEY (a) REFERENCES "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"(id); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +CONTEXT: SQL statement "SELECT fk."a" FROM ONLY "create_null_dist_key"."local_table_for_fkey" fk LEFT OUTER JOIN "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234" pk ON ( pk."id" OPERATOR(pg_catalog.=) fk."a") WHERE pk."id" IS NULL AND (fk."a" IS NOT NULL)" +-- foreign key to a citus local table, errors out +CREATE TABLE citus_local_table_for_fkey (a INT PRIMARY KEY); +SELECT citus_add_local_table_to_metadata('citus_local_table_for_fkey'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +ALTER TABLE "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" + ADD CONSTRAINT fkey_to_dummy_citus_local FOREIGN KEY (id) REFERENCES citus_local_table_for_fkey(a); +ERROR: cannot create foreign key constraint since relations are not colocated or not referencing a reference table +DETAIL: A distributed table can only have foreign keys if it is referencing another colocated hash distributed table or a reference table +-- reversed, still fails +ALTER TABLE citus_local_table_for_fkey + ADD CONSTRAINT fkey_from_dummy_citus_local FOREIGN KEY (a) REFERENCES "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"(id); +ERROR: cannot create foreign key constraint since foreign keys from reference tables and local tables to distributed tables are not supported +DETAIL: Reference tables and local tables can only have foreign keys to reference tables and local tables +-- foreign key to a distributed table, errors out because not colocated +CREATE TABLE dist_table_for_fkey (a INT PRIMARY KEY); +SELECT create_distributed_table('dist_table_for_fkey', 'a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +ALTER TABLE "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" + ADD CONSTRAINT fkey_to_dummy_dist FOREIGN KEY (id) REFERENCES dist_table_for_fkey(a); +ERROR: cannot create foreign key constraint since relations are not colocated or not referencing a reference table +DETAIL: A distributed table can only have foreign keys if it is referencing another colocated hash distributed table or a reference table +-- reversed, still fails +ALTER TABLE dist_table_for_fkey + ADD CONSTRAINT fkey_to_dummy_dist FOREIGN KEY (a) REFERENCES "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"(id); +ERROR: cannot create foreign key constraint since relations are not colocated or not referencing a reference table +DETAIL: A distributed table can only have foreign keys if it is referencing another colocated hash distributed table or a reference table +-- create a null key distributed table, not colocated with the partitioned table, and then try to create a fkey +CREATE TABLE null_key_dist_not_colocated (a INT PRIMARY KEY); +SELECT create_distributed_table('null_key_dist_not_colocated', null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +ALTER TABLE "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" + ADD CONSTRAINT fkey_to_dummy_dist FOREIGN KEY (id) REFERENCES null_key_dist_not_colocated(a); +ERROR: cannot create foreign key constraint since relations are not colocated or not referencing a reference table +DETAIL: A distributed table can only have foreign keys if it is referencing another colocated hash distributed table or a reference table +-- create a null key distributed table, colocated with the partitioned table, and then create a fkey +CREATE TABLE null_key_dist (a INT PRIMARY KEY); +SELECT create_distributed_table('null_key_dist', null, colocate_with=>'"NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +ALTER TABLE "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" + ADD CONSTRAINT fkey_to_dummy_dist FOREIGN KEY (id) REFERENCES null_key_dist(a); +-- check supported ON DELETE and ON UPDATE commands +ALTER TABLE null_key_dist ADD CONSTRAINT fkey_add_test_1 FOREIGN KEY(a) + REFERENCES "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"(id) ON DELETE SET DEFAULT; +ALTER TABLE null_key_dist ADD CONSTRAINT fkey_add_test_2 FOREIGN KEY(a) + REFERENCES "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"(id) ON UPDATE CASCADE; +ALTER TABLE null_key_dist ADD CONSTRAINT fkey_add_test_3 FOREIGN KEY(a) + REFERENCES dummy_reference_table(a) ON DELETE SET DEFAULT; +ALTER TABLE null_key_dist ADD CONSTRAINT fkey_add_test_4 FOREIGN KEY(a) + REFERENCES dummy_reference_table(a) ON UPDATE CASCADE; +ALTER TABLE null_key_dist DROP CONSTRAINT fkey_add_test_1; +ALTER TABLE null_key_dist DROP CONSTRAINT fkey_add_test_2; +ALTER TABLE null_key_dist DROP CONSTRAINT fkey_add_test_3; +ALTER TABLE null_key_dist DROP CONSTRAINT fkey_add_test_4; +ALTER TABLE "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" DROP CONSTRAINT fkey_to_dummy_dist; +-- create a view that depends on the null shard key table +CREATE VIEW public.v1 AS SELECT * FROM null_key_dist; +SELECT * FROM public.v1; + a +--------------------------------------------------------------------- +(0 rows) + +DELETE FROM null_key_dist; +VACUUM null_key_dist; +TRUNCATE null_key_dist; +DROP TABLE null_key_dist CASCADE; +RESET client_min_messages; CREATE TABLE multi_level_partitioning_parent( measureid integer, eventdatetime date, @@ -771,7 +1128,7 @@ BEGIN; INSERT INTO referencing_table VALUES (1, 2); -- fails INSERT INTO referencing_table VALUES (2, 2); -ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730049" +ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730098" DETAIL: Key (a)=(2) is not present in table "referenced_table_xxxxxxx". CONTEXT: while executing command on localhost:xxxxx ROLLBACK; @@ -817,7 +1174,7 @@ BEGIN; INSERT INTO referencing_table VALUES (1, 2); -- fails INSERT INTO referencing_table VALUES (2, 2); -ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730085" +ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730134" DETAIL: Key (a)=(2) is not present in table "referenced_table_xxxxxxx". CONTEXT: while executing command on localhost:xxxxx ROLLBACK; @@ -935,8 +1292,8 @@ SELECT result, success FROM run_command_on_workers($$ $$); result | success --------------------------------------------------------------------- - ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730102" | f - ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730102" | f + ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730151" | f + ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730151" | f (2 rows) DROP TABLE referencing_table, referenced_table; @@ -951,8 +1308,8 @@ SELECT create_distributed_table('self_fkey_test', NULL, distribution_type=>null) INSERT INTO self_fkey_test VALUES (1, 1); -- ok INSERT INTO self_fkey_test VALUES (2, 3); -- fails -ERROR: insert or update on table "self_fkey_test_1730103" violates foreign key constraint "self_fkey_test_b_fkey_1730103" -DETAIL: Key (b)=(3) is not present in table "self_fkey_test_1730103". +ERROR: insert or update on table "self_fkey_test_1730152" violates foreign key constraint "self_fkey_test_b_fkey_1730152" +DETAIL: Key (b)=(3) is not present in table "self_fkey_test_1730152". CONTEXT: while executing command on localhost:xxxxx -- similar foreign key tests but this time create the referencing table later on -- referencing table is a null shard key table @@ -976,7 +1333,7 @@ BEGIN; INSERT INTO referencing_table VALUES (1, 2); -- fails INSERT INTO referencing_table VALUES (2, 2); -ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730105" +ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730154" DETAIL: Key (a)=(2) is not present in table "referenced_table_xxxxxxx". CONTEXT: while executing command on localhost:xxxxx ROLLBACK; @@ -999,7 +1356,7 @@ BEGIN; INSERT INTO referencing_table VALUES (2, 1); -- fails INSERT INTO referencing_table VALUES (1, 2); -ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_b_fkey_1730107" +ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_b_fkey_1730156" DETAIL: Key (a, b)=(1, 2) is not present in table "referenced_table_xxxxxxx". CONTEXT: while executing command on localhost:xxxxx ROLLBACK; @@ -1040,6 +1397,24 @@ BEGIN; SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'referenced_table'); ERROR: cannot create foreign key constraint since Citus does not support ON DELETE / UPDATE SET DEFAULT actions on the columns that default to sequences ROLLBACK; +BEGIN; + CREATE TABLE referenced_table(a int UNIQUE, b int); + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + + CREATE TABLE referencing_table(a serial, b int); + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'referenced_table'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + + ALTER TABLE referencing_table ADD CONSTRAINT fkey_to_dummy_ref_on_update FOREIGN KEY (a) REFERENCES referenced_table(a) ON UPDATE SET DEFAULT; +ERROR: cannot create foreign key constraint since Citus does not support ON DELETE / UPDATE SET DEFAULT actions on the columns that default to sequences +ROLLBACK; -- to a non-colocated null dist key table BEGIN; CREATE TABLE referenced_table(a int UNIQUE, b int); @@ -1088,7 +1463,7 @@ BEGIN; INSERT INTO referencing_table VALUES (1, 2); -- fails INSERT INTO referencing_table VALUES (2, 2); -ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730146" +ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730197" DETAIL: Key (a)=(2) is not present in table "referenced_table_xxxxxxx". CONTEXT: while executing command on localhost:xxxxx ROLLBACK; @@ -1339,10 +1714,6 @@ CREATE TABLE trigger_table_1 (value int); CREATE TRIGGER trigger_1 BEFORE INSERT ON trigger_table_1 FOR EACH ROW EXECUTE FUNCTION increment_value(); -SELECT create_distributed_table('trigger_table_1', NULL, distribution_type=>null); -ERROR: cannot distribute relation "trigger_table_1" because it has triggers -HINT: Consider dropping all the triggers on "trigger_table_1" and retry. -SET citus.enable_unsafe_triggers TO ON; SELECT create_distributed_table('trigger_table_1', NULL, distribution_type=>null); create_distributed_table --------------------------------------------------------------------- @@ -1423,10 +1794,19 @@ TRUNCATE trigger_table_3; NOTICE: notice_truncate() CONTEXT: PL/pgSQL function notice_truncate() line XX at RAISE SET client_min_messages TO WARNING; +-- test rename, disable and drop trigger +ALTER TRIGGER trigger_4 ON trigger_table_3 RENAME TO trigger_new_name; +ALTER TABLE trigger_table_3 DISABLE TRIGGER ALL; +DROP TRIGGER trigger_new_name ON trigger_table_3; +-- enable the remaining triggers +ALTER TABLE trigger_table_3 ENABLE TRIGGER ALL; -- try a few simple queries at least to make sure that we don't crash BEGIN; INSERT INTO nullkey_c1_t1 SELECT * FROM nullkey_c2_t1; ROLLBACK; +DROP TRIGGER IF EXISTS trigger_1 ON trigger_table_1; +DROP TRIGGER trigger_2 ON trigger_table_2 CASCADE; +DROP TRIGGER trigger_3 ON trigger_table_3 RESTRICT; -- cleanup at exit SET client_min_messages TO ERROR; DROP SCHEMA create_null_dist_key, "NULL_!_dist_key" CASCADE; diff --git a/src/test/regress/multi_1_schedule b/src/test/regress/multi_1_schedule index a673a71d0..5d6fbb068 100644 --- a/src/test/regress/multi_1_schedule +++ b/src/test/regress/multi_1_schedule @@ -68,7 +68,7 @@ test: multi_master_protocol multi_load_data multi_load_data_superuser multi_beha test: multi_behavioral_analytics_basics multi_behavioral_analytics_single_shard_queries multi_insert_select_non_pushable_queries multi_insert_select multi_behavioral_analytics_create_table_superuser test: multi_shard_update_delete recursive_dml_with_different_planners_executors test: insert_select_repartition window_functions dml_recursive multi_insert_select_window -test: multi_insert_select_conflict citus_table_triggers +test: multi_insert_select_conflict citus_table_triggers alter_table_null_dist_key test: multi_row_insert insert_select_into_local_table alter_index # following should not run in parallel because it relies on connection counts to workers diff --git a/src/test/regress/sql/alter_table_null_dist_key.sql b/src/test/regress/sql/alter_table_null_dist_key.sql new file mode 100644 index 000000000..bcf0b4f74 --- /dev/null +++ b/src/test/regress/sql/alter_table_null_dist_key.sql @@ -0,0 +1,98 @@ +CREATE SCHEMA alter_null_dist_key; +SET search_path TO alter_null_dist_key; + +SET citus.next_shard_id TO 1720000; +SET citus.shard_count TO 32; +SET citus.shard_replication_factor TO 1; + +CREATE SEQUENCE dist_seq; +CREATE TABLE null_dist_table(a bigint DEFAULT nextval('dist_seq') UNIQUE, "b" text, c bigint GENERATED BY DEFAULT AS IDENTITY); +INSERT INTO null_dist_table("b") VALUES ('test'); +SELECT create_distributed_table('null_dist_table', null, colocate_with=>'none', distribution_type=>null); + +-- add column +ALTER TABLE null_dist_table ADD COLUMN d bigint DEFAULT 2; +SELECT * FROM null_dist_table ORDER BY c; + +-- alter default, set to 3 +ALTER TABLE null_dist_table ALTER COLUMN d SET DEFAULT 3; +INSERT INTO null_dist_table("b") VALUES ('test'); +SELECT * FROM null_dist_table ORDER BY c; + +-- drop default, see null +ALTER TABLE null_dist_table ALTER COLUMN d DROP DEFAULT; +INSERT INTO null_dist_table("b") VALUES ('test'); +SELECT * FROM null_dist_table ORDER BY c; + +-- cleanup the rows that were added to test the default behavior +DELETE FROM null_dist_table WHERE "b" = 'test' AND a > 1; + +-- alter column type +ALTER TABLE null_dist_table ALTER COLUMN d TYPE text; +UPDATE null_dist_table SET d = 'this is a text' WHERE d = '2'; +SELECT * FROM null_dist_table ORDER BY c; + +-- drop seq column +ALTER TABLE null_dist_table DROP COLUMN a; +SELECT * FROM null_dist_table ORDER BY c; + +-- add not null constraint +ALTER TABLE null_dist_table ALTER COLUMN b SET NOT NULL; + +-- not null constraint violation, error out +INSERT INTO null_dist_table VALUES (NULL, 2, 'test'); +-- drop not null constraint and try again +ALTER TABLE null_dist_table ALTER COLUMN b DROP NOT NULL; +INSERT INTO null_dist_table VALUES (NULL, 3, 'test'); +SELECT * FROM null_dist_table ORDER BY c; + +-- add exclusion constraint +ALTER TABLE null_dist_table ADD CONSTRAINT exc_b EXCLUDE USING btree (b with =); +-- rename the exclusion constraint, errors out +ALTER TABLE null_dist_table RENAME CONSTRAINT exc_b TO exc_b_1; +-- create exclusion constraint without a name +ALTER TABLE null_dist_table ADD EXCLUDE USING btree (b with =); + +-- test setting autovacuum option +ALTER TABLE null_dist_table SET (autovacuum_enabled = false); + +-- test multiple subcommands +ALTER TABLE null_dist_table ADD COLUMN int_column1 INTEGER, + DROP COLUMN d; + +SELECT * FROM null_dist_table ORDER BY c; + +-- test policy and row level security +CREATE TABLE null_dist_key_with_policy (table_user text); +INSERT INTO null_dist_key_with_policy VALUES ('user_1'); +SELECT create_distributed_table('null_dist_key_with_policy', null); + +-- enable rls +ALTER TABLE null_dist_key_with_policy ENABLE ROW LEVEL SECURITY; + +-- user_1 will be allowed to see the inserted row +CREATE ROLE user_1 WITH LOGIN; +GRANT ALL ON SCHEMA alter_null_dist_key TO user_1; +GRANT ALL ON TABLE alter_null_dist_key.null_dist_key_with_policy TO user_1; +CREATE POLICY table_policy ON null_dist_key_with_policy TO user_1 + USING (table_user = current_user); + +-- user_2 will not be allowed to see the inserted row +CREATE ROLE user_2 WITH LOGIN; +GRANT ALL ON SCHEMA alter_null_dist_key TO user_2; +GRANT ALL ON TABLE alter_null_dist_key.null_dist_key_with_policy TO user_2; +CREATE POLICY table_policy_1 ON null_dist_key_with_policy TO user_2 + USING (table_user = current_user); + +\c - user_1 - +SELECT * FROM alter_null_dist_key.null_dist_key_with_policy; +\c - user_2 - +SELECT * FROM alter_null_dist_key.null_dist_key_with_policy; +-- postgres will always be allowed to see the row as a superuser +\c - postgres - +SELECT * FROM alter_null_dist_key.null_dist_key_with_policy; + +-- cleanup +SET client_min_messages TO ERROR; +DROP SCHEMA alter_null_dist_key CASCADE; +DROP ROLE user_1, user_2; diff --git a/src/test/regress/sql/create_null_dist_key.sql b/src/test/regress/sql/create_null_dist_key.sql index b03cdde4d..9ca943d75 100644 --- a/src/test/regress/sql/create_null_dist_key.sql +++ b/src/test/regress/sql/create_null_dist_key.sql @@ -317,9 +317,11 @@ DROP TABLE null_dist_key_table_1, null_dist_key_table_2; -- create indexes before creating the null dist key tables -- .. for an initially empty table -CREATE TABLE null_dist_key_table_1(a int); +CREATE TABLE null_dist_key_table_1(a int, b int); +CREATE STATISTICS s1 (dependencies) ON a, b FROM null_dist_key_table_1; CREATE INDEX null_dist_key_table_1_idx ON null_dist_key_table_1(a); SELECT create_distributed_table('null_dist_key_table_1', null, colocate_with=>'none'); +CREATE STATISTICS s2 (dependencies) ON a, b FROM null_dist_key_table_1; -- .. and for another table having data in it before creating null dist key table CREATE TABLE null_dist_key_table_2(a int); @@ -327,6 +329,12 @@ INSERT INTO null_dist_key_table_2 VALUES(1); CREATE INDEX null_dist_key_table_2_idx ON null_dist_key_table_2(a); SELECT create_distributed_table('null_dist_key_table_2', null, colocate_with=>'none'); +-- test create index concurrently, then reindex +CREATE INDEX CONCURRENTLY ind_conc ON null_dist_key_table_2(a); +REINDEX INDEX ind_conc; +REINDEX INDEX CONCURRENTLY ind_conc; +DROP INDEX ind_conc; + SELECT * FROM null_dist_key_table_2 ORDER BY a; -- show that we do not support inheritance relationships @@ -347,11 +355,22 @@ BEGIN; CREATE POLICY table_policy ON null_dist_key_table_3 TO table_users USING (table_user = current_user); + GRANT ALL ON TABLE null_dist_key_table_3 TO table_users; + ALTER TABLE null_dist_key_table_3 OWNER TO table_users; + SELECT create_distributed_table('null_dist_key_table_3', null, colocate_with=>'none'); + + REVOKE ALL ON TABLE null_dist_key_table_3 FROM table_users; + ALTER TABLE null_dist_key_table_3 OWNER TO postgres; + GRANT ALL ON TABLE null_dist_key_table_3 TO table_users; ROLLBACK; +ALTER STATISTICS s2 SET STATISTICS 46; +ALTER TABLE null_dist_key_table_1 SET SCHEMA public; +DROP STATISTICS s1, s2; + -- drop them for next tests -DROP TABLE null_dist_key_table_1, null_dist_key_table_2, distributed_table; +DROP TABLE public.null_dist_key_table_1, null_dist_key_table_2, distributed_table; -- tests for object names that should be escaped properly @@ -438,6 +457,240 @@ SELECT create_distributed_table('sensors_2000', NULL, distribution_type=>null); SELECT create_distributed_table('sensors', NULL, distribution_type=>null); +-- verify we can create new partitions after distributing the parent table +CREATE TABLE sensors_2001 PARTITION OF sensors FOR VALUES FROM ('2001-01-01') TO ('2002-01-01'); + +-- verify we can attach to a null dist key table +CREATE TABLE sensors_2002 (measureid integer, eventdatetime date, measure_data jsonb, PRIMARY KEY (measureid, eventdatetime, measure_data)); +ALTER TABLE sensors ATTACH PARTITION sensors_2002 FOR VALUES FROM ('2002-01-01') TO ('2003-01-01'); + +-- verify we can detach from a null dist key table +ALTER TABLE sensors DETACH PARTITION sensors_2001; + +-- error out when attaching a noncolocated partition +CREATE TABLE sensors_2003 (measureid integer, eventdatetime date, measure_data jsonb, PRIMARY KEY (measureid, eventdatetime, measure_data)); +SELECT create_distributed_table('sensors_2003', NULL, distribution_type=>null, colocate_with=>'none'); +ALTER TABLE sensors ATTACH PARTITION sensors_2003 FOR VALUES FROM ('2003-01-01') TO ('2004-01-01'); +DROP TABLE sensors_2003; + +-- verify we can attach after distributing, if the parent and partition are colocated +CREATE TABLE sensors_2004 (measureid integer, eventdatetime date, measure_data jsonb, PRIMARY KEY (measureid, eventdatetime, measure_data)); +SELECT create_distributed_table('sensors_2004', NULL, distribution_type=>null, colocate_with=>'sensors'); +ALTER TABLE sensors ATTACH PARTITION sensors_2004 FOR VALUES FROM ('2004-01-01') TO ('2005-01-01'); + +-- verify we can attach a citus local table +CREATE TABLE sensors_2005 (measureid integer, eventdatetime date, measure_data jsonb, PRIMARY KEY (measureid, eventdatetime, measure_data)); +SELECT citus_add_local_table_to_metadata('sensors_2005'); +ALTER TABLE sensors ATTACH PARTITION sensors_2005 FOR VALUES FROM ('2005-01-01') TO ('2006-01-01'); + +-- check metadata +-- check all partitions and the parent on pg_dist_partition +SELECT logicalrelid::text FROM pg_dist_partition WHERE logicalrelid::text IN ('sensors', 'sensors_2000', 'sensors_2001', 'sensors_2002', 'sensors_2004', 'sensors_2005') ORDER BY logicalrelid::text; +-- verify they are all colocated +SELECT COUNT(DISTINCT(colocationid)) FROM pg_dist_partition WHERE logicalrelid::text IN ('sensors', 'sensors_2000', 'sensors_2001', 'sensors_2002', 'sensors_2004', 'sensors_2005'); +-- verify all partitions are placed on the same node +SELECT COUNT(DISTINCT(groupid)) FROM pg_dist_placement WHERE shardid IN + (SELECT shardid FROM pg_dist_shard WHERE logicalrelid::text IN ('sensors', 'sensors_2000', 'sensors_2001', 'sensors_2002', 'sensors_2004', 'sensors_2005')); + +-- verify the shard of sensors_2000 is attached to the parent shard, on the worker node +SELECT COUNT(*) FROM run_command_on_workers($$ + SELECT relpartbound FROM pg_class WHERE relname LIKE 'sensors_2000_1______';$$) + WHERE length(result) > 0; + +-- verify the shard of sensors_2001 is detached from the parent shard, on the worker node +SELECT COUNT(*) FROM run_command_on_workers($$ + SELECT relpartbound FROM pg_class WHERE relname LIKE 'sensors_2001_1______';$$) + WHERE length(result) > 0; + +-- verify the shard of sensors_2002 is attached to the parent shard, on the worker node +SELECT COUNT(*) FROM run_command_on_workers($$ + SELECT relpartbound FROM pg_class WHERE relname LIKE 'sensors_2002_1______';$$) + WHERE length(result) > 0; + +-- create a partitioned citus local table and verify we error out when attaching a partition with null dist key +CREATE TABLE partitioned_citus_local_tbl( + measureid integer, + eventdatetime date, + measure_data jsonb, +PRIMARY KEY (measureid, eventdatetime, measure_data)) +PARTITION BY RANGE(eventdatetime); +SELECT citus_add_local_table_to_metadata('partitioned_citus_local_tbl'); +CREATE TABLE partition_with_null_key (measureid integer, eventdatetime date, measure_data jsonb, PRIMARY KEY (measureid, eventdatetime, measure_data)); +SELECT create_distributed_table('partition_with_null_key', NULL, distribution_type=>null); +ALTER TABLE partitioned_citus_local_tbl ATTACH PARTITION partition_with_null_key FOR VALUES FROM ('2004-01-01') TO ('2005-01-01'); + +-- test partitioned tables + indexes with long names +CREATE TABLE "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"( + id int PRIMARY KEY, + "TeNANt_Id" int, + "jsondata" jsonb NOT NULL, + name text, + price numeric CHECK (price > 0), + serial_data bigserial, UNIQUE (id, price)) + PARTITION BY LIST(id); + +CREATE TABLE "NULL_!_dist_key"."partition1_nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" + PARTITION OF "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" + FOR VALUES IN (1); +CREATE TABLE "NULL_!_dist_key"."partition2_nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" + PARTITION OF "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" + FOR VALUES IN (2); +CREATE TABLE "NULL_!_dist_key"."partition100_nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" + PARTITION OF "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" + FOR VALUES IN (100); + +-- create some objects before create_distributed_table +CREATE INDEX "my!Index1New" ON "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"(id) WITH ( fillfactor = 80 ) WHERE id > 10; +CREATE UNIQUE INDEX uniqueIndexNew ON "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" (id); + +-- ingest some data before create_distributed_table +set client_min_messages to ERROR; +INSERT INTO "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" VALUES (1, 1, row_to_json(row(1,1), true)), + (2, 1, row_to_json(row(2,2), 'false')); +reset client_min_messages; +-- create a replica identity before create_distributed_table +ALTER TABLE "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" REPLICA IDENTITY USING INDEX uniqueIndexNew; + +-- test triggers +SET client_min_messages TO ERROR; +CREATE FUNCTION insert_id_100() RETURNS trigger AS $insert_100$ +BEGIN + INSERT INTO "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" VALUES (100, 1, row_to_json(row(1,1), true)); + RETURN NEW; +END; +$insert_100$ LANGUAGE plpgsql; + +CREATE TABLE null_key_table_with_trigger(a INT); +SELECT create_distributed_table('null_key_table_with_trigger', null); +-- try to add a trigger after distributing the table, fails +CREATE TRIGGER insert_100_trigger + AFTER UPDATE ON null_key_table_with_trigger + FOR EACH STATEMENT EXECUTE FUNCTION insert_id_100(); + +-- now try to distribute a table that already has a trigger on it +CREATE TRIGGER insert_100_trigger + AFTER UPDATE ON "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" + FOR EACH STATEMENT EXECUTE FUNCTION insert_id_100(); + +-- error out because of the trigger +SELECT create_distributed_table('"NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"', null); + +SET citus.enable_unsafe_triggers TO ON; +RESET client_min_messages; + +-- this shouldn't give any syntax errors +SELECT create_distributed_table('"NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"', null); + +-- now we can add triggers on distributed tables, because we set the GUC to on +CREATE TRIGGER insert_100_trigger_2 + AFTER UPDATE ON null_key_table_with_trigger + FOR EACH STATEMENT EXECUTE FUNCTION insert_id_100(); + +SET client_min_messages TO ERROR; +UPDATE "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" SET "TeNANt_Id"="TeNANt_Id"+1; +-- we should see one row with id = 100 +SELECT COUNT(*) FROM "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" WHERE id = 100; + +-- create some objects after create_distributed_table +CREATE INDEX "my!Index2New" ON "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"(id) WITH ( fillfactor = 90 ) WHERE id < 20; +CREATE UNIQUE INDEX uniqueIndex2New ON "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"(id); + +-- error out for already existing, because of the unique index +INSERT INTO "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" VALUES (1, 1, row_to_json(row(1,1), true)); + +-- verify all 4 shard indexes are created on the same node +SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*) FROM pg_indexes WHERE indexname LIKE '%my!Index_New_1%' OR indexname LIKE '%uniqueindex%new_1%';$$) + ORDER BY nodeport; + +-- foreign key to a ref table +CREATE TABLE dummy_reference_table (a INT PRIMARY KEY); +SELECT create_reference_table('dummy_reference_table'); +TRUNCATE "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"; +ALTER TABLE "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" + ADD CONSTRAINT fkey_to_dummy_ref FOREIGN KEY (id) REFERENCES dummy_reference_table(a); +BEGIN; -- try to add the same fkey, reversed + ALTER TABLE dummy_reference_table + ADD CONSTRAINT fkey_to_dummy_ref FOREIGN KEY (a) REFERENCES "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"(id); +ROLLBACK; + +-- errors out because of foreign key violation +INSERT INTO "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" VALUES (100, 1, row_to_json(row(1,1), true)); + +-- now inserts successfully +INSERT INTO dummy_reference_table VALUES (100); +INSERT INTO "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" VALUES (100, 1, row_to_json(row(1,1), true)); +DELETE FROM "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" WHERE id = 100; + +-- foreign key to a local table, errors out +CREATE TABLE local_table_for_fkey (a INT PRIMARY KEY); +ALTER TABLE "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" + ADD CONSTRAINT fkey_to_dummy_local FOREIGN KEY (id) REFERENCES local_table_for_fkey(a); + +-- Normally, we support foreign keys from Postgres tables to distributed +-- tables assuming that the user will soon distribute the local table too +-- anyway. However, this is not the case for null-shard-key tables before +-- we improve SQL support. +ALTER TABLE local_table_for_fkey + ADD CONSTRAINT fkey_from_dummy_local FOREIGN KEY (a) REFERENCES "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"(id); + +-- foreign key to a citus local table, errors out +CREATE TABLE citus_local_table_for_fkey (a INT PRIMARY KEY); +SELECT citus_add_local_table_to_metadata('citus_local_table_for_fkey'); +ALTER TABLE "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" + ADD CONSTRAINT fkey_to_dummy_citus_local FOREIGN KEY (id) REFERENCES citus_local_table_for_fkey(a); +-- reversed, still fails +ALTER TABLE citus_local_table_for_fkey + ADD CONSTRAINT fkey_from_dummy_citus_local FOREIGN KEY (a) REFERENCES "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"(id); + +-- foreign key to a distributed table, errors out because not colocated +CREATE TABLE dist_table_for_fkey (a INT PRIMARY KEY); +SELECT create_distributed_table('dist_table_for_fkey', 'a'); +ALTER TABLE "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" + ADD CONSTRAINT fkey_to_dummy_dist FOREIGN KEY (id) REFERENCES dist_table_for_fkey(a); +-- reversed, still fails +ALTER TABLE dist_table_for_fkey + ADD CONSTRAINT fkey_to_dummy_dist FOREIGN KEY (a) REFERENCES "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"(id); + +-- create a null key distributed table, not colocated with the partitioned table, and then try to create a fkey +CREATE TABLE null_key_dist_not_colocated (a INT PRIMARY KEY); +SELECT create_distributed_table('null_key_dist_not_colocated', null, colocate_with=>'none'); +ALTER TABLE "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" + ADD CONSTRAINT fkey_to_dummy_dist FOREIGN KEY (id) REFERENCES null_key_dist_not_colocated(a); + +-- create a null key distributed table, colocated with the partitioned table, and then create a fkey +CREATE TABLE null_key_dist (a INT PRIMARY KEY); +SELECT create_distributed_table('null_key_dist', null, colocate_with=>'"NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"'); +ALTER TABLE "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" + ADD CONSTRAINT fkey_to_dummy_dist FOREIGN KEY (id) REFERENCES null_key_dist(a); + +-- check supported ON DELETE and ON UPDATE commands +ALTER TABLE null_key_dist ADD CONSTRAINT fkey_add_test_1 FOREIGN KEY(a) + REFERENCES "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"(id) ON DELETE SET DEFAULT; +ALTER TABLE null_key_dist ADD CONSTRAINT fkey_add_test_2 FOREIGN KEY(a) + REFERENCES "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"(id) ON UPDATE CASCADE; +ALTER TABLE null_key_dist ADD CONSTRAINT fkey_add_test_3 FOREIGN KEY(a) + REFERENCES dummy_reference_table(a) ON DELETE SET DEFAULT; +ALTER TABLE null_key_dist ADD CONSTRAINT fkey_add_test_4 FOREIGN KEY(a) + REFERENCES dummy_reference_table(a) ON UPDATE CASCADE; + +ALTER TABLE null_key_dist DROP CONSTRAINT fkey_add_test_1; +ALTER TABLE null_key_dist DROP CONSTRAINT fkey_add_test_2; +ALTER TABLE null_key_dist DROP CONSTRAINT fkey_add_test_3; +ALTER TABLE null_key_dist DROP CONSTRAINT fkey_add_test_4; +ALTER TABLE "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" DROP CONSTRAINT fkey_to_dummy_dist; + +-- create a view that depends on the null shard key table +CREATE VIEW public.v1 AS SELECT * FROM null_key_dist; +SELECT * FROM public.v1; + +DELETE FROM null_key_dist; +VACUUM null_key_dist; +TRUNCATE null_key_dist; +DROP TABLE null_key_dist CASCADE; + +RESET client_min_messages; + CREATE TABLE multi_level_partitioning_parent( measureid integer, eventdatetime date, @@ -685,6 +938,15 @@ BEGIN; SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'referenced_table'); ROLLBACK; +BEGIN; + CREATE TABLE referenced_table(a int UNIQUE, b int); + SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); + + CREATE TABLE referencing_table(a serial, b int); + SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'referenced_table'); + ALTER TABLE referencing_table ADD CONSTRAINT fkey_to_dummy_ref_on_update FOREIGN KEY (a) REFERENCES referenced_table(a) ON UPDATE SET DEFAULT; +ROLLBACK; + -- to a non-colocated null dist key table BEGIN; CREATE TABLE referenced_table(a int UNIQUE, b int); @@ -888,9 +1150,6 @@ FOR EACH ROW EXECUTE FUNCTION increment_value(); SELECT create_distributed_table('trigger_table_1', NULL, distribution_type=>null); -SET citus.enable_unsafe_triggers TO ON; -SELECT create_distributed_table('trigger_table_1', NULL, distribution_type=>null); - INSERT INTO trigger_table_1 VALUES(1), (2); SELECT * FROM trigger_table_1 ORDER BY value; @@ -952,11 +1211,22 @@ SET client_min_messages TO NOTICE; TRUNCATE trigger_table_3; SET client_min_messages TO WARNING; +-- test rename, disable and drop trigger +ALTER TRIGGER trigger_4 ON trigger_table_3 RENAME TO trigger_new_name; +ALTER TABLE trigger_table_3 DISABLE TRIGGER ALL; +DROP TRIGGER trigger_new_name ON trigger_table_3; +-- enable the remaining triggers +ALTER TABLE trigger_table_3 ENABLE TRIGGER ALL; + -- try a few simple queries at least to make sure that we don't crash BEGIN; INSERT INTO nullkey_c1_t1 SELECT * FROM nullkey_c2_t1; ROLLBACK; +DROP TRIGGER IF EXISTS trigger_1 ON trigger_table_1; +DROP TRIGGER trigger_2 ON trigger_table_2 CASCADE; +DROP TRIGGER trigger_3 ON trigger_table_3 RESTRICT; + -- cleanup at exit SET client_min_messages TO ERROR; DROP SCHEMA create_null_dist_key, "NULL_!_dist_key" CASCADE; From ac0ffc9839782478a4400dc77667b5101f9a6f64 Mon Sep 17 00:00:00 2001 From: Onur Tirtir Date: Wed, 22 Mar 2023 11:13:31 +0300 Subject: [PATCH 027/118] Add a config for arbitrary config tests where all the tables are null-shard-key tables (#6783/#6788) --- .../citus_arbitrary_configs.py | 11 +++++ src/test/regress/citus_tests/config.py | 46 +++++++++++++++++++ .../regress/expected/null_dist_key_prep.out | 13 ++++++ src/test/regress/null_dist_key_prep_schedule | 1 + src/test/regress/sql/null_dist_key_prep.sql | 14 ++++++ 5 files changed, 85 insertions(+) create mode 100644 src/test/regress/expected/null_dist_key_prep.out create mode 100644 src/test/regress/null_dist_key_prep_schedule create mode 100644 src/test/regress/sql/null_dist_key_prep.sql diff --git a/src/test/regress/citus_tests/arbitrary_configs/citus_arbitrary_configs.py b/src/test/regress/citus_tests/arbitrary_configs/citus_arbitrary_configs.py index 6c9863434..8785de8f7 100755 --- a/src/test/regress/citus_tests/arbitrary_configs/citus_arbitrary_configs.py +++ b/src/test/regress/citus_tests/arbitrary_configs/citus_arbitrary_configs.py @@ -76,6 +76,17 @@ def run_for_config(config, lock, sql_schedule_name): cfg.SUPER_USER_NAME, ) common.save_regression_diff("postgres", config.output_dir) + elif config.all_null_dist_key: + exitCode |= common.run_pg_regress_without_exit( + config.bindir, + config.pg_srcdir, + config.coordinator_port(), + cfg.NULL_DIST_KEY_PREP_SCHEDULE, + config.output_dir, + config.input_dir, + cfg.SUPER_USER_NAME, + ) + common.save_regression_diff("null_dist_key_prep_regression", config.output_dir) exitCode |= _run_pg_regress_on_port( config, config.coordinator_port(), cfg.CREATE_SCHEDULE diff --git a/src/test/regress/citus_tests/config.py b/src/test/regress/citus_tests/config.py index 3dc47671b..cd1be125b 100644 --- a/src/test/regress/citus_tests/config.py +++ b/src/test/regress/citus_tests/config.py @@ -22,6 +22,7 @@ ARBITRARY_SCHEDULE_NAMES = [ "sql_schedule", "sql_base_schedule", "postgres_schedule", + "null_dist_key_prep_schedule", ] BEFORE_PG_UPGRADE_SCHEDULE = "./before_pg_upgrade_schedule" @@ -29,6 +30,7 @@ AFTER_PG_UPGRADE_SCHEDULE = "./after_pg_upgrade_schedule" CREATE_SCHEDULE = "./create_schedule" POSTGRES_SCHEDULE = "./postgres_schedule" +NULL_DIST_KEY_PREP_SCHEDULE = "./null_dist_key_prep_schedule" SQL_SCHEDULE = "./sql_schedule" SQL_BASE_SCHEDULE = "./sql_base_schedule" @@ -101,6 +103,7 @@ class CitusBaseClusterConfig(object, metaclass=NewInitCaller): self.dbname = DATABASE_NAME self.is_mx = True self.is_citus = True + self.all_null_dist_key = False self.name = type(self).__name__ self.settings = { "shared_preload_libraries": "citus", @@ -203,6 +206,49 @@ class PostgresConfig(CitusDefaultClusterConfig): ] +class AllNullDistKeyDefaultConfig(CitusDefaultClusterConfig): + def __init__(self, arguments): + super().__init__(arguments) + self.all_null_dist_key = True + self.skip_tests += [ + # i) Skip the following tests because they require SQL support beyond + # router planner / supporting more DDL command types. + # + # group 1 + "dropped_columns_create_load", + "dropped_columns_1", + # group 2 + "distributed_planning_create_load", + "distributed_planning", + # group 4 + "views_create", + "views", + # group 5 + "intermediate_result_pruning_create", + "intermediate_result_pruning_queries_1", + "intermediate_result_pruning_queries_2", + # group 6 + "local_dist_join_load", + "local_dist_join", + "arbitrary_configs_recurring_outer_join", + # group 7 + "sequences_create", + "sequences", + # group 8 + "function_create", + "functions", + # group 9 + "merge_arbitrary_create", + "merge_arbitrary", + # group 10 + "arbitrary_configs_router_create", + "arbitrary_configs_router", + # + # ii) Skip the following test as it requires support for create_distributed_function. + "nested_execution", + ] + + class CitusSingleNodeClusterConfig(CitusDefaultClusterConfig): def __init__(self, arguments): super().__init__(arguments) diff --git a/src/test/regress/expected/null_dist_key_prep.out b/src/test/regress/expected/null_dist_key_prep.out new file mode 100644 index 000000000..7a861b06f --- /dev/null +++ b/src/test/regress/expected/null_dist_key_prep.out @@ -0,0 +1,13 @@ +ALTER FUNCTION create_distributed_table RENAME TO create_distributed_table_internal; +CREATE OR REPLACE FUNCTION pg_catalog.create_distributed_table(table_name regclass, + distribution_column text, + distribution_type citus.distribution_type DEFAULT 'hash', + colocate_with text DEFAULT 'default', + shard_count int DEFAULT NULL) +RETURNS void +LANGUAGE plpgsql +AS $function$ +BEGIN + PERFORM create_distributed_table_internal(table_name, NULL, NULL, colocate_with, NULL); +END; +$function$; diff --git a/src/test/regress/null_dist_key_prep_schedule b/src/test/regress/null_dist_key_prep_schedule new file mode 100644 index 000000000..1a43130ec --- /dev/null +++ b/src/test/regress/null_dist_key_prep_schedule @@ -0,0 +1 @@ +test: null_dist_key_prep diff --git a/src/test/regress/sql/null_dist_key_prep.sql b/src/test/regress/sql/null_dist_key_prep.sql new file mode 100644 index 000000000..5a9a3ac01 --- /dev/null +++ b/src/test/regress/sql/null_dist_key_prep.sql @@ -0,0 +1,14 @@ +ALTER FUNCTION create_distributed_table RENAME TO create_distributed_table_internal; + +CREATE OR REPLACE FUNCTION pg_catalog.create_distributed_table(table_name regclass, + distribution_column text, + distribution_type citus.distribution_type DEFAULT 'hash', + colocate_with text DEFAULT 'default', + shard_count int DEFAULT NULL) +RETURNS void +LANGUAGE plpgsql +AS $function$ +BEGIN + PERFORM create_distributed_table_internal(table_name, NULL, NULL, colocate_with, NULL); +END; +$function$; From 85745b46d5e98d5b5fa667a9bc30f5bd42ba9ad6 Mon Sep 17 00:00:00 2001 From: Onur Tirtir Date: Tue, 28 Mar 2023 18:47:59 +0300 Subject: [PATCH 028/118] Add initial sql support for distributed tables that don't have a shard key (#6773/#6822) Enable router planner and a limited version of INSERT .. SELECT planner for the queries that reference colocated null shard key tables. * SELECT / UPDATE / DELETE / MERGE is supported as long as it's a router query. * INSERT .. SELECT is supported as long as it only references colocated null shard key tables. Note that this is not only limited to distributed INSERT .. SELECT but also covers a limited set of query types that require pull-to-coordinator, e.g., due to LIMIT clause, generate_series() etc. ... (Ideally distributed INSERT .. SELECT could handle such queries too, e.g., when we're only referencing tables that don't have a shard key, but today this is not the case. See https://github.com/citusdata/citus/pull/6773#discussion_r1140130562. --- .../distributed/planner/distributed_planner.c | 32 + .../planner/fast_path_router_planner.c | 17 +- .../planner/insert_select_planner.c | 92 +- .../distributed/planner/merge_planner.c | 5 + .../planner/multi_logical_planner.c | 16 +- .../planner/multi_router_planner.c | 24 +- src/include/distributed/distributed_planner.h | 13 +- .../distributed/multi_logical_planner.h | 1 + .../distributed/multi_router_planner.h | 1 + src/test/regress/citus_tests/config.py | 6 - .../regress/expected/create_null_dist_key.out | 1 + src/test/regress/expected/merge.out | 147 ++ .../regress/expected/query_null_dist_key.out | 1796 +++++++++++++++++ src/test/regress/multi_1_schedule | 1 + src/test/regress/sql/merge.sql | 112 + src/test/regress/sql/query_null_dist_key.sql | 1132 +++++++++++ 16 files changed, 3375 insertions(+), 21 deletions(-) create mode 100644 src/test/regress/expected/query_null_dist_key.out create mode 100644 src/test/regress/sql/query_null_dist_key.sql diff --git a/src/backend/distributed/planner/distributed_planner.c b/src/backend/distributed/planner/distributed_planner.c index 38962b333..50509baea 100644 --- a/src/backend/distributed/planner/distributed_planner.c +++ b/src/backend/distributed/planner/distributed_planner.c @@ -1025,6 +1025,17 @@ CreateDistributedPlan(uint64 planId, bool allowRecursivePlanning, Query *origina { return distributedPlan; } + else if (ContainsNullDistKeyTable(originalQuery)) + { + /* + * We only support router queries if the query contains reference to + * a null-dist-key table. This temporary restriction will be removed + * once we support recursive planning for the queries that reference + * null-dist-key tables. + */ + WrapRouterErrorForNullDistKeyTable(distributedPlan->planningError); + RaiseDeferredError(distributedPlan->planningError, ERROR); + } else { RaiseDeferredError(distributedPlan->planningError, DEBUG2); @@ -2462,6 +2473,18 @@ HasUnresolvedExternParamsWalker(Node *expression, ParamListInfo boundParams) } +/* + * ContainsNullDistKeyTable returns true if given query contains reference + * to a null-dist-key table. + */ +bool +ContainsNullDistKeyTable(Query *query) +{ + RTEListProperties *rteListProperties = GetRTEListPropertiesForQuery(query); + return rteListProperties->hasDistTableWithoutShardKey; +} + + /* * GetRTEListPropertiesForQuery is a wrapper around GetRTEListProperties that * returns RTEListProperties for the rte list retrieved from query. @@ -2538,6 +2561,15 @@ GetRTEListProperties(List *rangeTableList) else if (IsCitusTableTypeCacheEntry(cacheEntry, DISTRIBUTED_TABLE)) { rteListProperties->hasDistributedTable = true; + + if (!HasDistributionKeyCacheEntry(cacheEntry)) + { + rteListProperties->hasDistTableWithoutShardKey = true; + } + else + { + rteListProperties->hasDistTableWithShardKey = true; + } } else { diff --git a/src/backend/distributed/planner/fast_path_router_planner.c b/src/backend/distributed/planner/fast_path_router_planner.c index ecb62478a..2be4a5626 100644 --- a/src/backend/distributed/planner/fast_path_router_planner.c +++ b/src/backend/distributed/planner/fast_path_router_planner.c @@ -212,6 +212,16 @@ FastPathRouterQuery(Query *query, Node **distributionKeyValue) return false; } + /* + * If the table doesn't have a distribution column, we don't need to + * check anything further. + */ + Var *distributionKey = PartitionColumn(distributedTableId, 1); + if (!distributionKey) + { + return true; + } + /* WHERE clause should not be empty for distributed tables */ if (joinTree == NULL || (IsCitusTableTypeCacheEntry(cacheEntry, DISTRIBUTED_TABLE) && joinTree->quals == @@ -220,13 +230,6 @@ FastPathRouterQuery(Query *query, Node **distributionKeyValue) return false; } - /* if that's a reference table, we don't need to check anything further */ - Var *distributionKey = PartitionColumn(distributedTableId, 1); - if (!distributionKey) - { - return true; - } - /* convert list of expressions into expression tree for further processing */ quals = joinTree->quals; if (quals != NULL && IsA(quals, List)) diff --git a/src/backend/distributed/planner/insert_select_planner.c b/src/backend/distributed/planner/insert_select_planner.c index 53fe58cdb..175f6bc6f 100644 --- a/src/backend/distributed/planner/insert_select_planner.c +++ b/src/backend/distributed/planner/insert_select_planner.c @@ -57,6 +57,7 @@ static DistributedPlan * CreateInsertSelectPlanInternal(uint64 planId, PlannerRestrictionContext * plannerRestrictionContext, ParamListInfo boundParams); +static void ErrorIfInsertSelectWithNullDistKeyNotSupported(Query *originalQuery); static DistributedPlan * CreateDistributedInsertSelectPlan(Query *originalQuery, PlannerRestrictionContext * plannerRestrictionContext); @@ -241,6 +242,12 @@ CreateInsertSelectPlanInternal(uint64 planId, Query *originalQuery, RaiseDeferredError(deferredError, ERROR); } + /* + * We support a limited set of INSERT .. SELECT queries if the query + * references a null-dist-key table. + */ + ErrorIfInsertSelectWithNullDistKeyNotSupported(originalQuery); + DistributedPlan *distributedPlan = CreateDistributedInsertSelectPlan(originalQuery, plannerRestrictionContext); @@ -260,6 +267,74 @@ CreateInsertSelectPlanInternal(uint64 planId, Query *originalQuery, } +/* + * ErrorIfInsertSelectWithNullDistKeyNotSupported throws an error if given INSERT + * .. SELECT query references a null-dist-key table (as the target table or in + * the SELECT clause) and is unsupported. + * + * Such an INSERT .. SELECT query is supported as long as the it only references + * a "colocated" set of null-dist-key tables, no other relation rte types. + */ +static void +ErrorIfInsertSelectWithNullDistKeyNotSupported(Query *originalQuery) +{ + RangeTblEntry *subqueryRte = ExtractSelectRangeTableEntry(originalQuery); + Query *subquery = subqueryRte->subquery; + RTEListProperties *subqueryRteListProperties = GetRTEListPropertiesForQuery(subquery); + + RangeTblEntry *insertRte = ExtractResultRelationRTEOrError(originalQuery); + Oid targetRelationId = insertRte->relid; + if (!IsCitusTableType(targetRelationId, NULL_KEY_DISTRIBUTED_TABLE) && + subqueryRteListProperties->hasDistTableWithoutShardKey) + { + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot select from a distributed table that " + "does not have a shard key when inserting into " + "a different table type"))); + } + else if (IsCitusTableType(targetRelationId, NULL_KEY_DISTRIBUTED_TABLE)) + { + if (subqueryRteListProperties->hasPostgresLocalTable || + subqueryRteListProperties->hasReferenceTable || + subqueryRteListProperties->hasCitusLocalTable || + subqueryRteListProperties->hasDistTableWithShardKey || + subqueryRteListProperties->hasMaterializedView) + { + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot select from different table types " + "when inserting into a distributed table " + "that does not have a shard key"))); + } + + if (!subqueryRteListProperties->hasDistTableWithoutShardKey) + { + /* + * This means that the SELECT doesn't reference any Citus tables, + * Postgres tables or materialized views but references a function + * call, a values claue etc., or a cte from INSERT. + * + * In that case, we rely on the common restrictions enforced by the + * INSERT .. SELECT planners. + */ + Assert(!NeedsDistributedPlanning(subquery)); + return; + } + + List *distributedRelationIdList = DistributedRelationIdList(subquery); + distributedRelationIdList = lappend_oid(distributedRelationIdList, + targetRelationId); + + if (!AllDistributedRelationsInListColocated(distributedRelationIdList)) + { + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot select from a non-colocated distributed " + "table when inserting into a distributed table " + "that does not have a shard key"))); + } + } +} + + /* * CreateDistributedInsertSelectPlan creates a DistributedPlan for distributed * INSERT ... SELECT queries which could consist of multiple tasks. @@ -379,6 +454,16 @@ CreateInsertSelectIntoLocalTablePlan(uint64 planId, Query *insertSelectQuery, { RangeTblEntry *selectRte = ExtractSelectRangeTableEntry(insertSelectQuery); + RTEListProperties *selectRteListProperties = + GetRTEListPropertiesForQuery(selectRte->subquery); + if (selectRteListProperties->hasDistTableWithoutShardKey) + { + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot select from a distributed table that " + "does not have a shard key when inserting into " + "a local table"))); + } + PrepareInsertSelectForCitusPlanner(insertSelectQuery); /* get the SELECT query (may have changed after PrepareInsertSelectForCitusPlanner) */ @@ -717,10 +802,7 @@ DistributedInsertSelectSupported(Query *queryTree, RangeTblEntry *insertRte, } else if (IsCitusTableType(targetRelationId, NULL_KEY_DISTRIBUTED_TABLE)) { - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - "distributed INSERT ... SELECT cannot target a distributed " - "table with a null shard key", - NULL, NULL); + /* we've already checked the subquery via ErrorIfInsertSelectWithNullDistKeyNotSupported */ } else { @@ -874,7 +956,7 @@ RouterModifyTaskForShardInterval(Query *originalQuery, */ RTEListProperties *subqueryRteListProperties = GetRTEListPropertiesForQuery( copiedSubquery); - if (subqueryRteListProperties->hasDistributedTable) + if (subqueryRteListProperties->hasDistTableWithShardKey) { AddPartitionKeyNotNullFilterToSelect(copiedSubquery); } diff --git a/src/backend/distributed/planner/merge_planner.c b/src/backend/distributed/planner/merge_planner.c index 5b39aeba6..930a44db8 100644 --- a/src/backend/distributed/planner/merge_planner.c +++ b/src/backend/distributed/planner/merge_planner.c @@ -509,6 +509,11 @@ InsertDistributionColumnMatchesSource(Oid targetRelationId, Query *query) return NULL; } + if (!HasDistributionKey(targetRelationId)) + { + return NULL; + } + bool foundDistributionColumn = false; MergeAction *action = NULL; foreach_ptr(action, query->mergeActionList) diff --git a/src/backend/distributed/planner/multi_logical_planner.c b/src/backend/distributed/planner/multi_logical_planner.c index d9322bf5e..7732b6c5e 100644 --- a/src/backend/distributed/planner/multi_logical_planner.c +++ b/src/backend/distributed/planner/multi_logical_planner.c @@ -272,7 +272,7 @@ TargetListOnPartitionColumn(Query *query, List *targetEntryList) if (!targetListOnPartitionColumn) { if (!FindNodeMatchingCheckFunctionInRangeTableList(query->rtable, - IsDistributedTableRTE)) + IsTableWithDistKeyRTE)) { targetListOnPartitionColumn = true; } @@ -379,6 +379,20 @@ IsReferenceTableRTE(Node *node) } +/* + * IsTableWithDistKeyRTE gets a node and returns true if the node + * is a range table relation entry that points to a distributed table + * that has a distribution column. + */ +bool +IsTableWithDistKeyRTE(Node *node) +{ + Oid relationId = NodeTryGetRteRelid(node); + return relationId != InvalidOid && IsCitusTable(relationId) && + HasDistributionKey(relationId); +} + + /* * FullCompositeFieldList gets a composite field list, and checks if all fields * of composite type are used in the list. diff --git a/src/backend/distributed/planner/multi_router_planner.c b/src/backend/distributed/planner/multi_router_planner.c index 97c2cecf6..47d11172f 100644 --- a/src/backend/distributed/planner/multi_router_planner.c +++ b/src/backend/distributed/planner/multi_router_planner.c @@ -258,6 +258,22 @@ CreateModifyPlan(Query *originalQuery, Query *query, } +/* + * WrapRouterErrorForNullDistKeyTable wraps given planning error with a + * generic error message if given query references a distributed table + * that doesn't have a distribution key. + */ +void +WrapRouterErrorForNullDistKeyTable(DeferredErrorMessage *planningError) +{ + planningError->detail = planningError->message; + planningError->message = pstrdup("queries that reference a distributed " + "table without a shard key can only " + "reference colocated distributed " + "tables or reference tables"); +} + + /* * CreateSingleTaskRouterSelectPlan creates a physical plan for given SELECT query. * The returned plan is a router task that returns query results from a single worker. @@ -1870,6 +1886,11 @@ RouterJob(Query *originalQuery, PlannerRestrictionContext *plannerRestrictionCon */ if (IsMergeQuery(originalQuery)) { + if (ContainsNullDistKeyTable(originalQuery)) + { + WrapRouterErrorForNullDistKeyTable(*planningError); + } + RaiseDeferredError(*planningError, ERROR); } else @@ -3855,7 +3876,8 @@ ErrorIfQueryHasUnroutableModifyingCTE(Query *queryTree) CitusTableCacheEntry *modificationTableCacheEntry = GetCitusTableCacheEntry(distributedTableId); - if (!HasDistributionKeyCacheEntry(modificationTableCacheEntry)) + if (!IsCitusTableTypeCacheEntry(modificationTableCacheEntry, + DISTRIBUTED_TABLE)) { return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, "cannot router plan modification of a non-distributed table", diff --git a/src/include/distributed/distributed_planner.h b/src/include/distributed/distributed_planner.h index 412859449..753504131 100644 --- a/src/include/distributed/distributed_planner.h +++ b/src/include/distributed/distributed_planner.h @@ -147,9 +147,19 @@ typedef struct RTEListProperties bool hasReferenceTable; bool hasCitusLocalTable; - /* includes hash, append and range partitioned tables */ + /* includes hash, null dist key, append and range partitioned tables */ bool hasDistributedTable; + /* + * Effectively, hasDistributedTable is equal to + * "hasDistTableWithShardKey || hasDistTableWithoutShardKey". + * + * We provide below two for the callers that want to know what kind of + * distributed tables that given query has references to. + */ + bool hasDistTableWithShardKey; + bool hasDistTableWithoutShardKey; + /* union of hasReferenceTable, hasCitusLocalTable and hasDistributedTable */ bool hasCitusTable; @@ -243,6 +253,7 @@ extern int32 BlessRecordExpression(Expr *expr); extern void DissuadePlannerFromUsingPlan(PlannedStmt *plan); extern PlannedStmt * FinalizePlan(PlannedStmt *localPlan, struct DistributedPlan *distributedPlan); +extern bool ContainsNullDistKeyTable(Query *query); extern RTEListProperties * GetRTEListPropertiesForQuery(Query *query); diff --git a/src/include/distributed/multi_logical_planner.h b/src/include/distributed/multi_logical_planner.h index 189170358..de4901ea2 100644 --- a/src/include/distributed/multi_logical_planner.h +++ b/src/include/distributed/multi_logical_planner.h @@ -200,6 +200,7 @@ extern bool IsCitusTableRTE(Node *node); extern bool IsDistributedOrReferenceTableRTE(Node *node); extern bool IsDistributedTableRTE(Node *node); extern bool IsReferenceTableRTE(Node *node); +extern bool IsTableWithDistKeyRTE(Node *node); extern bool IsCitusExtraDataContainerRelation(RangeTblEntry *rte); extern bool ContainsReadIntermediateResultFunction(Node *node); extern bool ContainsReadIntermediateResultArrayFunction(Node *node); diff --git a/src/include/distributed/multi_router_planner.h b/src/include/distributed/multi_router_planner.h index a255fd520..40d92fead 100644 --- a/src/include/distributed/multi_router_planner.h +++ b/src/include/distributed/multi_router_planner.h @@ -36,6 +36,7 @@ extern DistributedPlan * CreateRouterPlan(Query *originalQuery, Query *query, extern DistributedPlan * CreateModifyPlan(Query *originalQuery, Query *query, PlannerRestrictionContext * plannerRestrictionContext); +extern void WrapRouterErrorForNullDistKeyTable(DeferredErrorMessage *planningError); extern DeferredErrorMessage * PlanRouterQuery(Query *originalQuery, PlannerRestrictionContext * plannerRestrictionContext, diff --git a/src/test/regress/citus_tests/config.py b/src/test/regress/citus_tests/config.py index cd1be125b..69cc5599c 100644 --- a/src/test/regress/citus_tests/config.py +++ b/src/test/regress/citus_tests/config.py @@ -237,12 +237,6 @@ class AllNullDistKeyDefaultConfig(CitusDefaultClusterConfig): # group 8 "function_create", "functions", - # group 9 - "merge_arbitrary_create", - "merge_arbitrary", - # group 10 - "arbitrary_configs_router_create", - "arbitrary_configs_router", # # ii) Skip the following test as it requires support for create_distributed_function. "nested_execution", diff --git a/src/test/regress/expected/create_null_dist_key.out b/src/test/regress/expected/create_null_dist_key.out index af6e66f62..43120a454 100644 --- a/src/test/regress/expected/create_null_dist_key.out +++ b/src/test/regress/expected/create_null_dist_key.out @@ -1803,6 +1803,7 @@ ALTER TABLE trigger_table_3 ENABLE TRIGGER ALL; -- try a few simple queries at least to make sure that we don't crash BEGIN; INSERT INTO nullkey_c1_t1 SELECT * FROM nullkey_c2_t1; +ERROR: cannot select from a non-colocated distributed table when inserting into a distributed table that does not have a shard key ROLLBACK; DROP TRIGGER IF EXISTS trigger_1 ON trigger_table_1; DROP TRIGGER trigger_2 ON trigger_table_2 CASCADE; diff --git a/src/test/regress/expected/merge.out b/src/test/regress/expected/merge.out index 2196d966d..fd82efa8c 100644 --- a/src/test/regress/expected/merge.out +++ b/src/test/regress/expected/merge.out @@ -3228,6 +3228,153 @@ WHEN NOT MATCHED THEN INSERT VALUES(dist_source.id, dist_source.val); ERROR: For MERGE command, all the distributed tables must be colocated, for append/range distribution, colocation is not supported HINT: Consider using hash distribution instead +-- test merge with null shard key tables +CREATE SCHEMA query_null_dist_key; +SET search_path TO query_null_dist_key; +SET client_min_messages TO DEBUG2; +CREATE TABLE nullkey_c1_t1(a int, b int); +CREATE TABLE nullkey_c1_t2(a int, b int); +SELECT create_distributed_table('nullkey_c1_t1', null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('nullkey_c1_t2', null, colocate_with=>'nullkey_c1_t1'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE nullkey_c2_t1(a int, b int); +CREATE TABLE nullkey_c2_t2(a int, b int); +SELECT create_distributed_table('nullkey_c2_t1', null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('nullkey_c2_t2', null, colocate_with=>'nullkey_c2_t1', distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE reference_table(a int, b int); +SELECT create_reference_table('reference_table'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO reference_table SELECT i, i FROM generate_series(0, 5) i; +DEBUG: distributed INSERT ... SELECT can only select from distributed tables +DEBUG: Collecting INSERT ... SELECT results on coordinator +CREATE TABLE distributed_table(a int, b int); +SELECT create_distributed_table('distributed_table', 'a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO distributed_table SELECT i, i FROM generate_series(3, 8) i; +DEBUG: distributed INSERT ... SELECT can only select from distributed tables +DEBUG: Collecting INSERT ... SELECT results on coordinator +CREATE TABLE citus_local_table(a int, b int); +SELECT citus_add_local_table_to_metadata('citus_local_table'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO citus_local_table SELECT i, i FROM generate_series(0, 10) i; +DEBUG: distributed INSERT ... SELECT can only select from distributed tables +DEBUG: Collecting INSERT ... SELECT results on coordinator +CREATE TABLE postgres_local_table(a int, b int); +INSERT INTO postgres_local_table SELECT i, i FROM generate_series(5, 10) i; +-- with a colocated table +MERGE INTO nullkey_c1_t1 USING nullkey_c1_t2 ON (nullkey_c1_t1.a = nullkey_c1_t2.a) +WHEN MATCHED THEN UPDATE SET b = nullkey_c1_t2.b; +DEBUG: +DEBUG: Creating MERGE router plan +MERGE INTO nullkey_c1_t1 USING nullkey_c1_t2 ON (nullkey_c1_t1.a = nullkey_c1_t2.a) +WHEN MATCHED THEN DELETE; +DEBUG: +DEBUG: Creating MERGE router plan +MERGE INTO nullkey_c1_t1 USING nullkey_c1_t2 ON (nullkey_c1_t1.a = nullkey_c1_t2.a) +WHEN MATCHED THEN UPDATE SET b = nullkey_c1_t2.b +WHEN NOT MATCHED THEN INSERT VALUES (nullkey_c1_t2.a, nullkey_c1_t2.b); +DEBUG: +DEBUG: Creating MERGE router plan +MERGE INTO nullkey_c1_t1 USING nullkey_c1_t2 ON (nullkey_c1_t1.a = nullkey_c1_t2.a) +WHEN MATCHED THEN DELETE +WHEN NOT MATCHED THEN INSERT VALUES (nullkey_c1_t2.a, nullkey_c1_t2.b); +DEBUG: +DEBUG: Creating MERGE router plan +-- with non-colocated null-dist-key table +MERGE INTO nullkey_c1_t1 USING nullkey_c2_t1 ON (nullkey_c1_t1.a = nullkey_c2_t1.a) +WHEN MATCHED THEN UPDATE SET b = nullkey_c2_t1.b; +ERROR: For MERGE command, all the distributed tables must be colocated +MERGE INTO nullkey_c1_t1 USING nullkey_c2_t1 ON (nullkey_c1_t1.a = nullkey_c2_t1.a) +WHEN MATCHED THEN UPDATE SET b = nullkey_c2_t1.b +WHEN NOT MATCHED THEN INSERT VALUES (nullkey_c2_t1.a, nullkey_c2_t1.b); +ERROR: For MERGE command, all the distributed tables must be colocated +-- with a distributed table +MERGE INTO nullkey_c1_t1 USING distributed_table ON (nullkey_c1_t1.a = distributed_table.a) +WHEN MATCHED THEN UPDATE SET b = distributed_table.b +WHEN NOT MATCHED THEN INSERT VALUES (distributed_table.a, distributed_table.b); +ERROR: For MERGE command, all the distributed tables must be colocated +MERGE INTO distributed_table USING nullkey_c1_t1 ON (nullkey_c1_t1.a = distributed_table.a) +WHEN MATCHED THEN DELETE +WHEN NOT MATCHED THEN INSERT VALUES (nullkey_c1_t1.a, nullkey_c1_t1.b); +ERROR: For MERGE command, all the distributed tables must be colocated +-- with a reference table +MERGE INTO nullkey_c1_t1 USING reference_table ON (nullkey_c1_t1.a = reference_table.a) +WHEN MATCHED THEN UPDATE SET b = reference_table.b; +ERROR: MERGE command is not supported on reference tables yet +MERGE INTO reference_table USING nullkey_c1_t1 ON (nullkey_c1_t1.a = reference_table.a) +WHEN MATCHED THEN UPDATE SET b = nullkey_c1_t1.b +WHEN NOT MATCHED THEN INSERT VALUES (nullkey_c1_t1.a, nullkey_c1_t1.b); +ERROR: MERGE command is not supported on reference tables yet +-- with a citus local table +MERGE INTO nullkey_c1_t1 USING citus_local_table ON (nullkey_c1_t1.a = citus_local_table.a) +WHEN MATCHED THEN UPDATE SET b = citus_local_table.b; +ERROR: MERGE command is not supported with combination of distributed/local tables yet +MERGE INTO citus_local_table USING nullkey_c1_t1 ON (nullkey_c1_t1.a = citus_local_table.a) +WHEN MATCHED THEN DELETE; +ERROR: MERGE command is not supported with combination of distributed/local tables yet +-- with a postgres table +MERGE INTO nullkey_c1_t1 USING postgres_local_table ON (nullkey_c1_t1.a = postgres_local_table.a) +WHEN MATCHED THEN UPDATE SET b = postgres_local_table.b; +ERROR: MERGE command is not supported with combination of distributed/local tables yet +MERGE INTO postgres_local_table USING nullkey_c1_t1 ON (nullkey_c1_t1.a = postgres_local_table.a) +WHEN MATCHED THEN UPDATE SET b = nullkey_c1_t1.b +WHEN NOT MATCHED THEN INSERT VALUES (nullkey_c1_t1.a, nullkey_c1_t1.b); +ERROR: MERGE command is not supported with combination of distributed/local tables yet +-- using ctes +WITH cte AS ( + SELECT * FROM nullkey_c1_t1 +) +MERGE INTO nullkey_c1_t1 USING cte ON (nullkey_c1_t1.a = cte.a) +WHEN MATCHED THEN UPDATE SET b = cte.b; +DEBUG: +DEBUG: Creating MERGE router plan +WITH cte AS ( + SELECT * FROM distributed_table +) +MERGE INTO nullkey_c1_t1 USING cte ON (nullkey_c1_t1.a = cte.a) +WHEN MATCHED THEN UPDATE SET b = cte.b; +ERROR: For MERGE command, all the distributed tables must be colocated +WITH cte AS materialized ( + SELECT * FROM distributed_table +) +MERGE INTO nullkey_c1_t1 USING cte ON (nullkey_c1_t1.a = cte.a) +WHEN MATCHED THEN UPDATE SET b = cte.b; +ERROR: For MERGE command, all the distributed tables must be colocated +SET client_min_messages TO WARNING; +DROP SCHEMA query_null_dist_key CASCADE; +RESET client_min_messages; +SET search_path TO merge_schema; DROP SERVER foreign_server CASCADE; NOTICE: drop cascades to 3 other objects DETAIL: drop cascades to user mapping for postgres on server foreign_server diff --git a/src/test/regress/expected/query_null_dist_key.out b/src/test/regress/expected/query_null_dist_key.out new file mode 100644 index 000000000..09413a3ea --- /dev/null +++ b/src/test/regress/expected/query_null_dist_key.out @@ -0,0 +1,1796 @@ +CREATE SCHEMA query_null_dist_key; +SET search_path TO query_null_dist_key; +SET citus.next_shard_id TO 1620000; +SET citus.shard_count TO 32; +SET client_min_messages TO WARNING; +SELECT 1 FROM citus_add_node('localhost', :master_port, groupid => 0); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +SET client_min_messages TO NOTICE; +CREATE TABLE nullkey_c1_t1(a int, b int); +CREATE TABLE nullkey_c1_t2(a int, b int); +SELECT create_distributed_table('nullkey_c1_t1', null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('nullkey_c1_t2', null, colocate_with=>'nullkey_c1_t1'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO nullkey_c1_t1 SELECT i, i FROM generate_series(1, 8) i; +INSERT INTO nullkey_c1_t2 SELECT i, i FROM generate_series(2, 7) i; +CREATE TABLE nullkey_c2_t1(a int, b int); +CREATE TABLE nullkey_c2_t2(a int, b int); +SELECT create_distributed_table('nullkey_c2_t1', null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('nullkey_c2_t2', null, colocate_with=>'nullkey_c2_t1', distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO nullkey_c2_t1 SELECT i, i FROM generate_series(2, 7) i; +INSERT INTO nullkey_c2_t2 SELECT i, i FROM generate_series(1, 8) i; +CREATE TABLE nullkey_c3_t1(a int, b int); +SELECT create_distributed_table('nullkey_c3_t1', null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO nullkey_c3_t1 SELECT i, i FROM generate_series(1, 8) i; +CREATE TABLE reference_table(a int, b int); +SELECT create_reference_table('reference_table'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO reference_table SELECT i, i FROM generate_series(0, 5) i; +CREATE TABLE distributed_table(a int, b int); +SELECT create_distributed_table('distributed_table', 'a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO distributed_table SELECT i, i FROM generate_series(3, 8) i; +CREATE TABLE citus_local_table(a int, b int); +SELECT citus_add_local_table_to_metadata('citus_local_table'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO citus_local_table SELECT i, i FROM generate_series(0, 10) i; +CREATE TABLE postgres_local_table(a int, b int); +INSERT INTO postgres_local_table SELECT i, i FROM generate_series(5, 10) i; +CREATE TABLE articles_hash ( + id bigint NOT NULL, + author_id bigint NOT NULL, + title varchar(20) NOT NULL, + word_count integer +); +INSERT INTO articles_hash VALUES ( 4, 4, 'altdorfer', 14551),( 5, 5, 'aruru', 11389), + (13, 3, 'aseyev', 2255),(15, 5, 'adversa', 3164), + (18, 8, 'assembly', 911),(19, 9, 'aubergiste', 4981), + (28, 8, 'aerophyte', 5454),(29, 9, 'amateur', 9524), + (42, 2, 'ausable', 15885),(43, 3, 'affixal', 12723), + (49, 9, 'anyone', 2681),(50, 10, 'anjanette', 19519); +SELECT create_distributed_table('articles_hash', null, colocate_with=>'none'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$query_null_dist_key.articles_hash$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE raw_events_first (user_id int, time timestamp, value_1 int, value_2 int, value_3 float, value_4 bigint, UNIQUE(user_id, value_1)); +SELECT create_distributed_table('raw_events_first', null, colocate_with=>'none', distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE raw_events_second (user_id int, time timestamp, value_1 int, value_2 int, value_3 float, value_4 bigint, UNIQUE(user_id, value_1)); +SELECT create_distributed_table('raw_events_second', null, colocate_with=>'raw_events_first', distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE agg_events (user_id int, value_1_agg int, value_2_agg int, value_3_agg float, value_4_agg bigint, agg_time timestamp, UNIQUE(user_id, value_1_agg)); +SELECT create_distributed_table('agg_events', null, colocate_with=>'raw_events_first', distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE users_ref_table (user_id int); +SELECT create_reference_table('users_ref_table'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO raw_events_first VALUES (1, '1970-01-01', 10, 100, 1000.1, 10000), (3, '1971-01-01', 30, 300, 3000.1, 30000), + (5, '1972-01-01', 50, 500, 5000.1, 50000), (2, '1973-01-01', 20, 200, 2000.1, 20000), + (4, '1974-01-01', 40, 400, 4000.1, 40000), (6, '1975-01-01', 60, 600, 6000.1, 60000); +CREATE TABLE modify_fast_path(key int, value_1 int, value_2 text); +SELECT create_distributed_table('modify_fast_path', null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE modify_fast_path_reference(key int, value_1 int, value_2 text); +SELECT create_reference_table('modify_fast_path_reference'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE bigserial_test (x int, y int, z bigserial); +SELECT create_distributed_table('bigserial_test', null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE append_table (text_col text, a int); +SELECT create_distributed_table('append_table', 'a', 'append'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT master_create_empty_shard('append_table') AS shardid1 \gset +SELECT master_create_empty_shard('append_table') AS shardid2 \gset +SELECT master_create_empty_shard('append_table') AS shardid3 \gset +COPY append_table (text_col, a) FROM STDIN WITH (format 'csv', append_to_shard :shardid1); +COPY append_table (text_col, a) FROM STDIN WITH (format 'csv', append_to_shard :shardid2); +CREATE TABLE range_table(a int, b int); +SELECT create_distributed_table('range_table', 'a', 'range'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CALL public.create_range_partitioned_shards('range_table', '{"0","25"}','{"24","49"}'); +INSERT INTO range_table VALUES (0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 50); +SET client_min_messages to DEBUG2; +-- simple insert +INSERT INTO nullkey_c1_t1 VALUES (1,2), (2,2), (3,4); +DEBUG: Creating router plan +INSERT INTO nullkey_c1_t2 VALUES (1,3), (3,4), (5,1), (6,2); +DEBUG: Creating router plan +INSERT INTO nullkey_c2_t1 VALUES (1,0), (2,5), (4,3), (5,2); +DEBUG: Creating router plan +INSERT INTO nullkey_c2_t2 VALUES (2,4), (3,2), (5,2), (7,4); +DEBUG: Creating router plan +-- simple select +SELECT * FROM nullkey_c1_t1 ORDER BY 1,2; +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan + a | b +--------------------------------------------------------------------- + 1 | 1 + 1 | 2 + 2 | 2 + 2 | 2 + 3 | 3 + 3 | 4 + 4 | 4 + 5 | 5 + 6 | 6 + 7 | 7 + 8 | 8 +(11 rows) + +-- for update / share +SELECT * FROM modify_fast_path WHERE key = 1 FOR UPDATE; +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan + key | value_1 | value_2 +--------------------------------------------------------------------- +(0 rows) + +SELECT * FROM modify_fast_path WHERE key = 1 FOR SHARE; +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan + key | value_1 | value_2 +--------------------------------------------------------------------- +(0 rows) + +SELECT * FROM modify_fast_path FOR UPDATE; +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan + key | value_1 | value_2 +--------------------------------------------------------------------- +(0 rows) + +SELECT * FROM modify_fast_path FOR SHARE; +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan + key | value_1 | value_2 +--------------------------------------------------------------------- +(0 rows) + +-- cartesian product with different table types +-- with other table types +SELECT COUNT(*) FROM distributed_table d1, nullkey_c1_t1; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Router planner cannot handle multi-shard select queries +SELECT COUNT(*) FROM reference_table d1, nullkey_c1_t1; +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 66 +(1 row) + +SELECT COUNT(*) FROM citus_local_table d1, nullkey_c1_t1; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +SELECT COUNT(*) FROM postgres_local_table d1, nullkey_c1_t1; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +-- with a colocated null dist key table +SELECT COUNT(*) FROM nullkey_c1_t1 d1, nullkey_c1_t2; +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 110 +(1 row) + +-- with a non-colocated null dist key table +SELECT COUNT(*) FROM nullkey_c1_t1 d1, nullkey_c2_t1; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: found no worker with all shard placements +-- First, show that nullkey_c1_t1 and nullkey_c3_t1 are not colocated. +SELECT + (SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'query_null_dist_key.nullkey_c1_t1'::regclass) != + (SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'query_null_dist_key.nullkey_c3_t1'::regclass); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- Now verify that we can join them via router planner because it doesn't care +-- about whether two tables are colocated or not but physical location of shards +-- when citus.enable_non_colocated_router_query_pushdown is set to on. +SET citus.enable_non_colocated_router_query_pushdown TO ON; +SELECT COUNT(*) FROM nullkey_c1_t1 JOIN nullkey_c3_t1 USING(a); +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 11 +(1 row) + +SET citus.enable_non_colocated_router_query_pushdown TO OFF; +SELECT COUNT(*) FROM nullkey_c1_t1 JOIN nullkey_c3_t1 USING(a); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: router planner does not support queries that reference non-colocated distributed tables +RESET citus.enable_non_colocated_router_query_pushdown; +-- colocated join between null dist key tables +SELECT COUNT(*) FROM nullkey_c1_t1 JOIN nullkey_c1_t2 USING(a); +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 14 +(1 row) + +SELECT COUNT(*) FROM nullkey_c1_t1 LEFT JOIN nullkey_c1_t2 USING(a); +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 15 +(1 row) + +SELECT COUNT(*) FROM nullkey_c1_t1 FULL JOIN nullkey_c1_t2 USING(a); +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 15 +(1 row) + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +LEFT JOIN LATERAL ( + SELECT * FROM nullkey_c1_t2 t2 WHERE t2.b > t1.a +) q USING(a); +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 11 +(1 row) + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +JOIN LATERAL ( + SELECT * FROM nullkey_c1_t2 t2 WHERE t2.b > t1.a +) q USING(a); +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 4 +(1 row) + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE EXISTS ( + SELECT * FROM nullkey_c1_t2 t2 WHERE t2.b > t1.a +); +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 9 +(1 row) + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE t1.b IN ( + SELECT b+1 FROM nullkey_c1_t2 t2 WHERE t2.b = t1.a +); +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 2 +(1 row) + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE t1.b NOT IN ( + SELECT a FROM nullkey_c1_t2 t2 WHERE t2.b > t1.a +); +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 7 +(1 row) + +-- non-colocated inner joins between null dist key tables +SELECT * FROM nullkey_c1_t1 JOIN nullkey_c2_t1 USING(a) ORDER BY 1,2,3; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: found no worker with all shard placements +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +JOIN LATERAL ( + SELECT * FROM nullkey_c2_t2 t2 WHERE t2.b > t1.a +) q USING(a); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: found no worker with all shard placements +-- non-colocated outer joins between null dist key tables +SELECT * FROM nullkey_c1_t1 LEFT JOIN nullkey_c2_t2 USING(a) ORDER BY 1,2,3 LIMIT 4; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: found no worker with all shard placements +SELECT * FROM nullkey_c1_t1 FULL JOIN nullkey_c2_t2 USING(a) ORDER BY 1,2,3 LIMIT 4; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: found no worker with all shard placements +SELECT * FROM nullkey_c1_t1 t1 +LEFT JOIN LATERAL ( + SELECT * FROM nullkey_c2_t2 t2 WHERE t2.b > t1.a +) q USING(a) ORDER BY 1,2,3 OFFSET 3 LIMIT 4; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: found no worker with all shard placements +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +LEFT JOIN LATERAL ( + SELECT * FROM nullkey_c2_t2 t2 WHERE t2.b > t1.a +) q USING(a); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: found no worker with all shard placements +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE EXISTS ( + SELECT * FROM nullkey_c2_t2 t2 WHERE t2.b > t1.a +); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: found no worker with all shard placements +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE t1.b IN ( + SELECT b+1 FROM nullkey_c2_t2 t2 WHERE t2.b = t1.a +); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: found no worker with all shard placements +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE t1.b NOT IN ( + SELECT a FROM nullkey_c2_t2 t2 WHERE t2.b > t1.a +); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: found no worker with all shard placements +-- join with a reference table +SELECT COUNT(*) FROM nullkey_c1_t1, reference_table WHERE nullkey_c1_t1.a = reference_table.a; +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 8 +(1 row) + +WITH cte_1 AS + (SELECT * FROM nullkey_c1_t1, reference_table WHERE nullkey_c1_t1.a = reference_table.a ORDER BY 1,2,3,4 FOR UPDATE) +SELECT COUNT(*) FROM cte_1; +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 8 +(1 row) + +-- join with postgres / citus local tables +SELECT * FROM nullkey_c1_t1 JOIN postgres_local_table USING(a); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +SELECT * FROM nullkey_c1_t1 JOIN citus_local_table USING(a); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +-- join with a distributed table +SELECT * FROM distributed_table d1 JOIN nullkey_c1_t1 USING(a); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Router planner cannot handle multi-shard select queries +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +JOIN LATERAL ( + SELECT * FROM distributed_table t2 WHERE t2.b > t1.a +) q USING(a); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Router planner cannot handle multi-shard select queries +SELECT COUNT(*) FROM distributed_table t1 +JOIN LATERAL ( + SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +) q USING(a); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Router planner cannot handle multi-shard select queries +-- outer joins with different table types +SELECT COUNT(*) FROM nullkey_c1_t1 LEFT JOIN reference_table USING(a); +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 11 +(1 row) + +SELECT COUNT(*) FROM reference_table LEFT JOIN nullkey_c1_t1 USING(a); +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 9 +(1 row) + +SELECT COUNT(*) FROM nullkey_c1_t1 LEFT JOIN citus_local_table USING(a); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +SELECT COUNT(*) FROM citus_local_table LEFT JOIN nullkey_c1_t1 USING(a); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +SELECT COUNT(*) FROM nullkey_c1_t1 LEFT JOIN postgres_local_table USING(a); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +SELECT COUNT(*) FROM postgres_local_table LEFT JOIN nullkey_c1_t1 USING(a); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +SELECT COUNT(*) FROM nullkey_c1_t1 FULL JOIN citus_local_table USING(a); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +SELECT COUNT(*) FROM nullkey_c1_t1 FULL JOIN postgres_local_table USING(a); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +SELECT COUNT(*) FROM nullkey_c1_t1 FULL JOIN reference_table USING(a); +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 12 +(1 row) + +SELECT COUNT(*) FROM nullkey_c1_t1 JOIN append_table USING(a); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Router planner does not support append-partitioned tables. +SELECT COUNT(*) FROM nullkey_c1_t1 JOIN range_table USING(a); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Router planner cannot handle multi-shard select queries +SET citus.enable_non_colocated_router_query_pushdown TO ON; +SELECT COUNT(*) FROM nullkey_c1_t1 JOIN range_table USING(a) WHERE range_table.a = 20; +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 0 +(1 row) + +SET citus.enable_non_colocated_router_query_pushdown TO OFF; +SELECT COUNT(*) FROM nullkey_c1_t1 JOIN range_table USING(a) WHERE range_table.a = 20; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: router planner does not support queries that reference non-colocated distributed tables +RESET citus.enable_non_colocated_router_query_pushdown; +-- lateral / semi / anti joins with different table types +-- with a reference table +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +LEFT JOIN LATERAL ( + SELECT * FROM reference_table t2 WHERE t2.b > t1.a +) q USING(a); +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 11 +(1 row) + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE EXISTS ( + SELECT * FROM reference_table t2 WHERE t2.b > t1.a +); +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 7 +(1 row) + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE NOT EXISTS ( + SELECT * FROM reference_table t2 WHERE t2.b > t1.a +); +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 4 +(1 row) + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE t1.b IN ( + SELECT b+1 FROM reference_table t2 WHERE t2.b = t1.a +); +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 2 +(1 row) + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE t1.b NOT IN ( + SELECT a FROM reference_table t2 WHERE t2.b > t1.a +); +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 9 +(1 row) + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +JOIN LATERAL ( + SELECT * FROM reference_table t2 WHERE t2.b > t1.a +) q USING(a); +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 0 +(1 row) + +SELECT COUNT(*) FROM reference_table t1 +LEFT JOIN LATERAL ( + SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +) q USING(a); +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 6 +(1 row) + +SELECT COUNT(*) FROM reference_table t1 +WHERE EXISTS ( + SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +); +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 6 +(1 row) + +SELECT COUNT(*) FROM reference_table t1 +WHERE t1.b IN ( + SELECT b+1 FROM nullkey_c1_t1 t2 WHERE t2.b = t1.a +); +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 0 +(1 row) + +SELECT COUNT(*) FROM reference_table t1 +WHERE t1.b NOT IN ( + SELECT a FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +); +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 4 +(1 row) + +SELECT COUNT(*) FROM reference_table t1 +JOIN LATERAL ( + SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +) q USING(a); +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 2 +(1 row) + +-- with a distributed table +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +LEFT JOIN LATERAL ( + SELECT * FROM distributed_table t2 WHERE t2.b > t1.a +) q USING(a); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Router planner cannot handle multi-shard select queries +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE EXISTS ( + SELECT * FROM distributed_table t2 WHERE t2.b > t1.a +); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Router planner cannot handle multi-shard select queries +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE NOT EXISTS ( + SELECT * FROM distributed_table t2 WHERE t2.b > t1.a +); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Router planner cannot handle multi-shard select queries +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE t1.b IN ( + SELECT b+1 FROM distributed_table t2 WHERE t2.b = t1.a +); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Router planner cannot handle multi-shard select queries +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE t1.b NOT IN ( + SELECT a FROM distributed_table t2 WHERE t2.b > t1.a +); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Router planner cannot handle multi-shard select queries +SELECT COUNT(*) FROM distributed_table t1 +LEFT JOIN LATERAL ( + SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +) q USING(a); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Router planner cannot handle multi-shard select queries +SELECT COUNT(*) FROM distributed_table t1 +WHERE EXISTS ( + SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Router planner cannot handle multi-shard select queries +SELECT COUNT(*) FROM distributed_table t1 +WHERE t1.b IN ( + SELECT b+1 FROM nullkey_c1_t1 t2 WHERE t2.b = t1.a +); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Router planner cannot handle multi-shard select queries +SELECT COUNT(*) FROM distributed_table t1 +WHERE t1.b NOT IN ( + SELECT a FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Router planner cannot handle multi-shard select queries +-- with postgres / citus local tables +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +LEFT JOIN LATERAL ( + SELECT * FROM citus_local_table t2 WHERE t2.b > t1.a +) q USING(a); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE EXISTS ( + SELECT * FROM citus_local_table t2 WHERE t2.b > t1.a +); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE NOT EXISTS ( + SELECT * FROM citus_local_table t2 WHERE t2.b > t1.a +); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE t1.b IN ( + SELECT b+1 FROM citus_local_table t2 WHERE t2.b = t1.a +); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: found no worker with all shard placements +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE t1.b NOT IN ( + SELECT a FROM citus_local_table t2 WHERE t2.b > t1.a +); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: found no worker with all shard placements +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +JOIN LATERAL ( + SELECT * FROM citus_local_table t2 WHERE t2.b > t1.a +) q USING(a); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +SELECT COUNT(*) FROM citus_local_table t1 +LEFT JOIN LATERAL ( + SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +) q USING(a); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +SELECT COUNT(*) FROM postgres_local_table t1 +LEFT JOIN LATERAL ( + SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +) q USING(a); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +SELECT COUNT(*) FROM citus_local_table t1 +WHERE EXISTS ( + SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +SELECT COUNT(*) FROM citus_local_table t1 +WHERE t1.b IN ( + SELECT b+1 FROM nullkey_c1_t1 t2 WHERE t2.b = t1.a +); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: found no worker with all shard placements +SELECT COUNT(*) FROM citus_local_table t1 +WHERE t1.b NOT IN ( + SELECT a FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: found no worker with all shard placements +SELECT COUNT(*) FROM citus_local_table t1 +JOIN LATERAL ( + SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +) q USING(a); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +LEFT JOIN LATERAL ( + SELECT * FROM postgres_local_table t2 WHERE t2.b > t1.a +) q USING(a); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE EXISTS ( + SELECT * FROM postgres_local_table t2 WHERE t2.b > t1.a +); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE NOT EXISTS ( + SELECT * FROM postgres_local_table t2 WHERE t2.b > t1.a +); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE t1.b IN ( + SELECT b+1 FROM postgres_local_table t2 WHERE t2.b = t1.a +); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: found no worker with all shard placements +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE t1.b NOT IN ( + SELECT a FROM postgres_local_table t2 WHERE t2.b > t1.a +); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: found no worker with all shard placements +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +JOIN LATERAL ( + SELECT * FROM postgres_local_table t2 WHERE t2.b > t1.a +) q USING(a); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +SELECT COUNT(*) FROM postgres_local_table t1 +WHERE EXISTS ( + SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +SELECT COUNT(*) FROM postgres_local_table t1 +WHERE t1.b IN ( + SELECT b+1 FROM nullkey_c1_t1 t2 WHERE t2.b = t1.a +); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: found no worker with all shard placements +SELECT COUNT(*) FROM postgres_local_table t1 +WHERE t1.b NOT IN ( + SELECT a FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: found no worker with all shard placements +SELECT COUNT(*) FROM postgres_local_table t1 +JOIN LATERAL ( + SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +) q USING(a); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +-- insert .. select +-- between two colocated null dist key tables +-- The target list of "distributed statement"s that we send to workers +-- differ(*) in Postgres versions < 15. For this reason, we temporarily +-- disable debug messages here and run the EXPLAIN'ed version of the +-- command. +-- +-- (*): < SELECT a, b > vs < SELECT table_name.a, table_name.b > +SET client_min_messages TO WARNING; +EXPLAIN (ANALYZE TRUE, TIMING FALSE, COSTS FALSE, SUMMARY FALSE, VERBOSE FALSE) +INSERT INTO nullkey_c1_t1 SELECT * FROM nullkey_c1_t2; + QUERY PLAN +--------------------------------------------------------------------- + Custom Scan (Citus Adaptive) (actual rows=0 loops=1) + Task Count: 1 + Tasks Shown: All + -> Task + Node: host=localhost port=xxxxx dbname=regression + -> Insert on nullkey_c1_t1_1620000 citus_table_alias (actual rows=0 loops=1) + -> Seq Scan on nullkey_c1_t2_1620001 nullkey_c1_t2 (actual rows=10 loops=1) +(7 rows) + +SET client_min_messages TO DEBUG2; +-- between two non-colocated null dist key tables +INSERT INTO nullkey_c1_t1 SELECT * FROM nullkey_c2_t1; +ERROR: cannot select from a non-colocated distributed table when inserting into a distributed table that does not have a shard key +-- between a null dist key table and a table of different type +INSERT INTO nullkey_c1_t1 SELECT * FROM reference_table; +ERROR: cannot select from different table types when inserting into a distributed table that does not have a shard key +INSERT INTO nullkey_c1_t1 SELECT * FROM distributed_table; +ERROR: cannot select from different table types when inserting into a distributed table that does not have a shard key +INSERT INTO nullkey_c1_t1 SELECT * FROM citus_local_table; +ERROR: cannot select from different table types when inserting into a distributed table that does not have a shard key +INSERT INTO nullkey_c1_t1 SELECT * FROM postgres_local_table; +ERROR: cannot select from different table types when inserting into a distributed table that does not have a shard key +INSERT INTO reference_table SELECT * FROM nullkey_c1_t1; +ERROR: cannot select from a distributed table that does not have a shard key when inserting into a different table type +INSERT INTO distributed_table SELECT * FROM nullkey_c1_t1; +ERROR: cannot select from a distributed table that does not have a shard key when inserting into a different table type +INSERT INTO citus_local_table SELECT * FROM nullkey_c1_t1; +ERROR: cannot select from a distributed table that does not have a shard key when inserting into a different table type +INSERT INTO postgres_local_table SELECT * FROM nullkey_c1_t1; +ERROR: cannot select from a distributed table that does not have a shard key when inserting into a local table +-- test subquery +SELECT count(*) FROM +( + SELECT * FROM (SELECT * FROM nullkey_c1_t2) as subquery_inner +) AS subquery_top; +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 10 +(1 row) + +-- test cte inlining +WITH cte_nullkey_c1_t1 AS (SELECT * FROM nullkey_c1_t1), + cte_postgres_local_table AS (SELECT * FROM postgres_local_table), + cte_distributed_table AS (SELECT * FROM distributed_table) +SELECT COUNT(*) FROM cte_distributed_table, cte_nullkey_c1_t1, cte_postgres_local_table +WHERE cte_nullkey_c1_t1.a > 3 AND cte_distributed_table.a < 5; +DEBUG: CTE cte_nullkey_c1_t1 is going to be inlined via distributed planning +DEBUG: CTE cte_postgres_local_table is going to be inlined via distributed planning +DEBUG: CTE cte_distributed_table is going to be inlined via distributed planning +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +-- test recursive ctes +WITH level_0 AS ( + WITH level_1 AS ( + WITH RECURSIVE level_2_recursive(x) AS ( + VALUES (1) + UNION ALL + SELECT a + 1 FROM nullkey_c1_t1 JOIN level_2_recursive ON (a = x) WHERE a < 100 + ) + SELECT * FROM level_2_recursive RIGHT JOIN reference_table ON (level_2_recursive.x = reference_table.a) + ) + SELECT * FROM level_1 +) +SELECT COUNT(*) FROM level_0; +DEBUG: CTE level_0 is going to be inlined via distributed planning +DEBUG: CTE level_1 is going to be inlined via distributed planning +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 122 +(1 row) + +WITH level_0 AS ( + WITH level_1 AS ( + WITH RECURSIVE level_2_recursive(x) AS ( + VALUES (1) + UNION ALL + SELECT a + 1 FROM nullkey_c1_t1 JOIN level_2_recursive ON (a = x) WHERE a < 100 + ) + SELECT * FROM level_2_recursive JOIN distributed_table ON (level_2_recursive.x = distributed_table.a) + ) + SELECT * FROM level_1 +) +SELECT COUNT(*) FROM level_0; +DEBUG: CTE level_0 is going to be inlined via distributed planning +DEBUG: CTE level_1 is going to be inlined via distributed planning +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Router planner cannot handle multi-shard select queries +-- grouping set +SELECT + id, substring(title, 2, 1) AS subtitle, count(*) + FROM articles_hash + WHERE author_id = 1 or author_id = 2 + GROUP BY GROUPING SETS ((id),(subtitle)) + ORDER BY id, subtitle; +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan + id | subtitle | count +--------------------------------------------------------------------- + 42 | | 1 + | u | 1 +(2 rows) + +-- subquery in SELECT clause +SELECT a.title AS name, (SELECT a2.id FROM articles_hash a2 WHERE a.id = a2.id LIMIT 1) + AS special_price FROM articles_hash a +ORDER BY 1,2; +DEBUG: Creating router plan + name | special_price +--------------------------------------------------------------------- + adversa | 15 + aerophyte | 28 + affixal | 43 + altdorfer | 4 + amateur | 29 + anjanette | 50 + anyone | 49 + aruru | 5 + aseyev | 13 + assembly | 18 + aubergiste | 19 + ausable | 42 +(12 rows) + +-- test prepared statements +-- prepare queries can be router plannable +PREPARE author_1_articles as + SELECT * + FROM articles_hash + WHERE author_id = 1; +EXECUTE author_1_articles; +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan + id | author_id | title | word_count +--------------------------------------------------------------------- +(0 rows) + +EXECUTE author_1_articles; + id | author_id | title | word_count +--------------------------------------------------------------------- +(0 rows) + +EXECUTE author_1_articles; + id | author_id | title | word_count +--------------------------------------------------------------------- +(0 rows) + +EXECUTE author_1_articles; + id | author_id | title | word_count +--------------------------------------------------------------------- +(0 rows) + +EXECUTE author_1_articles; + id | author_id | title | word_count +--------------------------------------------------------------------- +(0 rows) + +EXECUTE author_1_articles; + id | author_id | title | word_count +--------------------------------------------------------------------- +(0 rows) + +-- parametric prepare queries can be router plannable +PREPARE author_articles(int) as + SELECT * + FROM articles_hash + WHERE author_id = $1; +EXECUTE author_articles(1); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan + id | author_id | title | word_count +--------------------------------------------------------------------- +(0 rows) + +EXECUTE author_articles(1); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan + id | author_id | title | word_count +--------------------------------------------------------------------- +(0 rows) + +EXECUTE author_articles(1); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan + id | author_id | title | word_count +--------------------------------------------------------------------- +(0 rows) + +EXECUTE author_articles(1); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan + id | author_id | title | word_count +--------------------------------------------------------------------- +(0 rows) + +EXECUTE author_articles(1); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan + id | author_id | title | word_count +--------------------------------------------------------------------- +(0 rows) + +EXECUTE author_articles(1); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan + id | author_id | title | word_count +--------------------------------------------------------------------- +(0 rows) + +EXECUTE author_articles(NULL); + id | author_id | title | word_count +--------------------------------------------------------------------- +(0 rows) + +EXECUTE author_articles(NULL); + id | author_id | title | word_count +--------------------------------------------------------------------- +(0 rows) + +EXECUTE author_articles(NULL); + id | author_id | title | word_count +--------------------------------------------------------------------- +(0 rows) + +EXECUTE author_articles(NULL); + id | author_id | title | word_count +--------------------------------------------------------------------- +(0 rows) + +EXECUTE author_articles(NULL); + id | author_id | title | word_count +--------------------------------------------------------------------- +(0 rows) + +EXECUTE author_articles(NULL); + id | author_id | title | word_count +--------------------------------------------------------------------- +(0 rows) + +EXECUTE author_articles(NULL); + id | author_id | title | word_count +--------------------------------------------------------------------- +(0 rows) + +PREPARE author_articles_update(int) AS + UPDATE articles_hash SET title = 'test' WHERE author_id = $1; +EXECUTE author_articles_update(NULL); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE author_articles_update(NULL); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE author_articles_update(NULL); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE author_articles_update(NULL); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE author_articles_update(NULL); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE author_articles_update(NULL); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE author_articles_update(NULL); +-- More tests with insert .. select. +-- +-- The target list of "distributed statement"s that we send to workers +-- might differ(*) in Postgres versions < 15 and they are reported when +-- "log level >= DEBUG2". For this reason, we set log level to DEBUG1 to +-- avoid reporting them. +-- +-- DEBUG1 still allows reporting the reason why given INSERT .. SELECT +-- query is not distributed / requires pull-to-coordinator. +SET client_min_messages TO DEBUG1; +INSERT INTO bigserial_test (x, y) SELECT x, y FROM bigserial_test; +DEBUG: volatile functions are not allowed in distributed INSERT ... SELECT queries +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Sequences cannot be used in router queries +INSERT INTO agg_events + (user_id) +SELECT f2.id FROM +(SELECT + id +FROM (SELECT users_ref_table.user_id AS id + FROM raw_events_first, + users_ref_table + WHERE raw_events_first.user_id = users_ref_table.user_id ) AS foo) as f +INNER JOIN +(SELECT v4, + v1, + id +FROM (SELECT SUM(raw_events_second.value_4) AS v4, + SUM(raw_events_first.value_1) AS v1, + raw_events_second.user_id AS id + FROM raw_events_first, + raw_events_second + WHERE raw_events_first.user_id = raw_events_second.user_id + GROUP BY raw_events_second.user_id + HAVING SUM(raw_events_second.value_4) > 1000) AS foo2 ) as f2 +ON (f.id = f2.id) +WHERE f.id IN (SELECT user_id + FROM raw_events_second); +ERROR: cannot select from different table types when inserting into a distributed table that does not have a shard key +-- upsert with returning +INSERT INTO agg_events AS ae + ( + user_id, + value_1_agg, + agg_time + ) +SELECT user_id, + value_1, + time +FROM raw_events_first +ON conflict (user_id, value_1_agg) +DO UPDATE + SET agg_time = EXCLUDED.agg_time + WHERE ae.agg_time < EXCLUDED.agg_time +RETURNING user_id, value_1_agg; + user_id | value_1_agg +--------------------------------------------------------------------- + 1 | 10 + 2 | 20 + 3 | 30 + 4 | 40 + 5 | 50 + 6 | 60 +(6 rows) + +-- using a left join +INSERT INTO agg_events (user_id) +SELECT + raw_events_first.user_id +FROM + raw_events_first LEFT JOIN raw_events_second ON raw_events_first.user_id = raw_events_second.user_id + WHERE raw_events_second.user_id = 10 OR raw_events_second.user_id = 11; +-- using a full join +INSERT INTO agg_events (user_id, value_1_agg) +SELECT t1.user_id AS col1, + t2.user_id AS col2 +FROM raw_events_first t1 + FULL JOIN raw_events_second t2 + ON t1.user_id = t2.user_id; +-- using semi join +INSERT INTO raw_events_second + (user_id) +SELECT user_id +FROM raw_events_first +WHERE user_id IN (SELECT raw_events_second.user_id + FROM raw_events_second, raw_events_first + WHERE raw_events_second.user_id = raw_events_first.user_id AND raw_events_first.user_id = 200); +-- using lateral join +INSERT INTO raw_events_second + (user_id) +SELECT user_id +FROM raw_events_first +WHERE NOT EXISTS (SELECT 1 + FROM raw_events_second + WHERE raw_events_second.user_id =raw_events_first.user_id); +-- using inner join +INSERT INTO agg_events (user_id) +SELECT raw_events_first.user_id +FROM raw_events_first INNER JOIN raw_events_second ON raw_events_first.user_id = raw_events_second.value_1 +WHERE raw_events_first.value_1 IN (10, 11,12) OR raw_events_second.user_id IN (1,2,3,4); +-- We could relax distributed insert .. select checks to allow pushing +-- down more clauses down to the worker nodes when inserting into a single +-- shard by selecting from a colocated one. We might want to do something +-- like https://github.com/citusdata/citus/pull/6772. +-- +-- e.g., insert into null_shard_key_1/citus_local/reference +-- select * from null_shard_key_1/citus_local/reference limit 1 +-- +-- Below "limit / offset clause" test and some others are examples of this. +-- limit / offset clause +INSERT INTO agg_events (user_id) SELECT raw_events_first.user_id FROM raw_events_first LIMIT 1; +DEBUG: LIMIT clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO agg_events (user_id) SELECT raw_events_first.user_id FROM raw_events_first OFFSET 1; +DEBUG: OFFSET clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: Collecting INSERT ... SELECT results on coordinator +-- using a materialized cte +WITH cte AS MATERIALIZED + (SELECT max(value_1)+1 as v1_agg, user_id FROM raw_events_first GROUP BY user_id) +INSERT INTO agg_events (value_1_agg, user_id) +SELECT v1_agg, user_id FROM cte; +DEBUG: distributed INSERT ... SELECT can only select from distributed tables +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO raw_events_second + WITH cte AS MATERIALIZED (SELECT * FROM raw_events_first) + SELECT user_id * 1000, time, value_1, value_2, value_3, value_4 FROM cte; +-- using a regular cte +WITH cte AS (SELECT * FROM raw_events_first) +INSERT INTO raw_events_second + SELECT user_id * 7000, time, value_1, value_2, value_3, value_4 FROM cte; +DEBUG: distributed INSERT ... SELECT can only select from distributed tables +DEBUG: CTE cte is going to be inlined via distributed planning +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO raw_events_second + WITH cte AS (SELECT * FROM raw_events_first) + SELECT * FROM cte; +DEBUG: CTE cte is going to be inlined via distributed planning +INSERT INTO agg_events + WITH sub_cte AS (SELECT 1) + SELECT + raw_events_first.user_id, (SELECT * FROM sub_cte) + FROM + raw_events_first; +DEBUG: CTE sub_cte is going to be inlined via distributed planning +DEBUG: Subqueries without relations are not allowed in distributed INSERT ... SELECT queries +DEBUG: Collecting INSERT ... SELECT results on coordinator +-- we still support complex joins via INSERT's cte list .. +WITH cte AS ( + SELECT reference_table.a AS a, 1 AS b + FROM distributed_table RIGHT JOIN reference_table USING (a) +) +INSERT INTO raw_events_second (user_id, value_1) + SELECT (a+5)*-1, b FROM cte; +DEBUG: distributed INSERT ... SELECT can only select from distributed tables +DEBUG: CTE cte is going to be inlined via distributed planning +DEBUG: recursively planning left side of the right join since the outer side is a recurring rel +DEBUG: recursively planning distributed relation "distributed_table" since it is part of a distributed join node that is outer joined with a recurring rel +DEBUG: Wrapping relation "distributed_table" to a subquery +DEBUG: generating subplan XXX_1 for subquery SELECT a FROM query_null_dist_key.distributed_table WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT "?column?" AS user_id, b AS value_1 FROM (SELECT ((cte.a OPERATOR(pg_catalog.+) 5) OPERATOR(pg_catalog.*) '-1'::integer), cte.b FROM (SELECT reference_table.a, 1 AS b FROM ((SELECT distributed_table_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) distributed_table_1) distributed_table RIGHT JOIN query_null_dist_key.reference_table USING (a))) cte) citus_insert_select_subquery("?column?", b) +DEBUG: Collecting INSERT ... SELECT results on coordinator +-- .. but can't do so via via SELECT's cte list +INSERT INTO raw_events_second (user_id, value_1) +WITH cte AS ( + SELECT reference_table.a AS a, 1 AS b + FROM distributed_table RIGHT JOIN reference_table USING (a) +) + SELECT (a+5)*-1, b FROM cte; +DEBUG: CTE cte is going to be inlined via distributed planning +ERROR: cannot select from different table types when inserting into a distributed table that does not have a shard key +-- using set operations +INSERT INTO + raw_events_first(user_id) + (SELECT user_id FROM raw_events_first) INTERSECT + (SELECT user_id FROM raw_events_first); +DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +DEBUG: Collecting INSERT ... SELECT results on coordinator +-- group by clause inside subquery +INSERT INTO agg_events + (user_id) +SELECT f2.id FROM +(SELECT + id +FROM (SELECT raw_events_second.user_id AS id + FROM raw_events_first, + raw_events_second + WHERE raw_events_first.user_id = raw_events_second.user_id ) AS foo) as f +INNER JOIN +(SELECT v4, + v1, + id +FROM (SELECT SUM(raw_events_second.value_4) AS v4, + SUM(raw_events_first.value_1) AS v1, + raw_events_second.user_id AS id + FROM raw_events_first, + raw_events_second + WHERE raw_events_first.user_id = raw_events_second.user_id + GROUP BY raw_events_second.user_id + HAVING SUM(raw_events_second.value_4) > 1000) AS foo2 ) as f2 +ON (f.id = f2.id) +WHERE f.id IN (SELECT user_id + FROM raw_events_second); +-- group by clause inside lateral subquery +INSERT INTO agg_events (user_id, value_4_agg) +SELECT + averages.user_id, avg(averages.value_4) +FROM + (SELECT + t1.user_id + FROM + raw_events_second t1 JOIN raw_events_second t2 on (t1.user_id = t2.user_id) + ) reference_ids + JOIN LATERAL + (SELECT + user_id, value_4 + FROM + raw_events_first) as averages ON averages.value_4 = reference_ids.user_id + GROUP BY averages.user_id; +-- using aggregates +INSERT INTO agg_events + (value_3_agg, + value_4_agg, + value_1_agg, + value_2_agg, + user_id) +SELECT SUM(value_3), + Count(value_4), + user_id, + SUM(value_1), + Avg(value_2) +FROM raw_events_first +GROUP BY user_id; +-- using generate_series +INSERT INTO raw_events_first (user_id, value_1, value_2) +SELECT s, s, s FROM generate_series(1, 5) s; +DEBUG: distributed INSERT ... SELECT can only select from distributed tables +DEBUG: Collecting INSERT ... SELECT results on coordinator +CREATE SEQUENCE insert_select_test_seq; +-- nextval() expression in select's targetlist +INSERT INTO raw_events_first (user_id, value_1, value_2) +SELECT s, nextval('insert_select_test_seq'), (random()*10)::int +FROM generate_series(100, 105) s; +DEBUG: distributed INSERT ... SELECT can only select from distributed tables +DEBUG: Collecting INSERT ... SELECT results on coordinator +-- non-immutable function +INSERT INTO modify_fast_path (key, value_1) VALUES (2,1) RETURNING value_1, random() * key; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: non-IMMUTABLE functions are not allowed in the RETURNING clause +SET client_min_messages TO DEBUG2; +-- update / delete +UPDATE nullkey_c1_t1 SET a = 1 WHERE b = 5; +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +UPDATE nullkey_c1_t1 SET a = 1 WHERE a = 5; +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +UPDATE nullkey_c1_t1 SET a = random(); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: functions used in UPDATE queries on distributed tables must not be VOLATILE +UPDATE nullkey_c1_t1 SET a = 1 WHERE a = random(); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: functions used in the WHERE/ON/WHEN clause of modification queries on distributed tables must not be VOLATILE +DELETE FROM nullkey_c1_t1 WHERE b = 5; +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DELETE FROM nullkey_c1_t1 WHERE a = random(); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: functions used in the WHERE/ON/WHEN clause of modification queries on distributed tables must not be VOLATILE +-- simple update queries between different table types / colocated tables +UPDATE nullkey_c1_t1 SET b = 5 FROM nullkey_c1_t2 WHERE nullkey_c1_t1.b = nullkey_c1_t2.b; +DEBUG: Creating router plan +UPDATE nullkey_c1_t1 SET b = 5 FROM nullkey_c2_t1 WHERE nullkey_c1_t1.b = nullkey_c2_t1.b; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: found no worker with all shard placements +UPDATE nullkey_c1_t1 SET b = 5 FROM reference_table WHERE nullkey_c1_t1.b = reference_table.b; +DEBUG: Creating router plan +UPDATE nullkey_c1_t1 SET b = 5 FROM distributed_table WHERE nullkey_c1_t1.b = distributed_table.b; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +UPDATE nullkey_c1_t1 SET b = 5 FROM distributed_table WHERE nullkey_c1_t1.b = distributed_table.a; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +UPDATE nullkey_c1_t1 SET b = 5 FROM citus_local_table WHERE nullkey_c1_t1.b = citus_local_table.b; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: local table citus_local_table cannot be joined with these distributed tables +UPDATE nullkey_c1_t1 SET b = 5 FROM postgres_local_table WHERE nullkey_c1_t1.b = postgres_local_table.b; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: relation postgres_local_table is not distributed +UPDATE reference_table SET b = 5 FROM nullkey_c1_t1 WHERE nullkey_c1_t1.b = reference_table.b; +ERROR: cannot perform select on a distributed table and modify a reference table +UPDATE distributed_table SET b = 5 FROM nullkey_c1_t1 WHERE nullkey_c1_t1.b = distributed_table.b; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +UPDATE distributed_table SET b = 5 FROM nullkey_c1_t1 WHERE nullkey_c1_t1.b = distributed_table.a; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +UPDATE citus_local_table SET b = 5 FROM nullkey_c1_t1 WHERE nullkey_c1_t1.b = citus_local_table.b; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: local table citus_local_table cannot be joined with these distributed tables +UPDATE postgres_local_table SET b = 5 FROM nullkey_c1_t1 WHERE nullkey_c1_t1.b = postgres_local_table.b; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: relation postgres_local_table is not distributed +-- simple delete queries between different table types / colocated tables +DELETE FROM nullkey_c1_t1 USING nullkey_c1_t2 WHERE nullkey_c1_t1.b = nullkey_c1_t2.b; +DEBUG: Creating router plan +DELETE FROM nullkey_c1_t1 USING nullkey_c2_t1 WHERE nullkey_c1_t1.b = nullkey_c2_t1.b; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: found no worker with all shard placements +DELETE FROM nullkey_c1_t1 USING reference_table WHERE nullkey_c1_t1.b = reference_table.b; +DEBUG: Creating router plan +DELETE FROM nullkey_c1_t1 USING distributed_table WHERE nullkey_c1_t1.b = distributed_table.b; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +DELETE FROM nullkey_c1_t1 USING distributed_table WHERE nullkey_c1_t1.b = distributed_table.a; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +DELETE FROM nullkey_c1_t1 USING citus_local_table WHERE nullkey_c1_t1.b = citus_local_table.b; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: local table citus_local_table cannot be joined with these distributed tables +DELETE FROM nullkey_c1_t1 USING postgres_local_table WHERE nullkey_c1_t1.b = postgres_local_table.b; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: relation postgres_local_table is not distributed +DELETE FROM reference_table USING nullkey_c1_t1 WHERE nullkey_c1_t1.b = reference_table.b; +ERROR: cannot perform select on a distributed table and modify a reference table +DELETE FROM distributed_table USING nullkey_c1_t1 WHERE nullkey_c1_t1.b = distributed_table.b; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +DELETE FROM distributed_table USING nullkey_c1_t1 WHERE nullkey_c1_t1.b = distributed_table.a; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +DELETE FROM citus_local_table USING nullkey_c1_t1 WHERE nullkey_c1_t1.b = citus_local_table.b; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: local table citus_local_table cannot be joined with these distributed tables +DELETE FROM postgres_local_table USING nullkey_c1_t1 WHERE nullkey_c1_t1.b = postgres_local_table.b; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: relation postgres_local_table is not distributed +-- slightly more complex update queries +UPDATE nullkey_c1_t1 SET b = 5 WHERE nullkey_c1_t1.b IN (SELECT b FROM distributed_table); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +WITH cte AS materialized( + SELECT * FROM distributed_table +) +UPDATE nullkey_c1_t1 SET b = 5 FROM cte WHERE nullkey_c1_t1.b = cte.a; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +WITH cte AS ( + SELECT reference_table.a AS a, 1 AS b + FROM distributed_table RIGHT JOIN reference_table USING (a) +) +UPDATE nullkey_c1_t1 SET b = 5 WHERE nullkey_c1_t1.b IN (SELECT b FROM cte); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +UPDATE nullkey_c1_t1 SET b = 5 FROM reference_table WHERE EXISTS ( + SELECT 1 FROM reference_table LEFT JOIN nullkey_c1_t1 USING (a) WHERE nullkey_c1_t1.b IS NULL +); +DEBUG: Creating router plan +UPDATE nullkey_c1_t1 tx SET b = ( + SELECT nullkey_c1_t2.b FROM nullkey_c1_t2 JOIN nullkey_c1_t1 ON (nullkey_c1_t1.a != nullkey_c1_t2.a) WHERE nullkey_c1_t1.a = tx.a ORDER BY 1 LIMIT 1 +); +DEBUG: Creating router plan +UPDATE nullkey_c1_t1 tx SET b = t2.b FROM nullkey_c1_t1 t1 JOIN nullkey_c1_t2 t2 ON (t1.a = t2.a); +DEBUG: Creating router plan +WITH cte AS ( + SELECT * FROM nullkey_c1_t2 ORDER BY 1,2 LIMIT 10 +) +UPDATE nullkey_c1_t1 SET b = 5 WHERE nullkey_c1_t1.a IN (SELECT b FROM cte); +DEBUG: Creating router plan +UPDATE modify_fast_path SET value_1 = value_1 + 12 * value_1 WHERE key = 1; +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +UPDATE modify_fast_path SET value_1 = NULL WHERE value_1 = 15 AND (key = 1 OR value_2 = 'citus'); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +UPDATE modify_fast_path SET value_1 = 5 WHERE key = 2 RETURNING value_1 * 15, value_1::numeric * 16; +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan + ?column? | ?column? +--------------------------------------------------------------------- +(0 rows) + +UPDATE modify_fast_path + SET value_1 = 1 + FROM modify_fast_path_reference + WHERE + modify_fast_path.key = modify_fast_path_reference.key AND + modify_fast_path.key = 1 AND + modify_fast_path_reference.key = 1; +DEBUG: Creating router plan +PREPARE p1 (int, int, int) AS + UPDATE modify_fast_path SET value_1 = value_1 + $1 WHERE key = $2 AND value_1 = $3; +EXECUTE p1(1,1,1); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE p1(2,2,2); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE p1(3,3,3); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE p1(4,4,4); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE p1(5,5,5); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE p1(6,6,6); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE p1(7,7,7); +PREPARE prepared_zero_shard_update(int) AS UPDATE modify_fast_path SET value_1 = 1 WHERE key = $1 AND false; +EXECUTE prepared_zero_shard_update(1); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE prepared_zero_shard_update(2); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE prepared_zero_shard_update(3); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE prepared_zero_shard_update(4); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE prepared_zero_shard_update(5); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE prepared_zero_shard_update(6); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE prepared_zero_shard_update(7); +-- slightly more complex delete queries +DELETE FROM nullkey_c1_t1 WHERE nullkey_c1_t1.b IN (SELECT b FROM distributed_table); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +WITH cte AS materialized( + SELECT * FROM distributed_table +) +DELETE FROM nullkey_c1_t1 USING cte WHERE nullkey_c1_t1.b = cte.a; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +WITH cte AS ( + SELECT reference_table.a AS a, 1 AS b + FROM distributed_table RIGHT JOIN reference_table USING (a) +) +DELETE FROM nullkey_c1_t1 WHERE nullkey_c1_t1.b IN (SELECT b FROM cte); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +DELETE FROM nullkey_c1_t1 USING reference_table WHERE EXISTS ( + SELECT 1 FROM reference_table LEFT JOIN nullkey_c1_t1 USING (a) WHERE nullkey_c1_t1.b IS NULL +); +DEBUG: Creating router plan +DELETE FROM nullkey_c1_t1 tx USING nullkey_c1_t1 t1 JOIN nullkey_c1_t2 t2 ON (t1.a = t2.a); +DEBUG: Creating router plan +WITH cte AS ( + SELECT * FROM nullkey_c1_t2 ORDER BY 1,2 LIMIT 10 +) +DELETE FROM nullkey_c1_t1 WHERE nullkey_c1_t1.a IN (SELECT b FROM cte); +DEBUG: Creating router plan +DELETE FROM modify_fast_path WHERE value_1 = 15 AND (key = 1 OR value_2 = 'citus'); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DELETE FROM modify_fast_path WHERE key = 2 RETURNING value_1 * 15, value_1::numeric * 16; +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan + ?column? | ?column? +--------------------------------------------------------------------- +(0 rows) + +DELETE FROM modify_fast_path + USING modify_fast_path_reference + WHERE + modify_fast_path.key = modify_fast_path_reference.key AND + modify_fast_path.key = 1 AND + modify_fast_path_reference.key = 1; +DEBUG: Creating router plan +PREPARE p2 (int, int, int) AS + DELETE FROM modify_fast_path WHERE key = ($2)*$1 AND value_1 = $3; +EXECUTE p2(1,1,1); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE p2(2,2,2); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE p2(3,3,3); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE p2(4,4,4); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE p2(5,5,5); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE p2(6,6,6); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE p2(7,7,7); +PREPARE prepared_zero_shard_delete(int) AS DELETE FROM modify_fast_path WHERE key = $1 AND false; +EXECUTE prepared_zero_shard_delete(1); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE prepared_zero_shard_delete(2); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE prepared_zero_shard_delete(3); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE prepared_zero_shard_delete(4); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE prepared_zero_shard_delete(5); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE prepared_zero_shard_delete(6); +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +EXECUTE prepared_zero_shard_delete(7); +-- test modifying ctes +WITH cte AS ( + UPDATE modify_fast_path SET value_1 = value_1 + 1 WHERE key = 1 RETURNING * +) +SELECT * FROM cte; +DEBUG: Creating router plan + key | value_1 | value_2 +--------------------------------------------------------------------- +(0 rows) + +WITH cte AS ( + DELETE FROM modify_fast_path WHERE key = 1 RETURNING * +) +SELECT * FROM modify_fast_path; +DEBUG: Creating router plan + key | value_1 | value_2 +--------------------------------------------------------------------- +(0 rows) + +WITH cte AS ( + DELETE FROM modify_fast_path WHERE key = 1 RETURNING * +) +SELECT * FROM modify_fast_path_reference WHERE key IN (SELECT key FROM cte); +DEBUG: Creating router plan + key | value_1 | value_2 +--------------------------------------------------------------------- +(0 rows) + +WITH cte AS ( + DELETE FROM reference_table WHERE a = 1 RETURNING * +) +SELECT * FROM nullkey_c1_t1 WHERE a IN (SELECT a FROM cte); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: cannot router plan modification of a non-distributed table +WITH cte AS ( + DELETE FROM nullkey_c1_t1 WHERE a = 1 RETURNING * +) +SELECT * FROM nullkey_c1_t2 WHERE a IN (SELECT a FROM cte); +DEBUG: Creating router plan + a | b +--------------------------------------------------------------------- +(0 rows) + +WITH cte AS ( + DELETE FROM nullkey_c1_t1 WHERE a = 1 RETURNING * +) +SELECT * FROM nullkey_c2_t1 WHERE a IN (SELECT a FROM cte); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: found no worker with all shard placements +WITH cte AS ( + DELETE FROM nullkey_c1_t1 WHERE a = 1 RETURNING * +) +SELECT * FROM distributed_table WHERE a IN (SELECT a FROM cte); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Router planner cannot handle multi-shard select queries +-- Below two queries fail very late when +-- citus.enable_non_colocated_router_query_pushdown is set to on. +SET citus.enable_non_colocated_router_query_pushdown TO ON; +WITH cte AS ( + DELETE FROM distributed_table WHERE a = 1 RETURNING * +) +SELECT * FROM nullkey_c1_t1 WHERE a IN (SELECT a FROM cte); +DEBUG: Creating router plan +DEBUG: query has a single distribution column value: 1 +ERROR: relation "query_null_dist_key.nullkey_c1_t1_1620000" does not exist +CONTEXT: while executing command on localhost:xxxxx +WITH cte AS ( + DELETE FROM distributed_table WHERE a = 1 RETURNING * +) +SELECT * FROM nullkey_c1_t1 WHERE b IN (SELECT b FROM cte); +DEBUG: Creating router plan +DEBUG: query has a single distribution column value: 1 +ERROR: relation "query_null_dist_key.nullkey_c1_t1_1620000" does not exist +CONTEXT: while executing command on localhost:xxxxx +SET citus.enable_non_colocated_router_query_pushdown TO OFF; +WITH cte AS ( + DELETE FROM distributed_table WHERE a = 1 RETURNING * +) +SELECT * FROM nullkey_c1_t1 WHERE a IN (SELECT a FROM cte); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: router planner does not support queries that reference non-colocated distributed tables +WITH cte AS ( + DELETE FROM distributed_table WHERE a = 1 RETURNING * +) +SELECT * FROM nullkey_c1_t1 WHERE b IN (SELECT b FROM cte); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: router planner does not support queries that reference non-colocated distributed tables +RESET citus.enable_non_colocated_router_query_pushdown; +WITH cte AS ( + UPDATE modify_fast_path SET value_1 = value_1 + 1 WHERE key = 1 RETURNING * +) +UPDATE modify_fast_path SET value_1 = value_1 + 1 WHERE key = 1; +DEBUG: Creating router plan +WITH cte AS ( + DELETE FROM modify_fast_path WHERE key = 1 RETURNING * +) +DELETE FROM modify_fast_path WHERE key = 1; +DEBUG: Creating router plan +-- test window functions +SELECT + user_id, avg(avg(value_3)) OVER (PARTITION BY user_id, MIN(value_2)) +FROM + raw_events_first +GROUP BY + 1 +ORDER BY + 2 DESC NULLS LAST, 1 DESC; +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan + user_id | avg +--------------------------------------------------------------------- + 6 | 6000.1 + 5 | 5000.1 + 4 | 4000.1 + 3 | 3000.1 + 2 | 2000.1 + 1 | 1000.1 + 105 | + 104 | + 103 | + 102 | + 101 | + 100 | +(12 rows) + +SELECT + user_id, max(value_1) OVER (PARTITION BY user_id, MIN(value_2)) +FROM ( + SELECT + DISTINCT us.user_id, us.value_2, us.value_1, random() as r1 + FROM + raw_events_first as us, raw_events_second + WHERE + us.user_id = raw_events_second.user_id + ORDER BY + user_id, value_2 + ) s +GROUP BY + 1, value_1 +ORDER BY + 2 DESC, 1; +DEBUG: Creating router plan + user_id | max +--------------------------------------------------------------------- + 1 | + 2 | + 3 | + 4 | + 5 | + 6 | + 6 | 60 + 5 | 50 + 4 | 40 + 3 | 30 + 2 | 20 + 1 | 10 + 5 | 5 + 4 | 4 + 3 | 3 + 2 | 2 + 1 | 1 +(17 rows) + +SELECT + DISTINCT ON (raw_events_second.user_id, rnk) raw_events_second.user_id, rank() OVER my_win AS rnk +FROM + raw_events_second, raw_events_first +WHERE + raw_events_first.user_id = raw_events_second.user_id +WINDOW + my_win AS (PARTITION BY raw_events_second.user_id, raw_events_first.value_1 ORDER BY raw_events_second.time DESC) +ORDER BY + rnk DESC, 1 DESC +LIMIT 10; +DEBUG: Creating router plan + user_id | rnk +--------------------------------------------------------------------- + 6 | 2 + 5 | 2 + 4 | 2 + 3 | 2 + 2 | 2 + 1 | 2 + 6 | 1 + 5 | 1 + 4 | 1 + 3 | 1 +(10 rows) + +SET client_min_messages TO ERROR; +DROP SCHEMA query_null_dist_key CASCADE; +SELECT citus_remove_node('localhost', :master_port); + citus_remove_node +--------------------------------------------------------------------- + +(1 row) + diff --git a/src/test/regress/multi_1_schedule b/src/test/regress/multi_1_schedule index 5d6fbb068..45adf469e 100644 --- a/src/test/regress/multi_1_schedule +++ b/src/test/regress/multi_1_schedule @@ -200,6 +200,7 @@ test: local_table_join test: local_dist_join_mixed test: citus_local_dist_joins test: recurring_outer_join +test: query_null_dist_key test: pg_dump # --------- diff --git a/src/test/regress/sql/merge.sql b/src/test/regress/sql/merge.sql index 5b9190516..1fdc3a514 100644 --- a/src/test/regress/sql/merge.sql +++ b/src/test/regress/sql/merge.sql @@ -2051,6 +2051,118 @@ UPDATE SET val = dist_source.val WHEN NOT MATCHED THEN INSERT VALUES(dist_source.id, dist_source.val); +-- test merge with null shard key tables + +CREATE SCHEMA query_null_dist_key; + +SET search_path TO query_null_dist_key; +SET client_min_messages TO DEBUG2; + +CREATE TABLE nullkey_c1_t1(a int, b int); +CREATE TABLE nullkey_c1_t2(a int, b int); +SELECT create_distributed_table('nullkey_c1_t1', null, colocate_with=>'none'); +SELECT create_distributed_table('nullkey_c1_t2', null, colocate_with=>'nullkey_c1_t1'); + +CREATE TABLE nullkey_c2_t1(a int, b int); +CREATE TABLE nullkey_c2_t2(a int, b int); +SELECT create_distributed_table('nullkey_c2_t1', null, colocate_with=>'none'); +SELECT create_distributed_table('nullkey_c2_t2', null, colocate_with=>'nullkey_c2_t1', distribution_type=>null); + +CREATE TABLE reference_table(a int, b int); +SELECT create_reference_table('reference_table'); +INSERT INTO reference_table SELECT i, i FROM generate_series(0, 5) i; + +CREATE TABLE distributed_table(a int, b int); +SELECT create_distributed_table('distributed_table', 'a'); +INSERT INTO distributed_table SELECT i, i FROM generate_series(3, 8) i; + +CREATE TABLE citus_local_table(a int, b int); +SELECT citus_add_local_table_to_metadata('citus_local_table'); +INSERT INTO citus_local_table SELECT i, i FROM generate_series(0, 10) i; + +CREATE TABLE postgres_local_table(a int, b int); +INSERT INTO postgres_local_table SELECT i, i FROM generate_series(5, 10) i; + +-- with a colocated table +MERGE INTO nullkey_c1_t1 USING nullkey_c1_t2 ON (nullkey_c1_t1.a = nullkey_c1_t2.a) +WHEN MATCHED THEN UPDATE SET b = nullkey_c1_t2.b; + +MERGE INTO nullkey_c1_t1 USING nullkey_c1_t2 ON (nullkey_c1_t1.a = nullkey_c1_t2.a) +WHEN MATCHED THEN DELETE; + +MERGE INTO nullkey_c1_t1 USING nullkey_c1_t2 ON (nullkey_c1_t1.a = nullkey_c1_t2.a) +WHEN MATCHED THEN UPDATE SET b = nullkey_c1_t2.b +WHEN NOT MATCHED THEN INSERT VALUES (nullkey_c1_t2.a, nullkey_c1_t2.b); + +MERGE INTO nullkey_c1_t1 USING nullkey_c1_t2 ON (nullkey_c1_t1.a = nullkey_c1_t2.a) +WHEN MATCHED THEN DELETE +WHEN NOT MATCHED THEN INSERT VALUES (nullkey_c1_t2.a, nullkey_c1_t2.b); + +-- with non-colocated null-dist-key table +MERGE INTO nullkey_c1_t1 USING nullkey_c2_t1 ON (nullkey_c1_t1.a = nullkey_c2_t1.a) +WHEN MATCHED THEN UPDATE SET b = nullkey_c2_t1.b; + +MERGE INTO nullkey_c1_t1 USING nullkey_c2_t1 ON (nullkey_c1_t1.a = nullkey_c2_t1.a) +WHEN MATCHED THEN UPDATE SET b = nullkey_c2_t1.b +WHEN NOT MATCHED THEN INSERT VALUES (nullkey_c2_t1.a, nullkey_c2_t1.b); + +-- with a distributed table +MERGE INTO nullkey_c1_t1 USING distributed_table ON (nullkey_c1_t1.a = distributed_table.a) +WHEN MATCHED THEN UPDATE SET b = distributed_table.b +WHEN NOT MATCHED THEN INSERT VALUES (distributed_table.a, distributed_table.b); + +MERGE INTO distributed_table USING nullkey_c1_t1 ON (nullkey_c1_t1.a = distributed_table.a) +WHEN MATCHED THEN DELETE +WHEN NOT MATCHED THEN INSERT VALUES (nullkey_c1_t1.a, nullkey_c1_t1.b); + +-- with a reference table +MERGE INTO nullkey_c1_t1 USING reference_table ON (nullkey_c1_t1.a = reference_table.a) +WHEN MATCHED THEN UPDATE SET b = reference_table.b; + +MERGE INTO reference_table USING nullkey_c1_t1 ON (nullkey_c1_t1.a = reference_table.a) +WHEN MATCHED THEN UPDATE SET b = nullkey_c1_t1.b +WHEN NOT MATCHED THEN INSERT VALUES (nullkey_c1_t1.a, nullkey_c1_t1.b); + +-- with a citus local table +MERGE INTO nullkey_c1_t1 USING citus_local_table ON (nullkey_c1_t1.a = citus_local_table.a) +WHEN MATCHED THEN UPDATE SET b = citus_local_table.b; + +MERGE INTO citus_local_table USING nullkey_c1_t1 ON (nullkey_c1_t1.a = citus_local_table.a) +WHEN MATCHED THEN DELETE; + +-- with a postgres table +MERGE INTO nullkey_c1_t1 USING postgres_local_table ON (nullkey_c1_t1.a = postgres_local_table.a) +WHEN MATCHED THEN UPDATE SET b = postgres_local_table.b; + +MERGE INTO postgres_local_table USING nullkey_c1_t1 ON (nullkey_c1_t1.a = postgres_local_table.a) +WHEN MATCHED THEN UPDATE SET b = nullkey_c1_t1.b +WHEN NOT MATCHED THEN INSERT VALUES (nullkey_c1_t1.a, nullkey_c1_t1.b); + +-- using ctes +WITH cte AS ( + SELECT * FROM nullkey_c1_t1 +) +MERGE INTO nullkey_c1_t1 USING cte ON (nullkey_c1_t1.a = cte.a) +WHEN MATCHED THEN UPDATE SET b = cte.b; + +WITH cte AS ( + SELECT * FROM distributed_table +) +MERGE INTO nullkey_c1_t1 USING cte ON (nullkey_c1_t1.a = cte.a) +WHEN MATCHED THEN UPDATE SET b = cte.b; + +WITH cte AS materialized ( + SELECT * FROM distributed_table +) +MERGE INTO nullkey_c1_t1 USING cte ON (nullkey_c1_t1.a = cte.a) +WHEN MATCHED THEN UPDATE SET b = cte.b; + +SET client_min_messages TO WARNING; +DROP SCHEMA query_null_dist_key CASCADE; + +RESET client_min_messages; +SET search_path TO merge_schema; + DROP SERVER foreign_server CASCADE; DROP FUNCTION merge_when_and_write(); DROP SCHEMA merge_schema CASCADE; diff --git a/src/test/regress/sql/query_null_dist_key.sql b/src/test/regress/sql/query_null_dist_key.sql new file mode 100644 index 000000000..f5d1fe3fc --- /dev/null +++ b/src/test/regress/sql/query_null_dist_key.sql @@ -0,0 +1,1132 @@ +CREATE SCHEMA query_null_dist_key; +SET search_path TO query_null_dist_key; + +SET citus.next_shard_id TO 1620000; +SET citus.shard_count TO 32; + +SET client_min_messages TO WARNING; +SELECT 1 FROM citus_add_node('localhost', :master_port, groupid => 0); + +SET client_min_messages TO NOTICE; + +CREATE TABLE nullkey_c1_t1(a int, b int); +CREATE TABLE nullkey_c1_t2(a int, b int); +SELECT create_distributed_table('nullkey_c1_t1', null, colocate_with=>'none'); +SELECT create_distributed_table('nullkey_c1_t2', null, colocate_with=>'nullkey_c1_t1'); +INSERT INTO nullkey_c1_t1 SELECT i, i FROM generate_series(1, 8) i; +INSERT INTO nullkey_c1_t2 SELECT i, i FROM generate_series(2, 7) i; + +CREATE TABLE nullkey_c2_t1(a int, b int); +CREATE TABLE nullkey_c2_t2(a int, b int); +SELECT create_distributed_table('nullkey_c2_t1', null, colocate_with=>'none'); +SELECT create_distributed_table('nullkey_c2_t2', null, colocate_with=>'nullkey_c2_t1', distribution_type=>null); +INSERT INTO nullkey_c2_t1 SELECT i, i FROM generate_series(2, 7) i; +INSERT INTO nullkey_c2_t2 SELECT i, i FROM generate_series(1, 8) i; + +CREATE TABLE nullkey_c3_t1(a int, b int); +SELECT create_distributed_table('nullkey_c3_t1', null, colocate_with=>'none'); +INSERT INTO nullkey_c3_t1 SELECT i, i FROM generate_series(1, 8) i; + +CREATE TABLE reference_table(a int, b int); +SELECT create_reference_table('reference_table'); +INSERT INTO reference_table SELECT i, i FROM generate_series(0, 5) i; + +CREATE TABLE distributed_table(a int, b int); +SELECT create_distributed_table('distributed_table', 'a'); +INSERT INTO distributed_table SELECT i, i FROM generate_series(3, 8) i; + +CREATE TABLE citus_local_table(a int, b int); +SELECT citus_add_local_table_to_metadata('citus_local_table'); +INSERT INTO citus_local_table SELECT i, i FROM generate_series(0, 10) i; + +CREATE TABLE postgres_local_table(a int, b int); +INSERT INTO postgres_local_table SELECT i, i FROM generate_series(5, 10) i; + +CREATE TABLE articles_hash ( + id bigint NOT NULL, + author_id bigint NOT NULL, + title varchar(20) NOT NULL, + word_count integer +); + +INSERT INTO articles_hash VALUES ( 4, 4, 'altdorfer', 14551),( 5, 5, 'aruru', 11389), + (13, 3, 'aseyev', 2255),(15, 5, 'adversa', 3164), + (18, 8, 'assembly', 911),(19, 9, 'aubergiste', 4981), + (28, 8, 'aerophyte', 5454),(29, 9, 'amateur', 9524), + (42, 2, 'ausable', 15885),(43, 3, 'affixal', 12723), + (49, 9, 'anyone', 2681),(50, 10, 'anjanette', 19519); + +SELECT create_distributed_table('articles_hash', null, colocate_with=>'none'); + +CREATE TABLE raw_events_first (user_id int, time timestamp, value_1 int, value_2 int, value_3 float, value_4 bigint, UNIQUE(user_id, value_1)); +SELECT create_distributed_table('raw_events_first', null, colocate_with=>'none', distribution_type=>null); + +CREATE TABLE raw_events_second (user_id int, time timestamp, value_1 int, value_2 int, value_3 float, value_4 bigint, UNIQUE(user_id, value_1)); +SELECT create_distributed_table('raw_events_second', null, colocate_with=>'raw_events_first', distribution_type=>null); + +CREATE TABLE agg_events (user_id int, value_1_agg int, value_2_agg int, value_3_agg float, value_4_agg bigint, agg_time timestamp, UNIQUE(user_id, value_1_agg)); +SELECT create_distributed_table('agg_events', null, colocate_with=>'raw_events_first', distribution_type=>null); + +CREATE TABLE users_ref_table (user_id int); +SELECT create_reference_table('users_ref_table'); + +INSERT INTO raw_events_first VALUES (1, '1970-01-01', 10, 100, 1000.1, 10000), (3, '1971-01-01', 30, 300, 3000.1, 30000), + (5, '1972-01-01', 50, 500, 5000.1, 50000), (2, '1973-01-01', 20, 200, 2000.1, 20000), + (4, '1974-01-01', 40, 400, 4000.1, 40000), (6, '1975-01-01', 60, 600, 6000.1, 60000); + +CREATE TABLE modify_fast_path(key int, value_1 int, value_2 text); +SELECT create_distributed_table('modify_fast_path', null); + +CREATE TABLE modify_fast_path_reference(key int, value_1 int, value_2 text); +SELECT create_reference_table('modify_fast_path_reference'); + +CREATE TABLE bigserial_test (x int, y int, z bigserial); +SELECT create_distributed_table('bigserial_test', null); + +CREATE TABLE append_table (text_col text, a int); +SELECT create_distributed_table('append_table', 'a', 'append'); +SELECT master_create_empty_shard('append_table') AS shardid1 \gset +SELECT master_create_empty_shard('append_table') AS shardid2 \gset +SELECT master_create_empty_shard('append_table') AS shardid3 \gset + +COPY append_table (text_col, a) FROM STDIN WITH (format 'csv', append_to_shard :shardid1); +abc,234 +bcd,123 +bcd,234 +cde,345 +def,456 +efg,234 +\. + +COPY append_table (text_col, a) FROM STDIN WITH (format 'csv', append_to_shard :shardid2); +abc,123 +efg,123 +hij,123 +hij,234 +ijk,1 +jkl,0 +\. + +CREATE TABLE range_table(a int, b int); +SELECT create_distributed_table('range_table', 'a', 'range'); +CALL public.create_range_partitioned_shards('range_table', '{"0","25"}','{"24","49"}'); +INSERT INTO range_table VALUES (0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 50); + +SET client_min_messages to DEBUG2; + +-- simple insert +INSERT INTO nullkey_c1_t1 VALUES (1,2), (2,2), (3,4); +INSERT INTO nullkey_c1_t2 VALUES (1,3), (3,4), (5,1), (6,2); + +INSERT INTO nullkey_c2_t1 VALUES (1,0), (2,5), (4,3), (5,2); +INSERT INTO nullkey_c2_t2 VALUES (2,4), (3,2), (5,2), (7,4); + +-- simple select +SELECT * FROM nullkey_c1_t1 ORDER BY 1,2; + +-- for update / share +SELECT * FROM modify_fast_path WHERE key = 1 FOR UPDATE; +SELECT * FROM modify_fast_path WHERE key = 1 FOR SHARE; +SELECT * FROM modify_fast_path FOR UPDATE; +SELECT * FROM modify_fast_path FOR SHARE; + +-- cartesian product with different table types + +-- with other table types +SELECT COUNT(*) FROM distributed_table d1, nullkey_c1_t1; +SELECT COUNT(*) FROM reference_table d1, nullkey_c1_t1; +SELECT COUNT(*) FROM citus_local_table d1, nullkey_c1_t1; +SELECT COUNT(*) FROM postgres_local_table d1, nullkey_c1_t1; + +-- with a colocated null dist key table +SELECT COUNT(*) FROM nullkey_c1_t1 d1, nullkey_c1_t2; + +-- with a non-colocated null dist key table +SELECT COUNT(*) FROM nullkey_c1_t1 d1, nullkey_c2_t1; + +-- First, show that nullkey_c1_t1 and nullkey_c3_t1 are not colocated. +SELECT + (SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'query_null_dist_key.nullkey_c1_t1'::regclass) != + (SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'query_null_dist_key.nullkey_c3_t1'::regclass); + +-- Now verify that we can join them via router planner because it doesn't care +-- about whether two tables are colocated or not but physical location of shards +-- when citus.enable_non_colocated_router_query_pushdown is set to on. + +SET citus.enable_non_colocated_router_query_pushdown TO ON; + +SELECT COUNT(*) FROM nullkey_c1_t1 JOIN nullkey_c3_t1 USING(a); + +SET citus.enable_non_colocated_router_query_pushdown TO OFF; + +SELECT COUNT(*) FROM nullkey_c1_t1 JOIN nullkey_c3_t1 USING(a); + +RESET citus.enable_non_colocated_router_query_pushdown; + +-- colocated join between null dist key tables +SELECT COUNT(*) FROM nullkey_c1_t1 JOIN nullkey_c1_t2 USING(a); +SELECT COUNT(*) FROM nullkey_c1_t1 LEFT JOIN nullkey_c1_t2 USING(a); +SELECT COUNT(*) FROM nullkey_c1_t1 FULL JOIN nullkey_c1_t2 USING(a); + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +LEFT JOIN LATERAL ( + SELECT * FROM nullkey_c1_t2 t2 WHERE t2.b > t1.a +) q USING(a); + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +JOIN LATERAL ( + SELECT * FROM nullkey_c1_t2 t2 WHERE t2.b > t1.a +) q USING(a); + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE EXISTS ( + SELECT * FROM nullkey_c1_t2 t2 WHERE t2.b > t1.a +); + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE t1.b IN ( + SELECT b+1 FROM nullkey_c1_t2 t2 WHERE t2.b = t1.a +); + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE t1.b NOT IN ( + SELECT a FROM nullkey_c1_t2 t2 WHERE t2.b > t1.a +); + +-- non-colocated inner joins between null dist key tables +SELECT * FROM nullkey_c1_t1 JOIN nullkey_c2_t1 USING(a) ORDER BY 1,2,3; + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +JOIN LATERAL ( + SELECT * FROM nullkey_c2_t2 t2 WHERE t2.b > t1.a +) q USING(a); + +-- non-colocated outer joins between null dist key tables +SELECT * FROM nullkey_c1_t1 LEFT JOIN nullkey_c2_t2 USING(a) ORDER BY 1,2,3 LIMIT 4; +SELECT * FROM nullkey_c1_t1 FULL JOIN nullkey_c2_t2 USING(a) ORDER BY 1,2,3 LIMIT 4; +SELECT * FROM nullkey_c1_t1 t1 +LEFT JOIN LATERAL ( + SELECT * FROM nullkey_c2_t2 t2 WHERE t2.b > t1.a +) q USING(a) ORDER BY 1,2,3 OFFSET 3 LIMIT 4; + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +LEFT JOIN LATERAL ( + SELECT * FROM nullkey_c2_t2 t2 WHERE t2.b > t1.a +) q USING(a); + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE EXISTS ( + SELECT * FROM nullkey_c2_t2 t2 WHERE t2.b > t1.a +); + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE t1.b IN ( + SELECT b+1 FROM nullkey_c2_t2 t2 WHERE t2.b = t1.a +); + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE t1.b NOT IN ( + SELECT a FROM nullkey_c2_t2 t2 WHERE t2.b > t1.a +); + +-- join with a reference table +SELECT COUNT(*) FROM nullkey_c1_t1, reference_table WHERE nullkey_c1_t1.a = reference_table.a; + +WITH cte_1 AS + (SELECT * FROM nullkey_c1_t1, reference_table WHERE nullkey_c1_t1.a = reference_table.a ORDER BY 1,2,3,4 FOR UPDATE) +SELECT COUNT(*) FROM cte_1; + +-- join with postgres / citus local tables +SELECT * FROM nullkey_c1_t1 JOIN postgres_local_table USING(a); +SELECT * FROM nullkey_c1_t1 JOIN citus_local_table USING(a); + +-- join with a distributed table +SELECT * FROM distributed_table d1 JOIN nullkey_c1_t1 USING(a); + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +JOIN LATERAL ( + SELECT * FROM distributed_table t2 WHERE t2.b > t1.a +) q USING(a); + +SELECT COUNT(*) FROM distributed_table t1 +JOIN LATERAL ( + SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +) q USING(a); + +-- outer joins with different table types +SELECT COUNT(*) FROM nullkey_c1_t1 LEFT JOIN reference_table USING(a); +SELECT COUNT(*) FROM reference_table LEFT JOIN nullkey_c1_t1 USING(a); + +SELECT COUNT(*) FROM nullkey_c1_t1 LEFT JOIN citus_local_table USING(a); +SELECT COUNT(*) FROM citus_local_table LEFT JOIN nullkey_c1_t1 USING(a); + +SELECT COUNT(*) FROM nullkey_c1_t1 LEFT JOIN postgres_local_table USING(a); +SELECT COUNT(*) FROM postgres_local_table LEFT JOIN nullkey_c1_t1 USING(a); + +SELECT COUNT(*) FROM nullkey_c1_t1 FULL JOIN citus_local_table USING(a); +SELECT COUNT(*) FROM nullkey_c1_t1 FULL JOIN postgres_local_table USING(a); +SELECT COUNT(*) FROM nullkey_c1_t1 FULL JOIN reference_table USING(a); + +SELECT COUNT(*) FROM nullkey_c1_t1 JOIN append_table USING(a); +SELECT COUNT(*) FROM nullkey_c1_t1 JOIN range_table USING(a); + +SET citus.enable_non_colocated_router_query_pushdown TO ON; + +SELECT COUNT(*) FROM nullkey_c1_t1 JOIN range_table USING(a) WHERE range_table.a = 20; + +SET citus.enable_non_colocated_router_query_pushdown TO OFF; + +SELECT COUNT(*) FROM nullkey_c1_t1 JOIN range_table USING(a) WHERE range_table.a = 20; + +RESET citus.enable_non_colocated_router_query_pushdown; + +-- lateral / semi / anti joins with different table types + +-- with a reference table +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +LEFT JOIN LATERAL ( + SELECT * FROM reference_table t2 WHERE t2.b > t1.a +) q USING(a); + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE EXISTS ( + SELECT * FROM reference_table t2 WHERE t2.b > t1.a +); + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE NOT EXISTS ( + SELECT * FROM reference_table t2 WHERE t2.b > t1.a +); + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE t1.b IN ( + SELECT b+1 FROM reference_table t2 WHERE t2.b = t1.a +); + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE t1.b NOT IN ( + SELECT a FROM reference_table t2 WHERE t2.b > t1.a +); + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +JOIN LATERAL ( + SELECT * FROM reference_table t2 WHERE t2.b > t1.a +) q USING(a); + +SELECT COUNT(*) FROM reference_table t1 +LEFT JOIN LATERAL ( + SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +) q USING(a); + +SELECT COUNT(*) FROM reference_table t1 +WHERE EXISTS ( + SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +); + +SELECT COUNT(*) FROM reference_table t1 +WHERE t1.b IN ( + SELECT b+1 FROM nullkey_c1_t1 t2 WHERE t2.b = t1.a +); + +SELECT COUNT(*) FROM reference_table t1 +WHERE t1.b NOT IN ( + SELECT a FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +); + +SELECT COUNT(*) FROM reference_table t1 +JOIN LATERAL ( + SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +) q USING(a); + +-- with a distributed table +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +LEFT JOIN LATERAL ( + SELECT * FROM distributed_table t2 WHERE t2.b > t1.a +) q USING(a); + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE EXISTS ( + SELECT * FROM distributed_table t2 WHERE t2.b > t1.a +); + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE NOT EXISTS ( + SELECT * FROM distributed_table t2 WHERE t2.b > t1.a +); + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE t1.b IN ( + SELECT b+1 FROM distributed_table t2 WHERE t2.b = t1.a +); + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE t1.b NOT IN ( + SELECT a FROM distributed_table t2 WHERE t2.b > t1.a +); + +SELECT COUNT(*) FROM distributed_table t1 +LEFT JOIN LATERAL ( + SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +) q USING(a); + +SELECT COUNT(*) FROM distributed_table t1 +WHERE EXISTS ( + SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +); + +SELECT COUNT(*) FROM distributed_table t1 +WHERE t1.b IN ( + SELECT b+1 FROM nullkey_c1_t1 t2 WHERE t2.b = t1.a +); + +SELECT COUNT(*) FROM distributed_table t1 +WHERE t1.b NOT IN ( + SELECT a FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +); + +-- with postgres / citus local tables +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +LEFT JOIN LATERAL ( + SELECT * FROM citus_local_table t2 WHERE t2.b > t1.a +) q USING(a); + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE EXISTS ( + SELECT * FROM citus_local_table t2 WHERE t2.b > t1.a +); + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE NOT EXISTS ( + SELECT * FROM citus_local_table t2 WHERE t2.b > t1.a +); + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE t1.b IN ( + SELECT b+1 FROM citus_local_table t2 WHERE t2.b = t1.a +); + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE t1.b NOT IN ( + SELECT a FROM citus_local_table t2 WHERE t2.b > t1.a +); + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +JOIN LATERAL ( + SELECT * FROM citus_local_table t2 WHERE t2.b > t1.a +) q USING(a); + +SELECT COUNT(*) FROM citus_local_table t1 +LEFT JOIN LATERAL ( + SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +) q USING(a); + +SELECT COUNT(*) FROM postgres_local_table t1 +LEFT JOIN LATERAL ( + SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +) q USING(a); + +SELECT COUNT(*) FROM citus_local_table t1 +WHERE EXISTS ( + SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +); + +SELECT COUNT(*) FROM citus_local_table t1 +WHERE t1.b IN ( + SELECT b+1 FROM nullkey_c1_t1 t2 WHERE t2.b = t1.a +); + +SELECT COUNT(*) FROM citus_local_table t1 +WHERE t1.b NOT IN ( + SELECT a FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +); + +SELECT COUNT(*) FROM citus_local_table t1 +JOIN LATERAL ( + SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +) q USING(a); + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +LEFT JOIN LATERAL ( + SELECT * FROM postgres_local_table t2 WHERE t2.b > t1.a +) q USING(a); + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE EXISTS ( + SELECT * FROM postgres_local_table t2 WHERE t2.b > t1.a +); + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE NOT EXISTS ( + SELECT * FROM postgres_local_table t2 WHERE t2.b > t1.a +); + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE t1.b IN ( + SELECT b+1 FROM postgres_local_table t2 WHERE t2.b = t1.a +); + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +WHERE t1.b NOT IN ( + SELECT a FROM postgres_local_table t2 WHERE t2.b > t1.a +); + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +JOIN LATERAL ( + SELECT * FROM postgres_local_table t2 WHERE t2.b > t1.a +) q USING(a); + +SELECT COUNT(*) FROM postgres_local_table t1 +WHERE EXISTS ( + SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +); + +SELECT COUNT(*) FROM postgres_local_table t1 +WHERE t1.b IN ( + SELECT b+1 FROM nullkey_c1_t1 t2 WHERE t2.b = t1.a +); + +SELECT COUNT(*) FROM postgres_local_table t1 +WHERE t1.b NOT IN ( + SELECT a FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +); + +SELECT COUNT(*) FROM postgres_local_table t1 +JOIN LATERAL ( + SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a +) q USING(a); + +-- insert .. select + +-- between two colocated null dist key tables + +-- The target list of "distributed statement"s that we send to workers +-- differ(*) in Postgres versions < 15. For this reason, we temporarily +-- disable debug messages here and run the EXPLAIN'ed version of the +-- command. +-- +-- (*): < SELECT a, b > vs < SELECT table_name.a, table_name.b > +SET client_min_messages TO WARNING; +EXPLAIN (ANALYZE TRUE, TIMING FALSE, COSTS FALSE, SUMMARY FALSE, VERBOSE FALSE) +INSERT INTO nullkey_c1_t1 SELECT * FROM nullkey_c1_t2; +SET client_min_messages TO DEBUG2; + +-- between two non-colocated null dist key tables +INSERT INTO nullkey_c1_t1 SELECT * FROM nullkey_c2_t1; + +-- between a null dist key table and a table of different type +INSERT INTO nullkey_c1_t1 SELECT * FROM reference_table; +INSERT INTO nullkey_c1_t1 SELECT * FROM distributed_table; +INSERT INTO nullkey_c1_t1 SELECT * FROM citus_local_table; +INSERT INTO nullkey_c1_t1 SELECT * FROM postgres_local_table; + +INSERT INTO reference_table SELECT * FROM nullkey_c1_t1; +INSERT INTO distributed_table SELECT * FROM nullkey_c1_t1; +INSERT INTO citus_local_table SELECT * FROM nullkey_c1_t1; +INSERT INTO postgres_local_table SELECT * FROM nullkey_c1_t1; + +-- test subquery +SELECT count(*) FROM +( + SELECT * FROM (SELECT * FROM nullkey_c1_t2) as subquery_inner +) AS subquery_top; + +-- test cte inlining +WITH cte_nullkey_c1_t1 AS (SELECT * FROM nullkey_c1_t1), + cte_postgres_local_table AS (SELECT * FROM postgres_local_table), + cte_distributed_table AS (SELECT * FROM distributed_table) +SELECT COUNT(*) FROM cte_distributed_table, cte_nullkey_c1_t1, cte_postgres_local_table +WHERE cte_nullkey_c1_t1.a > 3 AND cte_distributed_table.a < 5; + +-- test recursive ctes +WITH level_0 AS ( + WITH level_1 AS ( + WITH RECURSIVE level_2_recursive(x) AS ( + VALUES (1) + UNION ALL + SELECT a + 1 FROM nullkey_c1_t1 JOIN level_2_recursive ON (a = x) WHERE a < 100 + ) + SELECT * FROM level_2_recursive RIGHT JOIN reference_table ON (level_2_recursive.x = reference_table.a) + ) + SELECT * FROM level_1 +) +SELECT COUNT(*) FROM level_0; + +WITH level_0 AS ( + WITH level_1 AS ( + WITH RECURSIVE level_2_recursive(x) AS ( + VALUES (1) + UNION ALL + SELECT a + 1 FROM nullkey_c1_t1 JOIN level_2_recursive ON (a = x) WHERE a < 100 + ) + SELECT * FROM level_2_recursive JOIN distributed_table ON (level_2_recursive.x = distributed_table.a) + ) + SELECT * FROM level_1 +) +SELECT COUNT(*) FROM level_0; + +-- grouping set +SELECT + id, substring(title, 2, 1) AS subtitle, count(*) + FROM articles_hash + WHERE author_id = 1 or author_id = 2 + GROUP BY GROUPING SETS ((id),(subtitle)) + ORDER BY id, subtitle; + +-- subquery in SELECT clause +SELECT a.title AS name, (SELECT a2.id FROM articles_hash a2 WHERE a.id = a2.id LIMIT 1) + AS special_price FROM articles_hash a +ORDER BY 1,2; + +-- test prepared statements + +-- prepare queries can be router plannable +PREPARE author_1_articles as + SELECT * + FROM articles_hash + WHERE author_id = 1; + +EXECUTE author_1_articles; +EXECUTE author_1_articles; +EXECUTE author_1_articles; +EXECUTE author_1_articles; +EXECUTE author_1_articles; +EXECUTE author_1_articles; + +-- parametric prepare queries can be router plannable +PREPARE author_articles(int) as + SELECT * + FROM articles_hash + WHERE author_id = $1; + +EXECUTE author_articles(1); +EXECUTE author_articles(1); +EXECUTE author_articles(1); +EXECUTE author_articles(1); +EXECUTE author_articles(1); +EXECUTE author_articles(1); + +EXECUTE author_articles(NULL); +EXECUTE author_articles(NULL); +EXECUTE author_articles(NULL); +EXECUTE author_articles(NULL); +EXECUTE author_articles(NULL); +EXECUTE author_articles(NULL); +EXECUTE author_articles(NULL); + +PREPARE author_articles_update(int) AS + UPDATE articles_hash SET title = 'test' WHERE author_id = $1; + +EXECUTE author_articles_update(NULL); +EXECUTE author_articles_update(NULL); +EXECUTE author_articles_update(NULL); +EXECUTE author_articles_update(NULL); +EXECUTE author_articles_update(NULL); +EXECUTE author_articles_update(NULL); +EXECUTE author_articles_update(NULL); + +-- More tests with insert .. select. +-- +-- The target list of "distributed statement"s that we send to workers +-- might differ(*) in Postgres versions < 15 and they are reported when +-- "log level >= DEBUG2". For this reason, we set log level to DEBUG1 to +-- avoid reporting them. +-- +-- DEBUG1 still allows reporting the reason why given INSERT .. SELECT +-- query is not distributed / requires pull-to-coordinator. + +SET client_min_messages TO DEBUG1; + +INSERT INTO bigserial_test (x, y) SELECT x, y FROM bigserial_test; + +INSERT INTO agg_events + (user_id) +SELECT f2.id FROM + +(SELECT + id +FROM (SELECT users_ref_table.user_id AS id + FROM raw_events_first, + users_ref_table + WHERE raw_events_first.user_id = users_ref_table.user_id ) AS foo) as f +INNER JOIN +(SELECT v4, + v1, + id +FROM (SELECT SUM(raw_events_second.value_4) AS v4, + SUM(raw_events_first.value_1) AS v1, + raw_events_second.user_id AS id + FROM raw_events_first, + raw_events_second + WHERE raw_events_first.user_id = raw_events_second.user_id + GROUP BY raw_events_second.user_id + HAVING SUM(raw_events_second.value_4) > 1000) AS foo2 ) as f2 +ON (f.id = f2.id) +WHERE f.id IN (SELECT user_id + FROM raw_events_second); + +-- upsert with returning +INSERT INTO agg_events AS ae + ( + user_id, + value_1_agg, + agg_time + ) +SELECT user_id, + value_1, + time +FROM raw_events_first +ON conflict (user_id, value_1_agg) +DO UPDATE + SET agg_time = EXCLUDED.agg_time + WHERE ae.agg_time < EXCLUDED.agg_time +RETURNING user_id, value_1_agg; + +-- using a left join +INSERT INTO agg_events (user_id) +SELECT + raw_events_first.user_id +FROM + raw_events_first LEFT JOIN raw_events_second ON raw_events_first.user_id = raw_events_second.user_id + WHERE raw_events_second.user_id = 10 OR raw_events_second.user_id = 11; + +-- using a full join +INSERT INTO agg_events (user_id, value_1_agg) +SELECT t1.user_id AS col1, + t2.user_id AS col2 +FROM raw_events_first t1 + FULL JOIN raw_events_second t2 + ON t1.user_id = t2.user_id; + +-- using semi join +INSERT INTO raw_events_second + (user_id) +SELECT user_id +FROM raw_events_first +WHERE user_id IN (SELECT raw_events_second.user_id + FROM raw_events_second, raw_events_first + WHERE raw_events_second.user_id = raw_events_first.user_id AND raw_events_first.user_id = 200); + +-- using lateral join +INSERT INTO raw_events_second + (user_id) +SELECT user_id +FROM raw_events_first +WHERE NOT EXISTS (SELECT 1 + FROM raw_events_second + WHERE raw_events_second.user_id =raw_events_first.user_id); + +-- using inner join +INSERT INTO agg_events (user_id) +SELECT raw_events_first.user_id +FROM raw_events_first INNER JOIN raw_events_second ON raw_events_first.user_id = raw_events_second.value_1 +WHERE raw_events_first.value_1 IN (10, 11,12) OR raw_events_second.user_id IN (1,2,3,4); + +-- We could relax distributed insert .. select checks to allow pushing +-- down more clauses down to the worker nodes when inserting into a single +-- shard by selecting from a colocated one. We might want to do something +-- like https://github.com/citusdata/citus/pull/6772. +-- +-- e.g., insert into null_shard_key_1/citus_local/reference +-- select * from null_shard_key_1/citus_local/reference limit 1 +-- +-- Below "limit / offset clause" test and some others are examples of this. + +-- limit / offset clause +INSERT INTO agg_events (user_id) SELECT raw_events_first.user_id FROM raw_events_first LIMIT 1; +INSERT INTO agg_events (user_id) SELECT raw_events_first.user_id FROM raw_events_first OFFSET 1; + +-- using a materialized cte +WITH cte AS MATERIALIZED + (SELECT max(value_1)+1 as v1_agg, user_id FROM raw_events_first GROUP BY user_id) +INSERT INTO agg_events (value_1_agg, user_id) +SELECT v1_agg, user_id FROM cte; + +INSERT INTO raw_events_second + WITH cte AS MATERIALIZED (SELECT * FROM raw_events_first) + SELECT user_id * 1000, time, value_1, value_2, value_3, value_4 FROM cte; + +-- using a regular cte +WITH cte AS (SELECT * FROM raw_events_first) +INSERT INTO raw_events_second + SELECT user_id * 7000, time, value_1, value_2, value_3, value_4 FROM cte; + +INSERT INTO raw_events_second + WITH cte AS (SELECT * FROM raw_events_first) + SELECT * FROM cte; + +INSERT INTO agg_events + WITH sub_cte AS (SELECT 1) + SELECT + raw_events_first.user_id, (SELECT * FROM sub_cte) + FROM + raw_events_first; + +-- we still support complex joins via INSERT's cte list .. +WITH cte AS ( + SELECT reference_table.a AS a, 1 AS b + FROM distributed_table RIGHT JOIN reference_table USING (a) +) +INSERT INTO raw_events_second (user_id, value_1) + SELECT (a+5)*-1, b FROM cte; + +-- .. but can't do so via via SELECT's cte list +INSERT INTO raw_events_second (user_id, value_1) +WITH cte AS ( + SELECT reference_table.a AS a, 1 AS b + FROM distributed_table RIGHT JOIN reference_table USING (a) +) + SELECT (a+5)*-1, b FROM cte; + +-- using set operations +INSERT INTO + raw_events_first(user_id) + (SELECT user_id FROM raw_events_first) INTERSECT + (SELECT user_id FROM raw_events_first); + +-- group by clause inside subquery +INSERT INTO agg_events + (user_id) +SELECT f2.id FROM + +(SELECT + id +FROM (SELECT raw_events_second.user_id AS id + FROM raw_events_first, + raw_events_second + WHERE raw_events_first.user_id = raw_events_second.user_id ) AS foo) as f +INNER JOIN +(SELECT v4, + v1, + id +FROM (SELECT SUM(raw_events_second.value_4) AS v4, + SUM(raw_events_first.value_1) AS v1, + raw_events_second.user_id AS id + FROM raw_events_first, + raw_events_second + WHERE raw_events_first.user_id = raw_events_second.user_id + GROUP BY raw_events_second.user_id + HAVING SUM(raw_events_second.value_4) > 1000) AS foo2 ) as f2 +ON (f.id = f2.id) +WHERE f.id IN (SELECT user_id + FROM raw_events_second); + +-- group by clause inside lateral subquery +INSERT INTO agg_events (user_id, value_4_agg) +SELECT + averages.user_id, avg(averages.value_4) +FROM + (SELECT + t1.user_id + FROM + raw_events_second t1 JOIN raw_events_second t2 on (t1.user_id = t2.user_id) + ) reference_ids + JOIN LATERAL + (SELECT + user_id, value_4 + FROM + raw_events_first) as averages ON averages.value_4 = reference_ids.user_id + GROUP BY averages.user_id; + +-- using aggregates +INSERT INTO agg_events + (value_3_agg, + value_4_agg, + value_1_agg, + value_2_agg, + user_id) +SELECT SUM(value_3), + Count(value_4), + user_id, + SUM(value_1), + Avg(value_2) +FROM raw_events_first +GROUP BY user_id; + +-- using generate_series +INSERT INTO raw_events_first (user_id, value_1, value_2) +SELECT s, s, s FROM generate_series(1, 5) s; + +CREATE SEQUENCE insert_select_test_seq; + +-- nextval() expression in select's targetlist +INSERT INTO raw_events_first (user_id, value_1, value_2) +SELECT s, nextval('insert_select_test_seq'), (random()*10)::int +FROM generate_series(100, 105) s; + +-- non-immutable function +INSERT INTO modify_fast_path (key, value_1) VALUES (2,1) RETURNING value_1, random() * key; + +SET client_min_messages TO DEBUG2; + +-- update / delete + +UPDATE nullkey_c1_t1 SET a = 1 WHERE b = 5; +UPDATE nullkey_c1_t1 SET a = 1 WHERE a = 5; +UPDATE nullkey_c1_t1 SET a = random(); +UPDATE nullkey_c1_t1 SET a = 1 WHERE a = random(); + +DELETE FROM nullkey_c1_t1 WHERE b = 5; +DELETE FROM nullkey_c1_t1 WHERE a = random(); + +-- simple update queries between different table types / colocated tables +UPDATE nullkey_c1_t1 SET b = 5 FROM nullkey_c1_t2 WHERE nullkey_c1_t1.b = nullkey_c1_t2.b; +UPDATE nullkey_c1_t1 SET b = 5 FROM nullkey_c2_t1 WHERE nullkey_c1_t1.b = nullkey_c2_t1.b; +UPDATE nullkey_c1_t1 SET b = 5 FROM reference_table WHERE nullkey_c1_t1.b = reference_table.b; +UPDATE nullkey_c1_t1 SET b = 5 FROM distributed_table WHERE nullkey_c1_t1.b = distributed_table.b; +UPDATE nullkey_c1_t1 SET b = 5 FROM distributed_table WHERE nullkey_c1_t1.b = distributed_table.a; +UPDATE nullkey_c1_t1 SET b = 5 FROM citus_local_table WHERE nullkey_c1_t1.b = citus_local_table.b; +UPDATE nullkey_c1_t1 SET b = 5 FROM postgres_local_table WHERE nullkey_c1_t1.b = postgres_local_table.b; + +UPDATE reference_table SET b = 5 FROM nullkey_c1_t1 WHERE nullkey_c1_t1.b = reference_table.b; +UPDATE distributed_table SET b = 5 FROM nullkey_c1_t1 WHERE nullkey_c1_t1.b = distributed_table.b; +UPDATE distributed_table SET b = 5 FROM nullkey_c1_t1 WHERE nullkey_c1_t1.b = distributed_table.a; +UPDATE citus_local_table SET b = 5 FROM nullkey_c1_t1 WHERE nullkey_c1_t1.b = citus_local_table.b; +UPDATE postgres_local_table SET b = 5 FROM nullkey_c1_t1 WHERE nullkey_c1_t1.b = postgres_local_table.b; + +-- simple delete queries between different table types / colocated tables +DELETE FROM nullkey_c1_t1 USING nullkey_c1_t2 WHERE nullkey_c1_t1.b = nullkey_c1_t2.b; +DELETE FROM nullkey_c1_t1 USING nullkey_c2_t1 WHERE nullkey_c1_t1.b = nullkey_c2_t1.b; +DELETE FROM nullkey_c1_t1 USING reference_table WHERE nullkey_c1_t1.b = reference_table.b; +DELETE FROM nullkey_c1_t1 USING distributed_table WHERE nullkey_c1_t1.b = distributed_table.b; +DELETE FROM nullkey_c1_t1 USING distributed_table WHERE nullkey_c1_t1.b = distributed_table.a; +DELETE FROM nullkey_c1_t1 USING citus_local_table WHERE nullkey_c1_t1.b = citus_local_table.b; +DELETE FROM nullkey_c1_t1 USING postgres_local_table WHERE nullkey_c1_t1.b = postgres_local_table.b; + +DELETE FROM reference_table USING nullkey_c1_t1 WHERE nullkey_c1_t1.b = reference_table.b; +DELETE FROM distributed_table USING nullkey_c1_t1 WHERE nullkey_c1_t1.b = distributed_table.b; +DELETE FROM distributed_table USING nullkey_c1_t1 WHERE nullkey_c1_t1.b = distributed_table.a; +DELETE FROM citus_local_table USING nullkey_c1_t1 WHERE nullkey_c1_t1.b = citus_local_table.b; +DELETE FROM postgres_local_table USING nullkey_c1_t1 WHERE nullkey_c1_t1.b = postgres_local_table.b; + +-- slightly more complex update queries +UPDATE nullkey_c1_t1 SET b = 5 WHERE nullkey_c1_t1.b IN (SELECT b FROM distributed_table); + +WITH cte AS materialized( + SELECT * FROM distributed_table +) +UPDATE nullkey_c1_t1 SET b = 5 FROM cte WHERE nullkey_c1_t1.b = cte.a; + +WITH cte AS ( + SELECT reference_table.a AS a, 1 AS b + FROM distributed_table RIGHT JOIN reference_table USING (a) +) +UPDATE nullkey_c1_t1 SET b = 5 WHERE nullkey_c1_t1.b IN (SELECT b FROM cte); + +UPDATE nullkey_c1_t1 SET b = 5 FROM reference_table WHERE EXISTS ( + SELECT 1 FROM reference_table LEFT JOIN nullkey_c1_t1 USING (a) WHERE nullkey_c1_t1.b IS NULL +); + +UPDATE nullkey_c1_t1 tx SET b = ( + SELECT nullkey_c1_t2.b FROM nullkey_c1_t2 JOIN nullkey_c1_t1 ON (nullkey_c1_t1.a != nullkey_c1_t2.a) WHERE nullkey_c1_t1.a = tx.a ORDER BY 1 LIMIT 1 +); + +UPDATE nullkey_c1_t1 tx SET b = t2.b FROM nullkey_c1_t1 t1 JOIN nullkey_c1_t2 t2 ON (t1.a = t2.a); + +WITH cte AS ( + SELECT * FROM nullkey_c1_t2 ORDER BY 1,2 LIMIT 10 +) +UPDATE nullkey_c1_t1 SET b = 5 WHERE nullkey_c1_t1.a IN (SELECT b FROM cte); + +UPDATE modify_fast_path SET value_1 = value_1 + 12 * value_1 WHERE key = 1; +UPDATE modify_fast_path SET value_1 = NULL WHERE value_1 = 15 AND (key = 1 OR value_2 = 'citus'); +UPDATE modify_fast_path SET value_1 = 5 WHERE key = 2 RETURNING value_1 * 15, value_1::numeric * 16; +UPDATE modify_fast_path + SET value_1 = 1 + FROM modify_fast_path_reference + WHERE + modify_fast_path.key = modify_fast_path_reference.key AND + modify_fast_path.key = 1 AND + modify_fast_path_reference.key = 1; + +PREPARE p1 (int, int, int) AS + UPDATE modify_fast_path SET value_1 = value_1 + $1 WHERE key = $2 AND value_1 = $3; +EXECUTE p1(1,1,1); +EXECUTE p1(2,2,2); +EXECUTE p1(3,3,3); +EXECUTE p1(4,4,4); +EXECUTE p1(5,5,5); +EXECUTE p1(6,6,6); +EXECUTE p1(7,7,7); + +PREPARE prepared_zero_shard_update(int) AS UPDATE modify_fast_path SET value_1 = 1 WHERE key = $1 AND false; +EXECUTE prepared_zero_shard_update(1); +EXECUTE prepared_zero_shard_update(2); +EXECUTE prepared_zero_shard_update(3); +EXECUTE prepared_zero_shard_update(4); +EXECUTE prepared_zero_shard_update(5); +EXECUTE prepared_zero_shard_update(6); +EXECUTE prepared_zero_shard_update(7); + +-- slightly more complex delete queries +DELETE FROM nullkey_c1_t1 WHERE nullkey_c1_t1.b IN (SELECT b FROM distributed_table); + +WITH cte AS materialized( + SELECT * FROM distributed_table +) +DELETE FROM nullkey_c1_t1 USING cte WHERE nullkey_c1_t1.b = cte.a; + +WITH cte AS ( + SELECT reference_table.a AS a, 1 AS b + FROM distributed_table RIGHT JOIN reference_table USING (a) +) +DELETE FROM nullkey_c1_t1 WHERE nullkey_c1_t1.b IN (SELECT b FROM cte); + +DELETE FROM nullkey_c1_t1 USING reference_table WHERE EXISTS ( + SELECT 1 FROM reference_table LEFT JOIN nullkey_c1_t1 USING (a) WHERE nullkey_c1_t1.b IS NULL +); + +DELETE FROM nullkey_c1_t1 tx USING nullkey_c1_t1 t1 JOIN nullkey_c1_t2 t2 ON (t1.a = t2.a); + +WITH cte AS ( + SELECT * FROM nullkey_c1_t2 ORDER BY 1,2 LIMIT 10 +) +DELETE FROM nullkey_c1_t1 WHERE nullkey_c1_t1.a IN (SELECT b FROM cte); + +DELETE FROM modify_fast_path WHERE value_1 = 15 AND (key = 1 OR value_2 = 'citus'); +DELETE FROM modify_fast_path WHERE key = 2 RETURNING value_1 * 15, value_1::numeric * 16; +DELETE FROM modify_fast_path + USING modify_fast_path_reference + WHERE + modify_fast_path.key = modify_fast_path_reference.key AND + modify_fast_path.key = 1 AND + modify_fast_path_reference.key = 1; + +PREPARE p2 (int, int, int) AS + DELETE FROM modify_fast_path WHERE key = ($2)*$1 AND value_1 = $3; +EXECUTE p2(1,1,1); +EXECUTE p2(2,2,2); +EXECUTE p2(3,3,3); +EXECUTE p2(4,4,4); +EXECUTE p2(5,5,5); +EXECUTE p2(6,6,6); +EXECUTE p2(7,7,7); + +PREPARE prepared_zero_shard_delete(int) AS DELETE FROM modify_fast_path WHERE key = $1 AND false; +EXECUTE prepared_zero_shard_delete(1); +EXECUTE prepared_zero_shard_delete(2); +EXECUTE prepared_zero_shard_delete(3); +EXECUTE prepared_zero_shard_delete(4); +EXECUTE prepared_zero_shard_delete(5); +EXECUTE prepared_zero_shard_delete(6); +EXECUTE prepared_zero_shard_delete(7); + +-- test modifying ctes + +WITH cte AS ( + UPDATE modify_fast_path SET value_1 = value_1 + 1 WHERE key = 1 RETURNING * +) +SELECT * FROM cte; + +WITH cte AS ( + DELETE FROM modify_fast_path WHERE key = 1 RETURNING * +) +SELECT * FROM modify_fast_path; + +WITH cte AS ( + DELETE FROM modify_fast_path WHERE key = 1 RETURNING * +) +SELECT * FROM modify_fast_path_reference WHERE key IN (SELECT key FROM cte); + +WITH cte AS ( + DELETE FROM reference_table WHERE a = 1 RETURNING * +) +SELECT * FROM nullkey_c1_t1 WHERE a IN (SELECT a FROM cte); + +WITH cte AS ( + DELETE FROM nullkey_c1_t1 WHERE a = 1 RETURNING * +) +SELECT * FROM nullkey_c1_t2 WHERE a IN (SELECT a FROM cte); + +WITH cte AS ( + DELETE FROM nullkey_c1_t1 WHERE a = 1 RETURNING * +) +SELECT * FROM nullkey_c2_t1 WHERE a IN (SELECT a FROM cte); + +WITH cte AS ( + DELETE FROM nullkey_c1_t1 WHERE a = 1 RETURNING * +) +SELECT * FROM distributed_table WHERE a IN (SELECT a FROM cte); + +-- Below two queries fail very late when +-- citus.enable_non_colocated_router_query_pushdown is set to on. + +SET citus.enable_non_colocated_router_query_pushdown TO ON; + +WITH cte AS ( + DELETE FROM distributed_table WHERE a = 1 RETURNING * +) +SELECT * FROM nullkey_c1_t1 WHERE a IN (SELECT a FROM cte); + +WITH cte AS ( + DELETE FROM distributed_table WHERE a = 1 RETURNING * +) +SELECT * FROM nullkey_c1_t1 WHERE b IN (SELECT b FROM cte); + +SET citus.enable_non_colocated_router_query_pushdown TO OFF; + +WITH cte AS ( + DELETE FROM distributed_table WHERE a = 1 RETURNING * +) +SELECT * FROM nullkey_c1_t1 WHERE a IN (SELECT a FROM cte); + +WITH cte AS ( + DELETE FROM distributed_table WHERE a = 1 RETURNING * +) +SELECT * FROM nullkey_c1_t1 WHERE b IN (SELECT b FROM cte); + +RESET citus.enable_non_colocated_router_query_pushdown; + +WITH cte AS ( + UPDATE modify_fast_path SET value_1 = value_1 + 1 WHERE key = 1 RETURNING * +) +UPDATE modify_fast_path SET value_1 = value_1 + 1 WHERE key = 1; + +WITH cte AS ( + DELETE FROM modify_fast_path WHERE key = 1 RETURNING * +) +DELETE FROM modify_fast_path WHERE key = 1; + +-- test window functions + +SELECT + user_id, avg(avg(value_3)) OVER (PARTITION BY user_id, MIN(value_2)) +FROM + raw_events_first +GROUP BY + 1 +ORDER BY + 2 DESC NULLS LAST, 1 DESC; + +SELECT + user_id, max(value_1) OVER (PARTITION BY user_id, MIN(value_2)) +FROM ( + SELECT + DISTINCT us.user_id, us.value_2, us.value_1, random() as r1 + FROM + raw_events_first as us, raw_events_second + WHERE + us.user_id = raw_events_second.user_id + ORDER BY + user_id, value_2 + ) s +GROUP BY + 1, value_1 +ORDER BY + 2 DESC, 1; + +SELECT + DISTINCT ON (raw_events_second.user_id, rnk) raw_events_second.user_id, rank() OVER my_win AS rnk +FROM + raw_events_second, raw_events_first +WHERE + raw_events_first.user_id = raw_events_second.user_id +WINDOW + my_win AS (PARTITION BY raw_events_second.user_id, raw_events_first.value_1 ORDER BY raw_events_second.time DESC) +ORDER BY + rnk DESC, 1 DESC +LIMIT 10; + +SET client_min_messages TO ERROR; +DROP SCHEMA query_null_dist_key CASCADE; + +SELECT citus_remove_node('localhost', :master_port); From 39b7711527aeb48af150a85f29e83a7ab778714e Mon Sep 17 00:00:00 2001 From: Onur Tirtir Date: Mon, 24 Apr 2023 13:19:22 +0300 Subject: [PATCH 029/118] Add support for more pushable / non-pushable insert .. select queries with null-shard-key tables (#6823) * Add support for dist insert select by selecting from a reference table. This was the only pushable insert .. select case that #6773 didn't cover. * For the cases where we insert into a Citus table but the INSERT .. SELECT query cannot be pushed down, allow pull-to-coordinator when possible. Remove the checks that we had at the very beginning of CreateInsertSelectPlanInternal so that we can try insert .. select via pull-to-coordinator for the cases where we cannot push-down the insert .. select query. What we support via pull-to-coordinator is still limited due to lacking of logical planner support for SELECT queries, but this commit at least allows using pull-to-coordinator for the cases where the select query can be planned via router planner, without limiting ourselves to restrictive top-level checks. Also introduce some additional restrictions into CreateDistributedInsertSelectPlan for the cases it was missing to check for null-shard-key tables. Indeed, it would make more sense to have those checks for distributed tables in general, via separate PRs against main branch. See https://github.com/citusdata/citus/pull/6817. * Add support for inserting into a Postgres table. --- .../planner/insert_select_planner.c | 156 ++-- .../regress/expected/create_null_dist_key.out | 1 - .../expected/insert_select_null_dist_key.out | 814 ++++++++++++++++++ .../regress/expected/query_null_dist_key.out | 113 ++- src/test/regress/multi_1_schedule | 1 + .../sql/insert_select_null_dist_key.sql | 470 ++++++++++ src/test/regress/sql/query_null_dist_key.sql | 57 +- 7 files changed, 1484 insertions(+), 128 deletions(-) create mode 100644 src/test/regress/expected/insert_select_null_dist_key.out create mode 100644 src/test/regress/sql/insert_select_null_dist_key.sql diff --git a/src/backend/distributed/planner/insert_select_planner.c b/src/backend/distributed/planner/insert_select_planner.c index 175f6bc6f..62c0e8d68 100644 --- a/src/backend/distributed/planner/insert_select_planner.c +++ b/src/backend/distributed/planner/insert_select_planner.c @@ -57,7 +57,6 @@ static DistributedPlan * CreateInsertSelectPlanInternal(uint64 planId, PlannerRestrictionContext * plannerRestrictionContext, ParamListInfo boundParams); -static void ErrorIfInsertSelectWithNullDistKeyNotSupported(Query *originalQuery); static DistributedPlan * CreateDistributedInsertSelectPlan(Query *originalQuery, PlannerRestrictionContext * plannerRestrictionContext); @@ -242,12 +241,6 @@ CreateInsertSelectPlanInternal(uint64 planId, Query *originalQuery, RaiseDeferredError(deferredError, ERROR); } - /* - * We support a limited set of INSERT .. SELECT queries if the query - * references a null-dist-key table. - */ - ErrorIfInsertSelectWithNullDistKeyNotSupported(originalQuery); - DistributedPlan *distributedPlan = CreateDistributedInsertSelectPlan(originalQuery, plannerRestrictionContext); @@ -267,74 +260,6 @@ CreateInsertSelectPlanInternal(uint64 planId, Query *originalQuery, } -/* - * ErrorIfInsertSelectWithNullDistKeyNotSupported throws an error if given INSERT - * .. SELECT query references a null-dist-key table (as the target table or in - * the SELECT clause) and is unsupported. - * - * Such an INSERT .. SELECT query is supported as long as the it only references - * a "colocated" set of null-dist-key tables, no other relation rte types. - */ -static void -ErrorIfInsertSelectWithNullDistKeyNotSupported(Query *originalQuery) -{ - RangeTblEntry *subqueryRte = ExtractSelectRangeTableEntry(originalQuery); - Query *subquery = subqueryRte->subquery; - RTEListProperties *subqueryRteListProperties = GetRTEListPropertiesForQuery(subquery); - - RangeTblEntry *insertRte = ExtractResultRelationRTEOrError(originalQuery); - Oid targetRelationId = insertRte->relid; - if (!IsCitusTableType(targetRelationId, NULL_KEY_DISTRIBUTED_TABLE) && - subqueryRteListProperties->hasDistTableWithoutShardKey) - { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot select from a distributed table that " - "does not have a shard key when inserting into " - "a different table type"))); - } - else if (IsCitusTableType(targetRelationId, NULL_KEY_DISTRIBUTED_TABLE)) - { - if (subqueryRteListProperties->hasPostgresLocalTable || - subqueryRteListProperties->hasReferenceTable || - subqueryRteListProperties->hasCitusLocalTable || - subqueryRteListProperties->hasDistTableWithShardKey || - subqueryRteListProperties->hasMaterializedView) - { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot select from different table types " - "when inserting into a distributed table " - "that does not have a shard key"))); - } - - if (!subqueryRteListProperties->hasDistTableWithoutShardKey) - { - /* - * This means that the SELECT doesn't reference any Citus tables, - * Postgres tables or materialized views but references a function - * call, a values claue etc., or a cte from INSERT. - * - * In that case, we rely on the common restrictions enforced by the - * INSERT .. SELECT planners. - */ - Assert(!NeedsDistributedPlanning(subquery)); - return; - } - - List *distributedRelationIdList = DistributedRelationIdList(subquery); - distributedRelationIdList = lappend_oid(distributedRelationIdList, - targetRelationId); - - if (!AllDistributedRelationsInListColocated(distributedRelationIdList)) - { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot select from a non-colocated distributed " - "table when inserting into a distributed table " - "that does not have a shard key"))); - } - } -} - - /* * CreateDistributedInsertSelectPlan creates a DistributedPlan for distributed * INSERT ... SELECT queries which could consist of multiple tasks. @@ -454,16 +379,6 @@ CreateInsertSelectIntoLocalTablePlan(uint64 planId, Query *insertSelectQuery, { RangeTblEntry *selectRte = ExtractSelectRangeTableEntry(insertSelectQuery); - RTEListProperties *selectRteListProperties = - GetRTEListPropertiesForQuery(selectRte->subquery); - if (selectRteListProperties->hasDistTableWithoutShardKey) - { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot select from a distributed table that " - "does not have a shard key when inserting into " - "a local table"))); - } - PrepareInsertSelectForCitusPlanner(insertSelectQuery); /* get the SELECT query (may have changed after PrepareInsertSelectForCitusPlanner) */ @@ -800,10 +715,6 @@ DistributedInsertSelectSupported(Query *queryTree, RangeTblEntry *insertRte, NULL, NULL); } } - else if (IsCitusTableType(targetRelationId, NULL_KEY_DISTRIBUTED_TABLE)) - { - /* we've already checked the subquery via ErrorIfInsertSelectWithNullDistKeyNotSupported */ - } else { /* @@ -819,25 +730,49 @@ DistributedInsertSelectSupported(Query *queryTree, RangeTblEntry *insertRte, "table", NULL, NULL); } - /* ensure that INSERT's partition column comes from SELECT's partition column */ - error = InsertPartitionColumnMatchesSelect(queryTree, insertRte, subqueryRte, - &selectPartitionColumnTableId); - if (error) + if (!HasDistributionKey(targetRelationId) || + subqueryRteListProperties->hasDistTableWithoutShardKey) { - return error; + /* + * XXX: Better to check this regardless of the fact that the target table + * has a distribution column or not. + */ + List *distributedRelationIdList = DistributedRelationIdList(subquery); + distributedRelationIdList = lappend_oid(distributedRelationIdList, + targetRelationId); + + if (!AllDistributedRelationsInListColocated(distributedRelationIdList)) + { + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "distributed INSERT ... SELECT cannot reference a " + "distributed table without a shard key together " + "with non-colocated distributed tables", + NULL, NULL); + } } - /* - * We expect partition column values come from colocated tables. Note that we - * skip this check from the reference table case given that all reference tables - * are already (and by default) co-located. - */ - if (!TablesColocated(insertRte->relid, selectPartitionColumnTableId)) + if (HasDistributionKey(targetRelationId)) { - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - "INSERT target table and the source relation of the SELECT partition " - "column value must be colocated in distributed INSERT ... SELECT", - NULL, NULL); + /* ensure that INSERT's partition column comes from SELECT's partition column */ + error = InsertPartitionColumnMatchesSelect(queryTree, insertRte, subqueryRte, + &selectPartitionColumnTableId); + if (error) + { + return error; + } + + /* + * We expect partition column values come from colocated tables. Note that we + * skip this check from the reference table case given that all reference tables + * are already (and by default) co-located. + */ + if (!TablesColocated(insertRte->relid, selectPartitionColumnTableId)) + { + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "INSERT target table and the source relation of the SELECT partition " + "column value must be colocated in distributed INSERT ... SELECT", + NULL, NULL); + } } } @@ -1626,6 +1561,19 @@ CreateNonPushableInsertSelectPlan(uint64 planId, Query *parse, ParamListInfo bou bool repartitioned = IsRedistributablePlan(selectPlan->planTree) && IsSupportedRedistributionTarget(targetRelationId); + /* + * Today it's not possible to generate a distributed plan for a SELECT + * having more than one tasks if it references a null-shard-key table. + * This is because, we don't support queries beyond router planner + * if the query references a null-shard-key table. + * + * For this reason, right now we don't expect an INSERT .. SELECT + * query to go through the repartitioned INSERT .. SELECT logic if the + * SELECT query references a null-shard-key table. + */ + Assert(!repartitioned || + !GetRTEListPropertiesForQuery(selectQueryCopy)->hasDistTableWithoutShardKey); + distributedPlan->insertSelectQuery = insertSelectQuery; distributedPlan->selectPlanForInsertSelect = selectPlan; distributedPlan->insertSelectMethod = repartitioned ? diff --git a/src/test/regress/expected/create_null_dist_key.out b/src/test/regress/expected/create_null_dist_key.out index 43120a454..af6e66f62 100644 --- a/src/test/regress/expected/create_null_dist_key.out +++ b/src/test/regress/expected/create_null_dist_key.out @@ -1803,7 +1803,6 @@ ALTER TABLE trigger_table_3 ENABLE TRIGGER ALL; -- try a few simple queries at least to make sure that we don't crash BEGIN; INSERT INTO nullkey_c1_t1 SELECT * FROM nullkey_c2_t1; -ERROR: cannot select from a non-colocated distributed table when inserting into a distributed table that does not have a shard key ROLLBACK; DROP TRIGGER IF EXISTS trigger_1 ON trigger_table_1; DROP TRIGGER trigger_2 ON trigger_table_2 CASCADE; diff --git a/src/test/regress/expected/insert_select_null_dist_key.out b/src/test/regress/expected/insert_select_null_dist_key.out new file mode 100644 index 000000000..b5391063c --- /dev/null +++ b/src/test/regress/expected/insert_select_null_dist_key.out @@ -0,0 +1,814 @@ +CREATE SCHEMA insert_select_null_dist_key; +SET search_path TO insert_select_null_dist_key; +SET citus.next_shard_id TO 1820000; +SET citus.shard_count TO 32; +SET client_min_messages TO WARNING; +SELECT 1 FROM citus_add_node('localhost', :master_port, groupid => 0); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +SET client_min_messages TO NOTICE; +CREATE TABLE nullkey_c1_t1(a int, b int); +CREATE TABLE nullkey_c1_t2(a int, b int); +SELECT create_distributed_table('nullkey_c1_t1', null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('nullkey_c1_t2', null, colocate_with=>'nullkey_c1_t1'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE nullkey_c2_t1(a int, b int); +SELECT create_distributed_table('nullkey_c2_t1', null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE reference_table(a int, b int); +SELECT create_reference_table('reference_table'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE distributed_table_c1_t1(a int, b int); +SELECT create_distributed_table('distributed_table_c1_t1', 'a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE distributed_table_c1_t2(a int, b int); +SELECT create_distributed_table('distributed_table_c1_t2', 'a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE distributed_table_c2_t1(a int, b int); +SELECT create_distributed_table('distributed_table_c2_t1', 'a', colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE citus_local_table(a int, b int); +SELECT citus_add_local_table_to_metadata('citus_local_table'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE postgres_local_table(a int, b int); +CREATE FUNCTION reload_tables() RETURNS void AS $$ + BEGIN + SET LOCAL client_min_messages TO WARNING; + + TRUNCATE nullkey_c1_t1, nullkey_c1_t2, nullkey_c2_t1, reference_table, distributed_table_c1_t1, + distributed_table_c1_t2, distributed_table_c2_t1, citus_local_table, postgres_local_table; + + INSERT INTO nullkey_c1_t1 SELECT i, i FROM generate_series(1, 8) i; + INSERT INTO nullkey_c1_t2 SELECT i, i FROM generate_series(2, 7) i; + INSERT INTO nullkey_c2_t1 SELECT i, i FROM generate_series(2, 7) i; + INSERT INTO reference_table SELECT i, i FROM generate_series(0, 5) i; + INSERT INTO distributed_table_c1_t1 SELECT i, i FROM generate_series(3, 8) i; + INSERT INTO distributed_table_c1_t2 SELECT i, i FROM generate_series(2, 9) i; + INSERT INTO distributed_table_c2_t1 SELECT i, i FROM generate_series(5, 10) i; + INSERT INTO citus_local_table SELECT i, i FROM generate_series(0, 10) i; + INSERT INTO postgres_local_table SELECT i, i FROM generate_series(5, 10) i; + END; +$$ LANGUAGE plpgsql; +SELECT reload_tables(); + reload_tables +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE append_table (a int, b int); +SELECT create_distributed_table('append_table', 'a', 'append'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT master_create_empty_shard('append_table') AS shardid1 \gset +SELECT master_create_empty_shard('append_table') AS shardid2 \gset +SELECT master_create_empty_shard('append_table') AS shardid3 \gset +COPY append_table (a, b) FROM STDIN WITH (format 'csv', append_to_shard :shardid1); +COPY append_table (a, b) FROM STDIN WITH (format 'csv', append_to_shard :shardid2); +CREATE TABLE range_table(a int, b int); +SELECT create_distributed_table('range_table', 'a', 'range'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CALL public.create_range_partitioned_shards('range_table', '{"0","25"}','{"24","49"}'); +INSERT INTO range_table VALUES (0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 50); +CREATE MATERIALIZED VIEW matview AS SELECT b*2+a AS a, a*a AS b FROM nullkey_c1_t1; +SET client_min_messages TO DEBUG2; +-- Test inserting into a distributed table by selecting from a combination of +-- different table types together with null-shard-key tables. +-- use a null-shard-key table +INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1; +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +-- use a reference table +INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN reference_table USING (a); +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 RIGHT JOIN reference_table USING (b) WHERE reference_table.a >= 1 AND reference_table.a <= 5; +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 LEFT JOIN reference_table USING (b); +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 INTERSECT SELECT * FROM reference_table; +DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +-- use a colocated null-shard-key table +INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN nullkey_c1_t2 USING (b); +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 FULL JOIN nullkey_c1_t2 USING (a); +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 FULL JOIN matview USING (a); +DEBUG: distributed INSERT ... SELECT cannot select from a local relation when inserting into a distributed table +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +INSERT INTO distributed_table_c1_t1 SELECT * FROM nullkey_c1_t1 UNION SELECT * FROM nullkey_c1_t2; +DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +-- use a non-colocated null-shard-key table +INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 LEFT JOIN nullkey_c2_t1 USING (a); +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: found no worker with all shard placements +INSERT INTO distributed_table_c1_t1 SELECT * FROM nullkey_c1_t1 UNION SELECT * FROM nullkey_c2_t1; +DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: found no worker with all shard placements +-- use a distributed table that is colocated with the target table +INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a); +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Router planner cannot handle multi-shard select queries +INSERT INTO distributed_table_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a); +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Router planner cannot handle multi-shard select queries +INSERT INTO distributed_table_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (b); +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Router planner cannot handle multi-shard select queries +INSERT INTO distributed_table_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a) WHERE distributed_table_c1_t2.a = 1; +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: Creating router plan +DEBUG: query has a single distribution column value: 1 +DEBUG: Collecting INSERT ... SELECT results on coordinator +-- use a distributed table that is not colocated with the target table +INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 JOIN distributed_table_c2_t1 USING (a); +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Router planner cannot handle multi-shard select queries +-- use a citus local table +INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN citus_local_table USING (a); +DEBUG: distributed INSERT ... SELECT cannot select from distributed tables and local tables at the same time +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +-- use a postgres local table +INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 JOIN postgres_local_table USING (a); +DEBUG: distributed INSERT ... SELECT cannot select from distributed tables and local tables at the same time +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +-- use append / range distributed tables +INSERT INTO range_table SELECT * FROM nullkey_c1_t1; +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO append_table SELECT * FROM nullkey_c1_t1; +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: INSERT ... SELECT into an append-distributed table is not supported +SELECT avg(a), avg(b) FROM distributed_table_c1_t1 ORDER BY 1, 2; +DEBUG: Router planner cannot handle multi-shard select queries + avg | avg +--------------------------------------------------------------------- + 4.2105263157894737 | 4.2105263157894737 +(1 row) + +TRUNCATE distributed_table_c1_t1; +INSERT INTO distributed_table_c1_t1 SELECT i, i FROM generate_series(3, 8) i; +DEBUG: distributed INSERT ... SELECT can only select from distributed tables +DEBUG: Collecting INSERT ... SELECT results on coordinator +-- Test inserting into a reference table by selecting from a combination of +-- different table types together with null-shard-key tables. +-- use a null-shard-key table +INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1; +DEBUG: only reference tables may be queried when targeting a reference table with distributed INSERT ... SELECT +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +-- use a reference table +INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN reference_table USING (a); +DEBUG: only reference tables may be queried when targeting a reference table with distributed INSERT ... SELECT +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO reference_table SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 LEFT JOIN reference_table USING (b); +DEBUG: only reference tables may be queried when targeting a reference table with distributed INSERT ... SELECT +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 UNION SELECT * FROM reference_table; +DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO reference_table SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 LEFT JOIN reference_table USING (b) WHERE b IN (SELECT b FROM matview); +DEBUG: only reference tables may be queried when targeting a reference table with distributed INSERT ... SELECT +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +-- use a colocated null-shard-key table +INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN nullkey_c1_t2 USING (b); +DEBUG: only reference tables may be queried when targeting a reference table with distributed INSERT ... SELECT +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 FULL JOIN nullkey_c1_t2 USING (a); +DEBUG: only reference tables may be queried when targeting a reference table with distributed INSERT ... SELECT +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +-- use a non-colocated null-shard-key table +INSERT INTO reference_table SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 LEFT JOIN nullkey_c2_t1 USING (a); +DEBUG: only reference tables may be queried when targeting a reference table with distributed INSERT ... SELECT +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: found no worker with all shard placements +-- use a distributed table +INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a); +DEBUG: only reference tables may be queried when targeting a reference table with distributed INSERT ... SELECT +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Router planner cannot handle multi-shard select queries +INSERT INTO reference_table SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a); +DEBUG: only reference tables may be queried when targeting a reference table with distributed INSERT ... SELECT +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Router planner cannot handle multi-shard select queries +INSERT INTO reference_table SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (b); +DEBUG: only reference tables may be queried when targeting a reference table with distributed INSERT ... SELECT +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Router planner cannot handle multi-shard select queries +INSERT INTO reference_table SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a) WHERE distributed_table_c1_t2.a = 1; +DEBUG: only reference tables may be queried when targeting a reference table with distributed INSERT ... SELECT +DEBUG: Creating router plan +DEBUG: query has a single distribution column value: 1 +DEBUG: Collecting INSERT ... SELECT results on coordinator +-- use a citus local table +INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN citus_local_table USING (a); +DEBUG: distributed INSERT ... SELECT cannot select from distributed tables and local tables at the same time +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +-- use a postgres local table +INSERT INTO reference_table SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 JOIN postgres_local_table USING (a); +DEBUG: distributed INSERT ... SELECT cannot select from distributed tables and local tables at the same time +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +SELECT avg(a), avg(b) FROM reference_table ORDER BY 1, 2; +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan + avg | avg +--------------------------------------------------------------------- + 4.0428571428571429 | 4.0428571428571429 +(1 row) + +TRUNCATE reference_table; +INSERT INTO reference_table SELECT i, i FROM generate_series(0, 5) i; +DEBUG: distributed INSERT ... SELECT can only select from distributed tables +DEBUG: Collecting INSERT ... SELECT results on coordinator +-- Test inserting into a citus local table by selecting from a combination of +-- different table types together with null-shard-key tables. +-- use a null-shard-key table +INSERT INTO citus_local_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1; +DEBUG: distributed INSERT ... SELECT cannot insert into a local table that is added to metadata +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +-- use a reference table +INSERT INTO citus_local_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN reference_table USING (a); +DEBUG: distributed INSERT ... SELECT cannot insert into a local table that is added to metadata +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +-- use a colocated null-shard-key table +INSERT INTO citus_local_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN nullkey_c1_t2 USING (b); +DEBUG: distributed INSERT ... SELECT cannot insert into a local table that is added to metadata +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +-- use a distributed table +INSERT INTO citus_local_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a); +DEBUG: distributed INSERT ... SELECT cannot insert into a local table that is added to metadata +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Router planner cannot handle multi-shard select queries +-- use a citus local table +INSERT INTO citus_local_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN citus_local_table USING (a); +DEBUG: distributed INSERT ... SELECT cannot select from distributed tables and local tables at the same time +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +-- use a postgres local table +INSERT INTO citus_local_table SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 JOIN postgres_local_table USING (a); +DEBUG: distributed INSERT ... SELECT cannot select from distributed tables and local tables at the same time +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +SELECT avg(a), avg(b) FROM citus_local_table ORDER BY 1, 2; +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan + avg | avg +--------------------------------------------------------------------- + 4.4333333333333333 | 4.4333333333333333 +(1 row) + +TRUNCATE citus_local_table; +INSERT INTO citus_local_table SELECT i, i FROM generate_series(0, 10) i; +DEBUG: distributed INSERT ... SELECT can only select from distributed tables +DEBUG: Collecting INSERT ... SELECT results on coordinator +-- Test inserting into a null-shard-key table by selecting from a combination of +-- different table types, together with or without null-shard-key tables. +-- use a postgres local table +INSERT INTO nullkey_c1_t1 SELECT postgres_local_table.a, postgres_local_table.b FROM postgres_local_table; +DEBUG: distributed INSERT ... SELECT can only select from distributed tables +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO nullkey_c1_t1 SELECT postgres_local_table.a, postgres_local_table.b FROM postgres_local_table JOIN reference_table USING (a); +DEBUG: distributed INSERT ... SELECT cannot select from a local table +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO nullkey_c1_t1 SELECT postgres_local_table.a, postgres_local_table.b FROM postgres_local_table LEFT JOIN nullkey_c1_t1 USING (a); +DEBUG: distributed INSERT ... SELECT cannot select from distributed tables and local tables at the same time +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +-- use a citus local table +INSERT INTO nullkey_c1_t1 SELECT citus_local_table.a, citus_local_table.b FROM citus_local_table; +DEBUG: distributed INSERT ... SELECT cannot select from a local relation when inserting into a distributed table +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO nullkey_c1_t1 SELECT citus_local_table.a, citus_local_table.b FROM citus_local_table JOIN reference_table USING (a) JOIN postgres_local_table USING (a) ORDER BY 1,2 OFFSET 7; +DEBUG: distributed INSERT ... SELECT cannot select from a local table +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO nullkey_c1_t1 SELECT citus_local_table.a, citus_local_table.b FROM citus_local_table JOIN nullkey_c1_t1 USING (a); +DEBUG: distributed INSERT ... SELECT cannot select from distributed tables and local tables at the same time +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +-- use a distributed table +INSERT INTO nullkey_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM distributed_table_c1_t2; +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO nullkey_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM distributed_table_c1_t2 JOIN reference_table USING (a); +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO nullkey_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM distributed_table_c1_t2 JOIN nullkey_c1_t1 USING (a); +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Router planner cannot handle multi-shard select queries +-- use a non-colocated null-shard-key table +INSERT INTO nullkey_c2_t1 SELECT q.* FROM (SELECT reference_table.* FROM reference_table LEFT JOIN nullkey_c1_t1 USING (a)) q JOIN nullkey_c1_t2 USING (a); +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +-- use a materialized view +INSERT INTO nullkey_c1_t1 SELECT * FROM matview; +DEBUG: distributed INSERT ... SELECT can only select from distributed tables +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO nullkey_c1_t1 SELECT reference_table.a, reference_table.b FROM reference_table JOIN matview ON (reference_table.a = matview.a); +DEBUG: distributed INSERT ... SELECT cannot select from a local relation when inserting into a distributed table +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO nullkey_c1_t1 SELECT q.* FROM (SELECT reference_table.* FROM reference_table JOIN nullkey_c1_t1 USING (a)) q JOIN matview USING (a); +DEBUG: distributed INSERT ... SELECT cannot select from a local relation when inserting into a distributed table +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +-- use append / range distributed tables +INSERT INTO nullkey_c1_t1 SELECT * FROM range_table; +DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO nullkey_c1_t1 SELECT * FROM append_table; +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: Router planner does not support append-partitioned tables. +DEBUG: Collecting INSERT ... SELECT results on coordinator +SELECT avg(a), avg(b) FROM nullkey_c1_t1 ORDER BY 1, 2; +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan + avg | avg +--------------------------------------------------------------------- + 5.8611111111111111 | 13.9305555555555556 +(1 row) + +SELECT avg(a), avg(b) FROM nullkey_c2_t1 ORDER BY 1, 2; +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan + avg | avg +--------------------------------------------------------------------- + 3.8750000000000000 | 3.8750000000000000 +(1 row) + +TRUNCATE nullkey_c1_t1, nullkey_c2_t1; +INSERT INTO nullkey_c1_t1 SELECT i, i FROM generate_series(1, 8) i; +DEBUG: distributed INSERT ... SELECT can only select from distributed tables +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO nullkey_c2_t1 SELECT i, i FROM generate_series(2, 7) i; +DEBUG: distributed INSERT ... SELECT can only select from distributed tables +DEBUG: Collecting INSERT ... SELECT results on coordinator +-- Test inserting into a local table by selecting from a combination of +-- different table types, together with or without null-shard-key tables. +INSERT INTO postgres_local_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN reference_table USING (a); +DEBUG: Creating router plan +INSERT INTO postgres_local_table SELECT * FROM nullkey_c1_t1 ORDER BY 1,2 OFFSET 3 LIMIT 2; +DEBUG: Creating router plan +WITH cte_1 AS ( + DELETE FROM nullkey_c1_t1 WHERE a >= 1 and a <= 4 RETURNING * +) +INSERT INTO postgres_local_table SELECT cte_1.* FROM cte_1 LEFT JOIN nullkey_c1_t2 USING (a) WHERE nullkey_c1_t2.a IS NULL; +DEBUG: Creating router plan +INSERT INTO postgres_local_table SELECT * FROM nullkey_c1_t1 EXCEPT SELECT * FROM postgres_local_table; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +SELECT avg(a), avg(b) FROM postgres_local_table ORDER BY 1, 2; + avg | avg +--------------------------------------------------------------------- + 5.0000000000000000 | 5.0000000000000000 +(1 row) + +TRUNCATE postgres_local_table; +INSERT INTO postgres_local_table SELECT i, i FROM generate_series(5, 10) i; +-- Try slightly more complex queries. +WITH cte_1 AS ( + SELECT nullkey_c1_t1.a, reference_table.b FROM nullkey_c1_t1 JOIN reference_table USING (a) +), +cte_2 AS ( + SELECT reference_table.a, postgres_local_table.b FROM postgres_local_table LEFT JOIN reference_table USING (b) +) +INSERT INTO distributed_table_c1_t1 +SELECT cte_1.* FROM cte_1 JOIN cte_2 USING (a) JOIN distributed_table_c1_t2 USING (a) ORDER BY 1,2; +DEBUG: cannot perform distributed INSERT INTO ... SELECT because the partition columns in the source table and subquery do not match +DETAIL: The target table's partition column should correspond to a partition column in the subquery. +DEBUG: CTE cte_1 is going to be inlined via distributed planning +DEBUG: CTE cte_2 is going to be inlined via distributed planning +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Local tables cannot be used in distributed queries. +WITH cte_1 AS ( + SELECT nullkey_c1_t1.a, reference_table.b FROM nullkey_c1_t1 JOIN reference_table USING (a) +), +cte_2 AS ( + SELECT * FROM nullkey_c1_t2 WHERE EXISTS ( + SELECT 1 FROM reference_table WHERE reference_table.a = nullkey_c1_t2.a + ) + ORDER BY 1,2 OFFSET 1 LIMIT 4 +) +INSERT INTO distributed_table_c1_t1 +SELECT * FROM cte_1 UNION SELECT * FROM cte_2 EXCEPT SELECT * FROM reference_table; +DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +DEBUG: CTE cte_1 is going to be inlined via distributed planning +DEBUG: CTE cte_2 is going to be inlined via distributed planning +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO distributed_table_c1_t1 (a, b) +SELECT t1.a, t2.b +FROM nullkey_c1_t1 t1 +JOIN ( + SELECT b FROM nullkey_c1_t2 ORDER BY b DESC LIMIT 1 +) t2 +ON t1.b < t2.b; +DEBUG: LIMIT clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO distributed_table_c1_t1 (a, b) +WITH cte AS ( + SELECT a, b, + (SELECT a FROM nullkey_c1_t2 WHERE b = t.b) AS d1, + (SELECT a FROM reference_table WHERE b = t.b) AS d2 + FROM nullkey_c1_t1 t +) +SELECT d1, COALESCE(d2, a) FROM cte WHERE d1 IS NOT NULL AND d2 IS NOT NULL; +DEBUG: CTE cte is going to be inlined via distributed planning +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO citus_local_table (a, b) +SELECT t1.a, t2.b +FROM nullkey_c1_t1 t1 +CROSS JOIN ( + SELECT b FROM nullkey_c2_t1 ORDER BY b LIMIT 1 +) t2; +DEBUG: distributed INSERT ... SELECT cannot insert into a local table that is added to metadata +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: found no worker with all shard placements +INSERT INTO distributed_table_c1_t1 (a, b) +SELECT t1.a, t2.b +FROM reference_table t1 +LEFT JOIN ( + SELECT b, ROW_NUMBER() OVER (ORDER BY b DESC) AS rn + FROM nullkey_c1_t1 +) t2 ON t1.b = t2.b +WHERE t2.rn > 0; +DEBUG: Window functions without PARTITION BY on distribution column is currently unsupported +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO nullkey_c1_t1 (a, b) +SELECT t1.a, t2.b +FROM nullkey_c1_t1 t1 +JOIN ( + SELECT rn, b + FROM ( + SELECT b, ROW_NUMBER() OVER (ORDER BY b DESC) AS rn + FROM distributed_table_c2_t1 + ) q +) t2 ON t1.b = t2.b +WHERE t2.rn > 2; +DEBUG: Window functions without PARTITION BY on distribution column is currently unsupported +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Router planner cannot handle multi-shard select queries +INSERT INTO distributed_table_c1_t1 (a, b) +SELECT t1.a, t2.b +FROM nullkey_c1_t1 t1 +JOIN ( + SELECT sum_val, b + FROM ( + SELECT b, SUM(a) OVER (PARTITION BY b) AS sum_val + FROM nullkey_c1_t1 + ) q +) t2 ON t1.b = t2.b +WHERE t2.sum_val > 2; +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +-- MultiTaskRouterSelectQuerySupported() is unnecessarily restrictive +-- about pushing down queries with DISTINCT ON clause even if the table +-- doesn't have a shard key. See https://github.com/citusdata/citus/pull/6752. +INSERT INTO nullkey_c1_t1 SELECT DISTINCT ON (a) a, b FROM nullkey_c1_t2; +DEBUG: DISTINCT ON (non-partition column) clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +-- Similarly, we could push down the following query as well. see +-- https://github.com/citusdata/citus/pull/6831. +INSERT INTO nullkey_c1_t1 SELECT b, SUM(a) OVER (ORDER BY b) AS sum_val FROM nullkey_c1_t1; +DEBUG: Window functions without PARTITION BY on distribution column is currently unsupported +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO nullkey_c2_t1 +SELECT t2.a, t2.b +FROM nullkey_c1_t1 AS t2 +JOIN reference_table AS t3 ON (t2.a = t3.a) +WHERE NOT EXISTS ( + SELECT 1 FROM nullkey_c1_t2 AS t1 WHERE t1.b = t3.b +); +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO distributed_table_c1_t1 +SELECT t1.a, t1.b +FROM nullkey_c1_t1 AS t1 +WHERE t1.a NOT IN ( + SELECT DISTINCT t2.a FROM distributed_table_c1_t2 AS t2 +); +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: Router planner cannot handle multi-shard select queries +INSERT INTO distributed_table_c1_t1 +SELECT t1.a, t1.b +FROM reference_table AS t1 +JOIN ( + SELECT t2.a FROM ( + SELECT a FROM nullkey_c1_t1 + UNION + SELECT a FROM nullkey_c1_t2 + ) AS t2 +) AS t3 ON t1.a = t3.a; +DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +-- Temporaryly reduce the verbosity to avoid noise +-- in the output of the next query. +SET client_min_messages TO DEBUG1; +INSERT INTO nullkey_c1_t1 +SELECT t1.a, t1.b +FROM reference_table AS t1 +WHERE t1.a IN ( + SELECT t2.a FROM ( + SELECT t3.a FROM ( + SELECT a FROM distributed_table_c1_t1 WHERE b > 4 + ) AS t3 + JOIN ( + SELECT a FROM distributed_table_c1_t2 WHERE b < 7 + ) AS t4 ON t3.a = t4.a + ) AS t2 +); +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: generating subplan XXX_1 for subquery SELECT a FROM (SELECT t3.a FROM ((SELECT distributed_table_c1_t1.a FROM insert_select_null_dist_key.distributed_table_c1_t1 WHERE (distributed_table_c1_t1.b OPERATOR(pg_catalog.>) 4)) t3 JOIN (SELECT distributed_table_c1_t2.a FROM insert_select_null_dist_key.distributed_table_c1_t2 WHERE (distributed_table_c1_t2.b OPERATOR(pg_catalog.<) 7)) t4 ON ((t3.a OPERATOR(pg_catalog.=) t4.a)))) t2 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT a, b FROM insert_select_null_dist_key.reference_table t1 WHERE (a OPERATOR(pg_catalog.=) ANY (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer))) +DEBUG: Collecting INSERT ... SELECT results on coordinator +SET client_min_messages TO DEBUG2; +-- test upsert with plain INSERT query +CREATE TABLE upsert_test_1 +( + unique_col int UNIQUE, + other_col int, + third_col int +); +DEBUG: CREATE TABLE / UNIQUE will create implicit index "upsert_test_1_unique_col_key" for table "upsert_test_1" +SELECT create_distributed_table('upsert_test_1', null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE upsert_test_2(key int primary key, value text); +DEBUG: CREATE TABLE / PRIMARY KEY will create implicit index "upsert_test_2_pkey" for table "upsert_test_2" +SELECT create_distributed_table('upsert_test_2', null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO upsert_test_2 AS upsert_test_2_alias (key, value) VALUES (1, '5') ON CONFLICT(key) + DO UPDATE SET value = (upsert_test_2_alias.value::int * 2)::text; +DEBUG: Creating router plan +INSERT INTO upsert_test_2 (key, value) VALUES (1, '5') ON CONFLICT(key) + DO UPDATE SET value = (upsert_test_2.value::int * 3)::text; +DEBUG: Creating router plan +INSERT INTO upsert_test_1 (unique_col, other_col) VALUES (1, 1) ON CONFLICT (unique_col) + DO UPDATE SET other_col = (SELECT count(*) from upsert_test_1); +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: subqueries are not supported within INSERT queries +HINT: Try rewriting your queries with 'INSERT INTO ... SELECT' syntax. +INSERT INTO upsert_test_1 (unique_col, other_col) VALUES (1, 1) ON CONFLICT (unique_col) + DO UPDATE SET other_col = random()::int; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: functions used in the DO UPDATE SET clause of INSERTs on distributed tables must be marked IMMUTABLE +INSERT INTO upsert_test_1 (unique_col, other_col) VALUES (1, 1) ON CONFLICT (unique_col) + DO UPDATE SET other_col = 5 WHERE upsert_test_1.other_col = random()::int; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: functions used in the WHERE clause of the ON CONFLICT clause of INSERTs on distributed tables must be marked IMMUTABLE +INSERT INTO upsert_test_1 VALUES (3, 5, 7); +DEBUG: Creating router plan +INSERT INTO upsert_test_1 (unique_col, other_col) VALUES (1, 1) ON CONFLICT (unique_col) WHERE unique_col = random()::int + DO UPDATE SET other_col = 5; +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: functions used in the WHERE clause of the ON CONFLICT clause of INSERTs on distributed tables must be marked IMMUTABLE +CREATE TABLE upsert_test_3 (key_1 int, key_2 bigserial, value text DEFAULT 'default_value', PRIMARY KEY (key_1, key_2)); +DEBUG: CREATE TABLE will create implicit sequence "upsert_test_3_key_2_seq" for serial column "upsert_test_3.key_2" +DEBUG: CREATE TABLE / PRIMARY KEY will create implicit index "upsert_test_3_pkey" for table "upsert_test_3" +SELECT create_distributed_table('upsert_test_3', null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO upsert_test_3 VALUES (1, DEFAULT, '1') RETURNING *; +DEBUG: Creating router plan + key_1 | key_2 | value +--------------------------------------------------------------------- + 1 | 1 | 1 +(1 row) + +INSERT INTO upsert_test_3 VALUES (5, DEFAULT, DEFAULT) RETURNING *; +DEBUG: Creating router plan + key_1 | key_2 | value +--------------------------------------------------------------------- + 5 | 2 | default_value +(1 row) + +SET client_min_messages TO DEBUG1; +INSERT INTO upsert_test_3 SELECT 7, other_col, 'harcoded_text_value' FROM upsert_test_1 RETURNING *; + key_1 | key_2 | value +--------------------------------------------------------------------- + 7 | 5 | harcoded_text_value +(1 row) + +SET client_min_messages TO DEBUG2; +-- test upsert with INSERT .. SELECT queries +SET client_min_messages TO DEBUG1; +INSERT INTO upsert_test_1 (unique_col, other_col) SELECT unique_col, other_col FROM upsert_test_1 ON CONFLICT (unique_col) + DO UPDATE SET other_col = upsert_test_1.other_col + 1; +-- Fails due to https://github.com/citusdata/citus/issues/6826. +INSERT INTO upsert_test_1 (unique_col, other_col) SELECT unique_col, other_col FROM upsert_test_1 ON CONFLICT (unique_col) + DO UPDATE SET other_col = (SELECT count(*) from upsert_test_1); +ERROR: cannot execute a distributed query from a query on a shard +DETAIL: Executing a distributed query in a function call that may be pushed to a remote node can lead to incorrect results. +HINT: Avoid nesting of distributed queries or use alter user current_user set citus.allow_nested_distributed_execution to on to allow it with possible incorrectness. +CONTEXT: while executing command on localhost:xxxxx +SET client_min_messages TO DEBUG2; +INSERT INTO upsert_test_1 (unique_col, other_col) SELECT unique_col, other_col FROM upsert_test_1 ON CONFLICT (unique_col) + DO UPDATE SET other_col = random()::int; +ERROR: functions used in the DO UPDATE SET clause of INSERTs on distributed tables must be marked IMMUTABLE +INSERT INTO upsert_test_1 (unique_col, other_col) SELECT unique_col, other_col FROM upsert_test_1 ON CONFLICT (unique_col) + DO UPDATE SET other_col = 5 WHERE upsert_test_1.other_col = random()::int; +ERROR: functions used in the WHERE clause of the ON CONFLICT clause of INSERTs on distributed tables must be marked IMMUTABLE +SELECT reload_tables(); +DEBUG: function does not have co-located tables + reload_tables +--------------------------------------------------------------------- + +(1 row) + +ALTER TABLE nullkey_c1_t1 ADD PRIMARY KEY (a); +DEBUG: ALTER TABLE / ADD PRIMARY KEY will create implicit index "nullkey_c1_t1_pkey" for table "nullkey_c1_t1" +DEBUG: verifying table "nullkey_c1_t1" +ALTER TABLE distributed_table_c1_t1 ADD PRIMARY KEY (a,b); +DEBUG: ALTER TABLE / ADD PRIMARY KEY will create implicit index "distributed_table_c1_t1_pkey" for table "distributed_table_c1_t1" +DEBUG: verifying table "distributed_table_c1_t1" +INSERT INTO nullkey_c1_t1 AS t1 (a, b) SELECT t3.a, t3.b FROM nullkey_c1_t2 t2 JOIN reference_table t3 ON (t2.a = t3.a) ON CONFLICT (a) + DO UPDATE SET a = t1.a + 10; +DEBUG: distributed statement: INSERT INTO insert_select_null_dist_key.nullkey_c1_t1_1820000 AS t1 (a, b) SELECT t3.a, t3.b FROM (insert_select_null_dist_key.nullkey_c1_t2_1820001 t2 JOIN insert_select_null_dist_key.reference_table_1820003 t3 ON ((t2.a OPERATOR(pg_catalog.=) t3.a))) ON CONFLICT(a) DO UPDATE SET a = (t1.a OPERATOR(pg_catalog.+) 10) +SET client_min_messages TO DEBUG1; +INSERT INTO distributed_table_c1_t1 AS t1 (a, b) SELECT t3.a, t3.b FROM nullkey_c1_t2 t2 JOIN reference_table t3 ON (t2.a = t3.a) ON CONFLICT (a, b) + DO UPDATE SET b = t1.b + 10; +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO nullkey_c1_t1 AS t1 (a, b) SELECT t3.a, t3.b FROM distributed_table_c1_t1 t2 JOIN reference_table t3 ON (t2.a = t3.a) ON CONFLICT (a) + DO UPDATE SET a = t1.a + 10; +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: Collecting INSERT ... SELECT results on coordinator +-- This also fails due to https://github.com/citusdata/citus/issues/6826. +INSERT INTO nullkey_c1_t1 AS t1 (a, b) SELECT t3.a, t3.b FROM distributed_table_c1_t1 t2 JOIN reference_table t3 ON (t2.a = t3.a) WHERE t2.a = 3 ON CONFLICT (a) + DO UPDATE SET a = (SELECT max(b)+1 FROM distributed_table_c1_t1 WHERE a = 3); +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: Collecting INSERT ... SELECT results on coordinator +ERROR: cannot execute a distributed query from a query on a shard +DETAIL: Executing a distributed query in a function call that may be pushed to a remote node can lead to incorrect results. +HINT: Avoid nesting of distributed queries or use alter user current_user set citus.allow_nested_distributed_execution to on to allow it with possible incorrectness. +CONTEXT: while executing command on localhost:xxxxx +SET client_min_messages TO DEBUG2; +SELECT avg(a), avg(b) FROM distributed_table_c1_t1; +DEBUG: Router planner cannot handle multi-shard select queries + avg | avg +--------------------------------------------------------------------- + 5.0000000000000000 | 9.2857142857142857 +(1 row) + +SELECT avg(a), avg(b) FROM nullkey_c1_t1; +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan + avg | avg +--------------------------------------------------------------------- + 7.5000000000000000 | 4.1666666666666667 +(1 row) + +SELECT avg(a), avg(b) FROM nullkey_c1_t2; +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan + avg | avg +--------------------------------------------------------------------- + 4.5000000000000000 | 4.5000000000000000 +(1 row) + +SELECT * FROM upsert_test_1 ORDER BY unique_col; +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan + unique_col | other_col | third_col +--------------------------------------------------------------------- + 3 | 6 | 7 +(1 row) + +SELECT * FROM upsert_test_2 ORDER BY key; +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan + key | value +--------------------------------------------------------------------- + 1 | 15 +(1 row) + +SELECT * FROM upsert_test_3 ORDER BY key_1, key_2; +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan + key_1 | key_2 | value +--------------------------------------------------------------------- + 1 | 1 | 1 + 5 | 2 | default_value + 7 | 5 | harcoded_text_value +(3 rows) + +SET client_min_messages TO WARNING; +DROP SCHEMA insert_select_null_dist_key CASCADE; +SELECT citus_remove_node('localhost', :master_port); + citus_remove_node +--------------------------------------------------------------------- + +(1 row) + diff --git a/src/test/regress/expected/query_null_dist_key.out b/src/test/regress/expected/query_null_dist_key.out index 09413a3ea..09907a99b 100644 --- a/src/test/regress/expected/query_null_dist_key.out +++ b/src/test/regress/expected/query_null_dist_key.out @@ -827,24 +827,55 @@ INSERT INTO nullkey_c1_t1 SELECT * FROM nullkey_c1_t2; SET client_min_messages TO DEBUG2; -- between two non-colocated null dist key tables INSERT INTO nullkey_c1_t1 SELECT * FROM nullkey_c2_t1; -ERROR: cannot select from a non-colocated distributed table when inserting into a distributed table that does not have a shard key +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator -- between a null dist key table and a table of different type +SET client_min_messages TO WARNING; +EXPLAIN (ANALYZE TRUE, TIMING FALSE, COSTS FALSE, SUMMARY FALSE, VERBOSE FALSE) INSERT INTO nullkey_c1_t1 SELECT * FROM reference_table; -ERROR: cannot select from different table types when inserting into a distributed table that does not have a shard key + QUERY PLAN +--------------------------------------------------------------------- + Custom Scan (Citus Adaptive) (actual rows=0 loops=1) + Task Count: 1 + Tasks Shown: All + -> Task + Node: host=localhost port=xxxxx dbname=regression + -> Insert on nullkey_c1_t1_1620000 citus_table_alias (actual rows=0 loops=1) + -> Seq Scan on reference_table_1620005 reference_table (actual rows=6 loops=1) +(7 rows) + +SET client_min_messages TO DEBUG2; INSERT INTO nullkey_c1_t1 SELECT * FROM distributed_table; -ERROR: cannot select from different table types when inserting into a distributed table that does not have a shard key +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO nullkey_c1_t1 SELECT * FROM citus_local_table; -ERROR: cannot select from different table types when inserting into a distributed table that does not have a shard key +DEBUG: distributed INSERT ... SELECT cannot select from a local relation when inserting into a distributed table +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO nullkey_c1_t1 SELECT * FROM postgres_local_table; -ERROR: cannot select from different table types when inserting into a distributed table that does not have a shard key +DEBUG: distributed INSERT ... SELECT can only select from distributed tables +DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO reference_table SELECT * FROM nullkey_c1_t1; -ERROR: cannot select from a distributed table that does not have a shard key when inserting into a different table type +DEBUG: only reference tables may be queried when targeting a reference table with distributed INSERT ... SELECT +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO distributed_table SELECT * FROM nullkey_c1_t1; -ERROR: cannot select from a distributed table that does not have a shard key when inserting into a different table type +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO citus_local_table SELECT * FROM nullkey_c1_t1; -ERROR: cannot select from a distributed table that does not have a shard key when inserting into a different table type +DEBUG: distributed INSERT ... SELECT cannot insert into a local table that is added to metadata +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO postgres_local_table SELECT * FROM nullkey_c1_t1; -ERROR: cannot select from a distributed table that does not have a shard key when inserting into a local table +DEBUG: Creating router plan -- test subquery SELECT count(*) FROM ( @@ -873,7 +904,7 @@ WITH level_0 AS ( WITH RECURSIVE level_2_recursive(x) AS ( VALUES (1) UNION ALL - SELECT a + 1 FROM nullkey_c1_t1 JOIN level_2_recursive ON (a = x) WHERE a < 100 + SELECT a + 1 FROM nullkey_c1_t1 JOIN level_2_recursive ON (a = x) WHERE a < 2 ) SELECT * FROM level_2_recursive RIGHT JOIN reference_table ON (level_2_recursive.x = reference_table.a) ) @@ -885,7 +916,7 @@ DEBUG: CTE level_1 is going to be inlined via distributed planning DEBUG: Creating router plan count --------------------------------------------------------------------- - 122 + 106 (1 row) WITH level_0 AS ( @@ -1095,6 +1126,9 @@ INSERT INTO bigserial_test (x, y) SELECT x, y FROM bigserial_test; DEBUG: volatile functions are not allowed in distributed INSERT ... SELECT queries ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables DETAIL: Sequences cannot be used in router queries +INSERT INTO bigserial_test (x, y) SELECT a, a FROM reference_table; +DEBUG: volatile functions are not allowed in distributed INSERT ... SELECT queries +DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO agg_events (user_id) SELECT f2.id FROM @@ -1119,7 +1153,6 @@ FROM (SELECT SUM(raw_events_second.value_4) AS v4, ON (f.id = f2.id) WHERE f.id IN (SELECT user_id FROM raw_events_second); -ERROR: cannot select from different table types when inserting into a distributed table that does not have a shard key -- upsert with returning INSERT INTO agg_events AS ae ( @@ -1153,6 +1186,17 @@ SELECT FROM raw_events_first LEFT JOIN raw_events_second ON raw_events_first.user_id = raw_events_second.user_id WHERE raw_events_second.user_id = 10 OR raw_events_second.user_id = 11; +INSERT INTO agg_events (user_id) +SELECT + users_ref_table.user_id +FROM + users_ref_table LEFT JOIN raw_events_second ON users_ref_table.user_id = raw_events_second.user_id + WHERE raw_events_second.user_id = 10 OR raw_events_second.user_id = 11; +INSERT INTO agg_events (user_id) +SELECT COALESCE(raw_events_first.user_id, users_ref_table.user_id) +FROM raw_events_first + RIGHT JOIN (users_ref_table LEFT JOIN raw_events_second ON users_ref_table.user_id = raw_events_second.user_id) + ON raw_events_first.user_id = users_ref_table.user_id; -- using a full join INSERT INTO agg_events (user_id, value_1_agg) SELECT t1.user_id AS col1, @@ -1176,11 +1220,22 @@ FROM raw_events_first WHERE NOT EXISTS (SELECT 1 FROM raw_events_second WHERE raw_events_second.user_id =raw_events_first.user_id); +INSERT INTO raw_events_second + (user_id) +SELECT user_id +FROM users_ref_table +WHERE NOT EXISTS (SELECT 1 + FROM raw_events_second + WHERE raw_events_second.user_id = users_ref_table.user_id); -- using inner join INSERT INTO agg_events (user_id) SELECT raw_events_first.user_id FROM raw_events_first INNER JOIN raw_events_second ON raw_events_first.user_id = raw_events_second.value_1 WHERE raw_events_first.value_1 IN (10, 11,12) OR raw_events_second.user_id IN (1,2,3,4); +INSERT INTO agg_events (user_id) +SELECT raw_events_first.user_id +FROM raw_events_first INNER JOIN users_ref_table ON raw_events_first.user_id = users_ref_table.user_id +WHERE raw_events_first.value_1 IN (10, 11,12) OR users_ref_table.user_id IN (1,2,3,4); -- We could relax distributed insert .. select checks to allow pushing -- down more clauses down to the worker nodes when inserting into a single -- shard by selecting from a colocated one. We might want to do something @@ -1197,6 +1252,9 @@ DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO agg_events (user_id) SELECT raw_events_first.user_id FROM raw_events_first OFFSET 1; DEBUG: OFFSET clauses are not allowed in distributed INSERT ... SELECT queries DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO agg_events (user_id) SELECT users_ref_table.user_id FROM users_ref_table LIMIT 1; +DEBUG: LIMIT clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: Collecting INSERT ... SELECT results on coordinator -- using a materialized cte WITH cte AS MATERIALIZED (SELECT max(value_1)+1 as v1_agg, user_id FROM raw_events_first GROUP BY user_id) @@ -1207,6 +1265,9 @@ DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO raw_events_second WITH cte AS MATERIALIZED (SELECT * FROM raw_events_first) SELECT user_id * 1000, time, value_1, value_2, value_3, value_4 FROM cte; +INSERT INTO raw_events_second (user_id) + WITH cte AS MATERIALIZED (SELECT * FROM users_ref_table) + SELECT user_id FROM cte; -- using a regular cte WITH cte AS (SELECT * FROM raw_events_first) INSERT INTO raw_events_second @@ -1229,7 +1290,7 @@ DEBUG: Subqueries without relations are not allowed in distributed INSERT ... S DEBUG: Collecting INSERT ... SELECT results on coordinator -- we still support complex joins via INSERT's cte list .. WITH cte AS ( - SELECT reference_table.a AS a, 1 AS b + SELECT DISTINCT(reference_table.a) AS a, 1 AS b FROM distributed_table RIGHT JOIN reference_table USING (a) ) INSERT INTO raw_events_second (user_id, value_1) @@ -1240,17 +1301,23 @@ DEBUG: recursively planning left side of the right join since the outer side is DEBUG: recursively planning distributed relation "distributed_table" since it is part of a distributed join node that is outer joined with a recurring rel DEBUG: Wrapping relation "distributed_table" to a subquery DEBUG: generating subplan XXX_1 for subquery SELECT a FROM query_null_dist_key.distributed_table WHERE true -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT "?column?" AS user_id, b AS value_1 FROM (SELECT ((cte.a OPERATOR(pg_catalog.+) 5) OPERATOR(pg_catalog.*) '-1'::integer), cte.b FROM (SELECT reference_table.a, 1 AS b FROM ((SELECT distributed_table_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) distributed_table_1) distributed_table RIGHT JOIN query_null_dist_key.reference_table USING (a))) cte) citus_insert_select_subquery("?column?", b) +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT "?column?" AS user_id, b AS value_1 FROM (SELECT ((cte.a OPERATOR(pg_catalog.+) 5) OPERATOR(pg_catalog.*) '-1'::integer), cte.b FROM (SELECT DISTINCT reference_table.a, 1 AS b FROM ((SELECT distributed_table_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) distributed_table_1) distributed_table RIGHT JOIN query_null_dist_key.reference_table USING (a))) cte) citus_insert_select_subquery("?column?", b) DEBUG: Collecting INSERT ... SELECT results on coordinator --- .. but can't do so via via SELECT's cte list +-- .. and via SELECT's cte list too INSERT INTO raw_events_second (user_id, value_1) WITH cte AS ( - SELECT reference_table.a AS a, 1 AS b + SELECT DISTINCT(reference_table.a) AS a, 1 AS b FROM distributed_table RIGHT JOIN reference_table USING (a) ) - SELECT (a+5)*-1, b FROM cte; + SELECT (a+5)*2, b FROM cte; DEBUG: CTE cte is going to be inlined via distributed planning -ERROR: cannot select from different table types when inserting into a distributed table that does not have a shard key +DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: recursively planning left side of the right join since the outer side is a recurring rel +DEBUG: recursively planning distributed relation "distributed_table" since it is part of a distributed join node that is outer joined with a recurring rel +DEBUG: Wrapping relation "distributed_table" to a subquery +DEBUG: generating subplan XXX_1 for subquery SELECT a FROM query_null_dist_key.distributed_table WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT ((a OPERATOR(pg_catalog.+) 5) OPERATOR(pg_catalog.*) 2) AS user_id, b AS value_1 FROM (SELECT DISTINCT reference_table.a, 1 AS b FROM ((SELECT distributed_table_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) distributed_table_1) distributed_table RIGHT JOIN query_null_dist_key.reference_table USING (a))) cte +DEBUG: Collecting INSERT ... SELECT results on coordinator -- using set operations INSERT INTO raw_events_first(user_id) @@ -1258,6 +1325,12 @@ INSERT INTO (SELECT user_id FROM raw_events_first); DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO + raw_events_first(user_id) + (SELECT user_id FROM users_ref_table) INTERSECT + (SELECT user_id FROM raw_events_first); +DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +DEBUG: Collecting INSERT ... SELECT results on coordinator -- group by clause inside subquery INSERT INTO agg_events (user_id) @@ -1313,6 +1386,10 @@ SELECT SUM(value_3), Avg(value_2) FROM raw_events_first GROUP BY user_id; +INSERT INTO agg_events (value_3_agg, value_1_agg) +SELECT AVG(user_id), SUM(user_id) +FROM users_ref_table +GROUP BY user_id; -- using generate_series INSERT INTO raw_events_first (user_id, value_1, value_2) SELECT s, s, s FROM generate_series(1, 5) s; diff --git a/src/test/regress/multi_1_schedule b/src/test/regress/multi_1_schedule index 45adf469e..9163a5864 100644 --- a/src/test/regress/multi_1_schedule +++ b/src/test/regress/multi_1_schedule @@ -201,6 +201,7 @@ test: local_dist_join_mixed test: citus_local_dist_joins test: recurring_outer_join test: query_null_dist_key +test: insert_select_null_dist_key test: pg_dump # --------- diff --git a/src/test/regress/sql/insert_select_null_dist_key.sql b/src/test/regress/sql/insert_select_null_dist_key.sql new file mode 100644 index 000000000..29454b0c1 --- /dev/null +++ b/src/test/regress/sql/insert_select_null_dist_key.sql @@ -0,0 +1,470 @@ +CREATE SCHEMA insert_select_null_dist_key; +SET search_path TO insert_select_null_dist_key; + +SET citus.next_shard_id TO 1820000; +SET citus.shard_count TO 32; + +SET client_min_messages TO WARNING; +SELECT 1 FROM citus_add_node('localhost', :master_port, groupid => 0); + +SET client_min_messages TO NOTICE; + +CREATE TABLE nullkey_c1_t1(a int, b int); +CREATE TABLE nullkey_c1_t2(a int, b int); +SELECT create_distributed_table('nullkey_c1_t1', null, colocate_with=>'none'); +SELECT create_distributed_table('nullkey_c1_t2', null, colocate_with=>'nullkey_c1_t1'); + +CREATE TABLE nullkey_c2_t1(a int, b int); +SELECT create_distributed_table('nullkey_c2_t1', null, colocate_with=>'none'); + +CREATE TABLE reference_table(a int, b int); +SELECT create_reference_table('reference_table'); + +CREATE TABLE distributed_table_c1_t1(a int, b int); +SELECT create_distributed_table('distributed_table_c1_t1', 'a'); + +CREATE TABLE distributed_table_c1_t2(a int, b int); +SELECT create_distributed_table('distributed_table_c1_t2', 'a'); + +CREATE TABLE distributed_table_c2_t1(a int, b int); +SELECT create_distributed_table('distributed_table_c2_t1', 'a', colocate_with=>'none'); + +CREATE TABLE citus_local_table(a int, b int); +SELECT citus_add_local_table_to_metadata('citus_local_table'); + +CREATE TABLE postgres_local_table(a int, b int); + +CREATE FUNCTION reload_tables() RETURNS void AS $$ + BEGIN + SET LOCAL client_min_messages TO WARNING; + + TRUNCATE nullkey_c1_t1, nullkey_c1_t2, nullkey_c2_t1, reference_table, distributed_table_c1_t1, + distributed_table_c1_t2, distributed_table_c2_t1, citus_local_table, postgres_local_table; + + INSERT INTO nullkey_c1_t1 SELECT i, i FROM generate_series(1, 8) i; + INSERT INTO nullkey_c1_t2 SELECT i, i FROM generate_series(2, 7) i; + INSERT INTO nullkey_c2_t1 SELECT i, i FROM generate_series(2, 7) i; + INSERT INTO reference_table SELECT i, i FROM generate_series(0, 5) i; + INSERT INTO distributed_table_c1_t1 SELECT i, i FROM generate_series(3, 8) i; + INSERT INTO distributed_table_c1_t2 SELECT i, i FROM generate_series(2, 9) i; + INSERT INTO distributed_table_c2_t1 SELECT i, i FROM generate_series(5, 10) i; + INSERT INTO citus_local_table SELECT i, i FROM generate_series(0, 10) i; + INSERT INTO postgres_local_table SELECT i, i FROM generate_series(5, 10) i; + END; +$$ LANGUAGE plpgsql; + +SELECT reload_tables(); + +CREATE TABLE append_table (a int, b int); +SELECT create_distributed_table('append_table', 'a', 'append'); +SELECT master_create_empty_shard('append_table') AS shardid1 \gset +SELECT master_create_empty_shard('append_table') AS shardid2 \gset +SELECT master_create_empty_shard('append_table') AS shardid3 \gset + +COPY append_table (a, b) FROM STDIN WITH (format 'csv', append_to_shard :shardid1); +1, 40 +2, 42 +3, 44 +4, 46 +5, 48 +\. + +COPY append_table (a, b) FROM STDIN WITH (format 'csv', append_to_shard :shardid2); +6, 50 +7, 52 +8, 54 +9, 56 +10, 58 +\. + +CREATE TABLE range_table(a int, b int); +SELECT create_distributed_table('range_table', 'a', 'range'); +CALL public.create_range_partitioned_shards('range_table', '{"0","25"}','{"24","49"}'); +INSERT INTO range_table VALUES (0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 50); + +CREATE MATERIALIZED VIEW matview AS SELECT b*2+a AS a, a*a AS b FROM nullkey_c1_t1; + +SET client_min_messages TO DEBUG2; + +-- Test inserting into a distributed table by selecting from a combination of +-- different table types together with null-shard-key tables. + +-- use a null-shard-key table +INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1; + +-- use a reference table +INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN reference_table USING (a); +INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 RIGHT JOIN reference_table USING (b) WHERE reference_table.a >= 1 AND reference_table.a <= 5; +INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 LEFT JOIN reference_table USING (b); +INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 INTERSECT SELECT * FROM reference_table; + +-- use a colocated null-shard-key table +INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN nullkey_c1_t2 USING (b); +INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 FULL JOIN nullkey_c1_t2 USING (a); +INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 FULL JOIN matview USING (a); +INSERT INTO distributed_table_c1_t1 SELECT * FROM nullkey_c1_t1 UNION SELECT * FROM nullkey_c1_t2; + +-- use a non-colocated null-shard-key table +INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 LEFT JOIN nullkey_c2_t1 USING (a); +INSERT INTO distributed_table_c1_t1 SELECT * FROM nullkey_c1_t1 UNION SELECT * FROM nullkey_c2_t1; + +-- use a distributed table that is colocated with the target table +INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a); +INSERT INTO distributed_table_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a); +INSERT INTO distributed_table_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (b); +INSERT INTO distributed_table_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a) WHERE distributed_table_c1_t2.a = 1; + +-- use a distributed table that is not colocated with the target table +INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 JOIN distributed_table_c2_t1 USING (a); + +-- use a citus local table +INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN citus_local_table USING (a); + +-- use a postgres local table +INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 JOIN postgres_local_table USING (a); + +-- use append / range distributed tables +INSERT INTO range_table SELECT * FROM nullkey_c1_t1; +INSERT INTO append_table SELECT * FROM nullkey_c1_t1; + +SELECT avg(a), avg(b) FROM distributed_table_c1_t1 ORDER BY 1, 2; +TRUNCATE distributed_table_c1_t1; +INSERT INTO distributed_table_c1_t1 SELECT i, i FROM generate_series(3, 8) i; + +-- Test inserting into a reference table by selecting from a combination of +-- different table types together with null-shard-key tables. + +-- use a null-shard-key table +INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1; + +-- use a reference table +INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN reference_table USING (a); +INSERT INTO reference_table SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 LEFT JOIN reference_table USING (b); +INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 UNION SELECT * FROM reference_table; +INSERT INTO reference_table SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 LEFT JOIN reference_table USING (b) WHERE b IN (SELECT b FROM matview); + +-- use a colocated null-shard-key table +INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN nullkey_c1_t2 USING (b); +INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 FULL JOIN nullkey_c1_t2 USING (a); + +-- use a non-colocated null-shard-key table +INSERT INTO reference_table SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 LEFT JOIN nullkey_c2_t1 USING (a); + +-- use a distributed table +INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a); +INSERT INTO reference_table SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a); +INSERT INTO reference_table SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (b); +INSERT INTO reference_table SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a) WHERE distributed_table_c1_t2.a = 1; + +-- use a citus local table +INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN citus_local_table USING (a); + +-- use a postgres local table +INSERT INTO reference_table SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 JOIN postgres_local_table USING (a); + +SELECT avg(a), avg(b) FROM reference_table ORDER BY 1, 2; +TRUNCATE reference_table; +INSERT INTO reference_table SELECT i, i FROM generate_series(0, 5) i; + +-- Test inserting into a citus local table by selecting from a combination of +-- different table types together with null-shard-key tables. + +-- use a null-shard-key table +INSERT INTO citus_local_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1; + +-- use a reference table +INSERT INTO citus_local_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN reference_table USING (a); + +-- use a colocated null-shard-key table +INSERT INTO citus_local_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN nullkey_c1_t2 USING (b); + +-- use a distributed table +INSERT INTO citus_local_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a); + +-- use a citus local table +INSERT INTO citus_local_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN citus_local_table USING (a); + +-- use a postgres local table +INSERT INTO citus_local_table SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 JOIN postgres_local_table USING (a); + +SELECT avg(a), avg(b) FROM citus_local_table ORDER BY 1, 2; +TRUNCATE citus_local_table; +INSERT INTO citus_local_table SELECT i, i FROM generate_series(0, 10) i; + +-- Test inserting into a null-shard-key table by selecting from a combination of +-- different table types, together with or without null-shard-key tables. + +-- use a postgres local table +INSERT INTO nullkey_c1_t1 SELECT postgres_local_table.a, postgres_local_table.b FROM postgres_local_table; +INSERT INTO nullkey_c1_t1 SELECT postgres_local_table.a, postgres_local_table.b FROM postgres_local_table JOIN reference_table USING (a); +INSERT INTO nullkey_c1_t1 SELECT postgres_local_table.a, postgres_local_table.b FROM postgres_local_table LEFT JOIN nullkey_c1_t1 USING (a); + +-- use a citus local table +INSERT INTO nullkey_c1_t1 SELECT citus_local_table.a, citus_local_table.b FROM citus_local_table; +INSERT INTO nullkey_c1_t1 SELECT citus_local_table.a, citus_local_table.b FROM citus_local_table JOIN reference_table USING (a) JOIN postgres_local_table USING (a) ORDER BY 1,2 OFFSET 7; +INSERT INTO nullkey_c1_t1 SELECT citus_local_table.a, citus_local_table.b FROM citus_local_table JOIN nullkey_c1_t1 USING (a); + +-- use a distributed table +INSERT INTO nullkey_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM distributed_table_c1_t2; +INSERT INTO nullkey_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM distributed_table_c1_t2 JOIN reference_table USING (a); +INSERT INTO nullkey_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM distributed_table_c1_t2 JOIN nullkey_c1_t1 USING (a); + +-- use a non-colocated null-shard-key table +INSERT INTO nullkey_c2_t1 SELECT q.* FROM (SELECT reference_table.* FROM reference_table LEFT JOIN nullkey_c1_t1 USING (a)) q JOIN nullkey_c1_t2 USING (a); + +-- use a materialized view +INSERT INTO nullkey_c1_t1 SELECT * FROM matview; +INSERT INTO nullkey_c1_t1 SELECT reference_table.a, reference_table.b FROM reference_table JOIN matview ON (reference_table.a = matview.a); +INSERT INTO nullkey_c1_t1 SELECT q.* FROM (SELECT reference_table.* FROM reference_table JOIN nullkey_c1_t1 USING (a)) q JOIN matview USING (a); + +-- use append / range distributed tables +INSERT INTO nullkey_c1_t1 SELECT * FROM range_table; +INSERT INTO nullkey_c1_t1 SELECT * FROM append_table; + +SELECT avg(a), avg(b) FROM nullkey_c1_t1 ORDER BY 1, 2; +SELECT avg(a), avg(b) FROM nullkey_c2_t1 ORDER BY 1, 2; +TRUNCATE nullkey_c1_t1, nullkey_c2_t1; +INSERT INTO nullkey_c1_t1 SELECT i, i FROM generate_series(1, 8) i; +INSERT INTO nullkey_c2_t1 SELECT i, i FROM generate_series(2, 7) i; + +-- Test inserting into a local table by selecting from a combination of +-- different table types, together with or without null-shard-key tables. + +INSERT INTO postgres_local_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN reference_table USING (a); + +INSERT INTO postgres_local_table SELECT * FROM nullkey_c1_t1 ORDER BY 1,2 OFFSET 3 LIMIT 2; + +WITH cte_1 AS ( + DELETE FROM nullkey_c1_t1 WHERE a >= 1 and a <= 4 RETURNING * +) +INSERT INTO postgres_local_table SELECT cte_1.* FROM cte_1 LEFT JOIN nullkey_c1_t2 USING (a) WHERE nullkey_c1_t2.a IS NULL; + +INSERT INTO postgres_local_table SELECT * FROM nullkey_c1_t1 EXCEPT SELECT * FROM postgres_local_table; + +SELECT avg(a), avg(b) FROM postgres_local_table ORDER BY 1, 2; +TRUNCATE postgres_local_table; +INSERT INTO postgres_local_table SELECT i, i FROM generate_series(5, 10) i; + +-- Try slightly more complex queries. + +WITH cte_1 AS ( + SELECT nullkey_c1_t1.a, reference_table.b FROM nullkey_c1_t1 JOIN reference_table USING (a) +), +cte_2 AS ( + SELECT reference_table.a, postgres_local_table.b FROM postgres_local_table LEFT JOIN reference_table USING (b) +) +INSERT INTO distributed_table_c1_t1 +SELECT cte_1.* FROM cte_1 JOIN cte_2 USING (a) JOIN distributed_table_c1_t2 USING (a) ORDER BY 1,2; + +WITH cte_1 AS ( + SELECT nullkey_c1_t1.a, reference_table.b FROM nullkey_c1_t1 JOIN reference_table USING (a) +), +cte_2 AS ( + SELECT * FROM nullkey_c1_t2 WHERE EXISTS ( + SELECT 1 FROM reference_table WHERE reference_table.a = nullkey_c1_t2.a + ) + ORDER BY 1,2 OFFSET 1 LIMIT 4 +) +INSERT INTO distributed_table_c1_t1 +SELECT * FROM cte_1 UNION SELECT * FROM cte_2 EXCEPT SELECT * FROM reference_table; + +INSERT INTO distributed_table_c1_t1 (a, b) +SELECT t1.a, t2.b +FROM nullkey_c1_t1 t1 +JOIN ( + SELECT b FROM nullkey_c1_t2 ORDER BY b DESC LIMIT 1 +) t2 +ON t1.b < t2.b; + +INSERT INTO distributed_table_c1_t1 (a, b) +WITH cte AS ( + SELECT a, b, + (SELECT a FROM nullkey_c1_t2 WHERE b = t.b) AS d1, + (SELECT a FROM reference_table WHERE b = t.b) AS d2 + FROM nullkey_c1_t1 t +) +SELECT d1, COALESCE(d2, a) FROM cte WHERE d1 IS NOT NULL AND d2 IS NOT NULL; + +INSERT INTO citus_local_table (a, b) +SELECT t1.a, t2.b +FROM nullkey_c1_t1 t1 +CROSS JOIN ( + SELECT b FROM nullkey_c2_t1 ORDER BY b LIMIT 1 +) t2; + +INSERT INTO distributed_table_c1_t1 (a, b) +SELECT t1.a, t2.b +FROM reference_table t1 +LEFT JOIN ( + SELECT b, ROW_NUMBER() OVER (ORDER BY b DESC) AS rn + FROM nullkey_c1_t1 +) t2 ON t1.b = t2.b +WHERE t2.rn > 0; + +INSERT INTO nullkey_c1_t1 (a, b) +SELECT t1.a, t2.b +FROM nullkey_c1_t1 t1 +JOIN ( + SELECT rn, b + FROM ( + SELECT b, ROW_NUMBER() OVER (ORDER BY b DESC) AS rn + FROM distributed_table_c2_t1 + ) q +) t2 ON t1.b = t2.b +WHERE t2.rn > 2; + +INSERT INTO distributed_table_c1_t1 (a, b) +SELECT t1.a, t2.b +FROM nullkey_c1_t1 t1 +JOIN ( + SELECT sum_val, b + FROM ( + SELECT b, SUM(a) OVER (PARTITION BY b) AS sum_val + FROM nullkey_c1_t1 + ) q +) t2 ON t1.b = t2.b +WHERE t2.sum_val > 2; + +-- MultiTaskRouterSelectQuerySupported() is unnecessarily restrictive +-- about pushing down queries with DISTINCT ON clause even if the table +-- doesn't have a shard key. See https://github.com/citusdata/citus/pull/6752. +INSERT INTO nullkey_c1_t1 SELECT DISTINCT ON (a) a, b FROM nullkey_c1_t2; + +-- Similarly, we could push down the following query as well. see +-- https://github.com/citusdata/citus/pull/6831. +INSERT INTO nullkey_c1_t1 SELECT b, SUM(a) OVER (ORDER BY b) AS sum_val FROM nullkey_c1_t1; + +INSERT INTO nullkey_c2_t1 +SELECT t2.a, t2.b +FROM nullkey_c1_t1 AS t2 +JOIN reference_table AS t3 ON (t2.a = t3.a) +WHERE NOT EXISTS ( + SELECT 1 FROM nullkey_c1_t2 AS t1 WHERE t1.b = t3.b +); + +INSERT INTO distributed_table_c1_t1 +SELECT t1.a, t1.b +FROM nullkey_c1_t1 AS t1 +WHERE t1.a NOT IN ( + SELECT DISTINCT t2.a FROM distributed_table_c1_t2 AS t2 +); + +INSERT INTO distributed_table_c1_t1 +SELECT t1.a, t1.b +FROM reference_table AS t1 +JOIN ( + SELECT t2.a FROM ( + SELECT a FROM nullkey_c1_t1 + UNION + SELECT a FROM nullkey_c1_t2 + ) AS t2 +) AS t3 ON t1.a = t3.a; + +-- Temporaryly reduce the verbosity to avoid noise +-- in the output of the next query. +SET client_min_messages TO DEBUG1; + +INSERT INTO nullkey_c1_t1 +SELECT t1.a, t1.b +FROM reference_table AS t1 +WHERE t1.a IN ( + SELECT t2.a FROM ( + SELECT t3.a FROM ( + SELECT a FROM distributed_table_c1_t1 WHERE b > 4 + ) AS t3 + JOIN ( + SELECT a FROM distributed_table_c1_t2 WHERE b < 7 + ) AS t4 ON t3.a = t4.a + ) AS t2 +); + +SET client_min_messages TO DEBUG2; + +-- test upsert with plain INSERT query + +CREATE TABLE upsert_test_1 +( + unique_col int UNIQUE, + other_col int, + third_col int +); +SELECT create_distributed_table('upsert_test_1', null); + +CREATE TABLE upsert_test_2(key int primary key, value text); +SELECT create_distributed_table('upsert_test_2', null); + +INSERT INTO upsert_test_2 AS upsert_test_2_alias (key, value) VALUES (1, '5') ON CONFLICT(key) + DO UPDATE SET value = (upsert_test_2_alias.value::int * 2)::text; + +INSERT INTO upsert_test_2 (key, value) VALUES (1, '5') ON CONFLICT(key) + DO UPDATE SET value = (upsert_test_2.value::int * 3)::text; + +INSERT INTO upsert_test_1 (unique_col, other_col) VALUES (1, 1) ON CONFLICT (unique_col) + DO UPDATE SET other_col = (SELECT count(*) from upsert_test_1); + +INSERT INTO upsert_test_1 (unique_col, other_col) VALUES (1, 1) ON CONFLICT (unique_col) + DO UPDATE SET other_col = random()::int; + +INSERT INTO upsert_test_1 (unique_col, other_col) VALUES (1, 1) ON CONFLICT (unique_col) + DO UPDATE SET other_col = 5 WHERE upsert_test_1.other_col = random()::int; + +INSERT INTO upsert_test_1 VALUES (3, 5, 7); + +INSERT INTO upsert_test_1 (unique_col, other_col) VALUES (1, 1) ON CONFLICT (unique_col) WHERE unique_col = random()::int + DO UPDATE SET other_col = 5; + +CREATE TABLE upsert_test_3 (key_1 int, key_2 bigserial, value text DEFAULT 'default_value', PRIMARY KEY (key_1, key_2)); +SELECT create_distributed_table('upsert_test_3', null); + +INSERT INTO upsert_test_3 VALUES (1, DEFAULT, '1') RETURNING *; +INSERT INTO upsert_test_3 VALUES (5, DEFAULT, DEFAULT) RETURNING *; + +SET client_min_messages TO DEBUG1; +INSERT INTO upsert_test_3 SELECT 7, other_col, 'harcoded_text_value' FROM upsert_test_1 RETURNING *; +SET client_min_messages TO DEBUG2; + +-- test upsert with INSERT .. SELECT queries + +SET client_min_messages TO DEBUG1; +INSERT INTO upsert_test_1 (unique_col, other_col) SELECT unique_col, other_col FROM upsert_test_1 ON CONFLICT (unique_col) + DO UPDATE SET other_col = upsert_test_1.other_col + 1; +-- Fails due to https://github.com/citusdata/citus/issues/6826. +INSERT INTO upsert_test_1 (unique_col, other_col) SELECT unique_col, other_col FROM upsert_test_1 ON CONFLICT (unique_col) + DO UPDATE SET other_col = (SELECT count(*) from upsert_test_1); +SET client_min_messages TO DEBUG2; + +INSERT INTO upsert_test_1 (unique_col, other_col) SELECT unique_col, other_col FROM upsert_test_1 ON CONFLICT (unique_col) + DO UPDATE SET other_col = random()::int; + +INSERT INTO upsert_test_1 (unique_col, other_col) SELECT unique_col, other_col FROM upsert_test_1 ON CONFLICT (unique_col) + DO UPDATE SET other_col = 5 WHERE upsert_test_1.other_col = random()::int; + +SELECT reload_tables(); + +ALTER TABLE nullkey_c1_t1 ADD PRIMARY KEY (a); +ALTER TABLE distributed_table_c1_t1 ADD PRIMARY KEY (a,b); + +INSERT INTO nullkey_c1_t1 AS t1 (a, b) SELECT t3.a, t3.b FROM nullkey_c1_t2 t2 JOIN reference_table t3 ON (t2.a = t3.a) ON CONFLICT (a) + DO UPDATE SET a = t1.a + 10; + +SET client_min_messages TO DEBUG1; +INSERT INTO distributed_table_c1_t1 AS t1 (a, b) SELECT t3.a, t3.b FROM nullkey_c1_t2 t2 JOIN reference_table t3 ON (t2.a = t3.a) ON CONFLICT (a, b) + DO UPDATE SET b = t1.b + 10; +INSERT INTO nullkey_c1_t1 AS t1 (a, b) SELECT t3.a, t3.b FROM distributed_table_c1_t1 t2 JOIN reference_table t3 ON (t2.a = t3.a) ON CONFLICT (a) + DO UPDATE SET a = t1.a + 10; +-- This also fails due to https://github.com/citusdata/citus/issues/6826. +INSERT INTO nullkey_c1_t1 AS t1 (a, b) SELECT t3.a, t3.b FROM distributed_table_c1_t1 t2 JOIN reference_table t3 ON (t2.a = t3.a) WHERE t2.a = 3 ON CONFLICT (a) + DO UPDATE SET a = (SELECT max(b)+1 FROM distributed_table_c1_t1 WHERE a = 3); +SET client_min_messages TO DEBUG2; + +SELECT avg(a), avg(b) FROM distributed_table_c1_t1; +SELECT avg(a), avg(b) FROM nullkey_c1_t1; +SELECT avg(a), avg(b) FROM nullkey_c1_t2; +SELECT * FROM upsert_test_1 ORDER BY unique_col; +SELECT * FROM upsert_test_2 ORDER BY key; +SELECT * FROM upsert_test_3 ORDER BY key_1, key_2; + +SET client_min_messages TO WARNING; +DROP SCHEMA insert_select_null_dist_key CASCADE; + +SELECT citus_remove_node('localhost', :master_port); diff --git a/src/test/regress/sql/query_null_dist_key.sql b/src/test/regress/sql/query_null_dist_key.sql index f5d1fe3fc..02eac5c80 100644 --- a/src/test/regress/sql/query_null_dist_key.sql +++ b/src/test/regress/sql/query_null_dist_key.sql @@ -514,7 +514,11 @@ SET client_min_messages TO DEBUG2; INSERT INTO nullkey_c1_t1 SELECT * FROM nullkey_c2_t1; -- between a null dist key table and a table of different type +SET client_min_messages TO WARNING; +EXPLAIN (ANALYZE TRUE, TIMING FALSE, COSTS FALSE, SUMMARY FALSE, VERBOSE FALSE) INSERT INTO nullkey_c1_t1 SELECT * FROM reference_table; +SET client_min_messages TO DEBUG2; + INSERT INTO nullkey_c1_t1 SELECT * FROM distributed_table; INSERT INTO nullkey_c1_t1 SELECT * FROM citus_local_table; INSERT INTO nullkey_c1_t1 SELECT * FROM postgres_local_table; @@ -543,7 +547,7 @@ WITH level_0 AS ( WITH RECURSIVE level_2_recursive(x) AS ( VALUES (1) UNION ALL - SELECT a + 1 FROM nullkey_c1_t1 JOIN level_2_recursive ON (a = x) WHERE a < 100 + SELECT a + 1 FROM nullkey_c1_t1 JOIN level_2_recursive ON (a = x) WHERE a < 2 ) SELECT * FROM level_2_recursive RIGHT JOIN reference_table ON (level_2_recursive.x = reference_table.a) ) @@ -638,6 +642,8 @@ SET client_min_messages TO DEBUG1; INSERT INTO bigserial_test (x, y) SELECT x, y FROM bigserial_test; +INSERT INTO bigserial_test (x, y) SELECT a, a FROM reference_table; + INSERT INTO agg_events (user_id) SELECT f2.id FROM @@ -689,6 +695,19 @@ FROM raw_events_first LEFT JOIN raw_events_second ON raw_events_first.user_id = raw_events_second.user_id WHERE raw_events_second.user_id = 10 OR raw_events_second.user_id = 11; +INSERT INTO agg_events (user_id) +SELECT + users_ref_table.user_id +FROM + users_ref_table LEFT JOIN raw_events_second ON users_ref_table.user_id = raw_events_second.user_id + WHERE raw_events_second.user_id = 10 OR raw_events_second.user_id = 11; + +INSERT INTO agg_events (user_id) +SELECT COALESCE(raw_events_first.user_id, users_ref_table.user_id) +FROM raw_events_first + RIGHT JOIN (users_ref_table LEFT JOIN raw_events_second ON users_ref_table.user_id = raw_events_second.user_id) + ON raw_events_first.user_id = users_ref_table.user_id; + -- using a full join INSERT INTO agg_events (user_id, value_1_agg) SELECT t1.user_id AS col1, @@ -715,12 +734,25 @@ WHERE NOT EXISTS (SELECT 1 FROM raw_events_second WHERE raw_events_second.user_id =raw_events_first.user_id); +INSERT INTO raw_events_second + (user_id) +SELECT user_id +FROM users_ref_table +WHERE NOT EXISTS (SELECT 1 + FROM raw_events_second + WHERE raw_events_second.user_id = users_ref_table.user_id); + -- using inner join INSERT INTO agg_events (user_id) SELECT raw_events_first.user_id FROM raw_events_first INNER JOIN raw_events_second ON raw_events_first.user_id = raw_events_second.value_1 WHERE raw_events_first.value_1 IN (10, 11,12) OR raw_events_second.user_id IN (1,2,3,4); +INSERT INTO agg_events (user_id) +SELECT raw_events_first.user_id +FROM raw_events_first INNER JOIN users_ref_table ON raw_events_first.user_id = users_ref_table.user_id +WHERE raw_events_first.value_1 IN (10, 11,12) OR users_ref_table.user_id IN (1,2,3,4); + -- We could relax distributed insert .. select checks to allow pushing -- down more clauses down to the worker nodes when inserting into a single -- shard by selecting from a colocated one. We might want to do something @@ -734,6 +766,7 @@ WHERE raw_events_first.value_1 IN (10, 11,12) OR raw_events_second.user_id IN (1 -- limit / offset clause INSERT INTO agg_events (user_id) SELECT raw_events_first.user_id FROM raw_events_first LIMIT 1; INSERT INTO agg_events (user_id) SELECT raw_events_first.user_id FROM raw_events_first OFFSET 1; +INSERT INTO agg_events (user_id) SELECT users_ref_table.user_id FROM users_ref_table LIMIT 1; -- using a materialized cte WITH cte AS MATERIALIZED @@ -745,6 +778,10 @@ INSERT INTO raw_events_second WITH cte AS MATERIALIZED (SELECT * FROM raw_events_first) SELECT user_id * 1000, time, value_1, value_2, value_3, value_4 FROM cte; +INSERT INTO raw_events_second (user_id) + WITH cte AS MATERIALIZED (SELECT * FROM users_ref_table) + SELECT user_id FROM cte; + -- using a regular cte WITH cte AS (SELECT * FROM raw_events_first) INSERT INTO raw_events_second @@ -763,19 +800,19 @@ INSERT INTO agg_events -- we still support complex joins via INSERT's cte list .. WITH cte AS ( - SELECT reference_table.a AS a, 1 AS b + SELECT DISTINCT(reference_table.a) AS a, 1 AS b FROM distributed_table RIGHT JOIN reference_table USING (a) ) INSERT INTO raw_events_second (user_id, value_1) SELECT (a+5)*-1, b FROM cte; --- .. but can't do so via via SELECT's cte list +-- .. and via SELECT's cte list too INSERT INTO raw_events_second (user_id, value_1) WITH cte AS ( - SELECT reference_table.a AS a, 1 AS b + SELECT DISTINCT(reference_table.a) AS a, 1 AS b FROM distributed_table RIGHT JOIN reference_table USING (a) ) - SELECT (a+5)*-1, b FROM cte; + SELECT (a+5)*2, b FROM cte; -- using set operations INSERT INTO @@ -783,6 +820,11 @@ INSERT INTO (SELECT user_id FROM raw_events_first) INTERSECT (SELECT user_id FROM raw_events_first); +INSERT INTO + raw_events_first(user_id) + (SELECT user_id FROM users_ref_table) INTERSECT + (SELECT user_id FROM raw_events_first); + -- group by clause inside subquery INSERT INTO agg_events (user_id) @@ -842,6 +884,11 @@ SELECT SUM(value_3), FROM raw_events_first GROUP BY user_id; +INSERT INTO agg_events (value_3_agg, value_1_agg) +SELECT AVG(user_id), SUM(user_id) +FROM users_ref_table +GROUP BY user_id; + -- using generate_series INSERT INTO raw_events_first (user_id, value_1, value_2) SELECT s, s, s FROM generate_series(1, 5) s; From db2514ef7864ac8de04c64741a1c2087716d2d7d Mon Sep 17 00:00:00 2001 From: Onur Tirtir Date: Wed, 3 May 2023 14:32:43 +0300 Subject: [PATCH 030/118] Call null-shard-key tables as single-shard distributed tables in code --- .../commands/create_distributed_table.c | 40 +++--- .../distributed/commands/foreign_constraint.c | 14 +- src/backend/distributed/commands/multi_copy.c | 2 +- src/backend/distributed/commands/table.c | 8 +- .../distributed/metadata/metadata_cache.c | 12 +- .../distributed/operations/create_shards.c | 12 +- .../distributed/operations/stage_protocol.c | 2 +- .../distributed/planner/distributed_planner.c | 18 +-- .../planner/insert_select_planner.c | 10 +- .../planner/multi_router_planner.c | 10 +- .../distributed/utils/distribution_column.c | 2 +- .../distributed/coordinator_protocol.h | 3 +- src/include/distributed/distributed_planner.h | 8 +- src/include/distributed/metadata_cache.h | 6 +- src/include/distributed/metadata_utility.h | 2 +- .../distributed/multi_router_planner.h | 2 +- .../citus_arbitrary_configs.py | 6 +- src/test/regress/citus_tests/config.py | 6 +- src/test/regress/citus_tests/run_test.py | 2 +- ...out => alter_table_single_shard_table.out} | 0 ..._key.out => create_single_shard_table.out} | 136 +++++++++--------- ...t => insert_select_single_shard_table.out} | 42 +++--- src/test/regress/expected/merge.out | 25 ++-- src/test/regress/expected/multi_extension.out | 2 +- ...t_key.out => query_single_shard_table.out} | 40 +++--- src/test/regress/expected/single_node.out | 2 +- src/test/regress/expected/single_node_0.out | 2 +- ...y_prep.out => single_shard_table_prep.out} | 0 src/test/regress/multi_1_schedule | 8 +- src/test/regress/null_dist_key_prep_schedule | 1 - .../regress/single_shard_table_prep_schedule | 1 + ...sql => alter_table_single_shard_table.sql} | 0 ..._key.sql => create_single_shard_table.sql} | 130 ++++++++--------- ...l => insert_select_single_shard_table.sql} | 36 ++--- src/test/regress/sql/merge.sql | 10 +- src/test/regress/sql/multi_extension.sql | 2 +- ...t_key.sql => query_single_shard_table.sql} | 26 ++-- src/test/regress/sql/single_node.sql | 2 +- ...y_prep.sql => single_shard_table_prep.sql} | 0 39 files changed, 318 insertions(+), 312 deletions(-) rename src/test/regress/expected/{alter_table_null_dist_key.out => alter_table_single_shard_table.out} (100%) rename src/test/regress/expected/{create_null_dist_key.out => create_single_shard_table.out} (94%) rename src/test/regress/expected/{insert_select_null_dist_key.out => insert_select_single_shard_table.out} (96%) rename src/test/regress/expected/{query_null_dist_key.out => query_single_shard_table.out} (98%) rename src/test/regress/expected/{null_dist_key_prep.out => single_shard_table_prep.out} (100%) delete mode 100644 src/test/regress/null_dist_key_prep_schedule create mode 100644 src/test/regress/single_shard_table_prep_schedule rename src/test/regress/sql/{alter_table_null_dist_key.sql => alter_table_single_shard_table.sql} (100%) rename src/test/regress/sql/{create_null_dist_key.sql => create_single_shard_table.sql} (91%) rename src/test/regress/sql/{insert_select_null_dist_key.sql => insert_select_single_shard_table.sql} (95%) rename src/test/regress/sql/{query_null_dist_key.sql => query_single_shard_table.sql} (98%) rename src/test/regress/sql/{null_dist_key_prep.sql => single_shard_table_prep.sql} (100%) diff --git a/src/backend/distributed/commands/create_distributed_table.c b/src/backend/distributed/commands/create_distributed_table.c index 544d8f04e..e55196100 100644 --- a/src/backend/distributed/commands/create_distributed_table.c +++ b/src/backend/distributed/commands/create_distributed_table.c @@ -141,8 +141,8 @@ static void CreateCitusTable(Oid relationId, CitusTableType tableType, DistributedTableParams *distributedTableParams); static void CreateHashDistributedTableShards(Oid relationId, int shardCount, Oid colocatedTableId, bool localTableEmpty); -static void CreateNullShardKeyDistTableShard(Oid relationId, Oid colocatedTableId, - uint32 colocationId); +static void CreateSingleShardTableShard(Oid relationId, Oid colocatedTableId, + uint32 colocationId); static uint32 ColocationIdForNewTable(Oid relationId, CitusTableType tableType, DistributedTableParams *distributedTableParams, Var *distributionColumn); @@ -285,7 +285,7 @@ create_distributed_table(PG_FUNCTION_ARGS) { /* * As we do for shard_count parameter, we could throw an error if - * distribution_type is not NULL when creating a null-shard-key table. + * distribution_type is not NULL when creating a single-shard table. * However, this requires changing the default value of distribution_type * parameter to NULL and this would mean a breaking change for most * users because they're mostly using this API to create sharded @@ -296,7 +296,7 @@ create_distributed_table(PG_FUNCTION_ARGS) "when the distribution column is null "))); } - CreateNullShardKeyDistTable(relationId, colocateWithTableName); + CreateSingleShardTable(relationId, colocateWithTableName); } PG_RETURN_VOID(); @@ -1027,11 +1027,11 @@ CreateReferenceTable(Oid relationId) /* - * CreateNullShardKeyDistTable is a wrapper around CreateCitusTable that creates a + * CreateSingleShardTable is a wrapper around CreateCitusTable that creates a * single shard distributed table that doesn't have a shard key. */ void -CreateNullShardKeyDistTable(Oid relationId, char *colocateWithTableName) +CreateSingleShardTable(Oid relationId, char *colocateWithTableName) { DistributedTableParams distributedTableParams = { .colocateWithTableName = colocateWithTableName, @@ -1039,7 +1039,7 @@ CreateNullShardKeyDistTable(Oid relationId, char *colocateWithTableName) .shardCountIsStrict = true, .distributionColumnName = NULL }; - CreateCitusTable(relationId, NULL_KEY_DISTRIBUTED_TABLE, &distributedTableParams); + CreateCitusTable(relationId, SINGLE_SHARD_DISTRIBUTED, &distributedTableParams); } @@ -1061,7 +1061,7 @@ CreateCitusTable(Oid relationId, CitusTableType tableType, DistributedTableParams *distributedTableParams) { if ((tableType == HASH_DISTRIBUTED || tableType == APPEND_DISTRIBUTED || - tableType == RANGE_DISTRIBUTED || tableType == NULL_KEY_DISTRIBUTED_TABLE) != + tableType == RANGE_DISTRIBUTED || tableType == SINGLE_SHARD_DISTRIBUTED) != (distributedTableParams != NULL)) { ereport(ERROR, (errmsg("distributed table params must be provided " @@ -1212,10 +1212,10 @@ CreateCitusTable(Oid relationId, CitusTableType tableType, { CreateReferenceTableShard(relationId); } - else if (tableType == NULL_KEY_DISTRIBUTED_TABLE) + else if (tableType == SINGLE_SHARD_DISTRIBUTED) { - CreateNullShardKeyDistTableShard(relationId, colocatedTableId, - colocationId); + CreateSingleShardTableShard(relationId, colocatedTableId, + colocationId); } if (ShouldSyncTableMetadata(relationId)) @@ -1271,7 +1271,7 @@ CreateCitusTable(Oid relationId, CitusTableType tableType, } /* copy over data for hash distributed and reference tables */ - if (tableType == HASH_DISTRIBUTED || tableType == NULL_KEY_DISTRIBUTED_TABLE || + if (tableType == HASH_DISTRIBUTED || tableType == SINGLE_SHARD_DISTRIBUTED || tableType == REFERENCE_TABLE) { if (RegularTable(relationId)) @@ -1336,7 +1336,7 @@ DecideCitusTableParams(CitusTableType tableType, break; } - case NULL_KEY_DISTRIBUTED_TABLE: + case SINGLE_SHARD_DISTRIBUTED: { citusTableParams.distributionMethod = DISTRIBUTE_BY_NONE; citusTableParams.replicationModel = REPLICATION_MODEL_STREAMING; @@ -1706,12 +1706,12 @@ CreateHashDistributedTableShards(Oid relationId, int shardCount, /* - * CreateHashDistributedTableShards creates the shard of given null-shard-key + * CreateHashDistributedTableShards creates the shard of given single-shard * distributed table. */ static void -CreateNullShardKeyDistTableShard(Oid relationId, Oid colocatedTableId, - uint32 colocationId) +CreateSingleShardTableShard(Oid relationId, Oid colocatedTableId, + uint32 colocationId) { if (colocatedTableId != InvalidOid) { @@ -1735,7 +1735,7 @@ CreateNullShardKeyDistTableShard(Oid relationId, Oid colocatedTableId, } else { - CreateNullKeyShardWithRoundRobinPolicy(relationId, colocationId); + CreateSingleShardTableShardWithRoundRobinPolicy(relationId, colocationId); } } @@ -1984,13 +1984,13 @@ EnsureRelationCanBeDistributed(Oid relationId, Var *distributionColumn, { /* * Distributing partitioned tables is only supported for hash-distribution - * or null-shard-key tables. + * or single-shard tables. */ - bool isNullShardKeyTable = + bool isSingleShardTable = distributionMethod == DISTRIBUTE_BY_NONE && replicationModel == REPLICATION_MODEL_STREAMING && colocationId != INVALID_COLOCATION_ID; - if (distributionMethod != DISTRIBUTE_BY_HASH && !isNullShardKeyTable) + if (distributionMethod != DISTRIBUTE_BY_HASH && !isSingleShardTable) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("distributing partitioned tables in only supported " diff --git a/src/backend/distributed/commands/foreign_constraint.c b/src/backend/distributed/commands/foreign_constraint.c index 0b2c5573e..09133f5a3 100644 --- a/src/backend/distributed/commands/foreign_constraint.c +++ b/src/backend/distributed/commands/foreign_constraint.c @@ -304,9 +304,9 @@ ErrorIfUnsupportedForeignConstraintExists(Relation relation, char referencingDis * Foreign keys from citus local tables or reference tables to distributed * tables are not supported. * - * We could support foreign keys from references tables to null-shard-key + * We could support foreign keys from references tables to single-shard * tables but this doesn't seem useful a lot. However, if we decide supporting - * this, then we need to expand relation access tracking check for the null-shard-key + * this, then we need to expand relation access tracking check for the single-shard * tables too. */ if (referencingIsCitusLocalOrRefTable && !referencedIsCitusLocalOrRefTable) @@ -366,11 +366,11 @@ ErrorIfUnsupportedForeignConstraintExists(Relation relation, char referencingDis * if tables are hash-distributed and colocated, we need to make sure that * the distribution key is included in foreign constraint. */ - bool referencedIsNullShardKeyTable = - IsNullShardKeyTableByDistParams(referencedDistMethod, - referencedReplicationModel, - referencedColocationId); - if (!referencedIsCitusLocalOrRefTable && !referencedIsNullShardKeyTable && + bool referencedIsSingleShardTable = + IsSingleShardTableByDistParams(referencedDistMethod, + referencedReplicationModel, + referencedColocationId); + if (!referencedIsCitusLocalOrRefTable && !referencedIsSingleShardTable && !foreignConstraintOnDistKey) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), diff --git a/src/backend/distributed/commands/multi_copy.c b/src/backend/distributed/commands/multi_copy.c index fdb9552ef..8e92fd7a8 100644 --- a/src/backend/distributed/commands/multi_copy.c +++ b/src/backend/distributed/commands/multi_copy.c @@ -2146,7 +2146,7 @@ CitusCopyDestReceiverStartup(DestReceiver *dest, int operation, } if (IsCitusTableTypeCacheEntry(cacheEntry, DISTRIBUTED_TABLE) && - !IsCitusTableTypeCacheEntry(cacheEntry, NULL_KEY_DISTRIBUTED_TABLE) && + !IsCitusTableTypeCacheEntry(cacheEntry, SINGLE_SHARD_DISTRIBUTED) && copyDest->partitionColumnIndex == INVALID_PARTITION_COLUMN_INDEX) { ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), diff --git a/src/backend/distributed/commands/table.c b/src/backend/distributed/commands/table.c index af3439ab5..87a6a11b8 100644 --- a/src/backend/distributed/commands/table.c +++ b/src/backend/distributed/commands/table.c @@ -385,7 +385,7 @@ PostprocessCreateTableStmtPartitionOf(CreateStmt *createStatement, const if (IsCitusTable(parentRelationId)) { /* - * We can create Citus local tables and distributed tables with null shard keys + * We can create Citus local tables and single-shard distributed tables * right away, without switching to sequential mode, because they are going to * have only one shard. */ @@ -398,9 +398,9 @@ PostprocessCreateTableStmtPartitionOf(CreateStmt *createStatement, const char *parentRelationName = generate_qualified_relation_name(parentRelationId); - if (IsCitusTableType(parentRelationId, NULL_KEY_DISTRIBUTED_TABLE)) + if (IsCitusTableType(parentRelationId, SINGLE_SHARD_DISTRIBUTED)) { - CreateNullShardKeyDistTable(relationId, parentRelationName); + CreateSingleShardTable(relationId, parentRelationName); return; } @@ -618,7 +618,7 @@ DistributePartitionUsingParent(Oid parentCitusRelationId, Oid partitionRelationI * If the parent is null key distributed, we should distribute the partition * with null distribution key as well. */ - CreateNullShardKeyDistTable(partitionRelationId, parentRelationName); + CreateSingleShardTable(partitionRelationId, parentRelationName); return; } diff --git a/src/backend/distributed/metadata/metadata_cache.c b/src/backend/distributed/metadata/metadata_cache.c index 4eab2aeed..cfcaa4e65 100644 --- a/src/backend/distributed/metadata/metadata_cache.c +++ b/src/backend/distributed/metadata/metadata_cache.c @@ -508,7 +508,7 @@ IsCitusTableTypeInternal(char partitionMethod, char replicationModel, return partitionMethod == DISTRIBUTE_BY_RANGE; } - case NULL_KEY_DISTRIBUTED_TABLE: + case SINGLE_SHARD_DISTRIBUTED: { return partitionMethod == DISTRIBUTE_BY_NONE && replicationModel != REPLICATION_MODEL_2PC && @@ -826,13 +826,13 @@ IsCitusLocalTableByDistParams(char partitionMethod, char replicationModel, /* - * IsNullShardKeyTableByDistParams returns true if given partitionMethod, - * replicationModel and colocationId would identify a distributed table that - * has a null shard key. + * IsSingleShardTableByDistParams returns true if given partitionMethod, + * replicationModel and colocationId would identify a single-shard distributed + * table that has a null shard key. */ bool -IsNullShardKeyTableByDistParams(char partitionMethod, char replicationModel, - uint32 colocationId) +IsSingleShardTableByDistParams(char partitionMethod, char replicationModel, + uint32 colocationId) { return partitionMethod == DISTRIBUTE_BY_NONE && replicationModel != REPLICATION_MODEL_2PC && diff --git a/src/backend/distributed/operations/create_shards.c b/src/backend/distributed/operations/create_shards.c index 1a2ce5f1c..358927a09 100644 --- a/src/backend/distributed/operations/create_shards.c +++ b/src/backend/distributed/operations/create_shards.c @@ -373,16 +373,18 @@ CreateReferenceTableShard(Oid distributedTableId) /* - * CreateNullKeyShardWithRoundRobinPolicy creates a single shard for the given - * distributedTableId. The created shard does not have min/max values. - * Unlike CreateReferenceTableShard, the shard is _not_ replicated to - * all nodes but would have a single placement like Citus local tables. + * CreateSingleShardTableShardWithRoundRobinPolicy creates a single + * shard for the given distributedTableId. The created shard does not + * have min/max values. Unlike CreateReferenceTableShard, the shard is + * _not_ replicated to all nodes but would have a single placement like + * Citus local tables. + * * However, this placement doesn't necessarily need to be placed on * coordinator. This is determined based on modulo of the colocation * id that given table has been associated to. */ void -CreateNullKeyShardWithRoundRobinPolicy(Oid relationId, uint32 colocationId) +CreateSingleShardTableShardWithRoundRobinPolicy(Oid relationId, uint32 colocationId) { EnsureTableOwner(relationId); diff --git a/src/backend/distributed/operations/stage_protocol.c b/src/backend/distributed/operations/stage_protocol.c index c1031ffdf..7ef988e5f 100644 --- a/src/backend/distributed/operations/stage_protocol.c +++ b/src/backend/distributed/operations/stage_protocol.c @@ -522,7 +522,7 @@ RelationShardListForShardCreate(ShardInterval *shardInterval) List *relationShardList = list_make1(relationShard); if ((IsCitusTableTypeCacheEntry(cacheEntry, HASH_DISTRIBUTED) || - IsCitusTableTypeCacheEntry(cacheEntry, NULL_KEY_DISTRIBUTED_TABLE)) && + IsCitusTableTypeCacheEntry(cacheEntry, SINGLE_SHARD_DISTRIBUTED)) && cacheEntry->colocationId != INVALID_COLOCATION_ID) { shardIndex = ShardIndex(shardInterval); diff --git a/src/backend/distributed/planner/distributed_planner.c b/src/backend/distributed/planner/distributed_planner.c index 50509baea..6c5d0f32a 100644 --- a/src/backend/distributed/planner/distributed_planner.c +++ b/src/backend/distributed/planner/distributed_planner.c @@ -1025,15 +1025,15 @@ CreateDistributedPlan(uint64 planId, bool allowRecursivePlanning, Query *origina { return distributedPlan; } - else if (ContainsNullDistKeyTable(originalQuery)) + else if (ContainsSingleShardTable(originalQuery)) { /* * We only support router queries if the query contains reference to - * a null-dist-key table. This temporary restriction will be removed + * a single-shard table. This temporary restriction will be removed * once we support recursive planning for the queries that reference - * null-dist-key tables. + * single-shard tables. */ - WrapRouterErrorForNullDistKeyTable(distributedPlan->planningError); + WrapRouterErrorForSingleShardTable(distributedPlan->planningError); RaiseDeferredError(distributedPlan->planningError, ERROR); } else @@ -2474,14 +2474,14 @@ HasUnresolvedExternParamsWalker(Node *expression, ParamListInfo boundParams) /* - * ContainsNullDistKeyTable returns true if given query contains reference - * to a null-dist-key table. + * ContainsSingleShardTable returns true if given query contains reference + * to a single-shard table. */ bool -ContainsNullDistKeyTable(Query *query) +ContainsSingleShardTable(Query *query) { RTEListProperties *rteListProperties = GetRTEListPropertiesForQuery(query); - return rteListProperties->hasDistTableWithoutShardKey; + return rteListProperties->hasSingleShardDistTable; } @@ -2564,7 +2564,7 @@ GetRTEListProperties(List *rangeTableList) if (!HasDistributionKeyCacheEntry(cacheEntry)) { - rteListProperties->hasDistTableWithoutShardKey = true; + rteListProperties->hasSingleShardDistTable = true; } else { diff --git a/src/backend/distributed/planner/insert_select_planner.c b/src/backend/distributed/planner/insert_select_planner.c index 62c0e8d68..cae71845b 100644 --- a/src/backend/distributed/planner/insert_select_planner.c +++ b/src/backend/distributed/planner/insert_select_planner.c @@ -731,7 +731,7 @@ DistributedInsertSelectSupported(Query *queryTree, RangeTblEntry *insertRte, } if (!HasDistributionKey(targetRelationId) || - subqueryRteListProperties->hasDistTableWithoutShardKey) + subqueryRteListProperties->hasSingleShardDistTable) { /* * XXX: Better to check this regardless of the fact that the target table @@ -1563,16 +1563,16 @@ CreateNonPushableInsertSelectPlan(uint64 planId, Query *parse, ParamListInfo bou /* * Today it's not possible to generate a distributed plan for a SELECT - * having more than one tasks if it references a null-shard-key table. + * having more than one tasks if it references a single-shard table. * This is because, we don't support queries beyond router planner - * if the query references a null-shard-key table. + * if the query references a single-shard table. * * For this reason, right now we don't expect an INSERT .. SELECT * query to go through the repartitioned INSERT .. SELECT logic if the - * SELECT query references a null-shard-key table. + * SELECT query references a single-shard table. */ Assert(!repartitioned || - !GetRTEListPropertiesForQuery(selectQueryCopy)->hasDistTableWithoutShardKey); + !GetRTEListPropertiesForQuery(selectQueryCopy)->hasSingleShardDistTable); distributedPlan->insertSelectQuery = insertSelectQuery; distributedPlan->selectPlanForInsertSelect = selectPlan; diff --git a/src/backend/distributed/planner/multi_router_planner.c b/src/backend/distributed/planner/multi_router_planner.c index 47d11172f..0c6ec9dca 100644 --- a/src/backend/distributed/planner/multi_router_planner.c +++ b/src/backend/distributed/planner/multi_router_planner.c @@ -259,12 +259,12 @@ CreateModifyPlan(Query *originalQuery, Query *query, /* - * WrapRouterErrorForNullDistKeyTable wraps given planning error with a + * WrapRouterErrorForSingleShardTable wraps given planning error with a * generic error message if given query references a distributed table * that doesn't have a distribution key. */ void -WrapRouterErrorForNullDistKeyTable(DeferredErrorMessage *planningError) +WrapRouterErrorForSingleShardTable(DeferredErrorMessage *planningError) { planningError->detail = planningError->message; planningError->message = pstrdup("queries that reference a distributed " @@ -1886,9 +1886,9 @@ RouterJob(Query *originalQuery, PlannerRestrictionContext *plannerRestrictionCon */ if (IsMergeQuery(originalQuery)) { - if (ContainsNullDistKeyTable(originalQuery)) + if (ContainsSingleShardTable(originalQuery)) { - WrapRouterErrorForNullDistKeyTable(*planningError); + WrapRouterErrorForSingleShardTable(*planningError); } RaiseDeferredError(*planningError, ERROR); @@ -3013,7 +3013,7 @@ BuildRoutesForInsert(Query *query, DeferredErrorMessage **planningError) ereport(ERROR, (errmsg("local table cannot have %d shards", shardCount))); } - else if (IsCitusTableTypeCacheEntry(cacheEntry, NULL_KEY_DISTRIBUTED_TABLE)) + else if (IsCitusTableTypeCacheEntry(cacheEntry, SINGLE_SHARD_DISTRIBUTED)) { ereport(ERROR, (errmsg("distributed tables having a null shard key " "cannot have %d shards", diff --git a/src/backend/distributed/utils/distribution_column.c b/src/backend/distributed/utils/distribution_column.c index 1f5ac9ec5..474133f73 100644 --- a/src/backend/distributed/utils/distribution_column.c +++ b/src/backend/distributed/utils/distribution_column.c @@ -135,7 +135,7 @@ BuildDistributionKeyFromColumnName(Oid relationId, char *columnName, LOCKMODE lo char *tableName = get_rel_name(relationId); - /* short circuit for reference tables and null-shard key tables */ + /* short circuit for reference tables and single-shard tables */ if (columnName == NULL) { return NULL; diff --git a/src/include/distributed/coordinator_protocol.h b/src/include/distributed/coordinator_protocol.h index 351efb790..7f90eadda 100644 --- a/src/include/distributed/coordinator_protocol.h +++ b/src/include/distributed/coordinator_protocol.h @@ -262,7 +262,8 @@ extern void CreateShardsWithRoundRobinPolicy(Oid distributedTableId, int32 shard extern void CreateColocatedShards(Oid targetRelationId, Oid sourceRelationId, bool useExclusiveConnections); extern void CreateReferenceTableShard(Oid distributedTableId); -extern void CreateNullKeyShardWithRoundRobinPolicy(Oid relationId, uint32 colocationId); +extern void CreateSingleShardTableShardWithRoundRobinPolicy(Oid relationId, + uint32 colocationId); extern List * WorkerCreateShardCommandList(Oid relationId, int shardIndex, uint64 shardId, List *ddlCommandList, List *foreignConstraintCommandList); diff --git a/src/include/distributed/distributed_planner.h b/src/include/distributed/distributed_planner.h index 753504131..aac936a98 100644 --- a/src/include/distributed/distributed_planner.h +++ b/src/include/distributed/distributed_planner.h @@ -147,18 +147,18 @@ typedef struct RTEListProperties bool hasReferenceTable; bool hasCitusLocalTable; - /* includes hash, null dist key, append and range partitioned tables */ + /* includes hash, single-shard, append and range partitioned tables */ bool hasDistributedTable; /* * Effectively, hasDistributedTable is equal to - * "hasDistTableWithShardKey || hasDistTableWithoutShardKey". + * "hasDistTableWithShardKey || hasSingleShardDistTable". * * We provide below two for the callers that want to know what kind of * distributed tables that given query has references to. */ bool hasDistTableWithShardKey; - bool hasDistTableWithoutShardKey; + bool hasSingleShardDistTable; /* union of hasReferenceTable, hasCitusLocalTable and hasDistributedTable */ bool hasCitusTable; @@ -253,7 +253,7 @@ extern int32 BlessRecordExpression(Expr *expr); extern void DissuadePlannerFromUsingPlan(PlannedStmt *plan); extern PlannedStmt * FinalizePlan(PlannedStmt *localPlan, struct DistributedPlan *distributedPlan); -extern bool ContainsNullDistKeyTable(Query *query); +extern bool ContainsSingleShardTable(Query *query); extern RTEListProperties * GetRTEListPropertiesForQuery(Query *query); diff --git a/src/include/distributed/metadata_cache.h b/src/include/distributed/metadata_cache.h index 6f29f9d63..5dfb80519 100644 --- a/src/include/distributed/metadata_cache.h +++ b/src/include/distributed/metadata_cache.h @@ -123,7 +123,7 @@ typedef enum HASH_DISTRIBUTED, APPEND_DISTRIBUTED, RANGE_DISTRIBUTED, - NULL_KEY_DISTRIBUTED_TABLE, + SINGLE_SHARD_DISTRIBUTED, /* hash, range or append distributed table */ DISTRIBUTED_TABLE, @@ -158,8 +158,8 @@ extern uint32 ColocationIdViaCatalog(Oid relationId); bool IsReferenceTableByDistParams(char partitionMethod, char replicationModel); extern bool IsCitusLocalTableByDistParams(char partitionMethod, char replicationModel, uint32 colocationId); -extern bool IsNullShardKeyTableByDistParams(char partitionMethod, char replicationModel, - uint32 colocationId); +extern bool IsSingleShardTableByDistParams(char partitionMethod, char replicationModel, + uint32 colocationId); extern List * CitusTableList(void); extern ShardInterval * LoadShardInterval(uint64 shardId); extern bool ShardExists(uint64 shardId); diff --git a/src/include/distributed/metadata_utility.h b/src/include/distributed/metadata_utility.h index fe404acf8..08d4896c1 100644 --- a/src/include/distributed/metadata_utility.h +++ b/src/include/distributed/metadata_utility.h @@ -326,7 +326,7 @@ extern void DeletePartitionRow(Oid distributedRelationId); extern void DeleteShardRow(uint64 shardId); extern void UpdatePlacementGroupId(uint64 placementId, int groupId); extern void DeleteShardPlacementRow(uint64 placementId); -extern void CreateNullShardKeyDistTable(Oid relationId, char *colocateWithTableName); +extern void CreateSingleShardTable(Oid relationId, char *colocateWithTableName); extern void CreateDistributedTable(Oid relationId, char *distributionColumnName, char distributionMethod, int shardCount, bool shardCountIsStrict, char *colocateWithTableName); diff --git a/src/include/distributed/multi_router_planner.h b/src/include/distributed/multi_router_planner.h index 40d92fead..506e50135 100644 --- a/src/include/distributed/multi_router_planner.h +++ b/src/include/distributed/multi_router_planner.h @@ -36,7 +36,7 @@ extern DistributedPlan * CreateRouterPlan(Query *originalQuery, Query *query, extern DistributedPlan * CreateModifyPlan(Query *originalQuery, Query *query, PlannerRestrictionContext * plannerRestrictionContext); -extern void WrapRouterErrorForNullDistKeyTable(DeferredErrorMessage *planningError); +extern void WrapRouterErrorForSingleShardTable(DeferredErrorMessage *planningError); extern DeferredErrorMessage * PlanRouterQuery(Query *originalQuery, PlannerRestrictionContext * plannerRestrictionContext, diff --git a/src/test/regress/citus_tests/arbitrary_configs/citus_arbitrary_configs.py b/src/test/regress/citus_tests/arbitrary_configs/citus_arbitrary_configs.py index 8785de8f7..52924aa11 100755 --- a/src/test/regress/citus_tests/arbitrary_configs/citus_arbitrary_configs.py +++ b/src/test/regress/citus_tests/arbitrary_configs/citus_arbitrary_configs.py @@ -81,12 +81,14 @@ def run_for_config(config, lock, sql_schedule_name): config.bindir, config.pg_srcdir, config.coordinator_port(), - cfg.NULL_DIST_KEY_PREP_SCHEDULE, + cfg.SINGLE_SHARD_PREP_SCHEDULE, config.output_dir, config.input_dir, cfg.SUPER_USER_NAME, ) - common.save_regression_diff("null_dist_key_prep_regression", config.output_dir) + common.save_regression_diff( + "single_shard_table_prep_regression", config.output_dir + ) exitCode |= _run_pg_regress_on_port( config, config.coordinator_port(), cfg.CREATE_SCHEDULE diff --git a/src/test/regress/citus_tests/config.py b/src/test/regress/citus_tests/config.py index 69cc5599c..0973ac58a 100644 --- a/src/test/regress/citus_tests/config.py +++ b/src/test/regress/citus_tests/config.py @@ -22,7 +22,7 @@ ARBITRARY_SCHEDULE_NAMES = [ "sql_schedule", "sql_base_schedule", "postgres_schedule", - "null_dist_key_prep_schedule", + "single_shard_table_prep_schedule", ] BEFORE_PG_UPGRADE_SCHEDULE = "./before_pg_upgrade_schedule" @@ -30,7 +30,7 @@ AFTER_PG_UPGRADE_SCHEDULE = "./after_pg_upgrade_schedule" CREATE_SCHEDULE = "./create_schedule" POSTGRES_SCHEDULE = "./postgres_schedule" -NULL_DIST_KEY_PREP_SCHEDULE = "./null_dist_key_prep_schedule" +SINGLE_SHARD_PREP_SCHEDULE = "./single_shard_table_prep_schedule" SQL_SCHEDULE = "./sql_schedule" SQL_BASE_SCHEDULE = "./sql_base_schedule" @@ -206,7 +206,7 @@ class PostgresConfig(CitusDefaultClusterConfig): ] -class AllNullDistKeyDefaultConfig(CitusDefaultClusterConfig): +class AllSingleShardTableDefaultConfig(CitusDefaultClusterConfig): def __init__(self, arguments): super().__init__(arguments) self.all_null_dist_key = True diff --git a/src/test/regress/citus_tests/run_test.py b/src/test/regress/citus_tests/run_test.py index cda9fc0ae..c9f86a3c4 100755 --- a/src/test/regress/citus_tests/run_test.py +++ b/src/test/regress/citus_tests/run_test.py @@ -128,7 +128,7 @@ DEPS = { "multi_mx_copy_data": TestDeps(None, ["multi_mx_create_table"]), "multi_mx_schema_support": TestDeps(None, ["multi_mx_copy_data"]), "multi_simple_queries": TestDeps("base_schedule"), - "create_null_dist_key": TestDeps("minimal_schedule"), + "create_single_shard_table": TestDeps("minimal_schedule"), } diff --git a/src/test/regress/expected/alter_table_null_dist_key.out b/src/test/regress/expected/alter_table_single_shard_table.out similarity index 100% rename from src/test/regress/expected/alter_table_null_dist_key.out rename to src/test/regress/expected/alter_table_single_shard_table.out diff --git a/src/test/regress/expected/create_null_dist_key.out b/src/test/regress/expected/create_single_shard_table.out similarity index 94% rename from src/test/regress/expected/create_null_dist_key.out rename to src/test/regress/expected/create_single_shard_table.out index af6e66f62..8478dc293 100644 --- a/src/test/regress/expected/create_null_dist_key.out +++ b/src/test/regress/expected/create_single_shard_table.out @@ -1,5 +1,5 @@ -CREATE SCHEMA create_null_dist_key; -SET search_path TO create_null_dist_key; +CREATE SCHEMA create_single_shard_table; +SET search_path TO create_single_shard_table; SET citus.next_shard_id TO 1720000; SET citus.shard_count TO 32; SET citus.shard_replication_factor TO 1; @@ -16,7 +16,7 @@ SELECT 1 FROM citus_remove_node('localhost', :worker_2_port); (1 row) CREATE TABLE add_node_test(a int, "b" text); --- add a node before creating the null-shard-key table +-- add a node before creating the single-shard table SELECT 1 FROM citus_add_node('localhost', :worker_1_port); ?column? --------------------------------------------------------------------- @@ -29,7 +29,7 @@ SELECT create_distributed_table('add_node_test', null, colocate_with=>'none', di (1 row) --- add a node after creating the null-shard-key table +-- add a node after creating the single-shard table SELECT 1 FROM citus_add_node('localhost', :worker_2_port); ?column? --------------------------------------------------------------------- @@ -38,7 +38,7 @@ SELECT 1 FROM citus_add_node('localhost', :worker_2_port); -- make sure that table is created on the worker nodes added before/after create_distributed_table SELECT result FROM run_command_on_workers($$ - SELECT COUNT(*)=1 FROM pg_class WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + SELECT COUNT(*)=1 FROM pg_class WHERE relnamespace = 'create_single_shard_table'::regnamespace AND relname='add_node_test' $$); result @@ -50,7 +50,7 @@ $$); -- and check the metadata tables SELECT result FROM run_command_on_workers($$ SELECT (partmethod, partkey, repmodel, autoconverted) FROM pg_dist_partition - WHERE logicalrelid = 'create_null_dist_key.add_node_test'::regclass + WHERE logicalrelid = 'create_single_shard_table.add_node_test'::regclass $$); result --------------------------------------------------------------------- @@ -60,7 +60,7 @@ $$); SELECT result FROM run_command_on_workers($$ SELECT (shardstorage, shardminvalue, shardmaxvalue) FROM pg_dist_shard - WHERE logicalrelid = 'create_null_dist_key.add_node_test'::regclass + WHERE logicalrelid = 'create_single_shard_table.add_node_test'::regclass $$); result --------------------------------------------------------------------- @@ -72,7 +72,7 @@ SELECT result FROM run_command_on_workers($$ SELECT COUNT(*)=1 FROM pg_dist_placement WHERE shardid = ( SELECT shardid FROM pg_dist_shard - WHERE logicalrelid = 'create_null_dist_key.add_node_test'::regclass + WHERE logicalrelid = 'create_single_shard_table.add_node_test'::regclass ); $$); result @@ -85,7 +85,7 @@ SELECT result FROM run_command_on_workers($$ SELECT (shardcount, replicationfactor, distributioncolumntype, distributioncolumncollation) FROM pg_dist_colocation WHERE colocationid = ( SELECT colocationid FROM pg_dist_partition - WHERE logicalrelid = 'create_null_dist_key.add_node_test'::regclass + WHERE logicalrelid = 'create_single_shard_table.add_node_test'::regclass ); $$); result @@ -116,7 +116,7 @@ SELECT create_distributed_table('nullkey_c1_t1', null, colocate_with=>'none'); (1 row) -SELECT colocationid AS nullkey_c1_t1_colocation_id FROM pg_dist_partition WHERE logicalrelid = 'create_null_dist_key.nullkey_c1_t1'::regclass \gset +SELECT colocationid AS nullkey_c1_t1_colocation_id FROM pg_dist_partition WHERE logicalrelid = 'create_single_shard_table.nullkey_c1_t1'::regclass \gset BEGIN; DROP TABLE nullkey_c1_t1; -- make sure that we delete the colocation group after dropping the last table that belongs to it @@ -171,7 +171,7 @@ SELECT create_distributed_table('nullkey_c2_t3', null, colocate_with=>'nullkey_c SELECT logicalrelid, partmethod, partkey, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid IN ( SELECT oid FROM pg_class - WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + WHERE relnamespace = 'create_single_shard_table'::regnamespace AND relname LIKE 'nullkey_c1_%' ) ORDER BY 1; @@ -186,7 +186,7 @@ ORDER BY 1; SELECT COUNT(*) FROM pg_dist_partition WHERE logicalrelid IN ( SELECT oid FROM pg_class - WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + WHERE relnamespace = 'create_single_shard_table'::regnamespace AND relname LIKE 'nullkey_c1_%' ) GROUP BY colocationid; @@ -198,7 +198,7 @@ GROUP BY colocationid; SELECT logicalrelid, shardstorage, shardminvalue, shardmaxvalue FROM pg_dist_shard WHERE logicalrelid IN ( SELECT oid FROM pg_class - WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + WHERE relnamespace = 'create_single_shard_table'::regnamespace AND relname LIKE 'nullkey_c1_%' ) ORDER BY 1; @@ -215,7 +215,7 @@ WHERE shardid IN ( SELECT shardid FROM pg_dist_shard WHERE logicalrelid IN ( SELECT oid FROM pg_class - WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + WHERE relnamespace = 'create_single_shard_table'::regnamespace AND relname LIKE 'nullkey_c1_%' ) ) @@ -229,7 +229,7 @@ GROUP BY groupid; SELECT logicalrelid, partmethod, partkey, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid IN ( SELECT oid FROM pg_class - WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + WHERE relnamespace = 'create_single_shard_table'::regnamespace AND relname LIKE 'nullkey_c2_%' ) ORDER BY 1; @@ -244,7 +244,7 @@ ORDER BY 1; SELECT COUNT(*) FROM pg_dist_partition WHERE logicalrelid IN ( SELECT oid FROM pg_class - WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + WHERE relnamespace = 'create_single_shard_table'::regnamespace AND relname LIKE 'nullkey_c2_%' ) GROUP BY colocationid; @@ -256,7 +256,7 @@ GROUP BY colocationid; SELECT logicalrelid, shardstorage, shardminvalue, shardmaxvalue FROM pg_dist_shard WHERE logicalrelid IN ( SELECT oid FROM pg_class - WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + WHERE relnamespace = 'create_single_shard_table'::regnamespace AND relname LIKE 'nullkey_c2_%' ) ORDER BY 1; @@ -273,7 +273,7 @@ WHERE shardid IN ( SELECT shardid FROM pg_dist_shard WHERE logicalrelid IN ( SELECT oid FROM pg_class - WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + WHERE relnamespace = 'create_single_shard_table'::regnamespace AND relname LIKE 'nullkey_c2_%' ) ) @@ -293,12 +293,12 @@ GROUP BY groupid; SELECT ( SELECT colocationid FROM pg_dist_partition - WHERE logicalrelid = 'create_null_dist_key.nullkey_c1_t1'::regclass + WHERE logicalrelid = 'create_single_shard_table.nullkey_c1_t1'::regclass ) != ( SELECT colocationid FROM pg_dist_partition - WHERE logicalrelid = 'create_null_dist_key.nullkey_c2_t1'::regclass + WHERE logicalrelid = 'create_single_shard_table.nullkey_c2_t1'::regclass ); ?column? --------------------------------------------------------------------- @@ -312,7 +312,7 @@ SELECT SELECT groupid FROM pg_dist_placement WHERE shardid = ( SELECT shardid FROM pg_dist_shard - WHERE logicalrelid = 'create_null_dist_key.nullkey_c1_t1'::regclass + WHERE logicalrelid = 'create_single_shard_table.nullkey_c1_t1'::regclass ) ) != @@ -320,7 +320,7 @@ SELECT SELECT groupid FROM pg_dist_placement WHERE shardid = ( SELECT shardid FROM pg_dist_shard - WHERE logicalrelid = 'create_null_dist_key.nullkey_c2_t1'::regclass + WHERE logicalrelid = 'create_single_shard_table.nullkey_c2_t1'::regclass ) ); ?column? @@ -334,7 +334,7 @@ SELECT SELECT shardcount, replicationfactor, distributioncolumntype, distributioncolumncollation FROM pg_dist_colocation WHERE colocationid = ( SELECT colocationid FROM pg_dist_partition - WHERE logicalrelid = 'create_null_dist_key.nullkey_c1_t1'::regclass + WHERE logicalrelid = 'create_single_shard_table.nullkey_c1_t1'::regclass ); shardcount | replicationfactor | distributioncolumntype | distributioncolumncollation --------------------------------------------------------------------- @@ -344,7 +344,7 @@ WHERE colocationid = ( SELECT shardcount, replicationfactor, distributioncolumntype, distributioncolumncollation FROM pg_dist_colocation WHERE colocationid = ( SELECT colocationid FROM pg_dist_partition - WHERE logicalrelid = 'create_null_dist_key.nullkey_c2_t1'::regclass + WHERE logicalrelid = 'create_single_shard_table.nullkey_c2_t1'::regclass ); shardcount | replicationfactor | distributioncolumntype | distributioncolumncollation --------------------------------------------------------------------- @@ -359,7 +359,7 @@ SELECT create_distributed_table('round_robin_test_c1', null, colocate_with=>'non (1 row) \c - - - :master_port -SET search_path TO create_null_dist_key; +SET search_path TO create_single_shard_table; SET citus.next_shard_id TO 1730000; SET citus.shard_count TO 32; SET citus.shard_replication_factor TO 1; @@ -379,7 +379,7 @@ SELECT SELECT groupid FROM pg_dist_placement WHERE shardid = ( SELECT shardid FROM pg_dist_shard - WHERE logicalrelid = 'create_null_dist_key.round_robin_test_c1'::regclass + WHERE logicalrelid = 'create_single_shard_table.round_robin_test_c1'::regclass ) ) != @@ -387,7 +387,7 @@ SELECT SELECT groupid FROM pg_dist_placement WHERE shardid = ( SELECT shardid FROM pg_dist_shard - WHERE logicalrelid = 'create_null_dist_key.round_robin_test_c2'::regclass + WHERE logicalrelid = 'create_single_shard_table.round_robin_test_c2'::regclass ) ); ?column? @@ -396,7 +396,7 @@ SELECT (1 row) CREATE TABLE distributed_table(a int, b int); --- cannot colocate a sharded table with null shard key table +-- cannot colocate a sharded table with single-shard table SELECT create_distributed_table('distributed_table', 'a', colocate_with=>'nullkey_c1_t1'); ERROR: cannot colocate tables nullkey_c1_t1 and distributed_table DETAIL: Distribution column types don't match for nullkey_c1_t1 and distributed_table. @@ -414,7 +414,7 @@ SELECT create_distributed_table('distributed_table', 'a'); (1 row) --- cannot colocate null shard key tables with other table types +-- cannot colocate single-shard tables with other table types CREATE TABLE cannot_colocate_with_other_types (a int, b int); SELECT create_distributed_table('cannot_colocate_with_other_types', null, colocate_with=>'reference_table'); ERROR: cannot colocate tables reference_table and cannot_colocate_with_other_types @@ -430,7 +430,7 @@ SELECT citus_add_local_table_to_metadata('local'); (1 row) --- cannot colocate null shard key tables with citus local tables +-- cannot colocate single-shard tables with citus local tables SELECT create_distributed_table('cannot_colocate_with_other_types', null, colocate_with=>'local'); -- citus local ERROR: cannot distribute relation DETAIL: Currently, colocate_with option is not supported with append / range distributed tables and local tables added to metadata. @@ -447,15 +447,15 @@ SELECT create_distributed_table('local', null, colocate_with=>'none'); (1 row) BEGIN; - -- creating a null-shard-key table from a temporary table is not supported + -- creating a single-shard table from a temporary table is not supported CREATE TEMPORARY TABLE temp_table (a int); SELECT create_distributed_table('temp_table', null, colocate_with=>'none', distribution_type=>null); ERROR: cannot distribute a temporary table ROLLBACK; --- creating a null-shard-key table from a catalog table is not supported +-- creating a single-shard table from a catalog table is not supported SELECT create_distributed_table('pg_catalog.pg_index', NULL, distribution_type=>null); ERROR: cannot create a citus table from a catalog table --- creating a null-shard-key table from an unlogged table is supported +-- creating a single-shard table from an unlogged table is supported CREATE UNLOGGED TABLE unlogged_table (a int); SELECT create_distributed_table('unlogged_table', null, colocate_with=>'none', distribution_type=>null); create_distributed_table @@ -463,15 +463,15 @@ SELECT create_distributed_table('unlogged_table', null, colocate_with=>'none', d (1 row) --- creating a null-shard-key table from a foreign table is not supported +-- creating a single-shard table from a foreign table is not supported CREATE FOREIGN TABLE foreign_table ( id bigint not null, full_name text not null default '' ) SERVER fake_fdw_server OPTIONS (encoding 'utf-8', compression 'true', table_name 'foreign_table'); SELECT create_distributed_table('foreign_table', null, colocate_with=>'none', distribution_type=>null); ERROR: foreign tables cannot be distributed -HINT: Can add foreign table "foreign_table" to metadata by running: SELECT citus_add_local_table_to_metadata($$create_null_dist_key.foreign_table$$); --- create a null dist key table that has no tuples +HINT: Can add foreign table "foreign_table" to metadata by running: SELECT citus_add_local_table_to_metadata($$create_single_shard_table.foreign_table$$); +-- create a single-shard table that has no tuples CREATE TABLE null_dist_key_table_1 (a int primary key); SELECT create_distributed_table('null_dist_key_table_1', null, colocate_with=>'none'); create_distributed_table @@ -479,7 +479,7 @@ SELECT create_distributed_table('null_dist_key_table_1', null, colocate_with=>'n (1 row) --- create a null dist key table that has some tuples +-- create a single-shard table that has some tuples CREATE TABLE null_dist_key_table_2(a int primary key); INSERT INTO null_dist_key_table_2 VALUES(1); SELECT create_distributed_table('null_dist_key_table_2', null, colocate_with=>'none'); @@ -495,7 +495,7 @@ SELECT * FROM null_dist_key_table_2 ORDER BY a; (1 row) DROP TABLE null_dist_key_table_1, null_dist_key_table_2; --- create indexes before creating the null dist key tables +-- create indexes before creating the single-shard tables -- .. for an initially empty table CREATE TABLE null_dist_key_table_1(a int, b int); CREATE STATISTICS s1 (dependencies) ON a, b FROM null_dist_key_table_1; @@ -507,7 +507,7 @@ SELECT create_distributed_table('null_dist_key_table_1', null, colocate_with=>'n (1 row) CREATE STATISTICS s2 (dependencies) ON a, b FROM null_dist_key_table_1; --- .. and for another table having data in it before creating null dist key table +-- .. and for another table having data in it before creating single-shard table CREATE TABLE null_dist_key_table_2(a int); INSERT INTO null_dist_key_table_2 VALUES(1); CREATE INDEX null_dist_key_table_2_idx ON null_dist_key_table_2(a); @@ -634,7 +634,7 @@ SELECT * FROM "Table?!.1Table" ORDER BY id; 10 | 15 | (150,"{""f1"": 4, ""f2"": 8}") | {} | text_1 | 10 | 27 | yes | 60 | 70 | 4204 (4 rows) -SET search_path TO create_null_dist_key; +SET search_path TO create_single_shard_table; -- create a partitioned table with some columns that -- are going to be dropped within the tests CREATE TABLE sensors( @@ -660,10 +660,10 @@ SELECT create_distributed_table('sensors', NULL, distribution_type=>null); -- verify we can create new partitions after distributing the parent table CREATE TABLE sensors_2001 PARTITION OF sensors FOR VALUES FROM ('2001-01-01') TO ('2002-01-01'); --- verify we can attach to a null dist key table +-- verify we can attach to a single-shard table CREATE TABLE sensors_2002 (measureid integer, eventdatetime date, measure_data jsonb, PRIMARY KEY (measureid, eventdatetime, measure_data)); ALTER TABLE sensors ATTACH PARTITION sensors_2002 FOR VALUES FROM ('2002-01-01') TO ('2003-01-01'); --- verify we can detach from a null dist key table +-- verify we can detach from a single-shard table ALTER TABLE sensors DETACH PARTITION sensors_2001; -- error out when attaching a noncolocated partition CREATE TABLE sensors_2003 (measureid integer, eventdatetime date, measure_data jsonb, PRIMARY KEY (measureid, eventdatetime, measure_data)); @@ -749,7 +749,7 @@ SELECT COUNT(*) FROM run_command_on_workers($$ 1 (1 row) --- create a partitioned citus local table and verify we error out when attaching a partition with null dist key +-- create a partitioned citus local table and verify we error out when attaching a partition with single-shard CREATE TABLE partitioned_citus_local_tbl( measureid integer, eventdatetime date, @@ -911,13 +911,13 @@ DETAIL: To enforce foreign keys, the referencing and referenced rows need to be HINT: You could use SELECT create_reference_table('local_table_for_fkey') to replicate the referenced table to all nodes or consider dropping the foreign key -- Normally, we support foreign keys from Postgres tables to distributed -- tables assuming that the user will soon distribute the local table too --- anyway. However, this is not the case for null-shard-key tables before +-- anyway. However, this is not the case for single-shard tables before -- we improve SQL support. ALTER TABLE local_table_for_fkey ADD CONSTRAINT fkey_from_dummy_local FOREIGN KEY (a) REFERENCES "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"(id); ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables DETAIL: Local tables cannot be used in distributed queries. -CONTEXT: SQL statement "SELECT fk."a" FROM ONLY "create_null_dist_key"."local_table_for_fkey" fk LEFT OUTER JOIN "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234" pk ON ( pk."id" OPERATOR(pg_catalog.=) fk."a") WHERE pk."id" IS NULL AND (fk."a" IS NOT NULL)" +CONTEXT: SQL statement "SELECT fk."a" FROM ONLY "create_single_shard_table"."local_table_for_fkey" fk LEFT OUTER JOIN "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234" pk ON ( pk."id" OPERATOR(pg_catalog.=) fk."a") WHERE pk."id" IS NULL AND (fk."a" IS NOT NULL)" -- foreign key to a citus local table, errors out CREATE TABLE citus_local_table_for_fkey (a INT PRIMARY KEY); SELECT citus_add_local_table_to_metadata('citus_local_table_for_fkey'); @@ -988,7 +988,7 @@ ALTER TABLE null_key_dist DROP CONSTRAINT fkey_add_test_2; ALTER TABLE null_key_dist DROP CONSTRAINT fkey_add_test_3; ALTER TABLE null_key_dist DROP CONSTRAINT fkey_add_test_4; ALTER TABLE "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" DROP CONSTRAINT fkey_to_dummy_dist; --- create a view that depends on the null shard key table +-- create a view that depends on the single-shard table CREATE VIEW public.v1 AS SELECT * FROM null_key_dist; SELECT * FROM public.v1; a @@ -1037,7 +1037,7 @@ CREATE TABLE identity_test ( c bigint GENERATED BY DEFAULT AS IDENTITY (START WITH 1000 INCREMENT BY 1000) ); SELECT create_distributed_table('identity_test', NULL, distribution_type=>null); -ERROR: cannot complete operation on create_null_dist_key.identity_test with smallint/int identity column +ERROR: cannot complete operation on create_single_shard_table.identity_test with smallint/int identity column HINT: Use bigint identity column instead. DROP TABLE identity_test; -- Above failed because we don't support using a data type other than BIGINT @@ -1061,7 +1061,7 @@ CONTEXT: PL/pgSQL function normalize_generate_always_as_error(text) line XX at INSERT INTO identity_test (b) OVERRIDING SYSTEM VALUE VALUES (5); INSERT INTO identity_test (c) VALUES (5); SELECT result, success FROM run_command_on_workers($$ - INSERT INTO create_null_dist_key.identity_test (a) VALUES (6) + INSERT INTO create_single_shard_table.identity_test (a) VALUES (6) $$); result | success --------------------------------------------------------------------- @@ -1070,7 +1070,7 @@ $$); (2 rows) SELECT result, success FROM run_command_on_workers($$ - SELECT create_null_dist_key.normalize_generate_always_as_error('INSERT INTO create_null_dist_key.identity_test (b) VALUES (1)') + SELECT create_single_shard_table.normalize_generate_always_as_error('INSERT INTO create_single_shard_table.identity_test (b) VALUES (1)') $$); result | success --------------------------------------------------------------------- @@ -1080,7 +1080,7 @@ $$); -- This should fail due to missing OVERRIDING SYSTEM VALUE. SELECT result, success FROM run_command_on_workers($$ - SELECT create_null_dist_key.normalize_generate_always_as_error('INSERT INTO create_null_dist_key.identity_test (a, b) VALUES (1, 1)') + SELECT create_single_shard_table.normalize_generate_always_as_error('INSERT INTO create_single_shard_table.identity_test (a, b) VALUES (1, 1)') $$); result | success --------------------------------------------------------------------- @@ -1089,7 +1089,7 @@ $$); (2 rows) SELECT result, success FROM run_command_on_workers($$ - INSERT INTO create_null_dist_key.identity_test (a, b) OVERRIDING SYSTEM VALUE VALUES (7, 7) + INSERT INTO create_single_shard_table.identity_test (a, b) OVERRIDING SYSTEM VALUE VALUES (7, 7) $$); result | success --------------------------------------------------------------------- @@ -1098,7 +1098,7 @@ $$); (2 rows) SELECT result, success FROM run_command_on_workers($$ - INSERT INTO create_null_dist_key.identity_test (c, a) OVERRIDING SYSTEM VALUE VALUES (8, 8) + INSERT INTO create_single_shard_table.identity_test (c, a) OVERRIDING SYSTEM VALUE VALUES (8, 8) $$); result | success --------------------------------------------------------------------- @@ -1110,7 +1110,7 @@ $$); CREATE TABLE referenced_table(a int UNIQUE, b int); CREATE TABLE referencing_table(a int, b int, FOREIGN KEY (a) REFERENCES referenced_table(a)); --- to a colocated null dist key table +-- to a colocated single-shard table BEGIN; SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); create_distributed_table @@ -1132,7 +1132,7 @@ ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign k DETAIL: Key (a)=(2) is not present in table "referenced_table_xxxxxxx". CONTEXT: while executing command on localhost:xxxxx ROLLBACK; --- to a non-colocated null dist key table +-- to a non-colocated single-shard table BEGIN; SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); create_distributed_table @@ -1278,7 +1278,7 @@ SELECT create_distributed_table('referencing_table', NULL, distribution_type=>nu INSERT INTO referenced_table VALUES (1, 1); -- ok SELECT result, success FROM run_command_on_workers($$ - INSERT INTO create_null_dist_key.referencing_table VALUES (1, 2) + INSERT INTO create_single_shard_table.referencing_table VALUES (1, 2) $$); result | success --------------------------------------------------------------------- @@ -1288,7 +1288,7 @@ $$); -- fails SELECT result, success FROM run_command_on_workers($$ - INSERT INTO create_null_dist_key.referencing_table VALUES (2, 2) + INSERT INTO create_single_shard_table.referencing_table VALUES (2, 2) $$); result | success --------------------------------------------------------------------- @@ -1312,8 +1312,8 @@ ERROR: insert or update on table "self_fkey_test_1730152" violates foreign key DETAIL: Key (b)=(3) is not present in table "self_fkey_test_1730152". CONTEXT: while executing command on localhost:xxxxx -- similar foreign key tests but this time create the referencing table later on --- referencing table is a null shard key table --- to a colocated null dist key table +-- referencing table is a single-shard table +-- to a colocated single-shard table BEGIN; CREATE TABLE referenced_table(a int UNIQUE, b int); SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); @@ -1415,7 +1415,7 @@ BEGIN; ALTER TABLE referencing_table ADD CONSTRAINT fkey_to_dummy_ref_on_update FOREIGN KEY (a) REFERENCES referenced_table(a) ON UPDATE SET DEFAULT; ERROR: cannot create foreign key constraint since Citus does not support ON DELETE / UPDATE SET DEFAULT actions on the columns that default to sequences ROLLBACK; --- to a non-colocated null dist key table +-- to a non-colocated single-shard table BEGIN; CREATE TABLE referenced_table(a int UNIQUE, b int); SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); @@ -1514,7 +1514,7 @@ ERROR: referenced table "referenced_table" must be a distributed table or a ref DETAIL: To enforce foreign keys, the referencing and referenced rows need to be stored on the same node. HINT: You could use SELECT create_reference_table('referenced_table') to replicate the referenced table to all nodes or consider dropping the foreign key ROLLBACK; --- referenced table is a null shard key table +-- referenced table is a single-shard table -- from a sharded table BEGIN; CREATE TABLE referenced_table(a int UNIQUE, b int); @@ -1587,9 +1587,9 @@ SELECT create_distributed_table('referencing_table', null, colocate_with=>'none' SET client_min_messages TO DEBUG1; BEGIN; -- Switches to sequential execution because referenced_table is a reference table - -- and referenced by a null-shard-key distributed table. + -- and referenced by a single-shard table. -- - -- Given that we cannot do parallel access on null-shard-key, this is not useful. + -- Given that we cannot do parallel access on a single-shard table, this is not useful. -- However, this is already what we're doing for, e.g., a foreign key from a -- reference table to another reference table. TRUNCATE referenced_table CASCADE; @@ -1642,9 +1642,9 @@ BEGIN; ROLLBACK; BEGIN; -- Switches to sequential execution because referenced_table is a reference table - -- and referenced by a null-shard-key distributed table. + -- and referenced by a single-shard table. -- - -- Given that we cannot do parallel access on null-shard-key, this is not useful. + -- Given that we cannot do parallel access on a single-shard table, this is not useful. -- However, this is already what we're doing for, e.g., a foreign key from a -- reference table to another reference table. UPDATE referenced_table SET id = 101 WHERE id = 99; @@ -1662,7 +1662,7 @@ ROLLBACK; SET client_min_messages TO WARNING; DROP TABLE referenced_table, referencing_table; -- Test whether we unnecessarily switch to sequential execution --- when the referenced relation is a null-shard-key table. +-- when the referenced relation is a single-shard table. CREATE TABLE referenced_table(id int PRIMARY KEY, value_1 int); SELECT create_distributed_table('referenced_table', null, colocate_with=>'none', distribution_type=>null); create_distributed_table @@ -1686,12 +1686,12 @@ BEGIN; (1 row) -- Doesn't switch to sequential execution because the referenced_table is - -- a null-shard-key distributed table. + -- a single-shard table. ALTER TABLE referencing_table ADD COLUMN X INT; ROLLBACK; BEGIN; -- Doesn't switch to sequential execution because the referenced_table is - -- a null-shard-key distributed table. + -- a single-shard table. TRUNCATE referenced_table CASCADE; NOTICE: truncate cascades to table "referencing_table" DEBUG: truncate cascades to table "referencing_table_xxxxxxx" @@ -1809,4 +1809,4 @@ DROP TRIGGER trigger_2 ON trigger_table_2 CASCADE; DROP TRIGGER trigger_3 ON trigger_table_3 RESTRICT; -- cleanup at exit SET client_min_messages TO ERROR; -DROP SCHEMA create_null_dist_key, "NULL_!_dist_key" CASCADE; +DROP SCHEMA create_single_shard_table, "NULL_!_dist_key" CASCADE; diff --git a/src/test/regress/expected/insert_select_null_dist_key.out b/src/test/regress/expected/insert_select_single_shard_table.out similarity index 96% rename from src/test/regress/expected/insert_select_null_dist_key.out rename to src/test/regress/expected/insert_select_single_shard_table.out index b5391063c..b80d62ad4 100644 --- a/src/test/regress/expected/insert_select_null_dist_key.out +++ b/src/test/regress/expected/insert_select_single_shard_table.out @@ -1,5 +1,5 @@ -CREATE SCHEMA insert_select_null_dist_key; -SET search_path TO insert_select_null_dist_key; +CREATE SCHEMA insert_select_single_shard_table; +SET search_path TO insert_select_single_shard_table; SET citus.next_shard_id TO 1820000; SET citus.shard_count TO 32; SET client_min_messages TO WARNING; @@ -115,8 +115,8 @@ INSERT INTO range_table VALUES (0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), ( CREATE MATERIALIZED VIEW matview AS SELECT b*2+a AS a, a*a AS b FROM nullkey_c1_t1; SET client_min_messages TO DEBUG2; -- Test inserting into a distributed table by selecting from a combination of --- different table types together with null-shard-key tables. --- use a null-shard-key table +-- different table types together with single-shard tables. +-- use a single-shard table INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1; DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables DEBUG: Distributed planning for a fast-path router query @@ -139,7 +139,7 @@ INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator --- use a colocated null-shard-key table +-- use a colocated single-shard table INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN nullkey_c1_t2 USING (b); DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables DEBUG: Creating router plan @@ -156,7 +156,7 @@ INSERT INTO distributed_table_c1_t1 SELECT * FROM nullkey_c1_t1 UNION SELECT * F DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator --- use a non-colocated null-shard-key table +-- use a non-colocated single-shard table INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 LEFT JOIN nullkey_c2_t1 USING (a); DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables @@ -220,8 +220,8 @@ INSERT INTO distributed_table_c1_t1 SELECT i, i FROM generate_series(3, 8) i; DEBUG: distributed INSERT ... SELECT can only select from distributed tables DEBUG: Collecting INSERT ... SELECT results on coordinator -- Test inserting into a reference table by selecting from a combination of --- different table types together with null-shard-key tables. --- use a null-shard-key table +-- different table types together with single-shard tables. +-- use a single-shard table INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1; DEBUG: only reference tables may be queried when targeting a reference table with distributed INSERT ... SELECT DEBUG: Distributed planning for a fast-path router query @@ -244,7 +244,7 @@ INSERT INTO reference_table SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey DEBUG: only reference tables may be queried when targeting a reference table with distributed INSERT ... SELECT ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables DETAIL: Local tables cannot be used in distributed queries. --- use a colocated null-shard-key table +-- use a colocated single-shard table INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN nullkey_c1_t2 USING (b); DEBUG: only reference tables may be queried when targeting a reference table with distributed INSERT ... SELECT DEBUG: Creating router plan @@ -253,7 +253,7 @@ INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey DEBUG: only reference tables may be queried when targeting a reference table with distributed INSERT ... SELECT DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator --- use a non-colocated null-shard-key table +-- use a non-colocated single-shard table INSERT INTO reference_table SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 LEFT JOIN nullkey_c2_t1 USING (a); DEBUG: only reference tables may be queried when targeting a reference table with distributed INSERT ... SELECT ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables @@ -299,8 +299,8 @@ INSERT INTO reference_table SELECT i, i FROM generate_series(0, 5) i; DEBUG: distributed INSERT ... SELECT can only select from distributed tables DEBUG: Collecting INSERT ... SELECT results on coordinator -- Test inserting into a citus local table by selecting from a combination of --- different table types together with null-shard-key tables. --- use a null-shard-key table +-- different table types together with single-shard tables. +-- use a single-shard table INSERT INTO citus_local_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1; DEBUG: distributed INSERT ... SELECT cannot insert into a local table that is added to metadata DEBUG: Distributed planning for a fast-path router query @@ -311,7 +311,7 @@ INSERT INTO citus_local_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullk DEBUG: distributed INSERT ... SELECT cannot insert into a local table that is added to metadata DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator --- use a colocated null-shard-key table +-- use a colocated single-shard table INSERT INTO citus_local_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN nullkey_c1_t2 USING (b); DEBUG: distributed INSERT ... SELECT cannot insert into a local table that is added to metadata DEBUG: Creating router plan @@ -343,8 +343,8 @@ TRUNCATE citus_local_table; INSERT INTO citus_local_table SELECT i, i FROM generate_series(0, 10) i; DEBUG: distributed INSERT ... SELECT can only select from distributed tables DEBUG: Collecting INSERT ... SELECT results on coordinator --- Test inserting into a null-shard-key table by selecting from a combination of --- different table types, together with or without null-shard-key tables. +-- Test inserting into a single-shard table by selecting from a combination of +-- different table types, together with or without single-shard tables. -- use a postgres local table INSERT INTO nullkey_c1_t1 SELECT postgres_local_table.a, postgres_local_table.b FROM postgres_local_table; DEBUG: distributed INSERT ... SELECT can only select from distributed tables @@ -384,7 +384,7 @@ INSERT INTO nullkey_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1 DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables DETAIL: Router planner cannot handle multi-shard select queries --- use a non-colocated null-shard-key table +-- use a non-colocated single-shard table INSERT INTO nullkey_c2_t1 SELECT q.* FROM (SELECT reference_table.* FROM reference_table LEFT JOIN nullkey_c1_t1 USING (a)) q JOIN nullkey_c1_t2 USING (a); DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables DEBUG: Creating router plan @@ -434,7 +434,7 @@ INSERT INTO nullkey_c2_t1 SELECT i, i FROM generate_series(2, 7) i; DEBUG: distributed INSERT ... SELECT can only select from distributed tables DEBUG: Collecting INSERT ... SELECT results on coordinator -- Test inserting into a local table by selecting from a combination of --- different table types, together with or without null-shard-key tables. +-- different table types, together with or without single-shard tables. INSERT INTO postgres_local_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN reference_table USING (a); DEBUG: Creating router plan INSERT INTO postgres_local_table SELECT * FROM nullkey_c1_t1 ORDER BY 1,2 OFFSET 3 LIMIT 2; @@ -620,8 +620,8 @@ WHERE t1.a IN ( ) AS t2 ); DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables -DEBUG: generating subplan XXX_1 for subquery SELECT a FROM (SELECT t3.a FROM ((SELECT distributed_table_c1_t1.a FROM insert_select_null_dist_key.distributed_table_c1_t1 WHERE (distributed_table_c1_t1.b OPERATOR(pg_catalog.>) 4)) t3 JOIN (SELECT distributed_table_c1_t2.a FROM insert_select_null_dist_key.distributed_table_c1_t2 WHERE (distributed_table_c1_t2.b OPERATOR(pg_catalog.<) 7)) t4 ON ((t3.a OPERATOR(pg_catalog.=) t4.a)))) t2 -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT a, b FROM insert_select_null_dist_key.reference_table t1 WHERE (a OPERATOR(pg_catalog.=) ANY (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer))) +DEBUG: generating subplan XXX_1 for subquery SELECT a FROM (SELECT t3.a FROM ((SELECT distributed_table_c1_t1.a FROM insert_select_single_shard_table.distributed_table_c1_t1 WHERE (distributed_table_c1_t1.b OPERATOR(pg_catalog.>) 4)) t3 JOIN (SELECT distributed_table_c1_t2.a FROM insert_select_single_shard_table.distributed_table_c1_t2 WHERE (distributed_table_c1_t2.b OPERATOR(pg_catalog.<) 7)) t4 ON ((t3.a OPERATOR(pg_catalog.=) t4.a)))) t2 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT a, b FROM insert_select_single_shard_table.reference_table t1 WHERE (a OPERATOR(pg_catalog.=) ANY (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer))) DEBUG: Collecting INSERT ... SELECT results on coordinator SET client_min_messages TO DEBUG2; -- test upsert with plain INSERT query @@ -735,7 +735,7 @@ DEBUG: ALTER TABLE / ADD PRIMARY KEY will create implicit index "distributed_ta DEBUG: verifying table "distributed_table_c1_t1" INSERT INTO nullkey_c1_t1 AS t1 (a, b) SELECT t3.a, t3.b FROM nullkey_c1_t2 t2 JOIN reference_table t3 ON (t2.a = t3.a) ON CONFLICT (a) DO UPDATE SET a = t1.a + 10; -DEBUG: distributed statement: INSERT INTO insert_select_null_dist_key.nullkey_c1_t1_1820000 AS t1 (a, b) SELECT t3.a, t3.b FROM (insert_select_null_dist_key.nullkey_c1_t2_1820001 t2 JOIN insert_select_null_dist_key.reference_table_1820003 t3 ON ((t2.a OPERATOR(pg_catalog.=) t3.a))) ON CONFLICT(a) DO UPDATE SET a = (t1.a OPERATOR(pg_catalog.+) 10) +DEBUG: distributed statement: INSERT INTO insert_select_single_shard_table.nullkey_c1_t1_1820000 AS t1 (a, b) SELECT t3.a, t3.b FROM (insert_select_single_shard_table.nullkey_c1_t2_1820001 t2 JOIN insert_select_single_shard_table.reference_table_1820003 t3 ON ((t2.a OPERATOR(pg_catalog.=) t3.a))) ON CONFLICT(a) DO UPDATE SET a = (t1.a OPERATOR(pg_catalog.+) 10) SET client_min_messages TO DEBUG1; INSERT INTO distributed_table_c1_t1 AS t1 (a, b) SELECT t3.a, t3.b FROM nullkey_c1_t2 t2 JOIN reference_table t3 ON (t2.a = t3.a) ON CONFLICT (a, b) DO UPDATE SET b = t1.b + 10; @@ -805,7 +805,7 @@ DEBUG: Creating router plan (3 rows) SET client_min_messages TO WARNING; -DROP SCHEMA insert_select_null_dist_key CASCADE; +DROP SCHEMA insert_select_single_shard_table CASCADE; SELECT citus_remove_node('localhost', :master_port); citus_remove_node --------------------------------------------------------------------- diff --git a/src/test/regress/expected/merge.out b/src/test/regress/expected/merge.out index fd82efa8c..190e6b2b3 100644 --- a/src/test/regress/expected/merge.out +++ b/src/test/regress/expected/merge.out @@ -3228,9 +3228,9 @@ WHEN NOT MATCHED THEN INSERT VALUES(dist_source.id, dist_source.val); ERROR: For MERGE command, all the distributed tables must be colocated, for append/range distribution, colocation is not supported HINT: Consider using hash distribution instead --- test merge with null shard key tables -CREATE SCHEMA query_null_dist_key; -SET search_path TO query_null_dist_key; +-- test merge with single-shard tables +CREATE SCHEMA query_single_shard_table; +SET search_path TO query_single_shard_table; SET client_min_messages TO DEBUG2; CREATE TABLE nullkey_c1_t1(a int, b int); CREATE TABLE nullkey_c1_t2(a int, b int); @@ -3295,23 +3295,23 @@ INSERT INTO postgres_local_table SELECT i, i FROM generate_series(5, 10) i; -- with a colocated table MERGE INTO nullkey_c1_t1 USING nullkey_c1_t2 ON (nullkey_c1_t1.a = nullkey_c1_t2.a) WHEN MATCHED THEN UPDATE SET b = nullkey_c1_t2.b; -DEBUG: +DEBUG: DEBUG: Creating MERGE router plan MERGE INTO nullkey_c1_t1 USING nullkey_c1_t2 ON (nullkey_c1_t1.a = nullkey_c1_t2.a) WHEN MATCHED THEN DELETE; -DEBUG: +DEBUG: DEBUG: Creating MERGE router plan MERGE INTO nullkey_c1_t1 USING nullkey_c1_t2 ON (nullkey_c1_t1.a = nullkey_c1_t2.a) WHEN MATCHED THEN UPDATE SET b = nullkey_c1_t2.b WHEN NOT MATCHED THEN INSERT VALUES (nullkey_c1_t2.a, nullkey_c1_t2.b); -DEBUG: +DEBUG: DEBUG: Creating MERGE router plan MERGE INTO nullkey_c1_t1 USING nullkey_c1_t2 ON (nullkey_c1_t1.a = nullkey_c1_t2.a) WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT VALUES (nullkey_c1_t2.a, nullkey_c1_t2.b); -DEBUG: +DEBUG: DEBUG: Creating MERGE router plan --- with non-colocated null-dist-key table +-- with non-colocated single-shard table MERGE INTO nullkey_c1_t1 USING nullkey_c2_t1 ON (nullkey_c1_t1.a = nullkey_c2_t1.a) WHEN MATCHED THEN UPDATE SET b = nullkey_c2_t1.b; ERROR: For MERGE command, all the distributed tables must be colocated @@ -3331,11 +3331,12 @@ ERROR: For MERGE command, all the distributed tables must be colocated -- with a reference table MERGE INTO nullkey_c1_t1 USING reference_table ON (nullkey_c1_t1.a = reference_table.a) WHEN MATCHED THEN UPDATE SET b = reference_table.b; -ERROR: MERGE command is not supported on reference tables yet +ERROR: MERGE command is not supported with combination of distributed/reference yet +HINT: If target is distributed, source must be distributed and co-located MERGE INTO reference_table USING nullkey_c1_t1 ON (nullkey_c1_t1.a = reference_table.a) WHEN MATCHED THEN UPDATE SET b = nullkey_c1_t1.b WHEN NOT MATCHED THEN INSERT VALUES (nullkey_c1_t1.a, nullkey_c1_t1.b); -ERROR: MERGE command is not supported on reference tables yet +ERROR: Reference table as target is not allowed in MERGE command -- with a citus local table MERGE INTO nullkey_c1_t1 USING citus_local_table ON (nullkey_c1_t1.a = citus_local_table.a) WHEN MATCHED THEN UPDATE SET b = citus_local_table.b; @@ -3357,7 +3358,7 @@ WITH cte AS ( ) MERGE INTO nullkey_c1_t1 USING cte ON (nullkey_c1_t1.a = cte.a) WHEN MATCHED THEN UPDATE SET b = cte.b; -DEBUG: +DEBUG: DEBUG: Creating MERGE router plan WITH cte AS ( SELECT * FROM distributed_table @@ -3372,7 +3373,7 @@ MERGE INTO nullkey_c1_t1 USING cte ON (nullkey_c1_t1.a = cte.a) WHEN MATCHED THEN UPDATE SET b = cte.b; ERROR: For MERGE command, all the distributed tables must be colocated SET client_min_messages TO WARNING; -DROP SCHEMA query_null_dist_key CASCADE; +DROP SCHEMA query_single_shard_table CASCADE; RESET client_min_messages; SET search_path TO merge_schema; DROP SERVER foreign_server CASCADE; diff --git a/src/test/regress/expected/multi_extension.out b/src/test/regress/expected/multi_extension.out index 56de93802..dad925072 100644 --- a/src/test/regress/expected/multi_extension.out +++ b/src/test/regress/expected/multi_extension.out @@ -1735,7 +1735,7 @@ HINT: To remove the local data, run: SELECT truncate_local_data_after_distribut DROP TABLE test; TRUNCATE pg_dist_node; --- confirm that we can create a null shard key table on an empty node +-- confirm that we can create a single-shard table on an empty node CREATE TABLE test (x int, y int); INSERT INTO test VALUES (1,2); SET citus.shard_replication_factor TO 1; diff --git a/src/test/regress/expected/query_null_dist_key.out b/src/test/regress/expected/query_single_shard_table.out similarity index 98% rename from src/test/regress/expected/query_null_dist_key.out rename to src/test/regress/expected/query_single_shard_table.out index 09907a99b..a7efbf7be 100644 --- a/src/test/regress/expected/query_null_dist_key.out +++ b/src/test/regress/expected/query_single_shard_table.out @@ -1,5 +1,5 @@ -CREATE SCHEMA query_null_dist_key; -SET search_path TO query_null_dist_key; +CREATE SCHEMA query_single_shard_table; +SET search_path TO query_single_shard_table; SET citus.next_shard_id TO 1620000; SET citus.shard_count TO 32; SET client_min_messages TO WARNING; @@ -92,7 +92,7 @@ SELECT create_distributed_table('articles_hash', null, colocate_with=>'none'); NOTICE: Copying data from local table... NOTICE: copying the data has completed DETAIL: The local data in the table is no longer visible, but is still on disk. -HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$query_null_dist_key.articles_hash$$) +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$query_single_shard_table.articles_hash$$) create_distributed_table --------------------------------------------------------------------- @@ -247,7 +247,7 @@ DETAIL: Local tables cannot be used in distributed queries. SELECT COUNT(*) FROM postgres_local_table d1, nullkey_c1_t1; ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables DETAIL: Local tables cannot be used in distributed queries. --- with a colocated null dist key table +-- with a colocated single-shard table SELECT COUNT(*) FROM nullkey_c1_t1 d1, nullkey_c1_t2; DEBUG: Creating router plan count @@ -255,14 +255,14 @@ DEBUG: Creating router plan 110 (1 row) --- with a non-colocated null dist key table +-- with a non-colocated single-shard table SELECT COUNT(*) FROM nullkey_c1_t1 d1, nullkey_c2_t1; ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables DETAIL: found no worker with all shard placements -- First, show that nullkey_c1_t1 and nullkey_c3_t1 are not colocated. SELECT - (SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'query_null_dist_key.nullkey_c1_t1'::regclass) != - (SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'query_null_dist_key.nullkey_c3_t1'::regclass); + (SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'query_single_shard_table.nullkey_c1_t1'::regclass) != + (SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'query_single_shard_table.nullkey_c3_t1'::regclass); ?column? --------------------------------------------------------------------- t @@ -284,7 +284,7 @@ SELECT COUNT(*) FROM nullkey_c1_t1 JOIN nullkey_c3_t1 USING(a); ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables DETAIL: router planner does not support queries that reference non-colocated distributed tables RESET citus.enable_non_colocated_router_query_pushdown; --- colocated join between null dist key tables +-- colocated join between single-shard tables SELECT COUNT(*) FROM nullkey_c1_t1 JOIN nullkey_c1_t2 USING(a); DEBUG: Creating router plan count @@ -356,7 +356,7 @@ DEBUG: Creating router plan 7 (1 row) --- non-colocated inner joins between null dist key tables +-- non-colocated inner joins between single-shard tables SELECT * FROM nullkey_c1_t1 JOIN nullkey_c2_t1 USING(a) ORDER BY 1,2,3; ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables DETAIL: found no worker with all shard placements @@ -366,7 +366,7 @@ JOIN LATERAL ( ) q USING(a); ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables DETAIL: found no worker with all shard placements --- non-colocated outer joins between null dist key tables +-- non-colocated outer joins between single-shard tables SELECT * FROM nullkey_c1_t1 LEFT JOIN nullkey_c2_t2 USING(a) ORDER BY 1,2,3 LIMIT 4; ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables DETAIL: found no worker with all shard placements @@ -803,7 +803,7 @@ JOIN LATERAL ( ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables DETAIL: Local tables cannot be used in distributed queries. -- insert .. select --- between two colocated null dist key tables +-- between two colocated single-shard tables -- The target list of "distributed statement"s that we send to workers -- differ(*) in Postgres versions < 15. For this reason, we temporarily -- disable debug messages here and run the EXPLAIN'ed version of the @@ -825,13 +825,13 @@ INSERT INTO nullkey_c1_t1 SELECT * FROM nullkey_c1_t2; (7 rows) SET client_min_messages TO DEBUG2; --- between two non-colocated null dist key tables +-- between two non-colocated single-shard tables INSERT INTO nullkey_c1_t1 SELECT * FROM nullkey_c2_t1; DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables DEBUG: Distributed planning for a fast-path router query DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator --- between a null dist key table and a table of different type +-- between a single-shard table and a table of different type SET client_min_messages TO WARNING; EXPLAIN (ANALYZE TRUE, TIMING FALSE, COSTS FALSE, SUMMARY FALSE, VERBOSE FALSE) INSERT INTO nullkey_c1_t1 SELECT * FROM reference_table; @@ -1300,8 +1300,8 @@ DEBUG: CTE cte is going to be inlined via distributed planning DEBUG: recursively planning left side of the right join since the outer side is a recurring rel DEBUG: recursively planning distributed relation "distributed_table" since it is part of a distributed join node that is outer joined with a recurring rel DEBUG: Wrapping relation "distributed_table" to a subquery -DEBUG: generating subplan XXX_1 for subquery SELECT a FROM query_null_dist_key.distributed_table WHERE true -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT "?column?" AS user_id, b AS value_1 FROM (SELECT ((cte.a OPERATOR(pg_catalog.+) 5) OPERATOR(pg_catalog.*) '-1'::integer), cte.b FROM (SELECT DISTINCT reference_table.a, 1 AS b FROM ((SELECT distributed_table_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) distributed_table_1) distributed_table RIGHT JOIN query_null_dist_key.reference_table USING (a))) cte) citus_insert_select_subquery("?column?", b) +DEBUG: generating subplan XXX_1 for subquery SELECT a FROM query_single_shard_table.distributed_table WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT "?column?" AS user_id, b AS value_1 FROM (SELECT ((cte.a OPERATOR(pg_catalog.+) 5) OPERATOR(pg_catalog.*) '-1'::integer), cte.b FROM (SELECT DISTINCT reference_table.a, 1 AS b FROM ((SELECT distributed_table_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) distributed_table_1) distributed_table RIGHT JOIN query_single_shard_table.reference_table USING (a))) cte) citus_insert_select_subquery("?column?", b) DEBUG: Collecting INSERT ... SELECT results on coordinator -- .. and via SELECT's cte list too INSERT INTO raw_events_second (user_id, value_1) @@ -1315,8 +1315,8 @@ DEBUG: distributed INSERT ... SELECT cannot reference a distributed table witho DEBUG: recursively planning left side of the right join since the outer side is a recurring rel DEBUG: recursively planning distributed relation "distributed_table" since it is part of a distributed join node that is outer joined with a recurring rel DEBUG: Wrapping relation "distributed_table" to a subquery -DEBUG: generating subplan XXX_1 for subquery SELECT a FROM query_null_dist_key.distributed_table WHERE true -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT ((a OPERATOR(pg_catalog.+) 5) OPERATOR(pg_catalog.*) 2) AS user_id, b AS value_1 FROM (SELECT DISTINCT reference_table.a, 1 AS b FROM ((SELECT distributed_table_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) distributed_table_1) distributed_table RIGHT JOIN query_null_dist_key.reference_table USING (a))) cte +DEBUG: generating subplan XXX_1 for subquery SELECT a FROM query_single_shard_table.distributed_table WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT ((a OPERATOR(pg_catalog.+) 5) OPERATOR(pg_catalog.*) 2) AS user_id, b AS value_1 FROM (SELECT DISTINCT reference_table.a, 1 AS b FROM ((SELECT distributed_table_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) distributed_table_1) distributed_table RIGHT JOIN query_single_shard_table.reference_table USING (a))) cte DEBUG: Collecting INSERT ... SELECT results on coordinator -- using set operations INSERT INTO @@ -1738,7 +1738,7 @@ WITH cte AS ( SELECT * FROM nullkey_c1_t1 WHERE a IN (SELECT a FROM cte); DEBUG: Creating router plan DEBUG: query has a single distribution column value: 1 -ERROR: relation "query_null_dist_key.nullkey_c1_t1_1620000" does not exist +ERROR: relation "query_single_shard_table.nullkey_c1_t1_1620000" does not exist CONTEXT: while executing command on localhost:xxxxx WITH cte AS ( DELETE FROM distributed_table WHERE a = 1 RETURNING * @@ -1746,7 +1746,7 @@ WITH cte AS ( SELECT * FROM nullkey_c1_t1 WHERE b IN (SELECT b FROM cte); DEBUG: Creating router plan DEBUG: query has a single distribution column value: 1 -ERROR: relation "query_null_dist_key.nullkey_c1_t1_1620000" does not exist +ERROR: relation "query_single_shard_table.nullkey_c1_t1_1620000" does not exist CONTEXT: while executing command on localhost:xxxxx SET citus.enable_non_colocated_router_query_pushdown TO OFF; WITH cte AS ( @@ -1864,7 +1864,7 @@ DEBUG: Creating router plan (10 rows) SET client_min_messages TO ERROR; -DROP SCHEMA query_null_dist_key CASCADE; +DROP SCHEMA query_single_shard_table CASCADE; SELECT citus_remove_node('localhost', :master_port); citus_remove_node --------------------------------------------------------------------- diff --git a/src/test/regress/expected/single_node.out b/src/test/regress/expected/single_node.out index ecb652931..5efad578e 100644 --- a/src/test/regress/expected/single_node.out +++ b/src/test/regress/expected/single_node.out @@ -152,7 +152,7 @@ WHERE shardid = ( t (1 row) --- try creating a null-shard-key distributed table from a shard relation +-- try creating a single-shard table from a shard relation SELECT shardid AS round_robin_test_c1_shard_id FROM pg_dist_shard WHERE logicalrelid = 'single_node.single_node_nullkey_c1'::regclass \gset SELECT create_distributed_table('single_node_nullkey_c1_' || :round_robin_test_c1_shard_id , null, colocate_with=>'none', distribution_type=>null); ERROR: relation "single_node_nullkey_c1_90630532" is a shard relation diff --git a/src/test/regress/expected/single_node_0.out b/src/test/regress/expected/single_node_0.out index 86b4982e6..446db9aed 100644 --- a/src/test/regress/expected/single_node_0.out +++ b/src/test/regress/expected/single_node_0.out @@ -152,7 +152,7 @@ WHERE shardid = ( t (1 row) --- try creating a null-shard-key distributed table from a shard relation +-- try creating a single-shard table from a shard relation SELECT shardid AS round_robin_test_c1_shard_id FROM pg_dist_shard WHERE logicalrelid = 'single_node.single_node_nullkey_c1'::regclass \gset SELECT create_distributed_table('single_node_nullkey_c1_' || :round_robin_test_c1_shard_id , null, colocate_with=>'none', distribution_type=>null); ERROR: relation "single_node_nullkey_c1_90630532" is a shard relation diff --git a/src/test/regress/expected/null_dist_key_prep.out b/src/test/regress/expected/single_shard_table_prep.out similarity index 100% rename from src/test/regress/expected/null_dist_key_prep.out rename to src/test/regress/expected/single_shard_table_prep.out diff --git a/src/test/regress/multi_1_schedule b/src/test/regress/multi_1_schedule index 9163a5864..b5943f899 100644 --- a/src/test/regress/multi_1_schedule +++ b/src/test/regress/multi_1_schedule @@ -32,7 +32,7 @@ test: escape_extension_name test: ref_citus_local_fkeys test: alter_database_owner test: distributed_triggers -test: create_null_dist_key +test: create_single_shard_table test: multi_test_catalog_views test: multi_table_ddl @@ -68,7 +68,7 @@ test: multi_master_protocol multi_load_data multi_load_data_superuser multi_beha test: multi_behavioral_analytics_basics multi_behavioral_analytics_single_shard_queries multi_insert_select_non_pushable_queries multi_insert_select multi_behavioral_analytics_create_table_superuser test: multi_shard_update_delete recursive_dml_with_different_planners_executors test: insert_select_repartition window_functions dml_recursive multi_insert_select_window -test: multi_insert_select_conflict citus_table_triggers alter_table_null_dist_key +test: multi_insert_select_conflict citus_table_triggers alter_table_single_shard_table test: multi_row_insert insert_select_into_local_table alter_index # following should not run in parallel because it relies on connection counts to workers @@ -200,8 +200,8 @@ test: local_table_join test: local_dist_join_mixed test: citus_local_dist_joins test: recurring_outer_join -test: query_null_dist_key -test: insert_select_null_dist_key +test: query_single_shard_table +test: insert_select_single_shard_table test: pg_dump # --------- diff --git a/src/test/regress/null_dist_key_prep_schedule b/src/test/regress/null_dist_key_prep_schedule deleted file mode 100644 index 1a43130ec..000000000 --- a/src/test/regress/null_dist_key_prep_schedule +++ /dev/null @@ -1 +0,0 @@ -test: null_dist_key_prep diff --git a/src/test/regress/single_shard_table_prep_schedule b/src/test/regress/single_shard_table_prep_schedule new file mode 100644 index 000000000..7410cf05f --- /dev/null +++ b/src/test/regress/single_shard_table_prep_schedule @@ -0,0 +1 @@ +test: single_shard_table_prep diff --git a/src/test/regress/sql/alter_table_null_dist_key.sql b/src/test/regress/sql/alter_table_single_shard_table.sql similarity index 100% rename from src/test/regress/sql/alter_table_null_dist_key.sql rename to src/test/regress/sql/alter_table_single_shard_table.sql diff --git a/src/test/regress/sql/create_null_dist_key.sql b/src/test/regress/sql/create_single_shard_table.sql similarity index 91% rename from src/test/regress/sql/create_null_dist_key.sql rename to src/test/regress/sql/create_single_shard_table.sql index 9ca943d75..c7a2d49c2 100644 --- a/src/test/regress/sql/create_null_dist_key.sql +++ b/src/test/regress/sql/create_single_shard_table.sql @@ -1,5 +1,5 @@ -CREATE SCHEMA create_null_dist_key; -SET search_path TO create_null_dist_key; +CREATE SCHEMA create_single_shard_table; +SET search_path TO create_single_shard_table; SET citus.next_shard_id TO 1720000; SET citus.shard_count TO 32; @@ -10,17 +10,17 @@ SELECT 1 FROM citus_remove_node('localhost', :worker_2_port); CREATE TABLE add_node_test(a int, "b" text); --- add a node before creating the null-shard-key table +-- add a node before creating the single-shard table SELECT 1 FROM citus_add_node('localhost', :worker_1_port); SELECT create_distributed_table('add_node_test', null, colocate_with=>'none', distribution_type=>null); --- add a node after creating the null-shard-key table +-- add a node after creating the single-shard table SELECT 1 FROM citus_add_node('localhost', :worker_2_port); -- make sure that table is created on the worker nodes added before/after create_distributed_table SELECT result FROM run_command_on_workers($$ - SELECT COUNT(*)=1 FROM pg_class WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + SELECT COUNT(*)=1 FROM pg_class WHERE relnamespace = 'create_single_shard_table'::regnamespace AND relname='add_node_test' $$); @@ -28,19 +28,19 @@ $$); SELECT result FROM run_command_on_workers($$ SELECT (partmethod, partkey, repmodel, autoconverted) FROM pg_dist_partition - WHERE logicalrelid = 'create_null_dist_key.add_node_test'::regclass + WHERE logicalrelid = 'create_single_shard_table.add_node_test'::regclass $$); SELECT result FROM run_command_on_workers($$ SELECT (shardstorage, shardminvalue, shardmaxvalue) FROM pg_dist_shard - WHERE logicalrelid = 'create_null_dist_key.add_node_test'::regclass + WHERE logicalrelid = 'create_single_shard_table.add_node_test'::regclass $$); SELECT result FROM run_command_on_workers($$ SELECT COUNT(*)=1 FROM pg_dist_placement WHERE shardid = ( SELECT shardid FROM pg_dist_shard - WHERE logicalrelid = 'create_null_dist_key.add_node_test'::regclass + WHERE logicalrelid = 'create_single_shard_table.add_node_test'::regclass ); $$); @@ -48,7 +48,7 @@ SELECT result FROM run_command_on_workers($$ SELECT (shardcount, replicationfactor, distributioncolumntype, distributioncolumncollation) FROM pg_dist_colocation WHERE colocationid = ( SELECT colocationid FROM pg_dist_partition - WHERE logicalrelid = 'create_null_dist_key.add_node_test'::regclass + WHERE logicalrelid = 'create_single_shard_table.add_node_test'::regclass ); $$); @@ -66,7 +66,7 @@ CREATE TABLE nullkey_c1_t2(a int, b int); CREATE TABLE nullkey_c1_t3(a int, b int); SELECT create_distributed_table('nullkey_c1_t1', null, colocate_with=>'none'); -SELECT colocationid AS nullkey_c1_t1_colocation_id FROM pg_dist_partition WHERE logicalrelid = 'create_null_dist_key.nullkey_c1_t1'::regclass \gset +SELECT colocationid AS nullkey_c1_t1_colocation_id FROM pg_dist_partition WHERE logicalrelid = 'create_single_shard_table.nullkey_c1_t1'::regclass \gset BEGIN; DROP TABLE nullkey_c1_t1; @@ -95,7 +95,7 @@ SELECT create_distributed_table('nullkey_c2_t3', null, colocate_with=>'nullkey_c SELECT logicalrelid, partmethod, partkey, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid IN ( SELECT oid FROM pg_class - WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + WHERE relnamespace = 'create_single_shard_table'::regnamespace AND relname LIKE 'nullkey_c1_%' ) ORDER BY 1; @@ -104,7 +104,7 @@ ORDER BY 1; SELECT COUNT(*) FROM pg_dist_partition WHERE logicalrelid IN ( SELECT oid FROM pg_class - WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + WHERE relnamespace = 'create_single_shard_table'::regnamespace AND relname LIKE 'nullkey_c1_%' ) GROUP BY colocationid; @@ -112,7 +112,7 @@ GROUP BY colocationid; SELECT logicalrelid, shardstorage, shardminvalue, shardmaxvalue FROM pg_dist_shard WHERE logicalrelid IN ( SELECT oid FROM pg_class - WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + WHERE relnamespace = 'create_single_shard_table'::regnamespace AND relname LIKE 'nullkey_c1_%' ) ORDER BY 1; @@ -123,7 +123,7 @@ WHERE shardid IN ( SELECT shardid FROM pg_dist_shard WHERE logicalrelid IN ( SELECT oid FROM pg_class - WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + WHERE relnamespace = 'create_single_shard_table'::regnamespace AND relname LIKE 'nullkey_c1_%' ) ) @@ -134,7 +134,7 @@ GROUP BY groupid; SELECT logicalrelid, partmethod, partkey, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid IN ( SELECT oid FROM pg_class - WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + WHERE relnamespace = 'create_single_shard_table'::regnamespace AND relname LIKE 'nullkey_c2_%' ) ORDER BY 1; @@ -143,7 +143,7 @@ ORDER BY 1; SELECT COUNT(*) FROM pg_dist_partition WHERE logicalrelid IN ( SELECT oid FROM pg_class - WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + WHERE relnamespace = 'create_single_shard_table'::regnamespace AND relname LIKE 'nullkey_c2_%' ) GROUP BY colocationid; @@ -151,7 +151,7 @@ GROUP BY colocationid; SELECT logicalrelid, shardstorage, shardminvalue, shardmaxvalue FROM pg_dist_shard WHERE logicalrelid IN ( SELECT oid FROM pg_class - WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + WHERE relnamespace = 'create_single_shard_table'::regnamespace AND relname LIKE 'nullkey_c2_%' ) ORDER BY 1; @@ -162,7 +162,7 @@ WHERE shardid IN ( SELECT shardid FROM pg_dist_shard WHERE logicalrelid IN ( SELECT oid FROM pg_class - WHERE relnamespace = 'create_null_dist_key'::regnamespace AND + WHERE relnamespace = 'create_single_shard_table'::regnamespace AND relname LIKE 'nullkey_c2_%' ) ) @@ -178,12 +178,12 @@ GROUP BY groupid; SELECT ( SELECT colocationid FROM pg_dist_partition - WHERE logicalrelid = 'create_null_dist_key.nullkey_c1_t1'::regclass + WHERE logicalrelid = 'create_single_shard_table.nullkey_c1_t1'::regclass ) != ( SELECT colocationid FROM pg_dist_partition - WHERE logicalrelid = 'create_null_dist_key.nullkey_c2_t1'::regclass + WHERE logicalrelid = 'create_single_shard_table.nullkey_c2_t1'::regclass ); -- Since we determine node for the placement based on the module of colocation id, @@ -193,7 +193,7 @@ SELECT SELECT groupid FROM pg_dist_placement WHERE shardid = ( SELECT shardid FROM pg_dist_shard - WHERE logicalrelid = 'create_null_dist_key.nullkey_c1_t1'::regclass + WHERE logicalrelid = 'create_single_shard_table.nullkey_c1_t1'::regclass ) ) != @@ -201,7 +201,7 @@ SELECT SELECT groupid FROM pg_dist_placement WHERE shardid = ( SELECT shardid FROM pg_dist_shard - WHERE logicalrelid = 'create_null_dist_key.nullkey_c2_t1'::regclass + WHERE logicalrelid = 'create_single_shard_table.nullkey_c2_t1'::regclass ) ); @@ -211,20 +211,20 @@ SELECT SELECT shardcount, replicationfactor, distributioncolumntype, distributioncolumncollation FROM pg_dist_colocation WHERE colocationid = ( SELECT colocationid FROM pg_dist_partition - WHERE logicalrelid = 'create_null_dist_key.nullkey_c1_t1'::regclass + WHERE logicalrelid = 'create_single_shard_table.nullkey_c1_t1'::regclass ); SELECT shardcount, replicationfactor, distributioncolumntype, distributioncolumncollation FROM pg_dist_colocation WHERE colocationid = ( SELECT colocationid FROM pg_dist_partition - WHERE logicalrelid = 'create_null_dist_key.nullkey_c2_t1'::regclass + WHERE logicalrelid = 'create_single_shard_table.nullkey_c2_t1'::regclass ); CREATE TABLE round_robin_test_c1(a int, b int); SELECT create_distributed_table('round_robin_test_c1', null, colocate_with=>'none', distribution_type=>null); \c - - - :master_port -SET search_path TO create_null_dist_key; +SET search_path TO create_single_shard_table; SET citus.next_shard_id TO 1730000; SET citus.shard_count TO 32; SET citus.shard_replication_factor TO 1; @@ -241,7 +241,7 @@ SELECT SELECT groupid FROM pg_dist_placement WHERE shardid = ( SELECT shardid FROM pg_dist_shard - WHERE logicalrelid = 'create_null_dist_key.round_robin_test_c1'::regclass + WHERE logicalrelid = 'create_single_shard_table.round_robin_test_c1'::regclass ) ) != @@ -249,13 +249,13 @@ SELECT SELECT groupid FROM pg_dist_placement WHERE shardid = ( SELECT shardid FROM pg_dist_shard - WHERE logicalrelid = 'create_null_dist_key.round_robin_test_c2'::regclass + WHERE logicalrelid = 'create_single_shard_table.round_robin_test_c2'::regclass ) ); CREATE TABLE distributed_table(a int, b int); --- cannot colocate a sharded table with null shard key table +-- cannot colocate a sharded table with single-shard table SELECT create_distributed_table('distributed_table', 'a', colocate_with=>'nullkey_c1_t1'); CREATE TABLE reference_table(a int, b int); @@ -263,7 +263,7 @@ CREATE TABLE local(a int, b int); SELECT create_reference_table('reference_table'); SELECT create_distributed_table('distributed_table', 'a'); --- cannot colocate null shard key tables with other table types +-- cannot colocate single-shard tables with other table types CREATE TABLE cannot_colocate_with_other_types (a int, b int); SELECT create_distributed_table('cannot_colocate_with_other_types', null, colocate_with=>'reference_table'); SELECT create_distributed_table('cannot_colocate_with_other_types', null, colocate_with=>'distributed_table'); @@ -271,7 +271,7 @@ SELECT create_distributed_table('cannot_colocate_with_other_types', null, coloca SELECT citus_add_local_table_to_metadata('local'); --- cannot colocate null shard key tables with citus local tables +-- cannot colocate single-shard tables with citus local tables SELECT create_distributed_table('cannot_colocate_with_other_types', null, colocate_with=>'local'); -- citus local SET client_min_messages TO WARNING; @@ -282,30 +282,30 @@ SELECT create_distributed_table('distributed_table', null, colocate_with=>'none' SELECT create_distributed_table('local', null, colocate_with=>'none'); BEGIN; - -- creating a null-shard-key table from a temporary table is not supported + -- creating a single-shard table from a temporary table is not supported CREATE TEMPORARY TABLE temp_table (a int); SELECT create_distributed_table('temp_table', null, colocate_with=>'none', distribution_type=>null); ROLLBACK; --- creating a null-shard-key table from a catalog table is not supported +-- creating a single-shard table from a catalog table is not supported SELECT create_distributed_table('pg_catalog.pg_index', NULL, distribution_type=>null); --- creating a null-shard-key table from an unlogged table is supported +-- creating a single-shard table from an unlogged table is supported CREATE UNLOGGED TABLE unlogged_table (a int); SELECT create_distributed_table('unlogged_table', null, colocate_with=>'none', distribution_type=>null); --- creating a null-shard-key table from a foreign table is not supported +-- creating a single-shard table from a foreign table is not supported CREATE FOREIGN TABLE foreign_table ( id bigint not null, full_name text not null default '' ) SERVER fake_fdw_server OPTIONS (encoding 'utf-8', compression 'true', table_name 'foreign_table'); SELECT create_distributed_table('foreign_table', null, colocate_with=>'none', distribution_type=>null); --- create a null dist key table that has no tuples +-- create a single-shard table that has no tuples CREATE TABLE null_dist_key_table_1 (a int primary key); SELECT create_distributed_table('null_dist_key_table_1', null, colocate_with=>'none'); --- create a null dist key table that has some tuples +-- create a single-shard table that has some tuples CREATE TABLE null_dist_key_table_2(a int primary key); INSERT INTO null_dist_key_table_2 VALUES(1); SELECT create_distributed_table('null_dist_key_table_2', null, colocate_with=>'none'); @@ -314,7 +314,7 @@ SELECT * FROM null_dist_key_table_2 ORDER BY a; DROP TABLE null_dist_key_table_1, null_dist_key_table_2; --- create indexes before creating the null dist key tables +-- create indexes before creating the single-shard tables -- .. for an initially empty table CREATE TABLE null_dist_key_table_1(a int, b int); @@ -323,7 +323,7 @@ CREATE INDEX null_dist_key_table_1_idx ON null_dist_key_table_1(a); SELECT create_distributed_table('null_dist_key_table_1', null, colocate_with=>'none'); CREATE STATISTICS s2 (dependencies) ON a, b FROM null_dist_key_table_1; --- .. and for another table having data in it before creating null dist key table +-- .. and for another table having data in it before creating single-shard table CREATE TABLE null_dist_key_table_2(a int); INSERT INTO null_dist_key_table_2 VALUES(1); CREATE INDEX null_dist_key_table_2_idx ON null_dist_key_table_2(a); @@ -435,7 +435,7 @@ INSERT INTO "Table?!.1Table"(id, jsondata, yes_no_enum_col) VALUES (101, '{"a": SELECT * FROM "Table?!.1Table" ORDER BY id; -SET search_path TO create_null_dist_key; +SET search_path TO create_single_shard_table; -- create a partitioned table with some columns that -- are going to be dropped within the tests @@ -460,11 +460,11 @@ SELECT create_distributed_table('sensors', NULL, distribution_type=>null); -- verify we can create new partitions after distributing the parent table CREATE TABLE sensors_2001 PARTITION OF sensors FOR VALUES FROM ('2001-01-01') TO ('2002-01-01'); --- verify we can attach to a null dist key table +-- verify we can attach to a single-shard table CREATE TABLE sensors_2002 (measureid integer, eventdatetime date, measure_data jsonb, PRIMARY KEY (measureid, eventdatetime, measure_data)); ALTER TABLE sensors ATTACH PARTITION sensors_2002 FOR VALUES FROM ('2002-01-01') TO ('2003-01-01'); --- verify we can detach from a null dist key table +-- verify we can detach from a single-shard table ALTER TABLE sensors DETACH PARTITION sensors_2001; -- error out when attaching a noncolocated partition @@ -507,7 +507,7 @@ SELECT COUNT(*) FROM run_command_on_workers($$ SELECT relpartbound FROM pg_class WHERE relname LIKE 'sensors_2002_1______';$$) WHERE length(result) > 0; --- create a partitioned citus local table and verify we error out when attaching a partition with null dist key +-- create a partitioned citus local table and verify we error out when attaching a partition with single-shard CREATE TABLE partitioned_citus_local_tbl( measureid integer, eventdatetime date, @@ -629,7 +629,7 @@ ALTER TABLE "NULL_!_dist_key"."nullKeyTable.1!?!90123456789012345678901234567890 -- Normally, we support foreign keys from Postgres tables to distributed -- tables assuming that the user will soon distribute the local table too --- anyway. However, this is not the case for null-shard-key tables before +-- anyway. However, this is not the case for single-shard tables before -- we improve SQL support. ALTER TABLE local_table_for_fkey ADD CONSTRAINT fkey_from_dummy_local FOREIGN KEY (a) REFERENCES "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"(id); @@ -680,7 +680,7 @@ ALTER TABLE null_key_dist DROP CONSTRAINT fkey_add_test_3; ALTER TABLE null_key_dist DROP CONSTRAINT fkey_add_test_4; ALTER TABLE "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" DROP CONSTRAINT fkey_to_dummy_dist; --- create a view that depends on the null shard key table +-- create a view that depends on the single-shard table CREATE VIEW public.v1 AS SELECT * FROM null_key_dist; SELECT * FROM public.v1; @@ -755,24 +755,24 @@ INSERT INTO identity_test (b) OVERRIDING SYSTEM VALUE VALUES (5); INSERT INTO identity_test (c) VALUES (5); SELECT result, success FROM run_command_on_workers($$ - INSERT INTO create_null_dist_key.identity_test (a) VALUES (6) + INSERT INTO create_single_shard_table.identity_test (a) VALUES (6) $$); SELECT result, success FROM run_command_on_workers($$ - SELECT create_null_dist_key.normalize_generate_always_as_error('INSERT INTO create_null_dist_key.identity_test (b) VALUES (1)') + SELECT create_single_shard_table.normalize_generate_always_as_error('INSERT INTO create_single_shard_table.identity_test (b) VALUES (1)') $$); -- This should fail due to missing OVERRIDING SYSTEM VALUE. SELECT result, success FROM run_command_on_workers($$ - SELECT create_null_dist_key.normalize_generate_always_as_error('INSERT INTO create_null_dist_key.identity_test (a, b) VALUES (1, 1)') + SELECT create_single_shard_table.normalize_generate_always_as_error('INSERT INTO create_single_shard_table.identity_test (a, b) VALUES (1, 1)') $$); SELECT result, success FROM run_command_on_workers($$ - INSERT INTO create_null_dist_key.identity_test (a, b) OVERRIDING SYSTEM VALUE VALUES (7, 7) + INSERT INTO create_single_shard_table.identity_test (a, b) OVERRIDING SYSTEM VALUE VALUES (7, 7) $$); SELECT result, success FROM run_command_on_workers($$ - INSERT INTO create_null_dist_key.identity_test (c, a) OVERRIDING SYSTEM VALUE VALUES (8, 8) + INSERT INTO create_single_shard_table.identity_test (c, a) OVERRIDING SYSTEM VALUE VALUES (8, 8) $$); -- test foreign keys @@ -781,7 +781,7 @@ CREATE TABLE referenced_table(a int UNIQUE, b int); CREATE TABLE referencing_table(a int, b int, FOREIGN KEY (a) REFERENCES referenced_table(a)); --- to a colocated null dist key table +-- to a colocated single-shard table BEGIN; SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'referenced_table'); @@ -793,7 +793,7 @@ BEGIN; INSERT INTO referencing_table VALUES (2, 2); ROLLBACK; --- to a non-colocated null dist key table +-- to a non-colocated single-shard table BEGIN; SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'none'); @@ -866,11 +866,11 @@ SELECT create_distributed_table('referencing_table', NULL, distribution_type=>nu INSERT INTO referenced_table VALUES (1, 1); -- ok SELECT result, success FROM run_command_on_workers($$ - INSERT INTO create_null_dist_key.referencing_table VALUES (1, 2) + INSERT INTO create_single_shard_table.referencing_table VALUES (1, 2) $$); -- fails SELECT result, success FROM run_command_on_workers($$ - INSERT INTO create_null_dist_key.referencing_table VALUES (2, 2) + INSERT INTO create_single_shard_table.referencing_table VALUES (2, 2) $$); DROP TABLE referencing_table, referenced_table; @@ -885,9 +885,9 @@ INSERT INTO self_fkey_test VALUES (2, 3); -- fails -- similar foreign key tests but this time create the referencing table later on --- referencing table is a null shard key table +-- referencing table is a single-shard table --- to a colocated null dist key table +-- to a colocated single-shard table BEGIN; CREATE TABLE referenced_table(a int UNIQUE, b int); SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); @@ -947,7 +947,7 @@ BEGIN; ALTER TABLE referencing_table ADD CONSTRAINT fkey_to_dummy_ref_on_update FOREIGN KEY (a) REFERENCES referenced_table(a) ON UPDATE SET DEFAULT; ROLLBACK; --- to a non-colocated null dist key table +-- to a non-colocated single-shard table BEGIN; CREATE TABLE referenced_table(a int UNIQUE, b int); SELECT create_distributed_table('referenced_table', NULL, distribution_type=>null); @@ -1010,7 +1010,7 @@ BEGIN; SELECT create_distributed_table('referencing_table', NULL, distribution_type=>null, colocate_with=>'none'); ROLLBACK; --- referenced table is a null shard key table +-- referenced table is a single-shard table -- from a sharded table BEGIN; @@ -1060,9 +1060,9 @@ SET client_min_messages TO DEBUG1; BEGIN; -- Switches to sequential execution because referenced_table is a reference table - -- and referenced by a null-shard-key distributed table. + -- and referenced by a single-shard table. -- - -- Given that we cannot do parallel access on null-shard-key, this is not useful. + -- Given that we cannot do parallel access on a single-shard table, this is not useful. -- However, this is already what we're doing for, e.g., a foreign key from a -- reference table to another reference table. TRUNCATE referenced_table CASCADE; @@ -1089,9 +1089,9 @@ ROLLBACK; BEGIN; -- Switches to sequential execution because referenced_table is a reference table - -- and referenced by a null-shard-key distributed table. + -- and referenced by a single-shard table. -- - -- Given that we cannot do parallel access on null-shard-key, this is not useful. + -- Given that we cannot do parallel access on a single-shard table, this is not useful. -- However, this is already what we're doing for, e.g., a foreign key from a -- reference table to another reference table. UPDATE referenced_table SET id = 101 WHERE id = 99; @@ -1109,7 +1109,7 @@ SET client_min_messages TO WARNING; DROP TABLE referenced_table, referencing_table; -- Test whether we unnecessarily switch to sequential execution --- when the referenced relation is a null-shard-key table. +-- when the referenced relation is a single-shard table. CREATE TABLE referenced_table(id int PRIMARY KEY, value_1 int); SELECT create_distributed_table('referenced_table', null, colocate_with=>'none', distribution_type=>null); @@ -1122,13 +1122,13 @@ SET client_min_messages TO DEBUG1; BEGIN; SELECT COUNT(*) FROM referenced_table; -- Doesn't switch to sequential execution because the referenced_table is - -- a null-shard-key distributed table. + -- a single-shard table. ALTER TABLE referencing_table ADD COLUMN X INT; ROLLBACK; BEGIN; -- Doesn't switch to sequential execution because the referenced_table is - -- a null-shard-key distributed table. + -- a single-shard table. TRUNCATE referenced_table CASCADE; SELECT COUNT(*) FROM referencing_table; COMMIT; @@ -1229,4 +1229,4 @@ DROP TRIGGER trigger_3 ON trigger_table_3 RESTRICT; -- cleanup at exit SET client_min_messages TO ERROR; -DROP SCHEMA create_null_dist_key, "NULL_!_dist_key" CASCADE; +DROP SCHEMA create_single_shard_table, "NULL_!_dist_key" CASCADE; diff --git a/src/test/regress/sql/insert_select_null_dist_key.sql b/src/test/regress/sql/insert_select_single_shard_table.sql similarity index 95% rename from src/test/regress/sql/insert_select_null_dist_key.sql rename to src/test/regress/sql/insert_select_single_shard_table.sql index 29454b0c1..f428752ec 100644 --- a/src/test/regress/sql/insert_select_null_dist_key.sql +++ b/src/test/regress/sql/insert_select_single_shard_table.sql @@ -1,5 +1,5 @@ -CREATE SCHEMA insert_select_null_dist_key; -SET search_path TO insert_select_null_dist_key; +CREATE SCHEMA insert_select_single_shard_table; +SET search_path TO insert_select_single_shard_table; SET citus.next_shard_id TO 1820000; SET citus.shard_count TO 32; @@ -87,9 +87,9 @@ CREATE MATERIALIZED VIEW matview AS SELECT b*2+a AS a, a*a AS b FROM nullkey_c1_ SET client_min_messages TO DEBUG2; -- Test inserting into a distributed table by selecting from a combination of --- different table types together with null-shard-key tables. +-- different table types together with single-shard tables. --- use a null-shard-key table +-- use a single-shard table INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1; -- use a reference table @@ -98,13 +98,13 @@ INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 LEFT JOIN reference_table USING (b); INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 INTERSECT SELECT * FROM reference_table; --- use a colocated null-shard-key table +-- use a colocated single-shard table INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN nullkey_c1_t2 USING (b); INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 FULL JOIN nullkey_c1_t2 USING (a); INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 FULL JOIN matview USING (a); INSERT INTO distributed_table_c1_t1 SELECT * FROM nullkey_c1_t1 UNION SELECT * FROM nullkey_c1_t2; --- use a non-colocated null-shard-key table +-- use a non-colocated single-shard table INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 LEFT JOIN nullkey_c2_t1 USING (a); INSERT INTO distributed_table_c1_t1 SELECT * FROM nullkey_c1_t1 UNION SELECT * FROM nullkey_c2_t1; @@ -132,9 +132,9 @@ TRUNCATE distributed_table_c1_t1; INSERT INTO distributed_table_c1_t1 SELECT i, i FROM generate_series(3, 8) i; -- Test inserting into a reference table by selecting from a combination of --- different table types together with null-shard-key tables. +-- different table types together with single-shard tables. --- use a null-shard-key table +-- use a single-shard table INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1; -- use a reference table @@ -143,11 +143,11 @@ INSERT INTO reference_table SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 UNION SELECT * FROM reference_table; INSERT INTO reference_table SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 LEFT JOIN reference_table USING (b) WHERE b IN (SELECT b FROM matview); --- use a colocated null-shard-key table +-- use a colocated single-shard table INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN nullkey_c1_t2 USING (b); INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 FULL JOIN nullkey_c1_t2 USING (a); --- use a non-colocated null-shard-key table +-- use a non-colocated single-shard table INSERT INTO reference_table SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 LEFT JOIN nullkey_c2_t1 USING (a); -- use a distributed table @@ -167,15 +167,15 @@ TRUNCATE reference_table; INSERT INTO reference_table SELECT i, i FROM generate_series(0, 5) i; -- Test inserting into a citus local table by selecting from a combination of --- different table types together with null-shard-key tables. +-- different table types together with single-shard tables. --- use a null-shard-key table +-- use a single-shard table INSERT INTO citus_local_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1; -- use a reference table INSERT INTO citus_local_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN reference_table USING (a); --- use a colocated null-shard-key table +-- use a colocated single-shard table INSERT INTO citus_local_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN nullkey_c1_t2 USING (b); -- use a distributed table @@ -191,8 +191,8 @@ SELECT avg(a), avg(b) FROM citus_local_table ORDER BY 1, 2; TRUNCATE citus_local_table; INSERT INTO citus_local_table SELECT i, i FROM generate_series(0, 10) i; --- Test inserting into a null-shard-key table by selecting from a combination of --- different table types, together with or without null-shard-key tables. +-- Test inserting into a single-shard table by selecting from a combination of +-- different table types, together with or without single-shard tables. -- use a postgres local table INSERT INTO nullkey_c1_t1 SELECT postgres_local_table.a, postgres_local_table.b FROM postgres_local_table; @@ -209,7 +209,7 @@ INSERT INTO nullkey_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1 INSERT INTO nullkey_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM distributed_table_c1_t2 JOIN reference_table USING (a); INSERT INTO nullkey_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM distributed_table_c1_t2 JOIN nullkey_c1_t1 USING (a); --- use a non-colocated null-shard-key table +-- use a non-colocated single-shard table INSERT INTO nullkey_c2_t1 SELECT q.* FROM (SELECT reference_table.* FROM reference_table LEFT JOIN nullkey_c1_t1 USING (a)) q JOIN nullkey_c1_t2 USING (a); -- use a materialized view @@ -228,7 +228,7 @@ INSERT INTO nullkey_c1_t1 SELECT i, i FROM generate_series(1, 8) i; INSERT INTO nullkey_c2_t1 SELECT i, i FROM generate_series(2, 7) i; -- Test inserting into a local table by selecting from a combination of --- different table types, together with or without null-shard-key tables. +-- different table types, together with or without single-shard tables. INSERT INTO postgres_local_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN reference_table USING (a); @@ -465,6 +465,6 @@ SELECT * FROM upsert_test_2 ORDER BY key; SELECT * FROM upsert_test_3 ORDER BY key_1, key_2; SET client_min_messages TO WARNING; -DROP SCHEMA insert_select_null_dist_key CASCADE; +DROP SCHEMA insert_select_single_shard_table CASCADE; SELECT citus_remove_node('localhost', :master_port); diff --git a/src/test/regress/sql/merge.sql b/src/test/regress/sql/merge.sql index 1fdc3a514..f10ab6c99 100644 --- a/src/test/regress/sql/merge.sql +++ b/src/test/regress/sql/merge.sql @@ -2051,11 +2051,11 @@ UPDATE SET val = dist_source.val WHEN NOT MATCHED THEN INSERT VALUES(dist_source.id, dist_source.val); --- test merge with null shard key tables +-- test merge with single-shard tables -CREATE SCHEMA query_null_dist_key; +CREATE SCHEMA query_single_shard_table; -SET search_path TO query_null_dist_key; +SET search_path TO query_single_shard_table; SET client_min_messages TO DEBUG2; CREATE TABLE nullkey_c1_t1(a int, b int); @@ -2098,7 +2098,7 @@ MERGE INTO nullkey_c1_t1 USING nullkey_c1_t2 ON (nullkey_c1_t1.a = nullkey_c1_t2 WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT VALUES (nullkey_c1_t2.a, nullkey_c1_t2.b); --- with non-colocated null-dist-key table +-- with non-colocated single-shard table MERGE INTO nullkey_c1_t1 USING nullkey_c2_t1 ON (nullkey_c1_t1.a = nullkey_c2_t1.a) WHEN MATCHED THEN UPDATE SET b = nullkey_c2_t1.b; @@ -2158,7 +2158,7 @@ MERGE INTO nullkey_c1_t1 USING cte ON (nullkey_c1_t1.a = cte.a) WHEN MATCHED THEN UPDATE SET b = cte.b; SET client_min_messages TO WARNING; -DROP SCHEMA query_null_dist_key CASCADE; +DROP SCHEMA query_single_shard_table CASCADE; RESET client_min_messages; SET search_path TO merge_schema; diff --git a/src/test/regress/sql/multi_extension.sql b/src/test/regress/sql/multi_extension.sql index 50b821b0c..03cf4c7fb 100644 --- a/src/test/regress/sql/multi_extension.sql +++ b/src/test/regress/sql/multi_extension.sql @@ -904,7 +904,7 @@ SELECT create_distributed_table('test','x'); DROP TABLE test; TRUNCATE pg_dist_node; --- confirm that we can create a null shard key table on an empty node +-- confirm that we can create a single-shard table on an empty node CREATE TABLE test (x int, y int); INSERT INTO test VALUES (1,2); SET citus.shard_replication_factor TO 1; diff --git a/src/test/regress/sql/query_null_dist_key.sql b/src/test/regress/sql/query_single_shard_table.sql similarity index 98% rename from src/test/regress/sql/query_null_dist_key.sql rename to src/test/regress/sql/query_single_shard_table.sql index 02eac5c80..0a05558af 100644 --- a/src/test/regress/sql/query_null_dist_key.sql +++ b/src/test/regress/sql/query_single_shard_table.sql @@ -1,5 +1,5 @@ -CREATE SCHEMA query_null_dist_key; -SET search_path TO query_null_dist_key; +CREATE SCHEMA query_single_shard_table; +SET search_path TO query_single_shard_table; SET citus.next_shard_id TO 1620000; SET citus.shard_count TO 32; @@ -138,16 +138,16 @@ SELECT COUNT(*) FROM reference_table d1, nullkey_c1_t1; SELECT COUNT(*) FROM citus_local_table d1, nullkey_c1_t1; SELECT COUNT(*) FROM postgres_local_table d1, nullkey_c1_t1; --- with a colocated null dist key table +-- with a colocated single-shard table SELECT COUNT(*) FROM nullkey_c1_t1 d1, nullkey_c1_t2; --- with a non-colocated null dist key table +-- with a non-colocated single-shard table SELECT COUNT(*) FROM nullkey_c1_t1 d1, nullkey_c2_t1; -- First, show that nullkey_c1_t1 and nullkey_c3_t1 are not colocated. SELECT - (SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'query_null_dist_key.nullkey_c1_t1'::regclass) != - (SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'query_null_dist_key.nullkey_c3_t1'::regclass); + (SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'query_single_shard_table.nullkey_c1_t1'::regclass) != + (SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'query_single_shard_table.nullkey_c3_t1'::regclass); -- Now verify that we can join them via router planner because it doesn't care -- about whether two tables are colocated or not but physical location of shards @@ -163,7 +163,7 @@ SELECT COUNT(*) FROM nullkey_c1_t1 JOIN nullkey_c3_t1 USING(a); RESET citus.enable_non_colocated_router_query_pushdown; --- colocated join between null dist key tables +-- colocated join between single-shard tables SELECT COUNT(*) FROM nullkey_c1_t1 JOIN nullkey_c1_t2 USING(a); SELECT COUNT(*) FROM nullkey_c1_t1 LEFT JOIN nullkey_c1_t2 USING(a); SELECT COUNT(*) FROM nullkey_c1_t1 FULL JOIN nullkey_c1_t2 USING(a); @@ -193,7 +193,7 @@ WHERE t1.b NOT IN ( SELECT a FROM nullkey_c1_t2 t2 WHERE t2.b > t1.a ); --- non-colocated inner joins between null dist key tables +-- non-colocated inner joins between single-shard tables SELECT * FROM nullkey_c1_t1 JOIN nullkey_c2_t1 USING(a) ORDER BY 1,2,3; SELECT COUNT(*) FROM nullkey_c1_t1 t1 @@ -201,7 +201,7 @@ JOIN LATERAL ( SELECT * FROM nullkey_c2_t2 t2 WHERE t2.b > t1.a ) q USING(a); --- non-colocated outer joins between null dist key tables +-- non-colocated outer joins between single-shard tables SELECT * FROM nullkey_c1_t1 LEFT JOIN nullkey_c2_t2 USING(a) ORDER BY 1,2,3 LIMIT 4; SELECT * FROM nullkey_c1_t1 FULL JOIN nullkey_c2_t2 USING(a) ORDER BY 1,2,3 LIMIT 4; SELECT * FROM nullkey_c1_t1 t1 @@ -497,7 +497,7 @@ JOIN LATERAL ( -- insert .. select --- between two colocated null dist key tables +-- between two colocated single-shard tables -- The target list of "distributed statement"s that we send to workers -- differ(*) in Postgres versions < 15. For this reason, we temporarily @@ -510,10 +510,10 @@ EXPLAIN (ANALYZE TRUE, TIMING FALSE, COSTS FALSE, SUMMARY FALSE, VERBOSE FALSE) INSERT INTO nullkey_c1_t1 SELECT * FROM nullkey_c1_t2; SET client_min_messages TO DEBUG2; --- between two non-colocated null dist key tables +-- between two non-colocated single-shard tables INSERT INTO nullkey_c1_t1 SELECT * FROM nullkey_c2_t1; --- between a null dist key table and a table of different type +-- between a single-shard table and a table of different type SET client_min_messages TO WARNING; EXPLAIN (ANALYZE TRUE, TIMING FALSE, COSTS FALSE, SUMMARY FALSE, VERBOSE FALSE) INSERT INTO nullkey_c1_t1 SELECT * FROM reference_table; @@ -1174,6 +1174,6 @@ ORDER BY LIMIT 10; SET client_min_messages TO ERROR; -DROP SCHEMA query_null_dist_key CASCADE; +DROP SCHEMA query_single_shard_table CASCADE; SELECT citus_remove_node('localhost', :master_port); diff --git a/src/test/regress/sql/single_node.sql b/src/test/regress/sql/single_node.sql index 8c612c1bb..bdfb3e260 100644 --- a/src/test/regress/sql/single_node.sql +++ b/src/test/regress/sql/single_node.sql @@ -94,7 +94,7 @@ WHERE shardid = ( WHERE logicalrelid = 'single_node.single_node_nullkey_c2'::regclass ); --- try creating a null-shard-key distributed table from a shard relation +-- try creating a single-shard table from a shard relation SELECT shardid AS round_robin_test_c1_shard_id FROM pg_dist_shard WHERE logicalrelid = 'single_node.single_node_nullkey_c1'::regclass \gset SELECT create_distributed_table('single_node_nullkey_c1_' || :round_robin_test_c1_shard_id , null, colocate_with=>'none', distribution_type=>null); diff --git a/src/test/regress/sql/null_dist_key_prep.sql b/src/test/regress/sql/single_shard_table_prep.sql similarity index 100% rename from src/test/regress/sql/null_dist_key_prep.sql rename to src/test/regress/sql/single_shard_table_prep.sql From 4321286005f7f4768ffb02200d30ad2b29f8cbba Mon Sep 17 00:00:00 2001 From: Ahmet Gedemenli Date: Wed, 3 May 2023 16:04:56 +0300 Subject: [PATCH 031/118] Disable master_create_empty_shard udf for single shard tables (#6902) --- src/backend/distributed/operations/stage_protocol.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/backend/distributed/operations/stage_protocol.c b/src/backend/distributed/operations/stage_protocol.c index 7ef988e5f..db7ebefca 100644 --- a/src/backend/distributed/operations/stage_protocol.c +++ b/src/backend/distributed/operations/stage_protocol.c @@ -138,6 +138,13 @@ master_create_empty_shard(PG_FUNCTION_ARGS) errdetail("We currently don't support creating shards " "on hash-partitioned tables"))); } + else if (IsCitusTableType(relationId, SINGLE_SHARD_DISTRIBUTED)) + { + ereport(ERROR, (errmsg("relation \"%s\" is a single shard table", + relationName), + errdetail("We currently don't support creating shards " + "on single shard tables"))); + } else if (IsCitusTableType(relationId, REFERENCE_TABLE)) { ereport(ERROR, (errmsg("relation \"%s\" is a reference table", From 1662694471e8b04906d959b83bb4a19311da7d63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96nder=20Kalac=C4=B1?= Date: Thu, 4 May 2023 11:45:02 +0300 Subject: [PATCH 032/118] Update CHANGELOG.md (#6907) Change `citus_stats_tenants` to `citus_stat_tenants` Thanks @clairegiordano for noticing --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c72e64cbf..de9bfeeb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ * Adds support for `MERGE` command on co-located distributed tables joined on distribution column (#6696, #6733) -* Adds the view `citus_stats_tenants` that monitor statistics on tenant usages +* Adds the view `citus_stat_tenants` that monitor statistics on tenant usages (#6725) * Adds the GUC `citus.max_background_task_executors_per_node` to control number From 072ae447423a8314a199ad4b36097ba82bc3a07d Mon Sep 17 00:00:00 2001 From: Naisila Puka <37271756+naisila@users.noreply.github.com> Date: Thu, 4 May 2023 16:46:02 +0300 Subject: [PATCH 033/118] Adjusts query's CoerceViaIO & RelabelType nodes that are improper for deparsing (#6391) Adjusts query's CoerceViaIO & RelabelType nodes that are improper for deparsing The standard planner converts some `::text` casts to `::cstring` and here we convert back because `cstring` is a pseudotype and it cannot be casted to most types. This problem occurs in CoerceViaIO nodes. There was another problem with RelabelType nodes fixed in the following PR: https://github.com/citusdata/citus/pull/4580 We undo the changes in that PR, and fix both CoerceViaIO and RelabelType nodes in the planning phase (not in the deparsing phase in ruleutils) Fixes https://github.com/citusdata/citus/issues/5646 Fixes https://github.com/citusdata/citus/issues/5033 Fixes https://github.com/citusdata/citus/issues/6061 --- .../distributed/deparser/ruleutils_13.c | 40 +-- .../distributed/deparser/ruleutils_14.c | 40 +-- .../distributed/deparser/ruleutils_15.c | 40 +-- .../planner/multi_physical_planner.c | 145 ++++++++- .../distributed/multi_physical_planner.h | 1 - .../expected/distributed_collations.out | 4 +- src/test/regress/expected/merge.out | 104 +++--- .../expected/prepared_statements_4.out | 304 ++++++++++++++++++ .../prepared_statements_create_load.out | 12 + .../regress/sql/prepared_statements_4.sql | 82 +++++ .../sql/prepared_statements_create_load.sql | 9 + 11 files changed, 632 insertions(+), 149 deletions(-) diff --git a/src/backend/distributed/deparser/ruleutils_13.c b/src/backend/distributed/deparser/ruleutils_13.c index 86eeb19a1..31ef67f97 100644 --- a/src/backend/distributed/deparser/ruleutils_13.c +++ b/src/backend/distributed/deparser/ruleutils_13.c @@ -5246,42 +5246,20 @@ get_rule_expr(Node *node, deparse_context *context, case T_RelabelType: { RelabelType *relabel = (RelabelType *) node; + Node *arg = (Node *) relabel->arg; - /* - * This is a Citus specific modification - * The planner converts CollateExpr to RelabelType - * and here we convert back. - */ - if (relabel->resultcollid != InvalidOid) + if (relabel->relabelformat == COERCE_IMPLICIT_CAST && + !showimplicit) { - CollateExpr *collate = RelabelTypeToCollateExpr(relabel); - Node *arg = (Node *) collate->arg; - - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, '('); - get_rule_expr_paren(arg, context, showimplicit, node); - appendStringInfo(buf, " COLLATE %s", - generate_collation_name(collate->collOid)); - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, ')'); + /* don't show the implicit cast */ + get_rule_expr_paren(arg, context, false, node); } else { - Node *arg = (Node *) relabel->arg; - - if (relabel->relabelformat == COERCE_IMPLICIT_CAST && - !showimplicit) - { - /* don't show the implicit cast */ - get_rule_expr_paren(arg, context, false, node); - } - else - { - get_coercion_expr(arg, context, - relabel->resulttype, - relabel->resulttypmod, - node); - } + get_coercion_expr(arg, context, + relabel->resulttype, + relabel->resulttypmod, + node); } } break; diff --git a/src/backend/distributed/deparser/ruleutils_14.c b/src/backend/distributed/deparser/ruleutils_14.c index 79fae9ac0..b364221d8 100644 --- a/src/backend/distributed/deparser/ruleutils_14.c +++ b/src/backend/distributed/deparser/ruleutils_14.c @@ -5470,42 +5470,20 @@ get_rule_expr(Node *node, deparse_context *context, case T_RelabelType: { RelabelType *relabel = (RelabelType *) node; + Node *arg = (Node *) relabel->arg; - /* - * This is a Citus specific modification - * The planner converts CollateExpr to RelabelType - * and here we convert back. - */ - if (relabel->resultcollid != InvalidOid) + if (relabel->relabelformat == COERCE_IMPLICIT_CAST && + !showimplicit) { - CollateExpr *collate = RelabelTypeToCollateExpr(relabel); - Node *arg = (Node *) collate->arg; - - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, '('); - get_rule_expr_paren(arg, context, showimplicit, node); - appendStringInfo(buf, " COLLATE %s", - generate_collation_name(collate->collOid)); - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, ')'); + /* don't show the implicit cast */ + get_rule_expr_paren(arg, context, false, node); } else { - Node *arg = (Node *) relabel->arg; - - if (relabel->relabelformat == COERCE_IMPLICIT_CAST && - !showimplicit) - { - /* don't show the implicit cast */ - get_rule_expr_paren(arg, context, false, node); - } - else - { - get_coercion_expr(arg, context, - relabel->resulttype, - relabel->resulttypmod, - node); - } + get_coercion_expr(arg, context, + relabel->resulttype, + relabel->resulttypmod, + node); } } break; diff --git a/src/backend/distributed/deparser/ruleutils_15.c b/src/backend/distributed/deparser/ruleutils_15.c index 827492d87..2dded9b01 100644 --- a/src/backend/distributed/deparser/ruleutils_15.c +++ b/src/backend/distributed/deparser/ruleutils_15.c @@ -5696,42 +5696,20 @@ get_rule_expr(Node *node, deparse_context *context, case T_RelabelType: { RelabelType *relabel = (RelabelType *) node; + Node *arg = (Node *) relabel->arg; - /* - * This is a Citus specific modification - * The planner converts CollateExpr to RelabelType - * and here we convert back. - */ - if (relabel->resultcollid != InvalidOid) + if (relabel->relabelformat == COERCE_IMPLICIT_CAST && + !showimplicit) { - CollateExpr *collate = RelabelTypeToCollateExpr(relabel); - Node *arg = (Node *) collate->arg; - - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, '('); - get_rule_expr_paren(arg, context, showimplicit, node); - appendStringInfo(buf, " COLLATE %s", - generate_collation_name(collate->collOid)); - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, ')'); + /* don't show the implicit cast */ + get_rule_expr_paren(arg, context, false, node); } else { - Node *arg = (Node *) relabel->arg; - - if (relabel->relabelformat == COERCE_IMPLICIT_CAST && - !showimplicit) - { - /* don't show the implicit cast */ - get_rule_expr_paren(arg, context, false, node); - } - else - { - get_coercion_expr(arg, context, - relabel->resulttype, - relabel->resulttypmod, - node); - } + get_coercion_expr(arg, context, + relabel->resulttype, + relabel->resulttypmod, + node); } } break; diff --git a/src/backend/distributed/planner/multi_physical_planner.c b/src/backend/distributed/planner/multi_physical_planner.c index b210da7d7..24fbd9935 100644 --- a/src/backend/distributed/planner/multi_physical_planner.c +++ b/src/backend/distributed/planner/multi_physical_planner.c @@ -28,6 +28,7 @@ #include "access/xlog.h" #include "catalog/pg_aggregate.h" #include "catalog/pg_am.h" +#include "catalog/pg_collation.h" #include "catalog/pg_operator.h" #include "catalog/pg_type.h" #include "commands/defrem.h" @@ -69,6 +70,7 @@ #include "optimizer/restrictinfo.h" #include "optimizer/tlist.h" #include "parser/parse_relation.h" +#include "parser/parse_type.h" #include "parser/parsetree.h" #include "rewrite/rewriteManip.h" #include "utils/builtins.h" @@ -79,6 +81,7 @@ #include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/rel.h" +#include "utils/syscache.h" #include "utils/typcache.h" /* RepartitionJoinBucketCountPerNode determines bucket amount during repartitions */ @@ -231,6 +234,11 @@ static List * FetchEqualityAttrNumsForRTEBoolExpr(BoolExpr *boolExpr); static List * FetchEqualityAttrNumsForList(List *nodeList); static int PartitionColumnIndex(Var *targetVar, List *targetList); static List * GetColumnOriginalIndexes(Oid relationId); +static bool QueryTreeHasImproperForDeparseNodes(Node *inputNode); +static Node * AdjustImproperForDeparseNodes(Node *inputNode); +static bool IsImproperForDeparseRelabelTypeNode(Node *inputNode); +static bool IsImproperForDeparseCoerceViaIONode(Node *inputNode); +static CollateExpr * RelabelTypeToCollateExpr(RelabelType *relabelType); /* @@ -2683,6 +2691,18 @@ SqlTaskList(Job *job) List *fragmentCombinationList = FragmentCombinationList(rangeTableFragmentsList, jobQuery, dependentJobList); + /* + * Adjust RelabelType and CoerceViaIO nodes that are improper for deparsing. + * We first check if there are any such nodes by using a query tree walker. + * The reason is that a query tree mutator will create a deep copy of all + * the query sublinks, and we don't want to do that unless necessary, as it + * would be inefficient. + */ + if (QueryTreeHasImproperForDeparseNodes((Node *) jobQuery)) + { + jobQuery = (Query *) AdjustImproperForDeparseNodes((Node *) jobQuery); + } + ListCell *fragmentCombinationCell = NULL; foreach(fragmentCombinationCell, fragmentCombinationList) { @@ -2733,7 +2753,7 @@ SqlTaskList(Job *job) * RelabelTypeToCollateExpr converts RelabelType's into CollationExpr's. * With that, we will be able to pushdown COLLATE's. */ -CollateExpr * +static CollateExpr * RelabelTypeToCollateExpr(RelabelType *relabelType) { Assert(OidIsValid(relabelType->resultcollid)); @@ -5593,3 +5613,126 @@ TaskListHighestTaskId(List *taskList) return highestTaskId; } + + +/* + * QueryTreeHasImproperForDeparseNodes walks over the node, + * and returns true if there are RelabelType or + * CoerceViaIONodes which are improper for deparse + */ +static bool +QueryTreeHasImproperForDeparseNodes(Node *inputNode) +{ + if (inputNode == NULL) + { + return false; + } + else if (IsImproperForDeparseRelabelTypeNode(inputNode) || + IsImproperForDeparseCoerceViaIONode(inputNode)) + { + return true; + } + else if (IsA(inputNode, Query)) + { + return query_tree_walker((Query *) inputNode, + QueryTreeHasImproperForDeparseNodes, + NULL, 0); + } + + return expression_tree_walker(inputNode, + QueryTreeHasImproperForDeparseNodes, + NULL); +} + + +/* + * AdjustImproperForDeparseNodes takes an input rewritten query and modifies + * nodes which, after going through our planner, pose a problem when + * deparsing. So far we have two such type of Nodes that may pose problems: + * RelabelType and CoerceIO nodes. + * Details will be written in comments in the corresponding if conditions. + */ +static Node * +AdjustImproperForDeparseNodes(Node *inputNode) +{ + if (inputNode == NULL) + { + return NULL; + } + + if (IsImproperForDeparseRelabelTypeNode(inputNode)) + { + /* + * The planner converts CollateExpr to RelabelType + * and here we convert back. + */ + return (Node *) RelabelTypeToCollateExpr((RelabelType *) inputNode); + } + else if (IsImproperForDeparseCoerceViaIONode(inputNode)) + { + /* + * The planner converts some ::text/::varchar casts to ::cstring + * and here we convert back to text because cstring is a pseudotype + * and it cannot be casted to most resulttypes + */ + + CoerceViaIO *iocoerce = (CoerceViaIO *) inputNode; + Node *arg = (Node *) iocoerce->arg; + Const *cstringToText = (Const *) arg; + + cstringToText->consttype = TEXTOID; + cstringToText->constlen = -1; + + Type textType = typeidType(TEXTOID); + char *constvalue = NULL; + + if (!cstringToText->constisnull) + { + constvalue = DatumGetCString(cstringToText->constvalue); + } + + cstringToText->constvalue = stringTypeDatum(textType, + constvalue, + cstringToText->consttypmod); + ReleaseSysCache(textType); + return inputNode; + } + else if (IsA(inputNode, Query)) + { + return (Node *) query_tree_mutator((Query *) inputNode, + AdjustImproperForDeparseNodes, + NULL, QTW_DONT_COPY_QUERY); + } + + return expression_tree_mutator(inputNode, AdjustImproperForDeparseNodes, NULL); +} + + +/* + * Checks if the given node is of Relabel type which is improper for deparsing + * The planner converts some CollateExpr to RelabelType nodes, and we need + * to find these nodes. They would be improperly deparsed without the + * "COLLATE" expression. + */ +static bool +IsImproperForDeparseRelabelTypeNode(Node *inputNode) +{ + return (IsA(inputNode, RelabelType) && + OidIsValid(((RelabelType *) inputNode)->resultcollid) && + ((RelabelType *) inputNode)->resultcollid != DEFAULT_COLLATION_OID); +} + + +/* + * Checks if the given node is of CoerceViaIO type which is improper for deparsing + * The planner converts some ::text/::varchar casts to ::cstring, and we need + * to find these nodes. They would be improperly deparsed with "cstring" which cannot + * be casted to most resulttypes. + */ +static bool +IsImproperForDeparseCoerceViaIONode(Node *inputNode) +{ + return (IsA(inputNode, CoerceViaIO) && + IsA(((CoerceViaIO *) inputNode)->arg, Const) && + ((Const *) ((CoerceViaIO *) inputNode)->arg)->consttype == CSTRINGOID); +} diff --git a/src/include/distributed/multi_physical_planner.h b/src/include/distributed/multi_physical_planner.h index ea5d15c83..26d074053 100644 --- a/src/include/distributed/multi_physical_planner.h +++ b/src/include/distributed/multi_physical_planner.h @@ -543,7 +543,6 @@ extern Node * WrapUngroupedVarsInAnyValueAggregate(Node *expression, List *groupClauseList, List *targetList, bool checkExpressionEquality); -extern CollateExpr * RelabelTypeToCollateExpr(RelabelType *relabelType); /* * Function declarations for building, updating constraints and simple operator diff --git a/src/test/regress/expected/distributed_collations.out b/src/test/regress/expected/distributed_collations.out index 0c03c8be7..5e7247b0a 100644 --- a/src/test/regress/expected/distributed_collations.out +++ b/src/test/regress/expected/distributed_collations.out @@ -75,9 +75,9 @@ NOTICE: renaming the new table to collation_tests.test_collate_pushed_down_aggr SET citus.log_remote_commands TO true; SELECT ALL MIN((lower(CAST(test_collate_pushed_down_aggregate.a AS VARCHAR)) COLLATE "C")) FROM ONLY test_collate_pushed_down_aggregate; -NOTICE: issuing SELECT min((lower(((a)::character varying COLLATE "default")) COLLATE "C")) AS min FROM ONLY collation_tests.test_collate_pushed_down_aggregate_20060004 test_collate_pushed_down_aggregate WHERE true +NOTICE: issuing SELECT min((lower(((a)::character varying)::text) COLLATE "C")) AS min FROM ONLY collation_tests.test_collate_pushed_down_aggregate_20060004 test_collate_pushed_down_aggregate WHERE true DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing SELECT min((lower(((a)::character varying COLLATE "default")) COLLATE "C")) AS min FROM ONLY collation_tests.test_collate_pushed_down_aggregate_20060005 test_collate_pushed_down_aggregate WHERE true +NOTICE: issuing SELECT min((lower(((a)::character varying)::text) COLLATE "C")) AS min FROM ONLY collation_tests.test_collate_pushed_down_aggregate_20060005 test_collate_pushed_down_aggregate WHERE true DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx min --------------------------------------------------------------------- diff --git a/src/test/regress/expected/merge.out b/src/test/regress/expected/merge.out index 190e6b2b3..3cf776ded 100644 --- a/src/test/regress/expected/merge.out +++ b/src/test/regress/expected/merge.out @@ -236,7 +236,7 @@ MERGE INTO target t last_order_id = s.order_id WHEN NOT MATCHED THEN DO NOTHING; -NOTICE: issuing MERGE INTO merge_schema.target_xxxxxxx t USING merge_schema.source_xxxxxxx s ON ((t.customer_id OPERATOR(pg_catalog.=) s.customer_id) AND (t.customer_id OPERATOR(pg_catalog.=) 30002)) WHEN MATCHED AND ((t.order_center COLLATE "default") OPERATOR(pg_catalog.=) 'XX'::text) THEN DELETE WHEN MATCHED THEN UPDATE SET last_order_id = s.order_id, order_count = (t.order_count OPERATOR(pg_catalog.+) 1) WHEN NOT MATCHED THEN DO NOTHING +NOTICE: issuing MERGE INTO merge_schema.target_xxxxxxx t USING merge_schema.source_xxxxxxx s ON ((t.customer_id OPERATOR(pg_catalog.=) s.customer_id) AND (t.customer_id OPERATOR(pg_catalog.=) 30002)) WHEN MATCHED AND ((t.order_center)::text OPERATOR(pg_catalog.=) 'XX'::text) THEN DELETE WHEN MATCHED THEN UPDATE SET last_order_id = s.order_id, order_count = (t.order_count OPERATOR(pg_catalog.+) 1) WHEN NOT MATCHED THEN DO NOTHING DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx SET citus.log_remote_commands to false; SELECT * from target t WHERE t.customer_id = 30002; @@ -1073,7 +1073,7 @@ UPDATE SET value = vl_source.value, id = vl_target.id + 1 WHEN NOT MATCHED THEN INSERT VALUES(vl_source.ID, vl_source.value); DEBUG: Creating MERGE router plan -DEBUG: +DEBUG: RESET client_min_messages; SELECT * INTO vl_local FROM vl_target ORDER BY 1 ; -- Should be equal @@ -1331,7 +1331,7 @@ MERGE INTO ft_target WHEN NOT MATCHED THEN INSERT (id, user_val) VALUES (foreign_table.id, foreign_table.user_val); DEBUG: Creating MERGE router plan -DEBUG: +DEBUG: RESET client_min_messages; SELECT * FROM ft_target; id | user_val @@ -1667,13 +1667,13 @@ NOTICE: issuing BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;SELECT assign_ DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx 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 MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON (t.id OPERATOR(pg_catalog.=) s.id) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 400) THEN UPDATE SET val = (((t.val COLLATE "default") OPERATOR(pg_catalog.||) 'Updated by Merge'::text) COLLATE "default") WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON (t.id OPERATOR(pg_catalog.=) s.id) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 400) THEN UPDATE SET val = ((t.val)::text OPERATOR(pg_catalog.||) 'Updated by Merge'::text) WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON (t.id OPERATOR(pg_catalog.=) s.id) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 400) THEN UPDATE SET val = (((t.val COLLATE "default") OPERATOR(pg_catalog.||) 'Updated by Merge'::text) COLLATE "default") WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON (t.id OPERATOR(pg_catalog.=) s.id) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 400) THEN UPDATE SET val = ((t.val)::text OPERATOR(pg_catalog.||) 'Updated by Merge'::text) WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON (t.id OPERATOR(pg_catalog.=) s.id) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 400) THEN UPDATE SET val = (((t.val COLLATE "default") OPERATOR(pg_catalog.||) 'Updated by Merge'::text) COLLATE "default") WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON (t.id OPERATOR(pg_catalog.=) s.id) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 400) THEN UPDATE SET val = ((t.val)::text OPERATOR(pg_catalog.||) 'Updated by Merge'::text) WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON (t.id OPERATOR(pg_catalog.=) s.id) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 400) THEN UPDATE SET val = (((t.val COLLATE "default") OPERATOR(pg_catalog.||) 'Updated by Merge'::text) COLLATE "default") WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON (t.id OPERATOR(pg_catalog.=) s.id) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 400) THEN UPDATE SET val = ((t.val)::text OPERATOR(pg_catalog.||) 'Updated by Merge'::text) WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx SET citus.log_remote_commands to false; SELECT compare_tables(); @@ -1710,13 +1710,13 @@ NOTICE: issuing BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;SELECT assign_ DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx 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 MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON ((t.id OPERATOR(pg_catalog.=) s.id) AND (s.id OPERATOR(pg_catalog.<) 100)) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 400) THEN UPDATE SET val = (((t.val COLLATE "default") OPERATOR(pg_catalog.||) 'Updated by Merge'::text) COLLATE "default") WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON ((t.id OPERATOR(pg_catalog.=) s.id) AND (s.id OPERATOR(pg_catalog.<) 100)) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 400) THEN UPDATE SET val = ((t.val)::text OPERATOR(pg_catalog.||) 'Updated by Merge'::text) WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON ((t.id OPERATOR(pg_catalog.=) s.id) AND (s.id OPERATOR(pg_catalog.<) 100)) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 400) THEN UPDATE SET val = (((t.val COLLATE "default") OPERATOR(pg_catalog.||) 'Updated by Merge'::text) COLLATE "default") WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON ((t.id OPERATOR(pg_catalog.=) s.id) AND (s.id OPERATOR(pg_catalog.<) 100)) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 400) THEN UPDATE SET val = ((t.val)::text OPERATOR(pg_catalog.||) 'Updated by Merge'::text) WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON ((t.id OPERATOR(pg_catalog.=) s.id) AND (s.id OPERATOR(pg_catalog.<) 100)) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 400) THEN UPDATE SET val = (((t.val COLLATE "default") OPERATOR(pg_catalog.||) 'Updated by Merge'::text) COLLATE "default") WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON ((t.id OPERATOR(pg_catalog.=) s.id) AND (s.id OPERATOR(pg_catalog.<) 100)) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 400) THEN UPDATE SET val = ((t.val)::text OPERATOR(pg_catalog.||) 'Updated by Merge'::text) WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON ((t.id OPERATOR(pg_catalog.=) s.id) AND (s.id OPERATOR(pg_catalog.<) 100)) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 400) THEN UPDATE SET val = (((t.val COLLATE "default") OPERATOR(pg_catalog.||) 'Updated by Merge'::text) COLLATE "default") WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON ((t.id OPERATOR(pg_catalog.=) s.id) AND (s.id OPERATOR(pg_catalog.<) 100)) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 400) THEN UPDATE SET val = ((t.val)::text OPERATOR(pg_catalog.||) 'Updated by Merge'::text) WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx SET citus.log_remote_commands to false; SELECT compare_tables(); @@ -1753,13 +1753,13 @@ NOTICE: issuing BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;SELECT assign_ DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx 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 MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON ((t.id OPERATOR(pg_catalog.=) s.id) AND (t.id OPERATOR(pg_catalog.<) 100)) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 400) THEN UPDATE SET val = (((t.val COLLATE "default") OPERATOR(pg_catalog.||) 'Updated by Merge'::text) COLLATE "default") WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON ((t.id OPERATOR(pg_catalog.=) s.id) AND (t.id OPERATOR(pg_catalog.<) 100)) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 400) THEN UPDATE SET val = ((t.val)::text OPERATOR(pg_catalog.||) 'Updated by Merge'::text) WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON ((t.id OPERATOR(pg_catalog.=) s.id) AND (t.id OPERATOR(pg_catalog.<) 100)) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 400) THEN UPDATE SET val = (((t.val COLLATE "default") OPERATOR(pg_catalog.||) 'Updated by Merge'::text) COLLATE "default") WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON ((t.id OPERATOR(pg_catalog.=) s.id) AND (t.id OPERATOR(pg_catalog.<) 100)) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 400) THEN UPDATE SET val = ((t.val)::text OPERATOR(pg_catalog.||) 'Updated by Merge'::text) WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON ((t.id OPERATOR(pg_catalog.=) s.id) AND (t.id OPERATOR(pg_catalog.<) 100)) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 400) THEN UPDATE SET val = (((t.val COLLATE "default") OPERATOR(pg_catalog.||) 'Updated by Merge'::text) COLLATE "default") WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON ((t.id OPERATOR(pg_catalog.=) s.id) AND (t.id OPERATOR(pg_catalog.<) 100)) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 400) THEN UPDATE SET val = ((t.val)::text OPERATOR(pg_catalog.||) 'Updated by Merge'::text) WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON ((t.id OPERATOR(pg_catalog.=) s.id) AND (t.id OPERATOR(pg_catalog.<) 100)) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 400) THEN UPDATE SET val = (((t.val COLLATE "default") OPERATOR(pg_catalog.||) 'Updated by Merge'::text) COLLATE "default") WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON ((t.id OPERATOR(pg_catalog.=) s.id) AND (t.id OPERATOR(pg_catalog.<) 100)) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 400) THEN UPDATE SET val = ((t.val)::text OPERATOR(pg_catalog.||) 'Updated by Merge'::text) WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx SET citus.log_remote_commands to false; SELECT compare_tables(); @@ -1836,13 +1836,13 @@ NOTICE: issuing BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;SELECT assign_ DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx 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 MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON ((t.id OPERATOR(pg_catalog.=) s.id) AND (s.id OPERATOR(pg_catalog.=) 250)) WHEN MATCHED THEN UPDATE SET val = (((t.val COLLATE "default") OPERATOR(pg_catalog.||) 'Updated by Merge'::text) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON ((t.id OPERATOR(pg_catalog.=) s.id) AND (s.id OPERATOR(pg_catalog.=) 250)) WHEN MATCHED THEN UPDATE SET val = ((t.val)::text OPERATOR(pg_catalog.||) 'Updated by Merge'::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON ((t.id OPERATOR(pg_catalog.=) s.id) AND (s.id OPERATOR(pg_catalog.=) 250)) WHEN MATCHED THEN UPDATE SET val = (((t.val COLLATE "default") OPERATOR(pg_catalog.||) 'Updated by Merge'::text) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON ((t.id OPERATOR(pg_catalog.=) s.id) AND (s.id OPERATOR(pg_catalog.=) 250)) WHEN MATCHED THEN UPDATE SET val = ((t.val)::text OPERATOR(pg_catalog.||) 'Updated by Merge'::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON ((t.id OPERATOR(pg_catalog.=) s.id) AND (s.id OPERATOR(pg_catalog.=) 250)) WHEN MATCHED THEN UPDATE SET val = (((t.val COLLATE "default") OPERATOR(pg_catalog.||) 'Updated by Merge'::text) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON ((t.id OPERATOR(pg_catalog.=) s.id) AND (s.id OPERATOR(pg_catalog.=) 250)) WHEN MATCHED THEN UPDATE SET val = ((t.val)::text OPERATOR(pg_catalog.||) 'Updated by Merge'::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON ((t.id OPERATOR(pg_catalog.=) s.id) AND (s.id OPERATOR(pg_catalog.=) 250)) WHEN MATCHED THEN UPDATE SET val = (((t.val COLLATE "default") OPERATOR(pg_catalog.||) 'Updated by Merge'::text) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON ((t.id OPERATOR(pg_catalog.=) s.id) AND (s.id OPERATOR(pg_catalog.=) 250)) WHEN MATCHED THEN UPDATE SET val = ((t.val)::text OPERATOR(pg_catalog.||) 'Updated by Merge'::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s.id, s.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx SET citus.log_remote_commands to false; SELECT compare_tables(); @@ -1871,7 +1871,7 @@ WHEN NOT MATCHED THEN DO NOTHING; 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 MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON ((t.id OPERATOR(pg_catalog.=) s.id) AND (s.id OPERATOR(pg_catalog.=) 250)) WHEN MATCHED THEN UPDATE SET val = (((t.val COLLATE "default") OPERATOR(pg_catalog.||) 'Updated by Merge'::text) COLLATE "default") WHEN NOT MATCHED THEN DO NOTHING +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING merge_schema.citus_source_xxxxxxx s ON ((t.id OPERATOR(pg_catalog.=) s.id) AND (s.id OPERATOR(pg_catalog.=) 250)) WHEN MATCHED THEN UPDATE SET val = ((t.val)::text OPERATOR(pg_catalog.||) 'Updated by Merge'::text) WHEN NOT MATCHED THEN DO NOTHING DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx SET citus.log_remote_commands to false; SELECT compare_tables(); @@ -1917,13 +1917,13 @@ NOTICE: issuing BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;SELECT assign_ DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx 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 WITH cte AS (SELECT citus_source_view.id, citus_source_view.val FROM (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source WHERE (citus_source.id OPERATOR(pg_catalog.<) 400)) citus_source_view) MERGE INTO merge_schema.citus_target_xxxxxxx t USING cte ON (cte.id OPERATOR(pg_catalog.=) t.id) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 350) THEN UPDATE SET val = (((t.val COLLATE "default") OPERATOR(pg_catalog.||) 'Updated by CTE'::text) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (cte.id, cte.val) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.<) 350) THEN DELETE +NOTICE: issuing WITH cte AS (SELECT citus_source_view.id, citus_source_view.val FROM (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source WHERE (citus_source.id OPERATOR(pg_catalog.<) 400)) citus_source_view) MERGE INTO merge_schema.citus_target_xxxxxxx t USING cte ON (cte.id OPERATOR(pg_catalog.=) t.id) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 350) THEN UPDATE SET val = ((t.val)::text OPERATOR(pg_catalog.||) 'Updated by CTE'::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (cte.id, cte.val) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.<) 350) THEN DELETE DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing WITH cte AS (SELECT citus_source_view.id, citus_source_view.val FROM (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source WHERE (citus_source.id OPERATOR(pg_catalog.<) 400)) citus_source_view) MERGE INTO merge_schema.citus_target_xxxxxxx t USING cte ON (cte.id OPERATOR(pg_catalog.=) t.id) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 350) THEN UPDATE SET val = (((t.val COLLATE "default") OPERATOR(pg_catalog.||) 'Updated by CTE'::text) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (cte.id, cte.val) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.<) 350) THEN DELETE +NOTICE: issuing WITH cte AS (SELECT citus_source_view.id, citus_source_view.val FROM (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source WHERE (citus_source.id OPERATOR(pg_catalog.<) 400)) citus_source_view) MERGE INTO merge_schema.citus_target_xxxxxxx t USING cte ON (cte.id OPERATOR(pg_catalog.=) t.id) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 350) THEN UPDATE SET val = ((t.val)::text OPERATOR(pg_catalog.||) 'Updated by CTE'::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (cte.id, cte.val) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.<) 350) THEN DELETE DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing WITH cte AS (SELECT citus_source_view.id, citus_source_view.val FROM (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source WHERE (citus_source.id OPERATOR(pg_catalog.<) 400)) citus_source_view) MERGE INTO merge_schema.citus_target_xxxxxxx t USING cte ON (cte.id OPERATOR(pg_catalog.=) t.id) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 350) THEN UPDATE SET val = (((t.val COLLATE "default") OPERATOR(pg_catalog.||) 'Updated by CTE'::text) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (cte.id, cte.val) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.<) 350) THEN DELETE +NOTICE: issuing WITH cte AS (SELECT citus_source_view.id, citus_source_view.val FROM (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source WHERE (citus_source.id OPERATOR(pg_catalog.<) 400)) citus_source_view) MERGE INTO merge_schema.citus_target_xxxxxxx t USING cte ON (cte.id OPERATOR(pg_catalog.=) t.id) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 350) THEN UPDATE SET val = ((t.val)::text OPERATOR(pg_catalog.||) 'Updated by CTE'::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (cte.id, cte.val) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.<) 350) THEN DELETE DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing WITH cte AS (SELECT citus_source_view.id, citus_source_view.val FROM (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source WHERE (citus_source.id OPERATOR(pg_catalog.<) 400)) citus_source_view) MERGE INTO merge_schema.citus_target_xxxxxxx t USING cte ON (cte.id OPERATOR(pg_catalog.=) t.id) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 350) THEN UPDATE SET val = (((t.val COLLATE "default") OPERATOR(pg_catalog.||) 'Updated by CTE'::text) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (cte.id, cte.val) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.<) 350) THEN DELETE +NOTICE: issuing WITH cte AS (SELECT citus_source_view.id, citus_source_view.val FROM (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source WHERE (citus_source.id OPERATOR(pg_catalog.<) 400)) citus_source_view) MERGE INTO merge_schema.citus_target_xxxxxxx t USING cte ON (cte.id OPERATOR(pg_catalog.=) t.id) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 350) THEN UPDATE SET val = ((t.val)::text OPERATOR(pg_catalog.||) 'Updated by CTE'::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (cte.id, cte.val) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.<) 350) THEN DELETE DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx SET citus.log_remote_commands to false; SELECT compare_tables(); @@ -1958,13 +1958,13 @@ NOTICE: issuing BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;SELECT assign_ DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx 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 MERGE INTO merge_schema.citus_target_xxxxxxx t USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) subq ON (subq.id OPERATOR(pg_catalog.=) t.id) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 350) THEN UPDATE SET val = (((t.val COLLATE "default") OPERATOR(pg_catalog.||) 'Updated by subquery'::text) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (subq.id, subq.val) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.<) 350) THEN DELETE +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) subq ON (subq.id OPERATOR(pg_catalog.=) t.id) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 350) THEN UPDATE SET val = ((t.val)::text OPERATOR(pg_catalog.||) 'Updated by subquery'::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (subq.id, subq.val) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.<) 350) THEN DELETE DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) subq ON (subq.id OPERATOR(pg_catalog.=) t.id) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 350) THEN UPDATE SET val = (((t.val COLLATE "default") OPERATOR(pg_catalog.||) 'Updated by subquery'::text) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (subq.id, subq.val) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.<) 350) THEN DELETE +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) subq ON (subq.id OPERATOR(pg_catalog.=) t.id) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 350) THEN UPDATE SET val = ((t.val)::text OPERATOR(pg_catalog.||) 'Updated by subquery'::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (subq.id, subq.val) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.<) 350) THEN DELETE DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) subq ON (subq.id OPERATOR(pg_catalog.=) t.id) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 350) THEN UPDATE SET val = (((t.val COLLATE "default") OPERATOR(pg_catalog.||) 'Updated by subquery'::text) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (subq.id, subq.val) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.<) 350) THEN DELETE +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) subq ON (subq.id OPERATOR(pg_catalog.=) t.id) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 350) THEN UPDATE SET val = ((t.val)::text OPERATOR(pg_catalog.||) 'Updated by subquery'::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (subq.id, subq.val) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.<) 350) THEN DELETE DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) subq ON (subq.id OPERATOR(pg_catalog.=) t.id) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 350) THEN UPDATE SET val = (((t.val COLLATE "default") OPERATOR(pg_catalog.||) 'Updated by subquery'::text) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (subq.id, subq.val) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.<) 350) THEN DELETE +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx t USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) subq ON (subq.id OPERATOR(pg_catalog.=) t.id) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.>) 350) THEN UPDATE SET val = ((t.val)::text OPERATOR(pg_catalog.||) 'Updated by subquery'::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (subq.id, subq.val) WHEN MATCHED AND (t.id OPERATOR(pg_catalog.<) 350) THEN DELETE DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx SET citus.log_remote_commands to false; SELECT compare_tables(); @@ -2042,13 +2042,13 @@ SELECT count(*) FROM citus_target; -- before merge SET citus.log_remote_commands to true; EXECUTE citus_prep(500); -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = (('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val COLLATE "default")) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = ('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val)::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = (('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val COLLATE "default")) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = ('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val)::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = (('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val COLLATE "default")) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = ('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val)::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = (('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val COLLATE "default")) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = ('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val)::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx SELECT * FROM citus_target WHERE id = 500; -- non-cached NOTICE: issuing SELECT id, val FROM merge_schema.citus_target_xxxxxxx citus_target WHERE (id OPERATOR(pg_catalog.=) 500) @@ -2059,49 +2059,49 @@ DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx (1 row) EXECUTE citus_prep(500); -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = (('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val COLLATE "default")) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = ('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val)::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = (('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val COLLATE "default")) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = ('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val)::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = (('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val COLLATE "default")) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = ('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val)::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = (('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val COLLATE "default")) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = ('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val)::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx EXECUTE citus_prep(500); -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = (('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val COLLATE "default")) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = ('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val)::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = (('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val COLLATE "default")) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = ('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val)::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = (('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val COLLATE "default")) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = ('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val)::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = (('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val COLLATE "default")) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = ('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val)::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx EXECUTE citus_prep(500); -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = (('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val COLLATE "default")) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = ('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val)::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = (('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val COLLATE "default")) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = ('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val)::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = (('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val COLLATE "default")) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = ('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val)::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = (('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val COLLATE "default")) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = ('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val)::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx EXECUTE citus_prep(500); -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = (('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val COLLATE "default")) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = ('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val)::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = (('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val COLLATE "default")) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = ('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val)::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = (('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val COLLATE "default")) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = ('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val)::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = (('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val COLLATE "default")) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = ('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val)::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx EXECUTE citus_prep(500); -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = (('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val COLLATE "default")) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = ('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val)::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = (('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val COLLATE "default")) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = ('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val)::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = (('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val COLLATE "default")) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = ('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val)::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = (('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val COLLATE "default")) COLLATE "default") WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) +NOTICE: issuing MERGE INTO merge_schema.citus_target_xxxxxxx citus_target USING (SELECT citus_source.id, citus_source.val FROM merge_schema.citus_source_xxxxxxx citus_source) sub ON ((citus_target.id OPERATOR(pg_catalog.=) sub.id) AND (citus_target.id OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = ('Updated by prepare using '::text OPERATOR(pg_catalog.||) (sub.val)::text) WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx SET citus.log_remote_commands to false; SELECT * FROM citus_target WHERE id = 500; -- cached diff --git a/src/test/regress/expected/prepared_statements_4.out b/src/test/regress/expected/prepared_statements_4.out index 0dba296e8..4b81ec260 100644 --- a/src/test/regress/expected/prepared_statements_4.out +++ b/src/test/regress/expected/prepared_statements_4.out @@ -50,3 +50,307 @@ SELECT count(distinct ingest_time) FROM http_request WHERE site_id = 1; 8 (1 row) +-- Standard planner converted text and varchar casts to cstring in some cases +-- We make sure we convert it back to text when parsing the expression +INSERT INTO test VALUES ('2022-02-02', 0); +INSERT INTO test VALUES ('2022-01-01', 1); +INSERT INTO test VALUES ('2021-01-01', 2); +-- try different planners +PREPARE test_statement_regular(text) AS +SELECT user_id FROM test WHERE t >= $1::timestamp ORDER BY user_id; +EXECUTE test_statement_regular('2022-01-01'); + user_id +--------------------------------------------------------------------- + 0 + 1 +(2 rows) + +EXECUTE test_statement_regular('2022-01-01'); + user_id +--------------------------------------------------------------------- + 0 + 1 +(2 rows) + +EXECUTE test_statement_regular('2022-01-01'); + user_id +--------------------------------------------------------------------- + 0 + 1 +(2 rows) + +EXECUTE test_statement_regular('2022-01-01'); + user_id +--------------------------------------------------------------------- + 0 + 1 +(2 rows) + +EXECUTE test_statement_regular('2022-01-01'); + user_id +--------------------------------------------------------------------- + 0 + 1 +(2 rows) + +EXECUTE test_statement_regular('2022-01-01'); + user_id +--------------------------------------------------------------------- + 0 + 1 +(2 rows) + +EXECUTE test_statement_regular('2022-01-01'); + user_id +--------------------------------------------------------------------- + 0 + 1 +(2 rows) + +EXECUTE test_statement_regular('2022-01-01'); + user_id +--------------------------------------------------------------------- + 0 + 1 +(2 rows) + +PREPARE test_statement_router(int, text) AS +SELECT user_id FROM test WHERE user_id = $1 AND t >= $2::timestamp ORDER BY user_id; +EXECUTE test_statement_router(1, '2022-01-01'); + user_id +--------------------------------------------------------------------- + 1 +(1 row) + +EXECUTE test_statement_router(1, '2022-01-01'); + user_id +--------------------------------------------------------------------- + 1 +(1 row) + +EXECUTE test_statement_router(1, '2022-01-01'); + user_id +--------------------------------------------------------------------- + 1 +(1 row) + +EXECUTE test_statement_router(1, '2022-01-01'); + user_id +--------------------------------------------------------------------- + 1 +(1 row) + +EXECUTE test_statement_router(1, '2022-01-01'); + user_id +--------------------------------------------------------------------- + 1 +(1 row) + +EXECUTE test_statement_router(1, '2022-01-01'); + user_id +--------------------------------------------------------------------- + 1 +(1 row) + +EXECUTE test_statement_router(1, '2022-01-01'); + user_id +--------------------------------------------------------------------- + 1 +(1 row) + +EXECUTE test_statement_router(1, '2022-01-01'); + user_id +--------------------------------------------------------------------- + 1 +(1 row) + +PREPARE test_statement_repartition(int, text) AS +SELECT count(*) FROM test t1 JOIN test t2 USING (t) WHERE t1.user_id = $1 AND t >= $2::timestamp; +EXECUTE test_statement_repartition(1, '2022-01-01'); + count +--------------------------------------------------------------------- + 1 +(1 row) + +EXECUTE test_statement_repartition(1, '2022-01-01'); + count +--------------------------------------------------------------------- + 1 +(1 row) + +EXECUTE test_statement_repartition(1, '2022-01-01'); + count +--------------------------------------------------------------------- + 1 +(1 row) + +EXECUTE test_statement_repartition(1, '2022-01-01'); + count +--------------------------------------------------------------------- + 1 +(1 row) + +EXECUTE test_statement_repartition(1, '2022-01-01'); + count +--------------------------------------------------------------------- + 1 +(1 row) + +EXECUTE test_statement_repartition(1, '2022-01-01'); + count +--------------------------------------------------------------------- + 1 +(1 row) + +EXECUTE test_statement_repartition(1, '2022-01-01'); + count +--------------------------------------------------------------------- + 1 +(1 row) + +EXECUTE test_statement_repartition(1, '2022-01-01'); + count +--------------------------------------------------------------------- + 1 +(1 row) + +PREPARE test_statement_cte(text, text) AS +WITH cte_1 AS MATERIALIZED (SELECT user_id, t FROM test WHERE t >= $1::timestamp ORDER BY user_id) +SELECT user_id FROM cte_1 WHERE t <= $2::timestamp; +EXECUTE test_statement_cte('2022-01-01', '2022-01-01'); + user_id +--------------------------------------------------------------------- + 1 +(1 row) + +EXECUTE test_statement_cte('2022-01-01', '2022-01-01'); + user_id +--------------------------------------------------------------------- + 1 +(1 row) + +EXECUTE test_statement_cte('2022-01-01', '2022-01-01'); + user_id +--------------------------------------------------------------------- + 1 +(1 row) + +EXECUTE test_statement_cte('2022-01-01', '2022-01-01'); + user_id +--------------------------------------------------------------------- + 1 +(1 row) + +EXECUTE test_statement_cte('2022-01-01', '2022-01-01'); + user_id +--------------------------------------------------------------------- + 1 +(1 row) + +EXECUTE test_statement_cte('2022-01-01', '2022-01-01'); + user_id +--------------------------------------------------------------------- + 1 +(1 row) + +EXECUTE test_statement_cte('2022-01-01', '2022-01-01'); + user_id +--------------------------------------------------------------------- + 1 +(1 row) + +EXECUTE test_statement_cte('2022-01-01', '2022-01-01'); + user_id +--------------------------------------------------------------------- + 1 +(1 row) + +PREPARE test_statement_insert(int, text) AS +INSERT INTO test VALUES ($2::timestamp, $1); +EXECUTE test_statement_insert(3, '2022-03-03'); +EXECUTE test_statement_insert(4, '2022-04-04'); +EXECUTE test_statement_insert(5, '2022-05-05'); +EXECUTE test_statement_insert(6, '2022-06-06'); +EXECUTE test_statement_insert(7, '2022-07-07'); +EXECUTE test_statement_insert(8, '2022-08-08'); +EXECUTE test_statement_insert(9, '2022-09-09'); +EXECUTE test_statement_insert(10, '2022-10-10'); +SELECT count(*) FROM test; + count +--------------------------------------------------------------------- + 11 +(1 row) + +EXECUTE test_statement_regular('2022-01-01'); + user_id +--------------------------------------------------------------------- + 0 + 1 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 +(10 rows) + +PREPARE test_statement_null(text) AS +SELECT user_id , $1::timestamp FROM test ORDER BY user_id LIMIT 2; +EXECUTE test_statement_null(NULL); + user_id | timestamp +--------------------------------------------------------------------- + 0 | + 1 | +(2 rows) + +EXECUTE test_statement_null(NULL); + user_id | timestamp +--------------------------------------------------------------------- + 0 | + 1 | +(2 rows) + +EXECUTE test_statement_null(NULL); + user_id | timestamp +--------------------------------------------------------------------- + 0 | + 1 | +(2 rows) + +EXECUTE test_statement_null(NULL); + user_id | timestamp +--------------------------------------------------------------------- + 0 | + 1 | +(2 rows) + +EXECUTE test_statement_null(NULL); + user_id | timestamp +--------------------------------------------------------------------- + 0 | + 1 | +(2 rows) + +EXECUTE test_statement_null(NULL); + user_id | timestamp +--------------------------------------------------------------------- + 0 | + 1 | +(2 rows) + +EXECUTE test_statement_null(NULL); + user_id | timestamp +--------------------------------------------------------------------- + 0 | + 1 | +(2 rows) + +EXECUTE test_statement_null(NULL); + user_id | timestamp +--------------------------------------------------------------------- + 0 | + 1 | +(2 rows) + diff --git a/src/test/regress/expected/prepared_statements_create_load.out b/src/test/regress/expected/prepared_statements_create_load.out index d1817f39f..5e3740abe 100644 --- a/src/test/regress/expected/prepared_statements_create_load.out +++ b/src/test/regress/expected/prepared_statements_create_load.out @@ -91,3 +91,15 @@ SELECT create_distributed_table('http_request', 'site_id'); (1 row) +-- Standard planner converted text and varchar casts to cstring in some cases +-- We make sure we convert it back to text when parsing the expression +-- https://github.com/citusdata/citus/issues/6061 +-- https://github.com/citusdata/citus/issues/5646 +-- https://github.com/citusdata/citus/issues/5033 +CREATE TABLE test(t timestamp, user_id int); +SELECT create_distributed_table('test', 'user_id'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + diff --git a/src/test/regress/sql/prepared_statements_4.sql b/src/test/regress/sql/prepared_statements_4.sql index a8f124568..bdbc32b08 100644 --- a/src/test/regress/sql/prepared_statements_4.sql +++ b/src/test/regress/sql/prepared_statements_4.sql @@ -43,3 +43,85 @@ EXECUTE foo; SELECT count(distinct ingest_time) FROM http_request WHERE site_id = 1; +-- Standard planner converted text and varchar casts to cstring in some cases +-- We make sure we convert it back to text when parsing the expression +INSERT INTO test VALUES ('2022-02-02', 0); +INSERT INTO test VALUES ('2022-01-01', 1); +INSERT INTO test VALUES ('2021-01-01', 2); + +-- try different planners +PREPARE test_statement_regular(text) AS +SELECT user_id FROM test WHERE t >= $1::timestamp ORDER BY user_id; + +EXECUTE test_statement_regular('2022-01-01'); +EXECUTE test_statement_regular('2022-01-01'); +EXECUTE test_statement_regular('2022-01-01'); +EXECUTE test_statement_regular('2022-01-01'); +EXECUTE test_statement_regular('2022-01-01'); +EXECUTE test_statement_regular('2022-01-01'); +EXECUTE test_statement_regular('2022-01-01'); +EXECUTE test_statement_regular('2022-01-01'); + +PREPARE test_statement_router(int, text) AS +SELECT user_id FROM test WHERE user_id = $1 AND t >= $2::timestamp ORDER BY user_id; + +EXECUTE test_statement_router(1, '2022-01-01'); +EXECUTE test_statement_router(1, '2022-01-01'); +EXECUTE test_statement_router(1, '2022-01-01'); +EXECUTE test_statement_router(1, '2022-01-01'); +EXECUTE test_statement_router(1, '2022-01-01'); +EXECUTE test_statement_router(1, '2022-01-01'); +EXECUTE test_statement_router(1, '2022-01-01'); +EXECUTE test_statement_router(1, '2022-01-01'); + +PREPARE test_statement_repartition(int, text) AS +SELECT count(*) FROM test t1 JOIN test t2 USING (t) WHERE t1.user_id = $1 AND t >= $2::timestamp; + +EXECUTE test_statement_repartition(1, '2022-01-01'); +EXECUTE test_statement_repartition(1, '2022-01-01'); +EXECUTE test_statement_repartition(1, '2022-01-01'); +EXECUTE test_statement_repartition(1, '2022-01-01'); +EXECUTE test_statement_repartition(1, '2022-01-01'); +EXECUTE test_statement_repartition(1, '2022-01-01'); +EXECUTE test_statement_repartition(1, '2022-01-01'); +EXECUTE test_statement_repartition(1, '2022-01-01'); + +PREPARE test_statement_cte(text, text) AS +WITH cte_1 AS MATERIALIZED (SELECT user_id, t FROM test WHERE t >= $1::timestamp ORDER BY user_id) +SELECT user_id FROM cte_1 WHERE t <= $2::timestamp; + +EXECUTE test_statement_cte('2022-01-01', '2022-01-01'); +EXECUTE test_statement_cte('2022-01-01', '2022-01-01'); +EXECUTE test_statement_cte('2022-01-01', '2022-01-01'); +EXECUTE test_statement_cte('2022-01-01', '2022-01-01'); +EXECUTE test_statement_cte('2022-01-01', '2022-01-01'); +EXECUTE test_statement_cte('2022-01-01', '2022-01-01'); +EXECUTE test_statement_cte('2022-01-01', '2022-01-01'); +EXECUTE test_statement_cte('2022-01-01', '2022-01-01'); + +PREPARE test_statement_insert(int, text) AS +INSERT INTO test VALUES ($2::timestamp, $1); + +EXECUTE test_statement_insert(3, '2022-03-03'); +EXECUTE test_statement_insert(4, '2022-04-04'); +EXECUTE test_statement_insert(5, '2022-05-05'); +EXECUTE test_statement_insert(6, '2022-06-06'); +EXECUTE test_statement_insert(7, '2022-07-07'); +EXECUTE test_statement_insert(8, '2022-08-08'); +EXECUTE test_statement_insert(9, '2022-09-09'); +EXECUTE test_statement_insert(10, '2022-10-10'); + +SELECT count(*) FROM test; +EXECUTE test_statement_regular('2022-01-01'); + +PREPARE test_statement_null(text) AS +SELECT user_id , $1::timestamp FROM test ORDER BY user_id LIMIT 2; + +EXECUTE test_statement_null(NULL); +EXECUTE test_statement_null(NULL); +EXECUTE test_statement_null(NULL); +EXECUTE test_statement_null(NULL); +EXECUTE test_statement_null(NULL); +EXECUTE test_statement_null(NULL); +EXECUTE test_statement_null(NULL); +EXECUTE test_statement_null(NULL); diff --git a/src/test/regress/sql/prepared_statements_create_load.sql b/src/test/regress/sql/prepared_statements_create_load.sql index 6afb1a12f..025ff1f6a 100644 --- a/src/test/regress/sql/prepared_statements_create_load.sql +++ b/src/test/regress/sql/prepared_statements_create_load.sql @@ -73,3 +73,12 @@ CREATE TABLE http_request ( ); SELECT create_distributed_table('http_request', 'site_id'); + +-- Standard planner converted text and varchar casts to cstring in some cases +-- We make sure we convert it back to text when parsing the expression +-- https://github.com/citusdata/citus/issues/6061 +-- https://github.com/citusdata/citus/issues/5646 +-- https://github.com/citusdata/citus/issues/5033 + +CREATE TABLE test(t timestamp, user_id int); +SELECT create_distributed_table('test', 'user_id'); From b58665773ba22a563ada3071ea92d1ec70a701d2 Mon Sep 17 00:00:00 2001 From: Teja Mupparti Date: Wed, 3 May 2023 19:08:08 -0700 Subject: [PATCH 034/118] Move all pre-15-defined routines to the bottom of the file --- .../distributed/planner/merge_planner.c | 339 +++++++++--------- 1 file changed, 168 insertions(+), 171 deletions(-) diff --git a/src/backend/distributed/planner/merge_planner.c b/src/backend/distributed/planner/merge_planner.c index 930a44db8..86163e131 100644 --- a/src/backend/distributed/planner/merge_planner.c +++ b/src/backend/distributed/planner/merge_planner.c @@ -49,179 +49,8 @@ static DeferredErrorMessage * MergeQualAndTargetListFunctionsSupported(Oid Node *quals, List *targetList, CmdType commandType); -#endif -/* - * CreateMergePlan attempts to create a plan for the given MERGE SQL - * statement. If planning fails ->planningError is set to a description - * of the failure. - */ -DistributedPlan * -CreateMergePlan(Query *originalQuery, Query *query, - PlannerRestrictionContext *plannerRestrictionContext) -{ - DistributedPlan *distributedPlan = CitusMakeNode(DistributedPlan); - bool multiShardQuery = false; - Oid targetRelationId = ModifyQueryResultRelationId(originalQuery); - - Assert(originalQuery->commandType == CMD_MERGE); - Assert(OidIsValid(targetRelationId)); - - distributedPlan->targetRelationId = targetRelationId; - distributedPlan->modLevel = RowModifyLevelForQuery(query); - distributedPlan->planningError = MergeQuerySupported(targetRelationId, - originalQuery, - multiShardQuery, - plannerRestrictionContext); - - if (distributedPlan->planningError != NULL) - { - return distributedPlan; - } - - Job *job = RouterJob(originalQuery, plannerRestrictionContext, - &distributedPlan->planningError); - - if (distributedPlan->planningError != NULL) - { - return distributedPlan; - } - - ereport(DEBUG1, (errmsg("Creating MERGE router plan"))); - - distributedPlan->workerJob = job; - distributedPlan->combineQuery = NULL; - - /* MERGE doesn't support RETURNING clause */ - distributedPlan->expectResults = false; - distributedPlan->fastPathRouterPlan = - plannerRestrictionContext->fastPathRestrictionContext->fastPathRouterQuery; - - return distributedPlan; -} - - -/* - * MergeQuerySupported does check for a MERGE command in the query, if it finds - * one, it will verify the below criteria - * - Supported tables and combinations in ErrorIfMergeHasUnsupportedTables - * - Distributed tables requirements in ErrorIfDistTablesNotColocated - * - Checks target-lists and functions-in-quals in TargetlistAndFunctionsSupported - */ -DeferredErrorMessage * -MergeQuerySupported(Oid resultRelationId, Query *originalQuery, bool multiShardQuery, - PlannerRestrictionContext *plannerRestrictionContext) -{ - /* function is void for pre-15 versions of Postgres */ - #if PG_VERSION_NUM < PG_VERSION_15 - - return NULL; - - #else - - /* - * TODO: For now, we are adding an exception where any volatile or stable - * functions are not allowed in the MERGE query, but this will become too - * restrictive as this will prevent many useful and simple cases, such as, - * INSERT VALUES(ts::timestamp), bigserial column inserts etc. But without - * this restriction, we have a potential danger of some of the function(s) - * getting executed at the worker which will result in incorrect behavior. - */ - if (contain_mutable_functions((Node *) originalQuery)) - { - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - "non-IMMUTABLE functions are not yet supported " - "in MERGE sql with distributed tables ", - NULL, NULL); - } - - List *rangeTableList = ExtractRangeTableEntryList(originalQuery); - - /* - * Fast path queries cannot have merge command, and we prevent the remaining here. - * In Citus we have limited support for MERGE, it's allowed only if all - * the tables(target, source or any CTE) tables are are local i.e. a - * combination of Citus local and Non-Citus tables (regular Postgres tables) - * or distributed tables with some restrictions, please see header of routine - * ErrorIfDistTablesNotColocated for details. - */ - DeferredErrorMessage *deferredError = - ErrorIfMergeHasUnsupportedTables(resultRelationId, - originalQuery, - rangeTableList, - plannerRestrictionContext); - if (deferredError) - { - /* MERGE's unsupported combination, raise the exception */ - RaiseDeferredError(deferredError, ERROR); - } - - deferredError = MergeQualAndTargetListFunctionsSupported(resultRelationId, - originalQuery->jointree, - originalQuery->jointree-> - quals, - originalQuery->targetList, - originalQuery->commandType); - if (deferredError) - { - return deferredError; - } - - /* - * MERGE is a special case where we have multiple modify statements - * within itself. Check each INSERT/UPDATE/DELETE individually. - */ - MergeAction *action = NULL; - foreach_ptr(action, originalQuery->mergeActionList) - { - Assert(originalQuery->returningList == NULL); - deferredError = MergeQualAndTargetListFunctionsSupported(resultRelationId, - originalQuery->jointree, - action->qual, - action->targetList, - action->commandType); - if (deferredError) - { - /* MERGE's unsupported scenario, raise the exception */ - RaiseDeferredError(deferredError, ERROR); - } - } - - deferredError = - InsertDistributionColumnMatchesSource(resultRelationId, originalQuery); - if (deferredError) - { - /* MERGE's unsupported scenario, raise the exception */ - RaiseDeferredError(deferredError, ERROR); - } - - if (multiShardQuery) - { - deferredError = - DeferErrorIfUnsupportedSubqueryPushdown(originalQuery, - plannerRestrictionContext); - if (deferredError) - { - return deferredError; - } - } - - if (HasDangerousJoinUsing(originalQuery->rtable, (Node *) originalQuery->jointree)) - { - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - "a join with USING causes an internal naming " - "conflict, use ON instead", NULL, NULL); - } - - return NULL; - - #endif -} - - -#if PG_VERSION_NUM >= PG_VERSION_15 - /* * ErrorIfDistTablesNotColocated Checks to see if * @@ -728,6 +557,174 @@ MergeQualAndTargetListFunctionsSupported(Oid resultRelationId, FromExpr *joinTre #endif +/* + * MergeQuerySupported does check for a MERGE command in the query, if it finds + * one, it will verify the below criteria + * - Supported tables and combinations in ErrorIfMergeHasUnsupportedTables + * - Distributed tables requirements in ErrorIfDistTablesNotColocated + * - Checks target-lists and functions-in-quals in TargetlistAndFunctionsSupported + */ +DeferredErrorMessage * +MergeQuerySupported(Oid resultRelationId, Query *originalQuery, bool multiShardQuery, + PlannerRestrictionContext *plannerRestrictionContext) +{ + /* function is void for pre-15 versions of Postgres */ + #if PG_VERSION_NUM < PG_VERSION_15 + + return NULL; + + #else + + /* + * TODO: For now, we are adding an exception where any volatile or stable + * functions are not allowed in the MERGE query, but this will become too + * restrictive as this will prevent many useful and simple cases, such as, + * INSERT VALUES(ts::timestamp), bigserial column inserts etc. But without + * this restriction, we have a potential danger of some of the function(s) + * getting executed at the worker which will result in incorrect behavior. + */ + if (contain_mutable_functions((Node *) originalQuery)) + { + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "non-IMMUTABLE functions are not yet supported " + "in MERGE sql with distributed tables ", + NULL, NULL); + } + + List *rangeTableList = ExtractRangeTableEntryList(originalQuery); + + /* + * Fast path queries cannot have merge command, and we prevent the remaining here. + * In Citus we have limited support for MERGE, it's allowed only if all + * the tables(target, source or any CTE) tables are are local i.e. a + * combination of Citus local and Non-Citus tables (regular Postgres tables) + * or distributed tables with some restrictions, please see header of routine + * ErrorIfDistTablesNotColocated for details. + */ + DeferredErrorMessage *deferredError = + ErrorIfMergeHasUnsupportedTables(resultRelationId, + originalQuery, + rangeTableList, + plannerRestrictionContext); + if (deferredError) + { + /* MERGE's unsupported combination, raise the exception */ + RaiseDeferredError(deferredError, ERROR); + } + + deferredError = MergeQualAndTargetListFunctionsSupported(resultRelationId, + originalQuery->jointree, + originalQuery->jointree-> + quals, + originalQuery->targetList, + originalQuery->commandType); + if (deferredError) + { + return deferredError; + } + + /* + * MERGE is a special case where we have multiple modify statements + * within itself. Check each INSERT/UPDATE/DELETE individually. + */ + MergeAction *action = NULL; + foreach_ptr(action, originalQuery->mergeActionList) + { + Assert(originalQuery->returningList == NULL); + deferredError = MergeQualAndTargetListFunctionsSupported(resultRelationId, + originalQuery->jointree, + action->qual, + action->targetList, + action->commandType); + if (deferredError) + { + /* MERGE's unsupported scenario, raise the exception */ + RaiseDeferredError(deferredError, ERROR); + } + } + + deferredError = + InsertDistributionColumnMatchesSource(resultRelationId, originalQuery); + if (deferredError) + { + /* MERGE's unsupported scenario, raise the exception */ + RaiseDeferredError(deferredError, ERROR); + } + + if (multiShardQuery) + { + deferredError = + DeferErrorIfUnsupportedSubqueryPushdown(originalQuery, + plannerRestrictionContext); + if (deferredError) + { + return deferredError; + } + } + + if (HasDangerousJoinUsing(originalQuery->rtable, (Node *) originalQuery->jointree)) + { + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "a join with USING causes an internal naming " + "conflict, use ON instead", NULL, NULL); + } + + return NULL; + + #endif +} + + +/* + * CreateMergePlan attempts to create a plan for the given MERGE SQL + * statement. If planning fails ->planningError is set to a description + * of the failure. + */ +DistributedPlan * +CreateMergePlan(Query *originalQuery, Query *query, + PlannerRestrictionContext *plannerRestrictionContext) +{ + DistributedPlan *distributedPlan = CitusMakeNode(DistributedPlan); + bool multiShardQuery = false; + Oid targetRelationId = ModifyQueryResultRelationId(originalQuery); + + Assert(originalQuery->commandType == CMD_MERGE); + Assert(OidIsValid(targetRelationId)); + + distributedPlan->targetRelationId = targetRelationId; + distributedPlan->modLevel = RowModifyLevelForQuery(query); + distributedPlan->planningError = MergeQuerySupported(targetRelationId, + originalQuery, + multiShardQuery, + plannerRestrictionContext); + + if (distributedPlan->planningError != NULL) + { + return distributedPlan; + } + + Job *job = RouterJob(originalQuery, plannerRestrictionContext, + &distributedPlan->planningError); + + if (distributedPlan->planningError != NULL) + { + return distributedPlan; + } + + ereport(DEBUG1, (errmsg("Creating MERGE router plan"))); + + distributedPlan->workerJob = job; + distributedPlan->combineQuery = NULL; + + /* MERGE doesn't support RETURNING clause */ + distributedPlan->expectResults = false; + distributedPlan->fastPathRouterPlan = + plannerRestrictionContext->fastPathRestrictionContext->fastPathRouterQuery; + + return distributedPlan; +} + + /* * IsLocalTableModification returns true if the table modified is a Postgres table. * We do not support recursive planning for MERGE yet, so we could have a join From 3217e3f1817b63df68ae79f0bf80448b4074e9fc Mon Sep 17 00:00:00 2001 From: Hanefi Onaldi Date: Fri, 5 May 2023 12:07:46 +0300 Subject: [PATCH 035/118] Fix flaky background rebalance parallel test (#6893) A test in background_rebalance_parallel.sql was failing intermittently where the order of tasks in the output was not deterministic. This commit fixes the test by removing id columns for the background tasks in the output. A sample failing diff before this patch is below: ```diff SELECT D.task_id, (SELECT T.command FROM pg_dist_background_task T WHERE T.task_id = D.task_id), D.depends_on, (SELECT T.command FROM pg_dist_background_task T WHERE T.task_id = D.depends_on) FROM pg_dist_background_task_depend D WHERE job_id in (:job_id) ORDER BY D.task_id, D.depends_on ASC; task_id | command | depends_on | command ---------+---------------------------------------------------------------------+------------+--------------------------------------------------------------------- - 1014 | SELECT pg_catalog.citus_move_shard_placement(85674026,50,57,'auto') | 1013 | SELECT pg_catalog.citus_move_shard_placement(85674025,50,56,'auto') - 1016 | SELECT pg_catalog.citus_move_shard_placement(85674032,50,57,'auto') | 1015 | SELECT pg_catalog.citus_move_shard_placement(85674031,50,56,'auto') - 1018 | SELECT pg_catalog.citus_move_shard_placement(85674038,50,57,'auto') | 1017 | SELECT pg_catalog.citus_move_shard_placement(85674037,50,56,'auto') - 1020 | SELECT pg_catalog.citus_move_shard_placement(85674044,50,57,'auto') | 1019 | SELECT pg_catalog.citus_move_shard_placement(85674043,50,56,'auto') + 1014 | SELECT pg_catalog.citus_move_shard_placement(85674038,50,57,'auto') | 1013 | SELECT pg_catalog.citus_move_shard_placement(85674037,50,56,'auto') + 1016 | SELECT pg_catalog.citus_move_shard_placement(85674044,50,57,'auto') | 1015 | SELECT pg_catalog.citus_move_shard_placement(85674043,50,56,'auto') + 1018 | SELECT pg_catalog.citus_move_shard_placement(85674026,50,57,'auto') | 1017 | SELECT pg_catalog.citus_move_shard_placement(85674025,50,56,'auto') + 1020 | SELECT pg_catalog.citus_move_shard_placement(85674032,50,57,'auto') | 1019 | SELECT pg_catalog.citus_move_shard_placement(85674031,50,56,'auto') (4 rows) ``` Notice that the dependent and dependee tasks have some commands, but they have different task ids. --- .../background_rebalance_parallel.out | 30 ++++++++++++------- .../sql/background_rebalance_parallel.sql | 13 ++++---- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/test/regress/expected/background_rebalance_parallel.out b/src/test/regress/expected/background_rebalance_parallel.out index 9c43fab9b..187f709e4 100644 --- a/src/test/regress/expected/background_rebalance_parallel.out +++ b/src/test/regress/expected/background_rebalance_parallel.out @@ -466,17 +466,27 @@ SELECT citus_rebalance_start AS job_id from citus_rebalance_start() \gset -- see dependent tasks to understand which tasks remain runnable because of -- citus.max_background_task_executors_per_node -- and which tasks are actually blocked from colocation group dependencies -SELECT D.task_id, - (SELECT T.command FROM pg_dist_background_task T WHERE T.task_id = D.task_id), - D.depends_on, - (SELECT T.command FROM pg_dist_background_task T WHERE T.task_id = D.depends_on) -FROM pg_dist_background_task_depend D WHERE job_id in (:job_id) ORDER BY D.task_id, D.depends_on ASC; - task_id | command | depends_on | command +SELECT (SELECT T.command FROM pg_dist_background_task T WHERE T.task_id = D.task_id), + (SELECT T.command depends_on_command FROM pg_dist_background_task T WHERE T.task_id = D.depends_on) +FROM pg_dist_background_task_depend D WHERE job_id in (:job_id) ORDER BY 1, 2 ASC; + command | depends_on_command --------------------------------------------------------------------- - 1014 | SELECT pg_catalog.citus_move_shard_placement(85674026,50,57,'auto') | 1013 | SELECT pg_catalog.citus_move_shard_placement(85674025,50,56,'auto') - 1016 | SELECT pg_catalog.citus_move_shard_placement(85674032,50,57,'auto') | 1015 | SELECT pg_catalog.citus_move_shard_placement(85674031,50,56,'auto') - 1018 | SELECT pg_catalog.citus_move_shard_placement(85674038,50,57,'auto') | 1017 | SELECT pg_catalog.citus_move_shard_placement(85674037,50,56,'auto') - 1020 | SELECT pg_catalog.citus_move_shard_placement(85674044,50,57,'auto') | 1019 | SELECT pg_catalog.citus_move_shard_placement(85674043,50,56,'auto') + SELECT pg_catalog.citus_move_shard_placement(85674026,50,57,'auto') | SELECT pg_catalog.citus_move_shard_placement(85674025,50,56,'auto') + SELECT pg_catalog.citus_move_shard_placement(85674032,50,57,'auto') | SELECT pg_catalog.citus_move_shard_placement(85674031,50,56,'auto') + SELECT pg_catalog.citus_move_shard_placement(85674038,50,57,'auto') | SELECT pg_catalog.citus_move_shard_placement(85674037,50,56,'auto') + SELECT pg_catalog.citus_move_shard_placement(85674044,50,57,'auto') | SELECT pg_catalog.citus_move_shard_placement(85674043,50,56,'auto') +(4 rows) + +SELECT task_id, depends_on +FROM pg_dist_background_task_depend +WHERE job_id in (:job_id) +ORDER BY 1, 2 ASC; + task_id | depends_on +--------------------------------------------------------------------- + 1014 | 1013 + 1016 | 1015 + 1018 | 1017 + 1020 | 1019 (4 rows) -- default citus.max_background_task_executors_per_node is 1 diff --git a/src/test/regress/sql/background_rebalance_parallel.sql b/src/test/regress/sql/background_rebalance_parallel.sql index 5229e7f88..e55fd93bb 100644 --- a/src/test/regress/sql/background_rebalance_parallel.sql +++ b/src/test/regress/sql/background_rebalance_parallel.sql @@ -204,11 +204,14 @@ SELECT citus_rebalance_start AS job_id from citus_rebalance_start() \gset -- see dependent tasks to understand which tasks remain runnable because of -- citus.max_background_task_executors_per_node -- and which tasks are actually blocked from colocation group dependencies -SELECT D.task_id, - (SELECT T.command FROM pg_dist_background_task T WHERE T.task_id = D.task_id), - D.depends_on, - (SELECT T.command FROM pg_dist_background_task T WHERE T.task_id = D.depends_on) -FROM pg_dist_background_task_depend D WHERE job_id in (:job_id) ORDER BY D.task_id, D.depends_on ASC; +SELECT (SELECT T.command FROM pg_dist_background_task T WHERE T.task_id = D.task_id), + (SELECT T.command depends_on_command FROM pg_dist_background_task T WHERE T.task_id = D.depends_on) +FROM pg_dist_background_task_depend D WHERE job_id in (:job_id) ORDER BY 1, 2 ASC; + +SELECT task_id, depends_on +FROM pg_dist_background_task_depend +WHERE job_id in (:job_id) +ORDER BY 1, 2 ASC; -- default citus.max_background_task_executors_per_node is 1 -- show that first exactly one task per node is running From 905fd46410544c98aac2be92d188c52cc0bb2802 Mon Sep 17 00:00:00 2001 From: Naisila Puka <37271756+naisila@users.noreply.github.com> Date: Fri, 5 May 2023 16:47:01 +0300 Subject: [PATCH 036/118] Fixes flakiness in background_rebalance_parallel test (#6910) Fixes the following flaky outputs by decreasing citus_task_wait loop interval, and changing the order of wait commands. https://app.circleci.com/pipelines/github/citusdata/citus/32102/workflows/19958297-6c7e-49ef-9bc2-8efe8aacb96f/jobs/1089589 ``` diff SELECT job_id, task_id, status, nodes_involved FROM pg_dist_background_task WHERE job_id in (:job_id) ORDER BY task_id; job_id | task_id | status | nodes_involved --------+---------+----------+---------------- 17779 | 1013 | done | {50,56} 17779 | 1014 | running | {50,57} - 17779 | 1015 | running | {50,56} - 17779 | 1016 | blocked | {50,57} + 17779 | 1015 | done | {50,56} + 17779 | 1016 | running | {50,57} 17779 | 1017 | runnable | {50,56} 17779 | 1018 | blocked | {50,57} 17779 | 1019 | runnable | {50,56} 17779 | 1020 | blocked | {50,57} (8 rows) ``` https://github.com/citusdata/citus/pull/6893#issuecomment-1525661408 ```diff SELECT job_id, task_id, status, nodes_involved FROM pg_dist_background_task WHERE job_id in (:job_id) ORDER BY task_id; job_id | task_id | status | nodes_involved --------+---------+----------+---------------- 17779 | 1013 | done | {50,56} - 17779 | 1014 | running | {50,57} + 17779 | 1014 | runnable | {50,57} 17779 | 1015 | running | {50,56} 17779 | 1016 | blocked | {50,57} 17779 | 1017 | runnable | {50,56} 17779 | 1018 | blocked | {50,57} 17779 | 1019 | runnable | {50,56} 17779 | 1020 | blocked | {50,57} (8 rows) ``` --- src/backend/distributed/utils/background_jobs.c | 2 +- .../regress/expected/background_rebalance_parallel.out | 10 ++++++++-- src/test/regress/sql/background_rebalance_parallel.sql | 4 +++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/backend/distributed/utils/background_jobs.c b/src/backend/distributed/utils/background_jobs.c index 789732d21..84ef4229f 100644 --- a/src/backend/distributed/utils/background_jobs.c +++ b/src/backend/distributed/utils/background_jobs.c @@ -395,7 +395,7 @@ citus_task_wait_internal(int64 taskid, BackgroundTaskStatus *desiredStatus) /* sleep for a while, before rechecking the task status */ CHECK_FOR_INTERRUPTS(); - const long delay_ms = 1000; + const long delay_ms = 100; (void) WaitLatch(MyLatch, WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH, delay_ms, diff --git a/src/test/regress/expected/background_rebalance_parallel.out b/src/test/regress/expected/background_rebalance_parallel.out index 187f709e4..dbdd963a9 100644 --- a/src/test/regress/expected/background_rebalance_parallel.out +++ b/src/test/regress/expected/background_rebalance_parallel.out @@ -513,6 +513,12 @@ FROM pg_dist_background_task WHERE job_id in (:job_id) ORDER BY task_id; (8 rows) -- increase citus.max_background_task_executors_per_node +SELECT citus_task_wait(1013, desired_status => 'done'); + citus_task_wait +--------------------------------------------------------------------- + +(1 row) + ALTER SYSTEM SET citus.max_background_task_executors_per_node = 2; SELECT pg_reload_conf(); pg_reload_conf @@ -520,13 +526,13 @@ SELECT pg_reload_conf(); t (1 row) -SELECT citus_task_wait(1015, desired_status => 'running'); +SELECT citus_task_wait(1014, desired_status => 'running'); citus_task_wait --------------------------------------------------------------------- (1 row) -SELECT citus_task_wait(1013, desired_status => 'done'); +SELECT citus_task_wait(1015, desired_status => 'running'); citus_task_wait --------------------------------------------------------------------- diff --git a/src/test/regress/sql/background_rebalance_parallel.sql b/src/test/regress/sql/background_rebalance_parallel.sql index e55fd93bb..2eb952b67 100644 --- a/src/test/regress/sql/background_rebalance_parallel.sql +++ b/src/test/regress/sql/background_rebalance_parallel.sql @@ -221,10 +221,12 @@ SELECT job_id, task_id, status, nodes_involved FROM pg_dist_background_task WHERE job_id in (:job_id) ORDER BY task_id; -- increase citus.max_background_task_executors_per_node +SELECT citus_task_wait(1013, desired_status => 'done'); ALTER SYSTEM SET citus.max_background_task_executors_per_node = 2; SELECT pg_reload_conf(); + +SELECT citus_task_wait(1014, desired_status => 'running'); SELECT citus_task_wait(1015, desired_status => 'running'); -SELECT citus_task_wait(1013, desired_status => 'done'); -- show that at most 2 tasks per node are running -- among the tasks that are not blocked From 73c771d6edf307412ec7e55ee409ada5dc2a345a Mon Sep 17 00:00:00 2001 From: aykut-bozkurt <51649454+aykut-bozkurt@users.noreply.github.com> Date: Fri, 5 May 2023 19:08:35 +0300 Subject: [PATCH 037/118] Update readme for 11.3 (#6903) Co-authored-by: Hanefi Onaldi Co-authored-by: Jelte Fennema --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7a15cd6d8..6b6dc5664 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -| **
The Citus database is 100% open source.

Learn what's new in the [Citus 11.2 release blog](https://www.citusdata.com/blog/2023/02/08/whats-new-in-citus-11-2-patroni-ha-support/) and the [Citus Updates page](https://www.citusdata.com/updates/).

**| +| **
The Citus database is 100% open source.

Learn what's new in the [Citus 11.3 release blog](https://www.citusdata.com/blog/2023/05/05/whats-new-in-citus-11-3-multi-tenant-saas/) and the [Citus Updates page](https://www.citusdata.com/updates/).

**| |---|
@@ -94,14 +94,14 @@ Install packages on Ubuntu / Debian: ```bash curl https://install.citusdata.com/community/deb.sh > add-citus-repo.sh sudo bash add-citus-repo.sh -sudo apt-get -y install postgresql-15-citus-11.2 +sudo apt-get -y install postgresql-15-citus-11.3 ``` Install packages on CentOS / Red Hat: ```bash curl https://install.citusdata.com/community/rpm.sh > add-citus-repo.sh sudo bash add-citus-repo.sh -sudo yum install -y citus112_15 +sudo yum install -y citus113_15 ``` To add Citus to your local PostgreSQL database, add the following to `postgresql.conf`: @@ -349,7 +349,7 @@ To learn more about columnar storage, check out the [columnar storage README](ht ## Setting up with High Availability -One of the most popular high availability solutions for PostgreSQL, [Patroni 3.0](https://github.com/zalando/patroni), has [first class support for Citus 10.0 and above](https://patroni.readthedocs.io/en/latest/citus.html#citus), additionally Citus 11.2 ships with improvements for smoother node switchover in Patroni. +One of the most popular high availability solutions for PostgreSQL, [Patroni 3.0](https://github.com/zalando/patroni), has [first class support for Citus 10.0 and above](https://patroni.readthedocs.io/en/latest/citus.html#citus), additionally since Citus 11.2 ships with improvements for smoother node switchover in Patroni. An example of patronictl list output for the Citus cluster: From 06e6f8e428e1db15a19a63e495965fd89f92043a Mon Sep 17 00:00:00 2001 From: Hanefi Onaldi Date: Mon, 8 May 2023 16:10:55 +0300 Subject: [PATCH 038/118] Normalize columnar version in tests (#6917) When we bump columnar version, some tests fail because of the output change. Instead of changing those lines every time, I think it is better to normalize it in tests. --- src/test/regress/bin/normalize.sed | 3 +++ .../expected/multi_fix_partition_shard_index_names.out | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/test/regress/bin/normalize.sed b/src/test/regress/bin/normalize.sed index 65692e1c9..731ae659a 100644 --- a/src/test/regress/bin/normalize.sed +++ b/src/test/regress/bin/normalize.sed @@ -309,3 +309,6 @@ s/(NOTICE: issuing SET LOCAL application_name TO 'citus_rebalancer gpid=)[0-9]+ s/improvement of 0.1[0-9]* is lower/improvement of 0.1xxxxx is lower/g # normalize tenants statistics annotations s/\/\*\{"tId":.*\*\///g + +# Notice message that contains current columnar version that makes it harder to bump versions +s/(NOTICE: issuing CREATE EXTENSION IF NOT EXISTS citus_columnar WITH SCHEMA pg_catalog VERSION )"[0-9]+\.[0-9]+-[0-9]+"/\1 "x.y-z"/ diff --git a/src/test/regress/expected/multi_fix_partition_shard_index_names.out b/src/test/regress/expected/multi_fix_partition_shard_index_names.out index ef47efed4..5f7526982 100644 --- a/src/test/regress/expected/multi_fix_partition_shard_index_names.out +++ b/src/test/regress/expected/multi_fix_partition_shard_index_names.out @@ -650,7 +650,7 @@ NOTICE: issuing BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;SELECT assign_ DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing SET citus.enable_ddl_propagation TO 'off' DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing CREATE EXTENSION IF NOT EXISTS citus_columnar WITH SCHEMA pg_catalog VERSION "11.3-1"; +NOTICE: issuing CREATE EXTENSION IF NOT EXISTS citus_columnar WITH SCHEMA pg_catalog VERSION "x.y-z"; DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing COMMIT DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx @@ -658,7 +658,7 @@ NOTICE: issuing BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;SELECT assign_ DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing SET citus.enable_ddl_propagation TO 'off' DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing CREATE EXTENSION IF NOT EXISTS citus_columnar WITH SCHEMA pg_catalog VERSION "11.3-1"; +NOTICE: issuing CREATE EXTENSION IF NOT EXISTS citus_columnar WITH SCHEMA pg_catalog VERSION "x.y-z"; DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing COMMIT DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx From e3c6b8a10e4b61da1ae83bad4d50673604079545 Mon Sep 17 00:00:00 2001 From: Ivan Kush Date: Tue, 9 May 2023 13:42:37 +0300 Subject: [PATCH 039/118] Fix flaky clolumnar_permissions test (#6913) As attr_num isn't ordered, order may be random. And regression test may be failed. This MR adds attr_num to ORDER BY ``` 3 --- /build/contrib/citus/src/test/regress/expected/columnar_permissions.out.modified 2023-05-05 11:13:44.926085432 +0000 4 +++ /build/contrib/citus/src/test/regress/results/columnar_permissions.out.modified 2023-05-05 11:13:44.934085414 +0000 5 @@ -124,24 +124,24 @@ 6 from columnar.chunk 7 where relation in ('no_access'::regclass, 'columnar_permissions'::regclass) 8 order by relation, stripe_num; 9 relation | stripe_num | attr_num | chunk_group_num | value_count 10 ----------------------+------------+----------+-----------------+------------- 11 no_access | 1 | 1 | 0 | 1 12 no_access | 2 | 1 | 0 | 1 13 no_access | 3 | 1 | 0 | 1 14 columnar_permissions | 1 | 1 | 0 | 1 15 columnar_permissions | 1 | 2 | 0 | 1 16 - columnar_permissions | 2 | 1 | 0 | 1 17 columnar_permissions | 2 | 2 | 0 | 1 18 - columnar_permissions | 3 | 1 | 0 | 1 19 + columnar_permissions | 2 | 1 | 0 | 1 20 columnar_permissions | 3 | 2 | 0 | 1 21 + columnar_permissions | 3 | 1 | 0 | 1 22 columnar_permissions | 4 | 1 | 0 | 1 23 columnar_permissions | 4 | 2 | 0 | 1 24 (11 rows) ``` Co-authored-by: Ivan Kush --- src/test/regress/expected/columnar_permissions.out | 2 +- src/test/regress/sql/columnar_permissions.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/regress/expected/columnar_permissions.out b/src/test/regress/expected/columnar_permissions.out index d8b70d830..7f9e4e2c6 100644 --- a/src/test/regress/expected/columnar_permissions.out +++ b/src/test/regress/expected/columnar_permissions.out @@ -123,7 +123,7 @@ select relation, stripe_num, row_count, first_row_number select relation, stripe_num, attr_num, chunk_group_num, value_count from columnar.chunk where relation in ('no_access'::regclass, 'columnar_permissions'::regclass) - order by relation, stripe_num; + order by relation, stripe_num, attr_num; relation | stripe_num | attr_num | chunk_group_num | value_count --------------------------------------------------------------------- no_access | 1 | 1 | 0 | 1 diff --git a/src/test/regress/sql/columnar_permissions.sql b/src/test/regress/sql/columnar_permissions.sql index 11492bfed..d0f26bc96 100644 --- a/src/test/regress/sql/columnar_permissions.sql +++ b/src/test/regress/sql/columnar_permissions.sql @@ -75,7 +75,7 @@ select relation, stripe_num, row_count, first_row_number select relation, stripe_num, attr_num, chunk_group_num, value_count from columnar.chunk where relation in ('no_access'::regclass, 'columnar_permissions'::regclass) - order by relation, stripe_num; + order by relation, stripe_num, attr_num; select relation, stripe_num, chunk_group_num, row_count from columnar.chunk_group where relation in ('no_access'::regclass, 'columnar_permissions'::regclass) From 07b8cd2634f2d1fa903f0635aa00733bb66cc6df Mon Sep 17 00:00:00 2001 From: Jelte Fennema Date: Tue, 9 May 2023 16:55:56 +0200 Subject: [PATCH 040/118] Forward to existing emit_log_hook in our log hook (#6877) DESCRIPTION: Forward to existing emit_log_hook in our log hook This makes us work better with other extensions installed in Postgres. Without this change we would overwrite their emit_log_hook, causing it to never be called. Fixes #6874 --- src/backend/distributed/shared_library_init.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/backend/distributed/shared_library_init.c b/src/backend/distributed/shared_library_init.c index e7fedd1d8..4e0f9d0de 100644 --- a/src/backend/distributed/shared_library_init.c +++ b/src/backend/distributed/shared_library_init.c @@ -213,6 +213,7 @@ static bool IsSuperuser(char *userName); static void AdjustDynamicLibraryPathForCdcDecoders(void); static ClientAuthentication_hook_type original_client_auth_hook = NULL; +static emit_log_hook_type original_emit_log_hook = NULL; /* *INDENT-OFF* */ /* GUC enum definitions */ @@ -458,6 +459,7 @@ _PG_init(void) ExecutorEnd_hook = CitusAttributeToEnd; /* register hook for error messages */ + original_emit_log_hook = emit_log_hook; emit_log_hook = multi_log_hook; @@ -698,6 +700,11 @@ multi_log_hook(ErrorData *edata) edata->message = pstrdup("canceling the transaction since it was " "involved in a distributed deadlock"); } + + if (original_emit_log_hook) + { + original_emit_log_hook(edata); + } } From 893ed416f1ee3d499bf4e27e9d871e9a2359fac0 Mon Sep 17 00:00:00 2001 From: Onur Tirtir Date: Mon, 15 May 2023 12:07:50 +0300 Subject: [PATCH 041/118] Disable citus.enable_non_colocated_router_query_pushdown by default (#6909) Fixes #6779. DESCRIPTION: Disables citus.enable_non_colocated_router_query_pushdown GUC by default to ensure generating a consistent distributed plan for the queries that reference non-colocated distributed tables We already have tests for the cases where this GUC is disabled, so I'm not adding any more tests in this PR. Also make multi_insert_select_window idempotent. Related to: #6793 --- src/backend/distributed/shared_library_init.c | 2 +- .../expected/insert_select_repartition.out | 2 + .../expected/insert_select_repartition_0.out | 2 + .../insert_select_single_shard_table.out | 38 +++++++------- .../expected/multi_insert_select_window.out | 1 + .../regress/expected/multi_router_planner.out | 50 +++++++++++++------ .../expected/query_single_shard_table.out | 42 ++++++++-------- src/test/regress/expected/set_operations.out | 4 +- .../regress/sql/insert_select_repartition.sql | 2 + .../sql/multi_insert_select_window.sql | 2 + 10 files changed, 86 insertions(+), 59 deletions(-) diff --git a/src/backend/distributed/shared_library_init.c b/src/backend/distributed/shared_library_init.c index 4e0f9d0de..d20eeeb08 100644 --- a/src/backend/distributed/shared_library_init.c +++ b/src/backend/distributed/shared_library_init.c @@ -1346,7 +1346,7 @@ RegisterCitusConfigVariables(void) "or altering the shard count of one of those distributed " "tables."), &EnableNonColocatedRouterQueryPushdown, - true, + false, PGC_USERSET, GUC_NO_SHOW_ALL, NULL, NULL, NULL); diff --git a/src/test/regress/expected/insert_select_repartition.out b/src/test/regress/expected/insert_select_repartition.out index 5d9ddca6a..bb77e9d47 100644 --- a/src/test/regress/expected/insert_select_repartition.out +++ b/src/test/regress/expected/insert_select_repartition.out @@ -756,6 +756,7 @@ DEALLOCATE insert_plan; -- TRUNCATE target_table; SET client_min_messages TO DEBUG2; +SET citus.enable_non_colocated_router_query_pushdown TO ON; WITH r AS ( INSERT INTO target_table SELECT * FROM source_table RETURNING * ) @@ -777,6 +778,7 @@ DEBUG: partitioning SELECT query by column index 0 with name 'a' DEBUG: distributed statement: INSERT INTO insert_select_repartition.target_table_4213610 AS citus_table_alias (a, b) SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_results('{repartitioned_results_xxxxx_from_4213606_to_0,repartitioned_results_xxxxx_from_4213607_to_0}'::text[], 'binary'::citus_copy_format) intermediate_result(a integer, b integer) DEBUG: distributed statement: INSERT INTO insert_select_repartition.target_table_4213611 AS citus_table_alias (a, b) SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_results('{repartitioned_results_xxxxx_from_4213607_to_1}'::text[], 'binary'::citus_copy_format) intermediate_result(a integer, b integer) DEBUG: distributed statement: INSERT INTO insert_select_repartition.target_table_4213612 AS citus_table_alias (a, b) SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_results('{repartitioned_results_xxxxx_from_4213609_to_2}'::text[], 'binary'::citus_copy_format) intermediate_result(a integer, b integer) +RESET citus.enable_non_colocated_router_query_pushdown; RESET client_min_messages; SELECT * FROM target_table ORDER BY a, b; a | b diff --git a/src/test/regress/expected/insert_select_repartition_0.out b/src/test/regress/expected/insert_select_repartition_0.out index 2eea30bdf..0aec4f49a 100644 --- a/src/test/regress/expected/insert_select_repartition_0.out +++ b/src/test/regress/expected/insert_select_repartition_0.out @@ -756,6 +756,7 @@ DEALLOCATE insert_plan; -- TRUNCATE target_table; SET client_min_messages TO DEBUG2; +SET citus.enable_non_colocated_router_query_pushdown TO ON; WITH r AS ( INSERT INTO target_table SELECT * FROM source_table RETURNING * ) @@ -777,6 +778,7 @@ DEBUG: partitioning SELECT query by column index 0 with name 'a' DEBUG: distributed statement: INSERT INTO insert_select_repartition.target_table_4213610 AS citus_table_alias (a, b) SELECT a, b FROM read_intermediate_results('{repartitioned_results_xxxxx_from_4213606_to_0,repartitioned_results_xxxxx_from_4213607_to_0}'::text[], 'binary'::citus_copy_format) intermediate_result(a integer, b integer) DEBUG: distributed statement: INSERT INTO insert_select_repartition.target_table_4213611 AS citus_table_alias (a, b) SELECT a, b FROM read_intermediate_results('{repartitioned_results_xxxxx_from_4213607_to_1}'::text[], 'binary'::citus_copy_format) intermediate_result(a integer, b integer) DEBUG: distributed statement: INSERT INTO insert_select_repartition.target_table_4213612 AS citus_table_alias (a, b) SELECT a, b FROM read_intermediate_results('{repartitioned_results_xxxxx_from_4213609_to_2}'::text[], 'binary'::citus_copy_format) intermediate_result(a integer, b integer) +RESET citus.enable_non_colocated_router_query_pushdown; RESET client_min_messages; SELECT * FROM target_table ORDER BY a, b; a | b diff --git a/src/test/regress/expected/insert_select_single_shard_table.out b/src/test/regress/expected/insert_select_single_shard_table.out index b80d62ad4..2843449f5 100644 --- a/src/test/regress/expected/insert_select_single_shard_table.out +++ b/src/test/regress/expected/insert_select_single_shard_table.out @@ -160,34 +160,33 @@ DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 LEFT JOIN nullkey_c2_t1 USING (a); DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: found no worker with all shard placements +DETAIL: router planner does not support queries that reference non-colocated distributed tables INSERT INTO distributed_table_c1_t1 SELECT * FROM nullkey_c1_t1 UNION SELECT * FROM nullkey_c2_t1; DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: found no worker with all shard placements +DETAIL: router planner does not support queries that reference non-colocated distributed tables -- use a distributed table that is colocated with the target table INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a); DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Router planner cannot handle multi-shard select queries +DETAIL: router planner does not support queries that reference non-colocated distributed tables INSERT INTO distributed_table_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a); DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Router planner cannot handle multi-shard select queries +DETAIL: router planner does not support queries that reference non-colocated distributed tables INSERT INTO distributed_table_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (b); DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Router planner cannot handle multi-shard select queries +DETAIL: router planner does not support queries that reference non-colocated distributed tables INSERT INTO distributed_table_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a) WHERE distributed_table_c1_t2.a = 1; DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables -DEBUG: Creating router plan -DEBUG: query has a single distribution column value: 1 -DEBUG: Collecting INSERT ... SELECT results on coordinator +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: router planner does not support queries that reference non-colocated distributed tables -- use a distributed table that is not colocated with the target table INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 JOIN distributed_table_c2_t1 USING (a); DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Router planner cannot handle multi-shard select queries +DETAIL: router planner does not support queries that reference non-colocated distributed tables -- use a citus local table INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN citus_local_table USING (a); DEBUG: distributed INSERT ... SELECT cannot select from distributed tables and local tables at the same time @@ -257,25 +256,24 @@ DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO reference_table SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 LEFT JOIN nullkey_c2_t1 USING (a); DEBUG: only reference tables may be queried when targeting a reference table with distributed INSERT ... SELECT ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: found no worker with all shard placements +DETAIL: router planner does not support queries that reference non-colocated distributed tables -- use a distributed table INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a); DEBUG: only reference tables may be queried when targeting a reference table with distributed INSERT ... SELECT ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Router planner cannot handle multi-shard select queries +DETAIL: router planner does not support queries that reference non-colocated distributed tables INSERT INTO reference_table SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a); DEBUG: only reference tables may be queried when targeting a reference table with distributed INSERT ... SELECT ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Router planner cannot handle multi-shard select queries +DETAIL: router planner does not support queries that reference non-colocated distributed tables INSERT INTO reference_table SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (b); DEBUG: only reference tables may be queried when targeting a reference table with distributed INSERT ... SELECT ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Router planner cannot handle multi-shard select queries +DETAIL: router planner does not support queries that reference non-colocated distributed tables INSERT INTO reference_table SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a) WHERE distributed_table_c1_t2.a = 1; DEBUG: only reference tables may be queried when targeting a reference table with distributed INSERT ... SELECT -DEBUG: Creating router plan -DEBUG: query has a single distribution column value: 1 -DEBUG: Collecting INSERT ... SELECT results on coordinator +ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables +DETAIL: router planner does not support queries that reference non-colocated distributed tables -- use a citus local table INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN citus_local_table USING (a); DEBUG: distributed INSERT ... SELECT cannot select from distributed tables and local tables at the same time @@ -320,7 +318,7 @@ DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO citus_local_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a); DEBUG: distributed INSERT ... SELECT cannot insert into a local table that is added to metadata ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Router planner cannot handle multi-shard select queries +DETAIL: router planner does not support queries that reference non-colocated distributed tables -- use a citus local table INSERT INTO citus_local_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN citus_local_table USING (a); DEBUG: distributed INSERT ... SELECT cannot select from distributed tables and local tables at the same time @@ -383,7 +381,7 @@ DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO nullkey_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM distributed_table_c1_t2 JOIN nullkey_c1_t1 USING (a); DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Router planner cannot handle multi-shard select queries +DETAIL: router planner does not support queries that reference non-colocated distributed tables -- use a non-colocated single-shard table INSERT INTO nullkey_c2_t1 SELECT q.* FROM (SELECT reference_table.* FROM reference_table LEFT JOIN nullkey_c1_t1 USING (a)) q JOIN nullkey_c1_t2 USING (a); DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables @@ -516,7 +514,7 @@ CROSS JOIN ( ) t2; DEBUG: distributed INSERT ... SELECT cannot insert into a local table that is added to metadata ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: found no worker with all shard placements +DETAIL: router planner does not support queries that reference non-colocated distributed tables INSERT INTO distributed_table_c1_t1 (a, b) SELECT t1.a, t2.b FROM reference_table t1 @@ -541,7 +539,7 @@ JOIN ( WHERE t2.rn > 2; DEBUG: Window functions without PARTITION BY on distribution column is currently unsupported ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Router planner cannot handle multi-shard select queries +DETAIL: router planner does not support queries that reference non-colocated distributed tables INSERT INTO distributed_table_c1_t1 (a, b) SELECT t1.a, t2.b FROM nullkey_c1_t1 t1 diff --git a/src/test/regress/expected/multi_insert_select_window.out b/src/test/regress/expected/multi_insert_select_window.out index 0cf605990..b87299d7e 100644 --- a/src/test/regress/expected/multi_insert_select_window.out +++ b/src/test/regress/expected/multi_insert_select_window.out @@ -816,4 +816,5 @@ FROM ( user_id ) ) AS ftop; +TRUNCATE agg_results_window; DROP VIEW view_with_window_func; diff --git a/src/test/regress/expected/multi_router_planner.out b/src/test/regress/expected/multi_router_planner.out index 1553309d2..a5d5b2c0a 100644 --- a/src/test/regress/expected/multi_router_planner.out +++ b/src/test/regress/expected/multi_router_planner.out @@ -830,7 +830,7 @@ SELECT a.author_id as first_author, b.word_count as second_word_count FROM articles_hash a, single_shard b WHERE a.author_id = 2 and a.author_id = b.author_id LIMIT 3; -DEBUG: found no worker with all shard placements +DEBUG: router planner does not support queries that reference non-colocated distributed tables DEBUG: generating subplan XXX_1 for CTE single_shard: SELECT id, author_id, title, word_count FROM multi_router_planner.articles_single_shard_hash DEBUG: Creating router plan DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT a.author_id AS first_author, b.word_count AS second_word_count FROM multi_router_planner.articles_hash a, (SELECT intermediate_result.id, intermediate_result.author_id, intermediate_result.title, intermediate_result.word_count FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(id bigint, author_id bigint, title character varying(20), word_count integer)) b WHERE ((a.author_id OPERATOR(pg_catalog.=) 2) AND (a.author_id OPERATOR(pg_catalog.=) b.author_id)) LIMIT 3 @@ -1513,21 +1513,41 @@ DEBUG: Creating router plan SELECT a.author_id as first_author, b.word_count as second_word_count FROM articles_hash a, articles_single_shard_hash b WHERE a.author_id = 10 and a.author_id = b.author_id and int4eq(1, 1); -DEBUG: Creating router plan -DEBUG: query has a single distribution column value: 10 - first_author | second_word_count ---------------------------------------------------------------------- - 10 | 19519 - 10 | 19519 - 10 | 19519 - 10 | 19519 - 10 | 19519 -(5 rows) - +DEBUG: router planner does not support queries that reference non-colocated distributed tables +DEBUG: join prunable for task partitionId 0 and 1 +DEBUG: join prunable for task partitionId 0 and 2 +DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 1 and 0 +DEBUG: join prunable for task partitionId 1 and 2 +DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 2 and 0 +DEBUG: join prunable for task partitionId 2 and 1 +DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 3 and 0 +DEBUG: join prunable for task partitionId 3 and 1 +DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: pruning merge fetch taskId 1 +DETAIL: Creating dependency on merge taskId 2 +DEBUG: pruning merge fetch taskId 2 +DETAIL: Creating dependency on merge taskId 2 +DEBUG: pruning merge fetch taskId 4 +DETAIL: Creating dependency on merge taskId 4 +DEBUG: pruning merge fetch taskId 5 +DETAIL: Creating dependency on merge taskId 4 +DEBUG: pruning merge fetch taskId 7 +DETAIL: Creating dependency on merge taskId 6 +DEBUG: pruning merge fetch taskId 8 +DETAIL: Creating dependency on merge taskId 6 +DEBUG: pruning merge fetch taskId 10 +DETAIL: Creating dependency on merge taskId 8 +DEBUG: pruning merge fetch taskId 11 +DETAIL: Creating dependency on merge taskId 8 +ERROR: the query contains a join that requires repartitioning +HINT: Set citus.enable_repartition_joins to on to enable repartitioning SELECT a.author_id as first_author, b.word_count as second_word_count FROM articles_hash a, articles_single_shard_hash b WHERE a.author_id = 10 and a.author_id = b.author_id and int4eq(1, 2); -DEBUG: Creating router plan +DEBUG: router planner does not support queries that reference non-colocated distributed tables first_author | second_word_count --------------------------------------------------------------------- (0 rows) @@ -1565,7 +1585,7 @@ SELECT a.author_id as first_author, b.word_count as second_word_count FROM articles_hash a, articles_single_shard_hash b WHERE a.author_id = 10 and a.author_id = b.author_id and date_ne_timestamp('1954-04-11', '1954-04-11'::timestamp); -DEBUG: Creating router plan +DEBUG: router planner does not support queries that reference non-colocated distributed tables first_author | second_word_count --------------------------------------------------------------------- (0 rows) @@ -2008,7 +2028,7 @@ RESET citus.enable_non_colocated_router_query_pushdown; -- not router plannable SELECT * FROM articles_hash ar join authors_range au on (ar.author_id = au.id) WHERE ar.author_id = 3; -DEBUG: found no worker with all shard placements +DEBUG: router planner does not support queries that reference non-colocated distributed tables DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 diff --git a/src/test/regress/expected/query_single_shard_table.out b/src/test/regress/expected/query_single_shard_table.out index a7efbf7be..6e1fe1529 100644 --- a/src/test/regress/expected/query_single_shard_table.out +++ b/src/test/regress/expected/query_single_shard_table.out @@ -233,7 +233,7 @@ DEBUG: Creating router plan -- with other table types SELECT COUNT(*) FROM distributed_table d1, nullkey_c1_t1; ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Router planner cannot handle multi-shard select queries +DETAIL: router planner does not support queries that reference non-colocated distributed tables SELECT COUNT(*) FROM reference_table d1, nullkey_c1_t1; DEBUG: Creating router plan count @@ -258,7 +258,7 @@ DEBUG: Creating router plan -- with a non-colocated single-shard table SELECT COUNT(*) FROM nullkey_c1_t1 d1, nullkey_c2_t1; ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: found no worker with all shard placements +DETAIL: router planner does not support queries that reference non-colocated distributed tables -- First, show that nullkey_c1_t1 and nullkey_c3_t1 are not colocated. SELECT (SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'query_single_shard_table.nullkey_c1_t1'::regclass) != @@ -359,38 +359,38 @@ DEBUG: Creating router plan -- non-colocated inner joins between single-shard tables SELECT * FROM nullkey_c1_t1 JOIN nullkey_c2_t1 USING(a) ORDER BY 1,2,3; ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: found no worker with all shard placements +DETAIL: router planner does not support queries that reference non-colocated distributed tables SELECT COUNT(*) FROM nullkey_c1_t1 t1 JOIN LATERAL ( SELECT * FROM nullkey_c2_t2 t2 WHERE t2.b > t1.a ) q USING(a); ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: found no worker with all shard placements +DETAIL: router planner does not support queries that reference non-colocated distributed tables -- non-colocated outer joins between single-shard tables SELECT * FROM nullkey_c1_t1 LEFT JOIN nullkey_c2_t2 USING(a) ORDER BY 1,2,3 LIMIT 4; ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: found no worker with all shard placements +DETAIL: router planner does not support queries that reference non-colocated distributed tables SELECT * FROM nullkey_c1_t1 FULL JOIN nullkey_c2_t2 USING(a) ORDER BY 1,2,3 LIMIT 4; ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: found no worker with all shard placements +DETAIL: router planner does not support queries that reference non-colocated distributed tables SELECT * FROM nullkey_c1_t1 t1 LEFT JOIN LATERAL ( SELECT * FROM nullkey_c2_t2 t2 WHERE t2.b > t1.a ) q USING(a) ORDER BY 1,2,3 OFFSET 3 LIMIT 4; ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: found no worker with all shard placements +DETAIL: router planner does not support queries that reference non-colocated distributed tables SELECT COUNT(*) FROM nullkey_c1_t1 t1 LEFT JOIN LATERAL ( SELECT * FROM nullkey_c2_t2 t2 WHERE t2.b > t1.a ) q USING(a); ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: found no worker with all shard placements +DETAIL: router planner does not support queries that reference non-colocated distributed tables SELECT COUNT(*) FROM nullkey_c1_t1 t1 WHERE EXISTS ( SELECT * FROM nullkey_c2_t2 t2 WHERE t2.b > t1.a ); ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: found no worker with all shard placements +DETAIL: router planner does not support queries that reference non-colocated distributed tables SELECT COUNT(*) FROM nullkey_c1_t1 t1 WHERE t1.b IN ( SELECT b+1 FROM nullkey_c2_t2 t2 WHERE t2.b = t1.a @@ -430,19 +430,19 @@ DETAIL: Local tables cannot be used in distributed queries. -- join with a distributed table SELECT * FROM distributed_table d1 JOIN nullkey_c1_t1 USING(a); ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Router planner cannot handle multi-shard select queries +DETAIL: router planner does not support queries that reference non-colocated distributed tables SELECT COUNT(*) FROM nullkey_c1_t1 t1 JOIN LATERAL ( SELECT * FROM distributed_table t2 WHERE t2.b > t1.a ) q USING(a); ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Router planner cannot handle multi-shard select queries +DETAIL: router planner does not support queries that reference non-colocated distributed tables SELECT COUNT(*) FROM distributed_table t1 JOIN LATERAL ( SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a ) q USING(a); ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Router planner cannot handle multi-shard select queries +DETAIL: router planner does not support queries that reference non-colocated distributed tables -- outer joins with different table types SELECT COUNT(*) FROM nullkey_c1_t1 LEFT JOIN reference_table USING(a); DEBUG: Creating router plan @@ -488,7 +488,7 @@ ERROR: queries that reference a distributed table without a shard key can only DETAIL: Router planner does not support append-partitioned tables. SELECT COUNT(*) FROM nullkey_c1_t1 JOIN range_table USING(a); ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Router planner cannot handle multi-shard select queries +DETAIL: router planner does not support queries that reference non-colocated distributed tables SET citus.enable_non_colocated_router_query_pushdown TO ON; SELECT COUNT(*) FROM nullkey_c1_t1 JOIN range_table USING(a) WHERE range_table.a = 20; DEBUG: Creating router plan @@ -620,19 +620,19 @@ LEFT JOIN LATERAL ( SELECT * FROM distributed_table t2 WHERE t2.b > t1.a ) q USING(a); ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Router planner cannot handle multi-shard select queries +DETAIL: router planner does not support queries that reference non-colocated distributed tables SELECT COUNT(*) FROM nullkey_c1_t1 t1 WHERE EXISTS ( SELECT * FROM distributed_table t2 WHERE t2.b > t1.a ); ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Router planner cannot handle multi-shard select queries +DETAIL: router planner does not support queries that reference non-colocated distributed tables SELECT COUNT(*) FROM nullkey_c1_t1 t1 WHERE NOT EXISTS ( SELECT * FROM distributed_table t2 WHERE t2.b > t1.a ); ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Router planner cannot handle multi-shard select queries +DETAIL: router planner does not support queries that reference non-colocated distributed tables SELECT COUNT(*) FROM nullkey_c1_t1 t1 WHERE t1.b IN ( SELECT b+1 FROM distributed_table t2 WHERE t2.b = t1.a @@ -650,13 +650,13 @@ LEFT JOIN LATERAL ( SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a ) q USING(a); ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Router planner cannot handle multi-shard select queries +DETAIL: router planner does not support queries that reference non-colocated distributed tables SELECT COUNT(*) FROM distributed_table t1 WHERE EXISTS ( SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a ); ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Router planner cannot handle multi-shard select queries +DETAIL: router planner does not support queries that reference non-colocated distributed tables SELECT COUNT(*) FROM distributed_table t1 WHERE t1.b IN ( SELECT b+1 FROM nullkey_c1_t1 t2 WHERE t2.b = t1.a @@ -934,7 +934,7 @@ SELECT COUNT(*) FROM level_0; DEBUG: CTE level_0 is going to be inlined via distributed planning DEBUG: CTE level_1 is going to be inlined via distributed planning ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Router planner cannot handle multi-shard select queries +DETAIL: router planner does not support queries that reference non-colocated distributed tables -- grouping set SELECT id, substring(title, 2, 1) AS subtitle, count(*) @@ -1722,13 +1722,13 @@ WITH cte AS ( ) SELECT * FROM nullkey_c2_t1 WHERE a IN (SELECT a FROM cte); ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: found no worker with all shard placements +DETAIL: router planner does not support queries that reference non-colocated distributed tables WITH cte AS ( DELETE FROM nullkey_c1_t1 WHERE a = 1 RETURNING * ) SELECT * FROM distributed_table WHERE a IN (SELECT a FROM cte); ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Router planner cannot handle multi-shard select queries +DETAIL: router planner does not support queries that reference non-colocated distributed tables -- Below two queries fail very late when -- citus.enable_non_colocated_router_query_pushdown is set to on. SET citus.enable_non_colocated_router_query_pushdown TO ON; diff --git a/src/test/regress/expected/set_operations.out b/src/test/regress/expected/set_operations.out index 9c9d22fb9..8580fd51a 100644 --- a/src/test/regress/expected/set_operations.out +++ b/src/test/regress/expected/set_operations.out @@ -1067,7 +1067,7 @@ DEBUG: Creating router plan -- queries on non-colocated tables that would push down if they were not colocated are recursivelu planned SELECT * FROM (SELECT * FROM test UNION SELECT * FROM test_not_colocated) u ORDER BY 1,2; -DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: router planner does not support queries that reference non-colocated distributed tables DEBUG: Router planner cannot handle multi-shard select queries DEBUG: generating subplan XXX_1 for subquery SELECT x, y FROM recursive_union.test DEBUG: Router planner cannot handle multi-shard select queries @@ -1083,7 +1083,7 @@ DEBUG: Creating router plan (2 rows) SELECT * FROM (SELECT * FROM test UNION ALL SELECT * FROM test_not_colocated) u ORDER BY 1,2; -DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: router planner does not support queries that reference non-colocated distributed tables DEBUG: Router planner cannot handle multi-shard select queries DEBUG: generating subplan XXX_1 for subquery SELECT x, y FROM recursive_union.test DEBUG: Router planner cannot handle multi-shard select queries diff --git a/src/test/regress/sql/insert_select_repartition.sql b/src/test/regress/sql/insert_select_repartition.sql index 94a16fed0..526b6eff5 100644 --- a/src/test/regress/sql/insert_select_repartition.sql +++ b/src/test/regress/sql/insert_select_repartition.sql @@ -389,10 +389,12 @@ DEALLOCATE insert_plan; TRUNCATE target_table; SET client_min_messages TO DEBUG2; +SET citus.enable_non_colocated_router_query_pushdown TO ON; WITH r AS ( INSERT INTO target_table SELECT * FROM source_table RETURNING * ) INSERT INTO target_table SELECT source_table.a, max(source_table.b) FROM source_table NATURAL JOIN r GROUP BY source_table.a; +RESET citus.enable_non_colocated_router_query_pushdown; RESET client_min_messages; SELECT * FROM target_table ORDER BY a, b; diff --git a/src/test/regress/sql/multi_insert_select_window.sql b/src/test/regress/sql/multi_insert_select_window.sql index 10c527ad4..ebadc356f 100644 --- a/src/test/regress/sql/multi_insert_select_window.sql +++ b/src/test/regress/sql/multi_insert_select_window.sql @@ -751,4 +751,6 @@ FROM ( ) ) AS ftop; +TRUNCATE agg_results_window; + DROP VIEW view_with_window_func; From e7abde7e8148b011574c9027697dee2777294b98 Mon Sep 17 00:00:00 2001 From: Onur Tirtir Date: Tue, 16 May 2023 10:44:28 +0300 Subject: [PATCH 042/118] Prevent downgrades when there is a single-shard table in the cluster (#6908) Also add a few tests for Citus/PG upgrade/downgrade scenarios. --- .../sql/downgrades/citus--12.0-1--11.3-1.sql | 21 ++++++++++- src/test/regress/after_pg_upgrade_schedule | 2 +- src/test/regress/before_pg_upgrade_schedule | 2 +- src/test/regress/expected/multi_extension.out | 15 ++++++++ .../upgrade_single_shard_table_after.out | 37 +++++++++++++++++++ .../upgrade_single_shard_table_before.out | 8 ++++ src/test/regress/sql/multi_extension.sql | 10 +++++ .../sql/upgrade_single_shard_table_after.sql | 18 +++++++++ .../sql/upgrade_single_shard_table_before.sql | 3 ++ 9 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 src/test/regress/expected/upgrade_single_shard_table_after.out create mode 100644 src/test/regress/expected/upgrade_single_shard_table_before.out create mode 100644 src/test/regress/sql/upgrade_single_shard_table_after.sql create mode 100644 src/test/regress/sql/upgrade_single_shard_table_before.sql diff --git a/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql b/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql index ded1fa571..0ca58bafd 100644 --- a/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql +++ b/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql @@ -1,2 +1,21 @@ -- citus--12.0-1--11.3-1 --- this is an empty downgrade path since citus--11.3-1--12.0-1.sql is empty for now + +-- Throw an error if user has any distributed tables without a shard key. +DO $$ +BEGIN + IF EXISTS ( + SELECT 1 FROM pg_dist_partition + WHERE repmodel != 't' AND partmethod = 'n' AND colocationid != 0) + THEN + RAISE EXCEPTION 'cannot downgrade Citus because there are ' + 'distributed tables without a shard key.' + USING HINT = 'You can find the distributed tables without a shard ' + 'key in the cluster by using the following query: ' + '"SELECT * FROM citus_tables WHERE distribution_column ' + '= '''' AND colocation_id > 0".', + DETAIL = 'To downgrade Citus to an older version, you should ' + 'first convert those tables to Postgres tables by ' + 'executing SELECT undistribute_table("%s").'; + END IF; +END; +$$ LANGUAGE plpgsql; diff --git a/src/test/regress/after_pg_upgrade_schedule b/src/test/regress/after_pg_upgrade_schedule index d02289510..3b49a76d0 100644 --- a/src/test/regress/after_pg_upgrade_schedule +++ b/src/test/regress/after_pg_upgrade_schedule @@ -1,4 +1,4 @@ -test: upgrade_basic_after upgrade_ref2ref_after upgrade_type_after upgrade_distributed_function_after upgrade_rebalance_strategy_after upgrade_list_citus_objects upgrade_autoconverted_after upgrade_citus_stat_activity upgrade_citus_locks +test: upgrade_basic_after upgrade_ref2ref_after upgrade_type_after upgrade_distributed_function_after upgrade_rebalance_strategy_after upgrade_list_citus_objects upgrade_autoconverted_after upgrade_citus_stat_activity upgrade_citus_locks upgrade_single_shard_table_after # This test cannot be run with run_test.py currently due to its dependence on # the specific PG versions that we use to run upgrade tests. For now we leave diff --git a/src/test/regress/before_pg_upgrade_schedule b/src/test/regress/before_pg_upgrade_schedule index 671d6fa6f..bb89de18a 100644 --- a/src/test/regress/before_pg_upgrade_schedule +++ b/src/test/regress/before_pg_upgrade_schedule @@ -5,7 +5,7 @@ test: upgrade_basic_before test: upgrade_ref2ref_before test: upgrade_type_before test: upgrade_distributed_function_before upgrade_rebalance_strategy_before -test: upgrade_autoconverted_before +test: upgrade_autoconverted_before upgrade_single_shard_table_before test: upgrade_citus_stat_activity test: upgrade_citus_locks test: upgrade_distributed_triggers_before diff --git a/src/test/regress/expected/multi_extension.out b/src/test/regress/expected/multi_extension.out index dad925072..c1c7a0401 100644 --- a/src/test/regress/expected/multi_extension.out +++ b/src/test/regress/expected/multi_extension.out @@ -1332,6 +1332,21 @@ SELECT * FROM multi_extension.print_extension_changes(); -- Test downgrade to 11.3-1 from 12.0-1 ALTER EXTENSION citus UPDATE TO '12.0-1'; +CREATE TABLE null_shard_key (x int, y int); +SELECT create_distributed_table('null_shard_key', null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- Show that we cannot downgrade to 11.3-1 becuase the cluster has a +-- distributed table with single-shard. +ALTER EXTENSION citus UPDATE TO '11.3-1'; +ERROR: cannot downgrade Citus because there are distributed tables without a shard key. +DETAIL: To downgrade Citus to an older version, you should first convert those tables to Postgres tables by executing SELECT undistribute_table("%s"). +HINT: You can find the distributed tables without a shard key in the cluster by using the following query: "SELECT * FROM citus_tables WHERE distribution_column = '' AND colocation_id > 0". +CONTEXT: PL/pgSQL function inline_code_block line XX at RAISE +DROP TABLE null_shard_key; ALTER EXTENSION citus UPDATE TO '11.3-1'; -- Should be empty result since upgrade+downgrade should be a no-op SELECT * FROM multi_extension.print_extension_changes(); diff --git a/src/test/regress/expected/upgrade_single_shard_table_after.out b/src/test/regress/expected/upgrade_single_shard_table_after.out new file mode 100644 index 000000000..5c6b81c66 --- /dev/null +++ b/src/test/regress/expected/upgrade_single_shard_table_after.out @@ -0,0 +1,37 @@ +-- check that we properly retained the single-shard table +SELECT 1 FROM pg_dist_partition +WHERE logicalrelid = 'citus_schema.null_shard_key'::regclass AND + partmethod = 'n' AND repmodel = 's' AND colocationid != 0; + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +BEGIN; + INSERT INTO citus_schema.null_shard_key (name) VALUES ('c'); + SELECT * FROM citus_schema.null_shard_key ORDER BY id; + id | name +--------------------------------------------------------------------- + 1 | a + 2 | b + | c +(3 rows) + +ROLLBACK; +-- Check that we can create a distributed table with a single-shard +-- after upgrade. +CREATE TABLE citus_schema.null_shard_key_after_upgrade (id bigserial, name text); +SELECT create_distributed_table('citus_schema.null_shard_key_after_upgrade', null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO citus_schema.null_shard_key_after_upgrade (name) VALUES ('c'); +SELECT * FROM citus_schema.null_shard_key_after_upgrade ORDER BY id; + id | name +--------------------------------------------------------------------- + 1 | c +(1 row) + +DROP TABLE citus_schema.null_shard_key_after_upgrade; diff --git a/src/test/regress/expected/upgrade_single_shard_table_before.out b/src/test/regress/expected/upgrade_single_shard_table_before.out new file mode 100644 index 000000000..d5abe2ae7 --- /dev/null +++ b/src/test/regress/expected/upgrade_single_shard_table_before.out @@ -0,0 +1,8 @@ +CREATE TABLE null_shard_key (id int, name text); +SELECT create_distributed_table('null_shard_key', null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO null_shard_key (id, name) VALUES (1, 'a'), (2, 'b'); diff --git a/src/test/regress/sql/multi_extension.sql b/src/test/regress/sql/multi_extension.sql index 03cf4c7fb..40b1e963b 100644 --- a/src/test/regress/sql/multi_extension.sql +++ b/src/test/regress/sql/multi_extension.sql @@ -593,6 +593,16 @@ SELECT * FROM multi_extension.print_extension_changes(); -- Test downgrade to 11.3-1 from 12.0-1 ALTER EXTENSION citus UPDATE TO '12.0-1'; + +CREATE TABLE null_shard_key (x int, y int); +SELECT create_distributed_table('null_shard_key', null); + +-- Show that we cannot downgrade to 11.3-1 becuase the cluster has a +-- distributed table with single-shard. +ALTER EXTENSION citus UPDATE TO '11.3-1'; + +DROP TABLE null_shard_key; + ALTER EXTENSION citus UPDATE TO '11.3-1'; -- Should be empty result since upgrade+downgrade should be a no-op SELECT * FROM multi_extension.print_extension_changes(); diff --git a/src/test/regress/sql/upgrade_single_shard_table_after.sql b/src/test/regress/sql/upgrade_single_shard_table_after.sql new file mode 100644 index 000000000..9b17b1101 --- /dev/null +++ b/src/test/regress/sql/upgrade_single_shard_table_after.sql @@ -0,0 +1,18 @@ +-- check that we properly retained the single-shard table +SELECT 1 FROM pg_dist_partition +WHERE logicalrelid = 'citus_schema.null_shard_key'::regclass AND + partmethod = 'n' AND repmodel = 's' AND colocationid != 0; + +BEGIN; + INSERT INTO citus_schema.null_shard_key (name) VALUES ('c'); + SELECT * FROM citus_schema.null_shard_key ORDER BY id; +ROLLBACK; + +-- Check that we can create a distributed table with a single-shard +-- after upgrade. +CREATE TABLE citus_schema.null_shard_key_after_upgrade (id bigserial, name text); +SELECT create_distributed_table('citus_schema.null_shard_key_after_upgrade', null); +INSERT INTO citus_schema.null_shard_key_after_upgrade (name) VALUES ('c'); +SELECT * FROM citus_schema.null_shard_key_after_upgrade ORDER BY id; + +DROP TABLE citus_schema.null_shard_key_after_upgrade; diff --git a/src/test/regress/sql/upgrade_single_shard_table_before.sql b/src/test/regress/sql/upgrade_single_shard_table_before.sql new file mode 100644 index 000000000..08eefe78c --- /dev/null +++ b/src/test/regress/sql/upgrade_single_shard_table_before.sql @@ -0,0 +1,3 @@ +CREATE TABLE null_shard_key (id int, name text); +SELECT create_distributed_table('null_shard_key', null); +INSERT INTO null_shard_key (id, name) VALUES (1, 'a'), (2, 'b'); From 56d217b1084cf3e55ba78092bbfaa4d43b63aed7 Mon Sep 17 00:00:00 2001 From: Onur Tirtir Date: Tue, 16 May 2023 11:45:42 +0300 Subject: [PATCH 043/118] Mark objects as distributed even when pg_dist_node is empty (#6900) We mark objects as distributed objects in Citus metadata only if we need to propagate given the command that creates it to worker nodes. For this reason, we were not doing this for the objects that are created while pg_dist_node is empty. One implication of doing so is that we defer the schema propagation to the time when user creates the first distributed table in the schema. However, this doesn't help for schema-based sharding (#6866) because we want to sync pg_dist_tenant_schema to the worker nodes even for empty schemas too. * Support test dependencies for isolation tests without a schedule * Comment out a test due to a known issue (#6901) * Also, reduce the verbosity for some log messages and make some tests compatible with run_test.py. --- .../distributed/commands/utility_hook.c | 56 ++--- .../distributed/executor/adaptive_executor.c | 6 + .../planner/function_call_delegation.c | 4 +- src/test/regress/citus_tests/run_test.py | 7 +- .../expected/fast_path_router_modify.out | 7 - .../expected/forcedelegation_functions.out | 17 -- .../insert_select_single_shard_table.out | 1 - ...lation_ensure_dependency_activate_node.out | 90 +++++--- .../expected/isolation_extension_commands.out | 217 +++++++++++++----- .../regress/expected/multi_drop_extension.out | 9 +- .../expected/multi_function_in_join.out | 5 - .../multi_mx_function_call_delegation.out | 9 - .../multi_mx_function_call_delegation_0.out | 9 - .../regress/expected/multi_router_planner.out | 4 - .../multi_router_planner_fast_path.out | 24 -- .../non_colocated_leaf_subquery_joins.out | 7 - .../expected/non_colocated_subquery_joins.out | 37 --- .../expected/single_node_enterprise.out | 2 + .../regress/expected/subquery_and_cte.out | 1 - .../spec/isolation_extension_commands.spec | 9 +- src/test/regress/sql/multi_drop_extension.sql | 9 +- 21 files changed, 284 insertions(+), 246 deletions(-) diff --git a/src/backend/distributed/commands/utility_hook.c b/src/backend/distributed/commands/utility_hook.c index cdef7ab97..1eb6f7321 100644 --- a/src/backend/distributed/commands/utility_hook.c +++ b/src/backend/distributed/commands/utility_hook.c @@ -1591,37 +1591,37 @@ DDLTaskList(Oid relationId, const char *commandString) List * NodeDDLTaskList(TargetWorkerSet targets, List *commands) { - /* don't allow concurrent node list changes that require an exclusive lock */ - List *workerNodes = TargetWorkerSetNodeList(targets, RowShareLock); - - if (list_length(workerNodes) <= 0) - { - /* - * if there are no nodes we don't have to plan any ddl tasks. Planning them would - * cause the executor to stop responding. - */ - return NIL; - } - - Task *task = CitusMakeNode(Task); - task->taskType = DDL_TASK; - SetTaskQueryStringList(task, commands); - - WorkerNode *workerNode = NULL; - foreach_ptr(workerNode, workerNodes) - { - ShardPlacement *targetPlacement = CitusMakeNode(ShardPlacement); - targetPlacement->nodeName = workerNode->workerName; - targetPlacement->nodePort = workerNode->workerPort; - targetPlacement->groupId = workerNode->groupId; - - task->taskPlacementList = lappend(task->taskPlacementList, targetPlacement); - } - DDLJob *ddlJob = palloc0(sizeof(DDLJob)); ddlJob->targetObjectAddress = InvalidObjectAddress; ddlJob->metadataSyncCommand = NULL; - ddlJob->taskList = list_make1(task); + + /* don't allow concurrent node list changes that require an exclusive lock */ + List *workerNodes = TargetWorkerSetNodeList(targets, RowShareLock); + + /* + * if there are no nodes we don't have to plan any ddl tasks. Planning them would + * cause the executor to stop responding. + */ + if (list_length(workerNodes) > 0) + { + Task *task = CitusMakeNode(Task); + task->taskType = DDL_TASK; + SetTaskQueryStringList(task, commands); + + WorkerNode *workerNode = NULL; + foreach_ptr(workerNode, workerNodes) + { + ShardPlacement *targetPlacement = CitusMakeNode(ShardPlacement); + targetPlacement->nodeName = workerNode->workerName; + targetPlacement->nodePort = workerNode->workerPort; + targetPlacement->groupId = workerNode->groupId; + + task->taskPlacementList = lappend(task->taskPlacementList, targetPlacement); + } + + ddlJob->taskList = list_make1(task); + } + return list_make1(ddlJob); } diff --git a/src/backend/distributed/executor/adaptive_executor.c b/src/backend/distributed/executor/adaptive_executor.c index 4bb2d5e57..22963c458 100644 --- a/src/backend/distributed/executor/adaptive_executor.c +++ b/src/backend/distributed/executor/adaptive_executor.c @@ -1040,6 +1040,12 @@ ExecuteTaskListIntoTupleDest(RowModifyLevel modLevel, List *taskList, uint64 ExecuteTaskListExtended(ExecutionParams *executionParams) { + /* if there are no tasks to execute, we can return early */ + if (list_length(executionParams->taskList) == 0) + { + return 0; + } + ParamListInfo paramListInfo = NULL; uint64 locallyProcessedRows = 0; diff --git a/src/backend/distributed/planner/function_call_delegation.c b/src/backend/distributed/planner/function_call_delegation.c index fe9d2ccef..0cdde52a6 100644 --- a/src/backend/distributed/planner/function_call_delegation.c +++ b/src/backend/distributed/planner/function_call_delegation.c @@ -337,7 +337,7 @@ TryToDelegateFunctionCall(DistributedPlanningContext *planContext) if (!procedure->forceDelegation) { /* cannot delegate function calls in a multi-statement transaction */ - ereport(DEBUG1, (errmsg("not pushing down function calls in " + ereport(DEBUG4, (errmsg("not pushing down function calls in " "a multi-statement transaction"))); return NULL; } @@ -388,7 +388,7 @@ TryToDelegateFunctionCall(DistributedPlanningContext *planContext) Oid colocatedRelationId = ColocatedTableId(procedure->colocationId); if (colocatedRelationId == InvalidOid) { - ereport(DEBUG1, (errmsg("function does not have co-located tables"))); + ereport(DEBUG4, (errmsg("function does not have co-located tables"))); return NULL; } diff --git a/src/test/regress/citus_tests/run_test.py b/src/test/regress/citus_tests/run_test.py index c9f86a3c4..6ccdc40c9 100755 --- a/src/test/regress/citus_tests/run_test.py +++ b/src/test/regress/citus_tests/run_test.py @@ -129,6 +129,9 @@ DEPS = { "multi_mx_schema_support": TestDeps(None, ["multi_mx_copy_data"]), "multi_simple_queries": TestDeps("base_schedule"), "create_single_shard_table": TestDeps("minimal_schedule"), + "isolation_extension_commands": TestDeps( + None, ["isolation_setup", "isolation_add_remove_node"] + ), } @@ -195,9 +198,9 @@ def run_schedule_with_multiregress(test_name, schedule, dependencies, args): worker_count = needed_worker_count(test_name, dependencies) # find suitable make recipe - if dependencies.schedule == "base_isolation_schedule": + if dependencies.schedule == "base_isolation_schedule" or "isolation" in test_name: make_recipe = "check-isolation-custom-schedule" - elif dependencies.schedule == "failure_base_schedule": + elif dependencies.schedule == "failure_base_schedule" or "failure" in test_name: make_recipe = "check-failure-custom-schedule" else: make_recipe = "check-custom-schedule" diff --git a/src/test/regress/expected/fast_path_router_modify.out b/src/test/regress/expected/fast_path_router_modify.out index bf7e07362..d27b0fb65 100644 --- a/src/test/regress/expected/fast_path_router_modify.out +++ b/src/test/regress/expected/fast_path_router_modify.out @@ -231,7 +231,6 @@ $$ LANGUAGE plpgsql; DEBUG: switching to sequential query execution mode DETAIL: A command for a distributed function is run. To make sure subsequent commands see the function correctly we need to make sure to use only one connection for all future commands SELECT modify_fast_path_plpsql(1,1); -DEBUG: function does not have co-located tables DEBUG: Deferred pruning for a fast-path router query CONTEXT: SQL statement "DELETE FROM modify_fast_path WHERE key = $1 AND value_1 = $2" PL/pgSQL function modify_fast_path_plpsql(integer,integer) line XX at SQL statement @@ -244,7 +243,6 @@ PL/pgSQL function modify_fast_path_plpsql(integer,integer) line XX at SQL statem (1 row) SELECT modify_fast_path_plpsql(2,2); -DEBUG: function does not have co-located tables DEBUG: Deferred pruning for a fast-path router query CONTEXT: SQL statement "DELETE FROM modify_fast_path WHERE key = $1 AND value_1 = $2" PL/pgSQL function modify_fast_path_plpsql(integer,integer) line XX at SQL statement @@ -257,7 +255,6 @@ PL/pgSQL function modify_fast_path_plpsql(integer,integer) line XX at SQL statem (1 row) SELECT modify_fast_path_plpsql(3,3); -DEBUG: function does not have co-located tables DEBUG: Deferred pruning for a fast-path router query CONTEXT: SQL statement "DELETE FROM modify_fast_path WHERE key = $1 AND value_1 = $2" PL/pgSQL function modify_fast_path_plpsql(integer,integer) line XX at SQL statement @@ -270,7 +267,6 @@ PL/pgSQL function modify_fast_path_plpsql(integer,integer) line XX at SQL statem (1 row) SELECT modify_fast_path_plpsql(4,4); -DEBUG: function does not have co-located tables DEBUG: Deferred pruning for a fast-path router query CONTEXT: SQL statement "DELETE FROM modify_fast_path WHERE key = $1 AND value_1 = $2" PL/pgSQL function modify_fast_path_plpsql(integer,integer) line XX at SQL statement @@ -283,7 +279,6 @@ PL/pgSQL function modify_fast_path_plpsql(integer,integer) line XX at SQL statem (1 row) SELECT modify_fast_path_plpsql(5,5); -DEBUG: function does not have co-located tables DEBUG: Deferred pruning for a fast-path router query CONTEXT: SQL statement "DELETE FROM modify_fast_path WHERE key = $1 AND value_1 = $2" PL/pgSQL function modify_fast_path_plpsql(integer,integer) line XX at SQL statement @@ -296,7 +291,6 @@ PL/pgSQL function modify_fast_path_plpsql(integer,integer) line XX at SQL statem (1 row) SELECT modify_fast_path_plpsql(6,6); -DEBUG: function does not have co-located tables DEBUG: Deferred pruning for a fast-path router query CONTEXT: SQL statement "DELETE FROM modify_fast_path WHERE key = $1 AND value_1 = $2" PL/pgSQL function modify_fast_path_plpsql(integer,integer) line XX at SQL statement @@ -309,7 +303,6 @@ PL/pgSQL function modify_fast_path_plpsql(integer,integer) line XX at SQL statem (1 row) SELECT modify_fast_path_plpsql(6,6); -DEBUG: function does not have co-located tables modify_fast_path_plpsql --------------------------------------------------------------------- diff --git a/src/test/regress/expected/forcedelegation_functions.out b/src/test/regress/expected/forcedelegation_functions.out index 103742c5e..2282bf626 100644 --- a/src/test/regress/expected/forcedelegation_functions.out +++ b/src/test/regress/expected/forcedelegation_functions.out @@ -322,7 +322,6 @@ SELECT public.wait_until_metadata_sync(30000); BEGIN; SELECT func_calls_forcepush_func(); -DEBUG: not pushing down function calls in a multi-statement transaction DEBUG: pushing down function call in a multi-statement transaction CONTEXT: SQL statement "SELECT inner_force_delegation_function FROM inner_force_delegation_function(100)" PL/pgSQL function func_calls_forcepush_func() line XX at SQL statement @@ -340,7 +339,6 @@ PL/pgSQL function func_calls_forcepush_func() line XX at SQL statement COMMIT; SELECT func_calls_forcepush_func(); -DEBUG: function does not have co-located tables NOTICE: inner_force_delegation_function():101 DETAIL: from localhost:xxxxx CONTEXT: SQL statement "SELECT inner_force_delegation_function FROM inner_force_delegation_function(100)" @@ -380,7 +378,6 @@ $$ LANGUAGE plpgsql; DEBUG: switching to sequential query execution mode DETAIL: A command for a distributed function is run. To make sure subsequent commands see the function correctly we need to make sure to use only one connection for all future commands SELECT func_calls_forcepush_func_infrom(); -DEBUG: function does not have co-located tables DEBUG: pushing down function call in a multi-statement transaction CONTEXT: SQL statement "SELECT inner_force_delegation_function FROM inner_force_delegation_function(add_val + 100)" PL/pgSQL function func_calls_forcepush_func_infrom() line XX at SQL statement @@ -398,7 +395,6 @@ PL/pgSQL function func_calls_forcepush_func_infrom() line XX at SQL statement BEGIN; SELECT func_calls_forcepush_func_infrom(); -DEBUG: not pushing down function calls in a multi-statement transaction DEBUG: pushing down function call in a multi-statement transaction CONTEXT: SQL statement "SELECT inner_force_delegation_function FROM inner_force_delegation_function(add_val + 100)" PL/pgSQL function func_calls_forcepush_func_infrom() line XX at SQL statement @@ -435,7 +431,6 @@ $$ LANGUAGE plpgsql; DEBUG: switching to sequential query execution mode DETAIL: A command for a distributed function is run. To make sure subsequent commands see the function correctly we need to make sure to use only one connection for all future commands SELECT func_calls_forcepush_func_intarget(); -DEBUG: function does not have co-located tables DEBUG: pushing down function call in a multi-statement transaction CONTEXT: SQL statement "SELECT inner_force_delegation_function(100 + 100) OFFSET 0" PL/pgSQL function func_calls_forcepush_func_intarget() line XX at SQL statement @@ -453,7 +448,6 @@ PL/pgSQL function func_calls_forcepush_func_intarget() line XX at SQL statement BEGIN; SELECT func_calls_forcepush_func_intarget(); -DEBUG: not pushing down function calls in a multi-statement transaction NOTICE: inner_force_delegation_function():201 DETAIL: from localhost:xxxxx CONTEXT: SQL statement "SELECT inner_force_delegation_function(100 + 100) OFFSET 0" @@ -648,7 +642,6 @@ DETAIL: A command for a distributed function is run. To make sure subsequent co (1 row) SELECT outer_emp(); -DEBUG: function does not have co-located tables DEBUG: Skipping pushdown of function from a PL/PgSQL simple expression CONTEXT: SQL statement "SELECT inner_emp('hello')" PL/pgSQL function outer_emp() line XX at PERFORM @@ -1282,7 +1275,6 @@ DETAIL: A command for a distributed function is run. To make sure subsequent co -- First 5 get delegated and succeeds BEGIN; SELECT outer_test_prepare(1,1); -DEBUG: not pushing down function calls in a multi-statement transaction DEBUG: pushing down function call in a multi-statement transaction CONTEXT: SQL statement "SELECT FROM test_prepare(x, y)" PL/pgSQL function outer_test_prepare(integer,integer) line XX at PERFORM @@ -1301,7 +1293,6 @@ PL/pgSQL function outer_test_prepare(integer,integer) line XX at PERFORM (1 row) SELECT outer_test_prepare(1,1); -DEBUG: not pushing down function calls in a multi-statement transaction DEBUG: pushing down function call in a multi-statement transaction CONTEXT: SQL statement "SELECT FROM test_prepare(x, y)" PL/pgSQL function outer_test_prepare(integer,integer) line XX at PERFORM @@ -1320,7 +1311,6 @@ PL/pgSQL function outer_test_prepare(integer,integer) line XX at PERFORM (1 row) SELECT outer_test_prepare(1,1); -DEBUG: not pushing down function calls in a multi-statement transaction DEBUG: pushing down function call in a multi-statement transaction CONTEXT: SQL statement "SELECT FROM test_prepare(x, y)" PL/pgSQL function outer_test_prepare(integer,integer) line XX at PERFORM @@ -1339,7 +1329,6 @@ PL/pgSQL function outer_test_prepare(integer,integer) line XX at PERFORM (1 row) SELECT outer_test_prepare(1,1); -DEBUG: not pushing down function calls in a multi-statement transaction DEBUG: pushing down function call in a multi-statement transaction CONTEXT: SQL statement "SELECT FROM test_prepare(x, y)" PL/pgSQL function outer_test_prepare(integer,integer) line XX at PERFORM @@ -1358,7 +1347,6 @@ PL/pgSQL function outer_test_prepare(integer,integer) line XX at PERFORM (1 row) SELECT outer_test_prepare(1,1); -DEBUG: not pushing down function calls in a multi-statement transaction DEBUG: pushing down function call in a multi-statement transaction CONTEXT: SQL statement "SELECT FROM test_prepare(x, y)" PL/pgSQL function outer_test_prepare(integer,integer) line XX at PERFORM @@ -1385,7 +1373,6 @@ SELECT COUNT(*) FROM table_test_prepare; -- 6th execution will be generic plan and should get delegated SELECT outer_test_prepare(1,1); -DEBUG: not pushing down function calls in a multi-statement transaction DEBUG: pushing down function call in a multi-statement transaction CONTEXT: SQL statement "SELECT FROM test_prepare(x, y)" PL/pgSQL function outer_test_prepare(integer,integer) line XX at PERFORM @@ -1404,7 +1391,6 @@ PL/pgSQL function outer_test_prepare(integer,integer) line XX at PERFORM (1 row) SELECT outer_test_prepare(1,1); -DEBUG: not pushing down function calls in a multi-statement transaction DEBUG: pushing down function call in a multi-statement transaction CONTEXT: SQL statement "SELECT FROM test_prepare(x, y)" PL/pgSQL function outer_test_prepare(integer,integer) line XX at PERFORM @@ -1425,7 +1411,6 @@ PL/pgSQL function outer_test_prepare(integer,integer) line XX at PERFORM END; -- Fails as expected SELECT outer_test_prepare(1,2); -DEBUG: function does not have co-located tables DEBUG: pushing down function call in a multi-statement transaction CONTEXT: SQL statement "SELECT FROM test_prepare(x, y)" PL/pgSQL function outer_test_prepare(integer,integer) line XX at PERFORM @@ -1525,7 +1510,6 @@ DETAIL: A command for a distributed function is run. To make sure subsequent co (1 row) SELECT outer_local_fn(); -DEBUG: function does not have co-located tables DEBUG: pushing down function call in a multi-statement transaction CONTEXT: SQL statement "SELECT 1 FROM inner_fn(1)" PL/pgSQL function outer_local_fn() line XX at PERFORM @@ -1555,7 +1539,6 @@ SELECT * FROM testnested_table ORDER BY 1; BEGIN; SELECT outer_local_fn(); -DEBUG: not pushing down function calls in a multi-statement transaction outer_local_fn --------------------------------------------------------------------- diff --git a/src/test/regress/expected/insert_select_single_shard_table.out b/src/test/regress/expected/insert_select_single_shard_table.out index 2843449f5..68391abb5 100644 --- a/src/test/regress/expected/insert_select_single_shard_table.out +++ b/src/test/regress/expected/insert_select_single_shard_table.out @@ -719,7 +719,6 @@ INSERT INTO upsert_test_1 (unique_col, other_col) SELECT unique_col, other_col F DO UPDATE SET other_col = 5 WHERE upsert_test_1.other_col = random()::int; ERROR: functions used in the WHERE clause of the ON CONFLICT clause of INSERTs on distributed tables must be marked IMMUTABLE SELECT reload_tables(); -DEBUG: function does not have co-located tables reload_tables --------------------------------------------------------------------- diff --git a/src/test/regress/expected/isolation_ensure_dependency_activate_node.out b/src/test/regress/expected/isolation_ensure_dependency_activate_node.out index 980cf3a82..77ff2db9c 100644 --- a/src/test/regress/expected/isolation_ensure_dependency_activate_node.out +++ b/src/test/regress/expected/isolation_ensure_dependency_activate_node.out @@ -29,9 +29,10 @@ step s1-print-distributed-objects: pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{public},{}) -(3 rows) +(4 rows) count --------------------------------------------------------------------- @@ -116,10 +117,11 @@ step s2-print-distributed-objects: pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{public},{}) (table,"{public,t1}",{}) -(4 rows) +(5 rows) count --------------------------------------------------------------------- @@ -200,9 +202,10 @@ step s1-print-distributed-objects: pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{public},{}) -(3 rows) +(4 rows) count --------------------------------------------------------------------- @@ -293,10 +296,11 @@ step s2-print-distributed-objects: pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{public},{}) (table,"{public,t1}",{}) -(4 rows) +(5 rows) count --------------------------------------------------------------------- @@ -377,9 +381,10 @@ step s1-print-distributed-objects: pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{public},{}) -(3 rows) +(4 rows) count --------------------------------------------------------------------- @@ -470,10 +475,11 @@ step s2-print-distributed-objects: pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{public},{}) (table,"{public,t1}",{}) -(4 rows) +(5 rows) count --------------------------------------------------------------------- @@ -554,9 +560,10 @@ step s1-print-distributed-objects: pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{public},{}) -(3 rows) +(4 rows) count --------------------------------------------------------------------- @@ -642,11 +649,12 @@ step s2-print-distributed-objects: pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{myschema},{}) (schema,{public},{}) (table,"{myschema,t1}",{}) -(5 rows) +(6 rows) count --------------------------------------------------------------------- @@ -727,9 +735,10 @@ step s1-print-distributed-objects: pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{public},{}) -(3 rows) +(4 rows) count --------------------------------------------------------------------- @@ -821,11 +830,12 @@ step s2-print-distributed-objects: pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{myschema},{}) (schema,{public},{}) (table,"{myschema,t1}",{}) -(5 rows) +(6 rows) count --------------------------------------------------------------------- @@ -906,9 +916,10 @@ step s1-print-distributed-objects: pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{public},{}) -(3 rows) +(4 rows) count --------------------------------------------------------------------- @@ -1000,11 +1011,12 @@ step s2-print-distributed-objects: pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{myschema},{}) (schema,{public},{}) (table,"{myschema,t1}",{}) -(5 rows) +(6 rows) count --------------------------------------------------------------------- @@ -1085,9 +1097,10 @@ step s1-print-distributed-objects: pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{public},{}) -(3 rows) +(4 rows) count --------------------------------------------------------------------- @@ -1179,11 +1192,12 @@ step s2-print-distributed-objects: pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{myschema},{}) (schema,{public},{}) (table,"{myschema,t1}",{}) -(5 rows) +(6 rows) count --------------------------------------------------------------------- @@ -1264,9 +1278,10 @@ step s1-print-distributed-objects: pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{public},{}) -(3 rows) +(4 rows) count --------------------------------------------------------------------- @@ -1381,6 +1396,7 @@ step s2-print-distributed-objects: pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{col_schema},{}) (schema,{myschema},{}) @@ -1388,7 +1404,7 @@ pg_identify_object_as_address (table,"{col_schema,col_tbl}",{}) (table,"{myschema,t1}",{}) (table,"{myschema,t2}",{}) -(8 rows) +(9 rows) count --------------------------------------------------------------------- @@ -1469,9 +1485,10 @@ step s1-print-distributed-objects: pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{public},{}) -(3 rows) +(4 rows) count --------------------------------------------------------------------- @@ -1585,13 +1602,14 @@ step s2-print-distributed-objects: pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{myschema},{}) (schema,{myschema2},{}) (schema,{public},{}) (table,"{myschema,t1}",{}) (table,"{myschema2,t2}",{}) -(7 rows) +(8 rows) count --------------------------------------------------------------------- @@ -1672,9 +1690,10 @@ step s1-print-distributed-objects: pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{public},{}) -(3 rows) +(4 rows) count --------------------------------------------------------------------- @@ -1751,10 +1770,11 @@ step s2-print-distributed-objects: pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{public},{}) (type,{public.tt1},{}) -(4 rows) +(5 rows) count --------------------------------------------------------------------- @@ -1835,9 +1855,10 @@ step s1-print-distributed-objects: pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{public},{}) -(3 rows) +(4 rows) count --------------------------------------------------------------------- @@ -1913,10 +1934,11 @@ step s2-print-distributed-objects: pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{public},{}) (type,{public.tt1},{}) -(4 rows) +(5 rows) count --------------------------------------------------------------------- @@ -1997,9 +2019,10 @@ step s1-print-distributed-objects: pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{public},{}) -(3 rows) +(4 rows) count --------------------------------------------------------------------- @@ -2094,12 +2117,13 @@ step s2-print-distributed-objects: pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{myschema},{}) (schema,{public},{}) (table,"{myschema,t1}",{}) (type,{myschema.tt1},{}) -(6 rows) +(7 rows) count --------------------------------------------------------------------- @@ -2180,9 +2204,10 @@ step s1-print-distributed-objects: pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{public},{}) -(3 rows) +(4 rows) count --------------------------------------------------------------------- @@ -2290,11 +2315,12 @@ pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) (function,"{public,add}","{integer,integer}") +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{col_schema},{}) (schema,{public},{}) (table,"{col_schema,col_tbl}",{}) -(6 rows) +(7 rows) count --------------------------------------------------------------------- @@ -2375,9 +2401,10 @@ step s1-print-distributed-objects: pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{public},{}) -(3 rows) +(4 rows) count --------------------------------------------------------------------- @@ -2492,11 +2519,12 @@ pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) (function,"{public,add}","{integer,integer}") +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{col_schema},{}) (schema,{public},{}) (table,"{col_schema,col_tbl}",{}) -(6 rows) +(7 rows) count --------------------------------------------------------------------- @@ -2577,9 +2605,10 @@ step s1-print-distributed-objects: pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{public},{}) -(3 rows) +(4 rows) count --------------------------------------------------------------------- @@ -2695,12 +2724,13 @@ pg_identify_object_as_address --------------------------------------------------------------------- (database,{regression},{}) (function,"{myschema,add}","{integer,integer}") +(function,"{public,wait_until_metadata_sync}",{integer}) (role,{postgres},{}) (schema,{col_schema},{}) (schema,{myschema},{}) (schema,{public},{}) (table,"{col_schema,col_tbl}",{}) -(7 rows) +(8 rows) count --------------------------------------------------------------------- diff --git a/src/test/regress/expected/isolation_extension_commands.out b/src/test/regress/expected/isolation_extension_commands.out index 028ec21f0..f1d6b64bb 100644 --- a/src/test/regress/expected/isolation_extension_commands.out +++ b/src/test/regress/expected/isolation_extension_commands.out @@ -20,16 +20,23 @@ step s1-commit: step s2-create-extension-version-11: <... completed> step s1-print: - select count(*) from pg_catalog.pg_dist_object ; + select pg_identify_object_as_address(classid, objid, objsubid)::text as obj_repr from pg_dist_object order by obj_repr; select extname, extversion, nspname from pg_extension, pg_namespace where pg_namespace.oid=pg_extension.extnamespace and extname='seg'; SELECT run_command_on_workers($$select extname from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select extversion from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select nspname from pg_extension, pg_namespace where extname='seg' and pg_extension.extnamespace=pg_namespace.oid$$); -count +obj_repr --------------------------------------------------------------------- - 7 -(1 row) +(database,{regression},{}) +(extension,{seg},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) +(role,{postgres},{}) +(schema,{public},{}) +(schema,{schema1},{}) +(schema,{schema2},{}) +(schema,{schema3},{}) +(8 rows) extname|extversion|nspname --------------------------------------------------------------------- @@ -81,16 +88,23 @@ step s1-commit: step s2-alter-extension-update-to-version-12: <... completed> step s1-print: - select count(*) from pg_catalog.pg_dist_object ; + select pg_identify_object_as_address(classid, objid, objsubid)::text as obj_repr from pg_dist_object order by obj_repr; select extname, extversion, nspname from pg_extension, pg_namespace where pg_namespace.oid=pg_extension.extnamespace and extname='seg'; SELECT run_command_on_workers($$select extname from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select extversion from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select nspname from pg_extension, pg_namespace where extname='seg' and pg_extension.extnamespace=pg_namespace.oid$$); -count +obj_repr --------------------------------------------------------------------- - 7 -(1 row) +(database,{regression},{}) +(extension,{seg},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) +(role,{postgres},{}) +(schema,{public},{}) +(schema,{schema1},{}) +(schema,{schema2},{}) +(schema,{schema3},{}) +(8 rows) extname|extversion|nspname --------------------------------------------------------------------- @@ -150,16 +164,22 @@ step s1-commit: step s2-drop-extension: <... completed> step s1-print: - select count(*) from pg_catalog.pg_dist_object ; + select pg_identify_object_as_address(classid, objid, objsubid)::text as obj_repr from pg_dist_object order by obj_repr; select extname, extversion, nspname from pg_extension, pg_namespace where pg_namespace.oid=pg_extension.extnamespace and extname='seg'; SELECT run_command_on_workers($$select extname from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select extversion from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select nspname from pg_extension, pg_namespace where extname='seg' and pg_extension.extnamespace=pg_namespace.oid$$); -count +obj_repr --------------------------------------------------------------------- - 6 -(1 row) +(database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) +(role,{postgres},{}) +(schema,{public},{}) +(schema,{schema1},{}) +(schema,{schema2},{}) +(schema,{schema3},{}) +(7 rows) extname|extversion|nspname --------------------------------------------------------------------- @@ -206,16 +226,23 @@ step s1-commit: step s2-create-extension-with-schema1: <... completed> step s1-print: - select count(*) from pg_catalog.pg_dist_object ; + select pg_identify_object_as_address(classid, objid, objsubid)::text as obj_repr from pg_dist_object order by obj_repr; select extname, extversion, nspname from pg_extension, pg_namespace where pg_namespace.oid=pg_extension.extnamespace and extname='seg'; SELECT run_command_on_workers($$select extname from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select extversion from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select nspname from pg_extension, pg_namespace where extname='seg' and pg_extension.extnamespace=pg_namespace.oid$$); -count +obj_repr --------------------------------------------------------------------- - 7 -(1 row) +(database,{regression},{}) +(extension,{seg},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) +(role,{postgres},{}) +(schema,{public},{}) +(schema,{schema1},{}) +(schema,{schema2},{}) +(schema,{schema3},{}) +(8 rows) extname|extversion|nspname --------------------------------------------------------------------- @@ -267,16 +294,22 @@ step s1-commit: step s2-drop-extension: <... completed> step s1-print: - select count(*) from pg_catalog.pg_dist_object ; + select pg_identify_object_as_address(classid, objid, objsubid)::text as obj_repr from pg_dist_object order by obj_repr; select extname, extversion, nspname from pg_extension, pg_namespace where pg_namespace.oid=pg_extension.extnamespace and extname='seg'; SELECT run_command_on_workers($$select extname from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select extversion from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select nspname from pg_extension, pg_namespace where extname='seg' and pg_extension.extnamespace=pg_namespace.oid$$); -count +obj_repr --------------------------------------------------------------------- - 6 -(1 row) +(database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) +(role,{postgres},{}) +(schema,{public},{}) +(schema,{schema1},{}) +(schema,{schema2},{}) +(schema,{schema3},{}) +(7 rows) extname|extversion|nspname --------------------------------------------------------------------- @@ -338,16 +371,23 @@ step s1-commit: step s2-alter-extension-set-schema3: <... completed> step s1-print: - select count(*) from pg_catalog.pg_dist_object ; + select pg_identify_object_as_address(classid, objid, objsubid)::text as obj_repr from pg_dist_object order by obj_repr; select extname, extversion, nspname from pg_extension, pg_namespace where pg_namespace.oid=pg_extension.extnamespace and extname='seg'; SELECT run_command_on_workers($$select extname from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select extversion from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select nspname from pg_extension, pg_namespace where extname='seg' and pg_extension.extnamespace=pg_namespace.oid$$); -count +obj_repr --------------------------------------------------------------------- - 7 -(1 row) +(database,{regression},{}) +(extension,{seg},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) +(role,{postgres},{}) +(schema,{public},{}) +(schema,{schema1},{}) +(schema,{schema2},{}) +(schema,{schema3},{}) +(8 rows) extname|extversion|nspname --------------------------------------------------------------------- @@ -406,16 +446,23 @@ step s1-commit: step s2-create-extension-with-schema1: <... completed> step s1-print: - select count(*) from pg_catalog.pg_dist_object ; + select pg_identify_object_as_address(classid, objid, objsubid)::text as obj_repr from pg_dist_object order by obj_repr; select extname, extversion, nspname from pg_extension, pg_namespace where pg_namespace.oid=pg_extension.extnamespace and extname='seg'; SELECT run_command_on_workers($$select extname from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select extversion from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select nspname from pg_extension, pg_namespace where extname='seg' and pg_extension.extnamespace=pg_namespace.oid$$); -count +obj_repr --------------------------------------------------------------------- - 7 -(1 row) +(database,{regression},{}) +(extension,{seg},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) +(role,{postgres},{}) +(schema,{public},{}) +(schema,{schema1},{}) +(schema,{schema2},{}) +(schema,{schema3},{}) +(8 rows) extname|extversion|nspname --------------------------------------------------------------------- @@ -482,16 +529,23 @@ step s1-add-node-1: <... completed> (1 row) step s1-print: - select count(*) from pg_catalog.pg_dist_object ; + select pg_identify_object_as_address(classid, objid, objsubid)::text as obj_repr from pg_dist_object order by obj_repr; select extname, extversion, nspname from pg_extension, pg_namespace where pg_namespace.oid=pg_extension.extnamespace and extname='seg'; SELECT run_command_on_workers($$select extname from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select extversion from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select nspname from pg_extension, pg_namespace where extname='seg' and pg_extension.extnamespace=pg_namespace.oid$$); -count +obj_repr --------------------------------------------------------------------- - 7 -(1 row) +(database,{regression},{}) +(extension,{seg},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) +(role,{postgres},{}) +(schema,{public},{}) +(schema,{schema1},{}) +(schema,{schema2},{}) +(schema,{schema3},{}) +(8 rows) extname|extversion|nspname --------------------------------------------------------------------- @@ -565,16 +619,23 @@ step s1-add-node-1: <... completed> (1 row) step s1-print: - select count(*) from pg_catalog.pg_dist_object ; + select pg_identify_object_as_address(classid, objid, objsubid)::text as obj_repr from pg_dist_object order by obj_repr; select extname, extversion, nspname from pg_extension, pg_namespace where pg_namespace.oid=pg_extension.extnamespace and extname='seg'; SELECT run_command_on_workers($$select extname from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select extversion from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select nspname from pg_extension, pg_namespace where extname='seg' and pg_extension.extnamespace=pg_namespace.oid$$); -count +obj_repr --------------------------------------------------------------------- - 7 -(1 row) +(database,{regression},{}) +(extension,{seg},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) +(role,{postgres},{}) +(schema,{public},{}) +(schema,{schema1},{}) +(schema,{schema2},{}) +(schema,{schema3},{}) +(8 rows) extname|extversion|nspname --------------------------------------------------------------------- @@ -634,16 +695,22 @@ step s1-remove-node-1: <... completed> (1 row) step s1-print: - select count(*) from pg_catalog.pg_dist_object ; + select pg_identify_object_as_address(classid, objid, objsubid)::text as obj_repr from pg_dist_object order by obj_repr; select extname, extversion, nspname from pg_extension, pg_namespace where pg_namespace.oid=pg_extension.extnamespace and extname='seg'; SELECT run_command_on_workers($$select extname from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select extversion from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select nspname from pg_extension, pg_namespace where extname='seg' and pg_extension.extnamespace=pg_namespace.oid$$); -count +obj_repr --------------------------------------------------------------------- - 6 -(1 row) +(database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) +(role,{postgres},{}) +(schema,{public},{}) +(schema,{schema1},{}) +(schema,{schema2},{}) +(schema,{schema3},{}) +(7 rows) extname|extversion|nspname --------------------------------------------------------------------- @@ -690,16 +757,23 @@ step s1-add-node-1: <... completed> (1 row) step s1-print: - select count(*) from pg_catalog.pg_dist_object ; + select pg_identify_object_as_address(classid, objid, objsubid)::text as obj_repr from pg_dist_object order by obj_repr; select extname, extversion, nspname from pg_extension, pg_namespace where pg_namespace.oid=pg_extension.extnamespace and extname='seg'; SELECT run_command_on_workers($$select extname from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select extversion from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select nspname from pg_extension, pg_namespace where extname='seg' and pg_extension.extnamespace=pg_namespace.oid$$); -count +obj_repr --------------------------------------------------------------------- - 7 -(1 row) +(database,{regression},{}) +(extension,{seg},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) +(role,{postgres},{}) +(schema,{public},{}) +(schema,{schema1},{}) +(schema,{schema2},{}) +(schema,{schema3},{}) +(8 rows) extname|extversion|nspname --------------------------------------------------------------------- @@ -765,16 +839,23 @@ step s1-remove-node-1: <... completed> (1 row) step s1-print: - select count(*) from pg_catalog.pg_dist_object ; + select pg_identify_object_as_address(classid, objid, objsubid)::text as obj_repr from pg_dist_object order by obj_repr; select extname, extversion, nspname from pg_extension, pg_namespace where pg_namespace.oid=pg_extension.extnamespace and extname='seg'; SELECT run_command_on_workers($$select extname from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select extversion from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select nspname from pg_extension, pg_namespace where extname='seg' and pg_extension.extnamespace=pg_namespace.oid$$); -count +obj_repr --------------------------------------------------------------------- - 7 -(1 row) +(database,{regression},{}) +(extension,{seg},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) +(role,{postgres},{}) +(schema,{public},{}) +(schema,{schema1},{}) +(schema,{schema2},{}) +(schema,{schema3},{}) +(8 rows) extname|extversion|nspname --------------------------------------------------------------------- @@ -833,16 +914,23 @@ step s1-remove-node-1: <... completed> (1 row) step s1-print: - select count(*) from pg_catalog.pg_dist_object ; + select pg_identify_object_as_address(classid, objid, objsubid)::text as obj_repr from pg_dist_object order by obj_repr; select extname, extversion, nspname from pg_extension, pg_namespace where pg_namespace.oid=pg_extension.extnamespace and extname='seg'; SELECT run_command_on_workers($$select extname from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select extversion from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select nspname from pg_extension, pg_namespace where extname='seg' and pg_extension.extnamespace=pg_namespace.oid$$); -count +obj_repr --------------------------------------------------------------------- - 7 -(1 row) +(database,{regression},{}) +(extension,{seg},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) +(role,{postgres},{}) +(schema,{public},{}) +(schema,{schema1},{}) +(schema,{schema2},{}) +(schema,{schema3},{}) +(8 rows) extname|extversion|nspname --------------------------------------------------------------------- @@ -870,7 +958,7 @@ master_remove_node (1 row) -starting permutation: s2-drop-extension s2-add-node-1 s2-create-extension-version-11 s2-remove-node-1 s2-begin s2-drop-extension s1-add-node-1 s2-commit s1-print +starting permutation: s2-drop-extension s2-add-node-1 s2-create-extension-version-11 s2-remove-node-1 s2-begin s2-drop-extension s1-add-node-1 s2-commit s1-print s1-cleanup-node-1 step s2-drop-extension: drop extension seg; @@ -912,16 +1000,22 @@ step s1-add-node-1: <... completed> (1 row) step s1-print: - select count(*) from pg_catalog.pg_dist_object ; + select pg_identify_object_as_address(classid, objid, objsubid)::text as obj_repr from pg_dist_object order by obj_repr; select extname, extversion, nspname from pg_extension, pg_namespace where pg_namespace.oid=pg_extension.extnamespace and extname='seg'; SELECT run_command_on_workers($$select extname from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select extversion from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select nspname from pg_extension, pg_namespace where extname='seg' and pg_extension.extnamespace=pg_namespace.oid$$); -count +obj_repr --------------------------------------------------------------------- - 6 -(1 row) +(database,{regression},{}) +(function,"{public,wait_until_metadata_sync}",{integer}) +(role,{postgres},{}) +(schema,{public},{}) +(schema,{schema1},{}) +(schema,{schema2},{}) +(schema,{schema3},{}) +(7 rows) extname|extversion|nspname --------------------------------------------------------------------- @@ -945,6 +1039,15 @@ run_command_on_workers (localhost,57638,t,"") (2 rows) +step s1-cleanup-node-1: + SELECT run_command_on_workers($$drop extension if exists seg$$); + +run_command_on_workers +--------------------------------------------------------------------- +(localhost,57637,t,"DROP EXTENSION") +(localhost,57638,t,"DROP EXTENSION") +(2 rows) + master_remove_node --------------------------------------------------------------------- diff --git a/src/test/regress/expected/multi_drop_extension.out b/src/test/regress/expected/multi_drop_extension.out index dfc34dea9..112fa7f70 100644 --- a/src/test/regress/expected/multi_drop_extension.out +++ b/src/test/regress/expected/multi_drop_extension.out @@ -43,8 +43,13 @@ BEGIN; (1 row) alter table other_schema.l3 add constraint fkey foreign key (a) references l1(a); - -- show that works fine - drop schema public cascade; + -- Commented out because it fails due to the issue documented in + -- https://github.com/citusdata/citus/issues/6901. + -- + -- This wasn't the case before https://github.com/citusdata/citus/pull/6900. + -- This is because, we were not marking the schemas as distributed when there + -- are no worker nodes in the cluster before that PR. + -- drop schema public cascade; ROLLBACK; CREATE EXTENSION citus; CREATE SCHEMA test_schema; diff --git a/src/test/regress/expected/multi_function_in_join.out b/src/test/regress/expected/multi_function_in_join.out index 7d62e286b..f77860945 100644 --- a/src/test/regress/expected/multi_function_in_join.out +++ b/src/test/regress/expected/multi_function_in_join.out @@ -57,7 +57,6 @@ $$ LANGUAGE plpgsql; DEBUG: switching to sequential query execution mode DETAIL: A command for a distributed function is run. To make sure subsequent commands see the function correctly we need to make sure to use only one connection for all future commands SELECT * FROM table1 JOIN increment(2) val ON (id = val) ORDER BY id ASC; -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT val FROM functions_in_joins.increment(2) val(val) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT table1.id, table1.data, val.val FROM (functions_in_joins.table1 JOIN (SELECT intermediate_result.val FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(val integer)) val ON ((table1.id OPERATOR(pg_catalog.=) val.val))) ORDER BY table1.id id | data | val @@ -96,7 +95,6 @@ LANGUAGE SQL; DEBUG: switching to sequential query execution mode DETAIL: A command for a distributed function is run. To make sure subsequent commands see the function correctly we need to make sure to use only one connection for all future commands SELECT * FROM table1 JOIN get_set_of_records() AS t2(x int, y int) ON (id = x) ORDER BY id ASC; -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT x, y FROM functions_in_joins.get_set_of_records() t2(x integer, y integer) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT table1.id, table1.data, t2.x, t2.y FROM (functions_in_joins.table1 JOIN (SELECT intermediate_result.x, intermediate_result.y FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(x integer, y integer)) t2 ON ((table1.id OPERATOR(pg_catalog.=) t2.x))) ORDER BY table1.id id | data | x | y @@ -114,7 +112,6 @@ LANGUAGE SQL; DEBUG: switching to sequential query execution mode DETAIL: A command for a distributed function is run. To make sure subsequent commands see the function correctly we need to make sure to use only one connection for all future commands SELECT f.* FROM table1 t JOIN dup(32) f ON (f1 = id); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT f1, f2 FROM functions_in_joins.dup(32) f(f1, f2) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT f.f1, f.f2 FROM (functions_in_joins.table1 t JOIN (SELECT intermediate_result.f1, intermediate_result.f2 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(f1 integer, f2 text)) f ON ((f.f1 OPERATOR(pg_catalog.=) t.id))) f1 | f2 @@ -128,7 +125,6 @@ CREATE OR REPLACE FUNCTION the_minimum_id() DEBUG: switching to sequential query execution mode DETAIL: A command for a distributed function is run. To make sure subsequent commands see the function correctly we need to make sure to use only one connection for all future commands SELECT * FROM table1 JOIN the_minimum_id() min_id ON (id = min_id); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT min_id FROM functions_in_joins.the_minimum_id() min_id(min_id) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT table1.id, table1.data, min_id.min_id FROM (functions_in_joins.table1 JOIN (SELECT intermediate_result.min_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(min_id integer)) min_id ON ((table1.id OPERATOR(pg_catalog.=) min_id.min_id))) id | data | min_id @@ -192,7 +188,6 @@ $$ language plpgsql; DEBUG: switching to sequential query execution mode DETAIL: A command for a distributed function is run. To make sure subsequent commands see the function correctly we need to make sure to use only one connection for all future commands SELECT * FROM table1 JOIN max_and_min() m ON (m.maximum = data OR m.minimum = data) ORDER BY 1,2,3,4; -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT minimum, maximum FROM functions_in_joins.max_and_min() m(minimum, maximum) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT table1.id, table1.data, m.minimum, m.maximum FROM (functions_in_joins.table1 JOIN (SELECT intermediate_result.minimum, intermediate_result.maximum FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(minimum integer, maximum integer)) m ON (((m.maximum OPERATOR(pg_catalog.=) table1.data) OR (m.minimum OPERATOR(pg_catalog.=) table1.data)))) ORDER BY table1.id, table1.data, m.minimum, m.maximum id | data | minimum | maximum diff --git a/src/test/regress/expected/multi_mx_function_call_delegation.out b/src/test/regress/expected/multi_mx_function_call_delegation.out index ffb35b08e..be758a1d1 100644 --- a/src/test/regress/expected/multi_mx_function_call_delegation.out +++ b/src/test/regress/expected/multi_mx_function_call_delegation.out @@ -170,7 +170,6 @@ NOTICE: procedure multi_mx_function_call_delegation.squares is already distribu -- colocated with any distributed tables. SET client_min_messages TO DEBUG1; select mx_call_func(2, 0); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT sum((t1.val OPERATOR(pg_catalog.+) t2.val)) AS sum FROM (multi_mx_function_call_delegation.mx_call_dist_table_1 t1 JOIN multi_mx_function_call_delegation.mx_call_dist_table_2 t2 ON ((t1.id OPERATOR(pg_catalog.=) t2.id))) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT ((3 OPERATOR(pg_catalog.+) (SELECT intermediate_result.sum FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(sum bigint))))::integer mx_call_func @@ -179,14 +178,12 @@ DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT ((3 OPERATOR( (1 row) select multi_mx_function_call_delegation.mx_call_func_bigint(4, 2); -DEBUG: function does not have co-located tables mx_call_func_bigint --------------------------------------------------------------------- 8 (1 row) select mx_call_func_custom_types('S', 'A'); -DEBUG: function does not have co-located tables mx_call_func_custom_types --------------------------------------------------------------------- (F,S) @@ -286,7 +283,6 @@ RESET citus.enable_binary_protocol; -- We don't allow distributing calls inside transactions begin; select mx_call_func(2, 0); -DEBUG: not pushing down function calls in a multi-statement transaction DEBUG: generating subplan XXX_1 for subquery SELECT sum((t1.val OPERATOR(pg_catalog.+) t2.val)) AS sum FROM (multi_mx_function_call_delegation.mx_call_dist_table_1 t1 JOIN multi_mx_function_call_delegation.mx_call_dist_table_2 t2 ON ((t1.id OPERATOR(pg_catalog.=) t2.id))) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT ((3 OPERATOR(pg_catalog.+) (SELECT intermediate_result.sum FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(sum bigint))))::integer mx_call_func @@ -301,7 +297,6 @@ SET client_min_messages TO NOTICE; drop table mx_call_dist_table_enum; SET client_min_messages TO DEBUG1; select mx_call_func_custom_types('S', 'A'); -DEBUG: function does not have co-located tables mx_call_func_custom_types --------------------------------------------------------------------- (F,S) @@ -395,7 +390,6 @@ END;$$; DEBUG: switching to sequential query execution mode -- before distribution ... select mx_call_func_tbl(10); -DEBUG: function does not have co-located tables mx_call_func_tbl --------------------------------------------------------------------- (10,-1) @@ -685,7 +679,6 @@ ERROR: cannot execute a distributed query from a query on a shard select mx_call_func_copy(2) from mx_call_dist_table_1 where id = 3; ERROR: cannot execute a distributed query from a query on a shard DO $$ BEGIN perform mx_call_func_tbl(40); END; $$; -DEBUG: not pushing down function calls in a multi-statement transaction SELECT * FROM mx_call_dist_table_1 WHERE id >= 40 ORDER BY id, val; id | val --------------------------------------------------------------------- @@ -769,7 +762,6 @@ DEBUG: pushing down the function call -- not delegated in a transaction block BEGIN; SELECT mx_call_func(2, 0); -DEBUG: not pushing down function calls in a multi-statement transaction DEBUG: generating subplan XXX_1 for subquery SELECT sum((t1.val OPERATOR(pg_catalog.+) t2.val)) AS sum FROM (multi_mx_function_call_delegation.mx_call_dist_table_1 t1 JOIN multi_mx_function_call_delegation.mx_call_dist_table_2 t2 ON ((t1.id OPERATOR(pg_catalog.=) t2.id))) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT ((2 OPERATOR(pg_catalog.+) (SELECT intermediate_result.sum FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(sum bigint))))::integer mx_call_func @@ -784,7 +776,6 @@ BEGIN PERFORM mx_call_func(2, 0); END; $$ LANGUAGE plpgsql; -DEBUG: not pushing down function calls in a multi-statement transaction DEBUG: generating subplan XXX_1 for subquery SELECT sum((t1.val OPERATOR(pg_catalog.+) t2.val)) AS sum FROM (multi_mx_function_call_delegation.mx_call_dist_table_1 t1 JOIN multi_mx_function_call_delegation.mx_call_dist_table_2 t2 ON ((t1.id OPERATOR(pg_catalog.=) t2.id))) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT ((2 OPERATOR(pg_catalog.+) (SELECT intermediate_result.sum FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(sum bigint))))::integer -- forced calls are delegated in a transaction block diff --git a/src/test/regress/expected/multi_mx_function_call_delegation_0.out b/src/test/regress/expected/multi_mx_function_call_delegation_0.out index 6706ec6f8..ddf867067 100644 --- a/src/test/regress/expected/multi_mx_function_call_delegation_0.out +++ b/src/test/regress/expected/multi_mx_function_call_delegation_0.out @@ -170,7 +170,6 @@ NOTICE: procedure multi_mx_function_call_delegation.squares is already distribu -- colocated with any distributed tables. SET client_min_messages TO DEBUG1; select mx_call_func(2, 0); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT sum((t1.val OPERATOR(pg_catalog.+) t2.val)) AS sum FROM (multi_mx_function_call_delegation.mx_call_dist_table_1 t1 JOIN multi_mx_function_call_delegation.mx_call_dist_table_2 t2 ON ((t1.id OPERATOR(pg_catalog.=) t2.id))) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT (3 OPERATOR(pg_catalog.+) (SELECT intermediate_result.sum FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(sum bigint))) mx_call_func @@ -179,14 +178,12 @@ DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT (3 OPERATOR(p (1 row) select multi_mx_function_call_delegation.mx_call_func_bigint(4, 2); -DEBUG: function does not have co-located tables mx_call_func_bigint --------------------------------------------------------------------- 8 (1 row) select mx_call_func_custom_types('S', 'A'); -DEBUG: function does not have co-located tables mx_call_func_custom_types --------------------------------------------------------------------- (F,S) @@ -286,7 +283,6 @@ RESET citus.enable_binary_protocol; -- We don't allow distributing calls inside transactions begin; select mx_call_func(2, 0); -DEBUG: not pushing down function calls in a multi-statement transaction DEBUG: generating subplan XXX_1 for subquery SELECT sum((t1.val OPERATOR(pg_catalog.+) t2.val)) AS sum FROM (multi_mx_function_call_delegation.mx_call_dist_table_1 t1 JOIN multi_mx_function_call_delegation.mx_call_dist_table_2 t2 ON ((t1.id OPERATOR(pg_catalog.=) t2.id))) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT (3 OPERATOR(pg_catalog.+) (SELECT intermediate_result.sum FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(sum bigint))) mx_call_func @@ -301,7 +297,6 @@ SET client_min_messages TO NOTICE; drop table mx_call_dist_table_enum; SET client_min_messages TO DEBUG1; select mx_call_func_custom_types('S', 'A'); -DEBUG: function does not have co-located tables mx_call_func_custom_types --------------------------------------------------------------------- (F,S) @@ -395,7 +390,6 @@ END;$$; DEBUG: switching to sequential query execution mode -- before distribution ... select mx_call_func_tbl(10); -DEBUG: function does not have co-located tables mx_call_func_tbl --------------------------------------------------------------------- (10,-1) @@ -685,7 +679,6 @@ ERROR: cannot execute a distributed query from a query on a shard select mx_call_func_copy(2) from mx_call_dist_table_1 where id = 3; ERROR: cannot execute a distributed query from a query on a shard DO $$ BEGIN perform mx_call_func_tbl(40); END; $$; -DEBUG: not pushing down function calls in a multi-statement transaction SELECT * FROM mx_call_dist_table_1 WHERE id >= 40 ORDER BY id, val; id | val --------------------------------------------------------------------- @@ -769,7 +762,6 @@ DEBUG: pushing down the function call -- not delegated in a transaction block BEGIN; SELECT mx_call_func(2, 0); -DEBUG: not pushing down function calls in a multi-statement transaction DEBUG: generating subplan XXX_1 for subquery SELECT sum((t1.val OPERATOR(pg_catalog.+) t2.val)) AS sum FROM (multi_mx_function_call_delegation.mx_call_dist_table_1 t1 JOIN multi_mx_function_call_delegation.mx_call_dist_table_2 t2 ON ((t1.id OPERATOR(pg_catalog.=) t2.id))) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT (2 OPERATOR(pg_catalog.+) (SELECT intermediate_result.sum FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(sum bigint))) mx_call_func @@ -784,7 +776,6 @@ BEGIN PERFORM mx_call_func(2, 0); END; $$ LANGUAGE plpgsql; -DEBUG: not pushing down function calls in a multi-statement transaction DEBUG: generating subplan XXX_1 for subquery SELECT sum((t1.val OPERATOR(pg_catalog.+) t2.val)) AS sum FROM (multi_mx_function_call_delegation.mx_call_dist_table_1 t1 JOIN multi_mx_function_call_delegation.mx_call_dist_table_2 t2 ON ((t1.id OPERATOR(pg_catalog.=) t2.id))) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT (2 OPERATOR(pg_catalog.+) (SELECT intermediate_result.sum FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(sum bigint))) -- forced calls are delegated in a transaction block diff --git a/src/test/regress/expected/multi_router_planner.out b/src/test/regress/expected/multi_router_planner.out index a5d5b2c0a..a2f840aa6 100644 --- a/src/test/regress/expected/multi_router_planner.out +++ b/src/test/regress/expected/multi_router_planner.out @@ -1653,7 +1653,6 @@ SELECT 1 FROM authors_reference r JOIN ( SELECT s.datid FROM number1() s LEFT JOIN pg_database d ON s.datid = d.oid ) num_db ON (r.id = num_db.datid) LIMIT 1; DEBUG: found no worker with all shard placements -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT datid FROM multi_router_planner.number1() s(datid) DEBUG: Creating router plan DEBUG: generating subplan XXX_2 for subquery SELECT s.datid FROM ((SELECT intermediate_result.datid FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(datid integer)) s LEFT JOIN pg_database d ON (((s.datid)::oid OPERATOR(pg_catalog.=) d.oid))) @@ -1668,7 +1667,6 @@ CREATE VIEW num_db AS SELECT s.datid FROM number1() s LEFT JOIN pg_database d ON s.datid = d.oid; SELECT 1 FROM authors_reference r JOIN num_db ON (r.id = num_db.datid) LIMIT 1; DEBUG: found no worker with all shard placements -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT datid FROM multi_router_planner.number1() s(datid) DEBUG: Creating router plan DEBUG: generating subplan XXX_2 for subquery SELECT s.datid FROM ((SELECT intermediate_result.datid FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(datid integer)) s LEFT JOIN pg_database d ON (((s.datid)::oid OPERATOR(pg_catalog.=) d.oid))) @@ -2422,7 +2420,6 @@ $$ LANGUAGE plpgsql; DEBUG: switching to sequential query execution mode DETAIL: A command for a distributed function is run. To make sure subsequent commands see the function correctly we need to make sure to use only one connection for all future commands SELECT author_articles_max_id(); -DEBUG: function does not have co-located tables DEBUG: Creating router plan CONTEXT: SQL statement "SELECT MAX(id) FROM articles_hash ah WHERE author_id = 1" @@ -2450,7 +2447,6 @@ $$ LANGUAGE plpgsql; DEBUG: switching to sequential query execution mode DETAIL: A command for a distributed function is run. To make sure subsequent commands see the function correctly we need to make sure to use only one connection for all future commands SELECT * FROM author_articles_id_word_count() ORDER BY 1; -DEBUG: function does not have co-located tables DEBUG: Creating router plan CONTEXT: SQL statement "SELECT ah.id, ah.word_count FROM articles_hash ah diff --git a/src/test/regress/expected/multi_router_planner_fast_path.out b/src/test/regress/expected/multi_router_planner_fast_path.out index e95d7517a..f2f18266b 100644 --- a/src/test/regress/expected/multi_router_planner_fast_path.out +++ b/src/test/regress/expected/multi_router_planner_fast_path.out @@ -1612,7 +1612,6 @@ DETAIL: A command for a distributed function is run. To make sure subsequent co -- since the query results verifies the correctness \set VERBOSITY terse SELECT author_articles_max_id(); -DEBUG: function does not have co-located tables DEBUG: Distributed planning for a fast-path router query DEBUG: Creating router plan DEBUG: query has a single distribution column value: 1 @@ -1622,35 +1621,30 @@ DEBUG: query has a single distribution column value: 1 (1 row) SELECT author_articles_max_id(); -DEBUG: function does not have co-located tables author_articles_max_id --------------------------------------------------------------------- 41 (1 row) SELECT author_articles_max_id(); -DEBUG: function does not have co-located tables author_articles_max_id --------------------------------------------------------------------- 41 (1 row) SELECT author_articles_max_id(); -DEBUG: function does not have co-located tables author_articles_max_id --------------------------------------------------------------------- 41 (1 row) SELECT author_articles_max_id(); -DEBUG: function does not have co-located tables author_articles_max_id --------------------------------------------------------------------- 41 (1 row) SELECT author_articles_max_id(); -DEBUG: function does not have co-located tables author_articles_max_id --------------------------------------------------------------------- 41 @@ -1669,7 +1663,6 @@ END; $$ LANGUAGE plpgsql; DEBUG: switching to sequential query execution mode SELECT author_articles_max_id(1); -DEBUG: function does not have co-located tables DEBUG: Deferred pruning for a fast-path router query DEBUG: Creating router plan author_articles_max_id @@ -1678,7 +1671,6 @@ DEBUG: Creating router plan (1 row) SELECT author_articles_max_id(1); -DEBUG: function does not have co-located tables DEBUG: Deferred pruning for a fast-path router query DEBUG: Creating router plan author_articles_max_id @@ -1687,7 +1679,6 @@ DEBUG: Creating router plan (1 row) SELECT author_articles_max_id(1); -DEBUG: function does not have co-located tables DEBUG: Deferred pruning for a fast-path router query DEBUG: Creating router plan author_articles_max_id @@ -1696,7 +1687,6 @@ DEBUG: Creating router plan (1 row) SELECT author_articles_max_id(1); -DEBUG: function does not have co-located tables DEBUG: Deferred pruning for a fast-path router query DEBUG: Creating router plan author_articles_max_id @@ -1705,7 +1695,6 @@ DEBUG: Creating router plan (1 row) SELECT author_articles_max_id(1); -DEBUG: function does not have co-located tables DEBUG: Deferred pruning for a fast-path router query DEBUG: Creating router plan author_articles_max_id @@ -1714,7 +1703,6 @@ DEBUG: Creating router plan (1 row) SELECT author_articles_max_id(1); -DEBUG: function does not have co-located tables DEBUG: Deferred pruning for a fast-path router query DEBUG: Creating router plan author_articles_max_id @@ -1735,7 +1723,6 @@ END; $$ LANGUAGE plpgsql; DEBUG: switching to sequential query execution mode SELECT * FROM author_articles_id_word_count(); -DEBUG: function does not have co-located tables DEBUG: Distributed planning for a fast-path router query DEBUG: Creating router plan DEBUG: query has a single distribution column value: 1 @@ -1749,7 +1736,6 @@ DEBUG: query has a single distribution column value: 1 (5 rows) SELECT * FROM author_articles_id_word_count(); -DEBUG: function does not have co-located tables id | word_count --------------------------------------------------------------------- 1 | 9572 @@ -1760,7 +1746,6 @@ DEBUG: function does not have co-located tables (5 rows) SELECT * FROM author_articles_id_word_count(); -DEBUG: function does not have co-located tables id | word_count --------------------------------------------------------------------- 1 | 9572 @@ -1771,7 +1756,6 @@ DEBUG: function does not have co-located tables (5 rows) SELECT * FROM author_articles_id_word_count(); -DEBUG: function does not have co-located tables id | word_count --------------------------------------------------------------------- 1 | 9572 @@ -1782,7 +1766,6 @@ DEBUG: function does not have co-located tables (5 rows) SELECT * FROM author_articles_id_word_count(); -DEBUG: function does not have co-located tables id | word_count --------------------------------------------------------------------- 1 | 9572 @@ -1793,7 +1776,6 @@ DEBUG: function does not have co-located tables (5 rows) SELECT * FROM author_articles_id_word_count(); -DEBUG: function does not have co-located tables id | word_count --------------------------------------------------------------------- 1 | 9572 @@ -1816,7 +1798,6 @@ END; $$ LANGUAGE plpgsql; DEBUG: switching to sequential query execution mode SELECT * FROM author_articles_id_word_count(1); -DEBUG: function does not have co-located tables DEBUG: Deferred pruning for a fast-path router query DEBUG: Creating router plan id | word_count @@ -1829,7 +1810,6 @@ DEBUG: Creating router plan (5 rows) SELECT * FROM author_articles_id_word_count(1); -DEBUG: function does not have co-located tables DEBUG: Deferred pruning for a fast-path router query DEBUG: Creating router plan id | word_count @@ -1842,7 +1822,6 @@ DEBUG: Creating router plan (5 rows) SELECT * FROM author_articles_id_word_count(1); -DEBUG: function does not have co-located tables DEBUG: Deferred pruning for a fast-path router query DEBUG: Creating router plan id | word_count @@ -1855,7 +1834,6 @@ DEBUG: Creating router plan (5 rows) SELECT * FROM author_articles_id_word_count(1); -DEBUG: function does not have co-located tables DEBUG: Deferred pruning for a fast-path router query DEBUG: Creating router plan id | word_count @@ -1868,7 +1846,6 @@ DEBUG: Creating router plan (5 rows) SELECT * FROM author_articles_id_word_count(1); -DEBUG: function does not have co-located tables DEBUG: Deferred pruning for a fast-path router query DEBUG: Creating router plan id | word_count @@ -1881,7 +1858,6 @@ DEBUG: Creating router plan (5 rows) SELECT * FROM author_articles_id_word_count(1); -DEBUG: function does not have co-located tables DEBUG: Deferred pruning for a fast-path router query DEBUG: Creating router plan id | word_count diff --git a/src/test/regress/expected/non_colocated_leaf_subquery_joins.out b/src/test/regress/expected/non_colocated_leaf_subquery_joins.out index 976f3d438..7c032f31f 100644 --- a/src/test/regress/expected/non_colocated_leaf_subquery_joins.out +++ b/src/test/regress/expected/non_colocated_leaf_subquery_joins.out @@ -37,7 +37,6 @@ FROM (SELECT users_table.user_id FROM users_table, events_table WHERE users_table.user_id = events_table.user_id AND event_type IN (5,6,7,8)) as bar WHERE foo.user_id = bar.user_id;$$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT users_table.user_id, random() AS random FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.value_2) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4]))) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT intermediate_result.user_id, intermediate_result.random FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, random double precision)) foo, (SELECT users_table.user_id FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[5, 6, 7, 8])))) bar WHERE (foo.user_id OPERATOR(pg_catalog.=) bar.user_id) valid @@ -53,7 +52,6 @@ FROM (SELECT users_table.user_id FROM users_table, events_table WHERE users_table.user_id = events_table.value_2 AND event_type IN (5,6,7,8)) as bar WHERE foo.user_id = bar.user_id;$$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT users_table.user_id, random() AS random FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.value_2) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4]))) DEBUG: generating subplan XXX_2 for subquery SELECT users_table.user_id FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.value_2) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[5, 6, 7, 8]))) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT intermediate_result.user_id, intermediate_result.random FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, random double precision)) foo, (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) bar WHERE (foo.user_id OPERATOR(pg_catalog.=) bar.user_id) @@ -76,7 +74,6 @@ WHERE users_table, events_table WHERE users_table.user_id = events_table.value_2 AND event_type IN (5,6));$$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT users_table.user_id FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.value_2) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[5, 6]))) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM public.users_table WHERE (value_1 OPERATOR(pg_catalog.=) ANY (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer))) valid @@ -93,7 +90,6 @@ SELECT count(*) FROM q1, (SELECT users_table, events_table WHERE users_table.user_id = events_table.value_2 AND event_type IN (1,2,3,4)) as bar WHERE bar.user_id = q1.user_id ;$$); -DEBUG: function does not have co-located tables DEBUG: CTE q1 is going to be inlined via distributed planning DEBUG: generating subplan XXX_1 for subquery SELECT users_table.user_id, random() AS random FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.value_2) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4]))) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT users_table.user_id FROM public.users_table) q1, (SELECT intermediate_result.user_id, intermediate_result.random FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, random double precision)) bar WHERE (bar.user_id OPERATOR(pg_catalog.=) q1.user_id) @@ -106,7 +102,6 @@ DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS c SELECT true AS valid FROM explain_json($$ (SELECT users_table.user_id FROM users_table, events_table WHERE users_table.user_id = events_table.value_2 AND event_type IN (1,2,3,4)) UNION (SELECT users_table.user_id FROM users_table, events_table WHERE users_table.user_id = events_table.user_id AND event_type IN (5,6,7,8));$$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT users_table.user_id FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.value_2) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4]))) DEBUG: generating subplan XXX_2 for subquery SELECT users_table.user_id FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[5, 6, 7, 8]))) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) UNION SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) @@ -143,7 +138,6 @@ FROM ( ) q ORDER BY 2 DESC, 1; $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT users_table.user_id FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.value_2) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4]))) DEBUG: push down of limit count: 5 DEBUG: generating subplan XXX_2 for subquery SELECT user_id FROM public.users_table WHERE ((value_2 OPERATOR(pg_catalog.>=) 5) AND (EXISTS (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)))) LIMIT 5 @@ -165,7 +159,6 @@ FROM (SELECT users_table.user_id, value_1 FROM users_table, events_table WHERE users_table.user_id = events_table.user_id AND event_type IN (5,6,7,8)) as bar WHERE foo.user_id = bar.value_1;$$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT users_table.user_id, users_table.value_1 FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[5, 6, 7, 8]))) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT users_table.user_id, random() AS random FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4])))) foo, (SELECT intermediate_result.user_id, intermediate_result.value_1 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, value_1 integer)) bar WHERE (foo.user_id OPERATOR(pg_catalog.=) bar.value_1) valid diff --git a/src/test/regress/expected/non_colocated_subquery_joins.out b/src/test/regress/expected/non_colocated_subquery_joins.out index d03d4ecf3..1c1a7d935 100644 --- a/src/test/regress/expected/non_colocated_subquery_joins.out +++ b/src/test/regress/expected/non_colocated_subquery_joins.out @@ -44,7 +44,6 @@ SELECT true AS valid FROM explain_json_2($$ WHERE foo.value_2 = bar.value_2; $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT users_table.value_2 FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[5, 6, 7, 8]))) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT foo.value_2 FROM (SELECT users_table.value_2 FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4])))) foo, (SELECT intermediate_result.value_2 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value_2 integer)) bar WHERE (foo.value_2 OPERATOR(pg_catalog.=) bar.value_2) valid @@ -65,7 +64,6 @@ SELECT true AS valid FROM explain_json_2($$ (SELECT event_type FROM events_table WHERE user_id < 100); $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT event_type FROM public.events_table WHERE (user_id OPERATOR(pg_catalog.<) 100) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM public.events_table WHERE (event_type OPERATOR(pg_catalog.=) ANY (SELECT intermediate_result.event_type FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(event_type integer))) valid @@ -85,7 +83,6 @@ SELECT true AS valid FROM explain_json_2($$ NOT IN (SELECT user_id FROM events_table WHERE event_type = 2); $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT user_id FROM public.events_table WHERE (event_type OPERATOR(pg_catalog.=) 2) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM public.events_table WHERE (NOT (user_id OPERATOR(pg_catalog.=) ANY (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)))) valid @@ -107,7 +104,6 @@ SELECT true AS valid FROM explain_json_2($$ foo.event_type IN (SELECT event_type FROM events_table WHERE user_id < 3); $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT event_type FROM public.events_table WHERE (user_id OPERATOR(pg_catalog.<) 3) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT foo.user_id FROM (SELECT users_table.user_id, events_table.event_type FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4])))) foo, (SELECT users_table.user_id FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[5, 6, 7, 8])))) bar WHERE ((foo.user_id OPERATOR(pg_catalog.=) bar.user_id) AND (foo.event_type OPERATOR(pg_catalog.=) ANY (SELECT intermediate_result.event_type FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(event_type integer)))) valid @@ -128,7 +124,6 @@ SELECT true AS valid FROM explain_json_2($$ foo.user_id = bar.user_id AND foo.user_id IN (SELECT user_id FROM events_table WHERE user_id < 10); $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT (users_table.user_id OPERATOR(pg_catalog./) 2) AS user_id FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[5, 6, 7, 8]))) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT foo.user_id FROM (SELECT users_table.user_id, events_table.event_type FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4])))) foo, (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) bar WHERE ((foo.user_id OPERATOR(pg_catalog.=) bar.user_id) AND (foo.user_id OPERATOR(pg_catalog.=) ANY (SELECT events_table.user_id FROM public.events_table WHERE (events_table.user_id OPERATOR(pg_catalog.<) 10)))) valid @@ -149,7 +144,6 @@ SELECT true AS valid FROM explain_json_2($$ foo.user_id = bar.user_id AND foo.user_id NOT IN (SELECT user_id FROM events_table WHERE user_id < 10); $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT (users_table.user_id OPERATOR(pg_catalog./) 2) AS user_id FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[5, 6, 7, 8]))) DEBUG: generating subplan XXX_2 for subquery SELECT user_id FROM public.events_table WHERE (user_id OPERATOR(pg_catalog.<) 10) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT foo.user_id FROM (SELECT users_table.user_id, events_table.event_type FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4])))) foo, (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) bar WHERE ((foo.user_id OPERATOR(pg_catalog.=) bar.user_id) AND (NOT (foo.user_id OPERATOR(pg_catalog.=) ANY (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer))))) @@ -171,7 +165,6 @@ SELECT true AS valid FROM explain_json_2($$ foo.user_id = bar.user_id AND foo.event_type IN (SELECT event_type FROM events_table WHERE user_id < 4); $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT users_table.user_id, events_table.event_type FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.value_2) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4]))) DEBUG: generating subplan XXX_2 for subquery SELECT event_type FROM public.events_table WHERE (user_id OPERATOR(pg_catalog.<) 4) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT foo.user_id FROM (SELECT intermediate_result.user_id, intermediate_result.event_type FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, event_type integer)) foo, (SELECT users_table.user_id FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[5, 6, 7, 8])))) bar WHERE ((foo.user_id OPERATOR(pg_catalog.=) bar.user_id) AND (foo.event_type OPERATOR(pg_catalog.=) ANY (SELECT intermediate_result.event_type FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(event_type integer)))) @@ -196,7 +189,6 @@ SELECT true AS valid FROM explain_json_2($$ ) as foo_top, events_table WHERE events_table.user_id = foo_top.user_id; $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT users_table.user_id, events_table.event_type FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.value_2) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4]))) DEBUG: generating subplan XXX_2 for subquery SELECT users_table.user_id FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.event_type) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[5, 6, 7, 8]))) DEBUG: generating subplan XXX_3 for subquery SELECT event_type FROM public.events_table WHERE (user_id OPERATOR(pg_catalog.=) 5) @@ -231,7 +223,6 @@ SELECT true AS valid FROM explain_json_2($$ ) as foo_top; $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT users_table.user_id, users_table.value_1 FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[17, 18, 19, 20]))) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT user_id, random FROM (SELECT foo1.user_id, random() AS random FROM (SELECT users_table.user_id, users_table.value_1 FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4])))) foo1, (SELECT users_table.user_id, users_table.value_1 FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[5, 6, 7, 8])))) foo2, (SELECT users_table.user_id, users_table.value_1 FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[9, 10, 11, 12])))) foo3, (SELECT users_table.user_id, users_table.value_1 FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[13, 14, 15, 16])))) foo4, (SELECT intermediate_result.user_id, intermediate_result.value_1 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, value_1 integer)) foo5 WHERE ((foo1.user_id OPERATOR(pg_catalog.=) foo4.user_id) AND (foo1.user_id OPERATOR(pg_catalog.=) foo2.user_id) AND (foo1.user_id OPERATOR(pg_catalog.=) foo3.user_id) AND (foo1.user_id OPERATOR(pg_catalog.=) foo4.user_id) AND (foo1.user_id OPERATOR(pg_catalog.=) foo5.value_1))) foo_top valid @@ -263,7 +254,6 @@ SELECT true AS valid FROM explain_json_2($$ foo1.user_id = foo5.user_id ) as foo_top; $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT users_table.user_id, users_table.value_1 FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.value_2) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[17, 18, 19, 20]))) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT user_id, random FROM (SELECT foo1.user_id, random() AS random FROM (SELECT users_table.user_id, users_table.value_1 FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4])))) foo1, (SELECT users_table.user_id, users_table.value_1 FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[5, 6, 7, 8])))) foo2, (SELECT users_table.user_id, users_table.value_1 FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[9, 10, 11, 12])))) foo3, (SELECT users_table.user_id, users_table.value_1 FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[13, 14, 15, 16])))) foo4, (SELECT intermediate_result.user_id, intermediate_result.value_1 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, value_1 integer)) foo5 WHERE ((foo1.user_id OPERATOR(pg_catalog.=) foo4.user_id) AND (foo1.user_id OPERATOR(pg_catalog.=) foo2.user_id) AND (foo1.user_id OPERATOR(pg_catalog.=) foo3.user_id) AND (foo1.user_id OPERATOR(pg_catalog.=) foo4.user_id) AND (foo1.user_id OPERATOR(pg_catalog.=) foo5.user_id))) foo_top valid @@ -293,7 +283,6 @@ SELECT true AS valid FROM explain_json_2($$ foo1.user_id = foo5.value_1 ) as foo_top; $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT users_table.user_id, users_table.value_1 FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.value_2) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[5, 6, 7, 8]))) DEBUG: generating subplan XXX_2 for subquery SELECT users_table.user_id, users_table.value_1 FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[17, 18, 19, 20]))) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT user_id, random FROM (SELECT foo1.user_id, random() AS random FROM (SELECT users_table.user_id, users_table.value_1 FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4])))) foo1, (SELECT intermediate_result.user_id, intermediate_result.value_1 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, value_1 integer)) foo2, (SELECT users_table.user_id, users_table.value_1 FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[9, 10, 11, 12])))) foo3, (SELECT users_table.user_id, users_table.value_1 FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[13, 14, 15, 16])))) foo4, (SELECT intermediate_result.user_id, intermediate_result.value_1 FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, value_1 integer)) foo5 WHERE ((foo1.user_id OPERATOR(pg_catalog.=) foo4.user_id) AND (foo1.user_id OPERATOR(pg_catalog.=) foo2.user_id) AND (foo1.user_id OPERATOR(pg_catalog.=) foo3.user_id) AND (foo1.user_id OPERATOR(pg_catalog.=) foo4.user_id) AND (foo1.user_id OPERATOR(pg_catalog.=) foo5.value_1))) foo_top @@ -325,7 +314,6 @@ SELECT true AS valid FROM explain_json_2($$ foo2.user_id = foo5.value_1 ) as foo_top; $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT users_table.user_id, users_table.value_1 FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.value_2) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[5, 6, 7, 8]))) DEBUG: generating subplan XXX_2 for subquery SELECT users_table.user_id, users_table.value_1 FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[17, 18, 19, 20]))) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT user_id, random FROM (SELECT foo1.user_id, random() AS random FROM (SELECT users_table.user_id, users_table.value_1 FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4])))) foo1, (SELECT intermediate_result.user_id, intermediate_result.value_1 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, value_1 integer)) foo2, (SELECT users_table.user_id, users_table.value_1 FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[9, 10, 11, 12])))) foo3, (SELECT users_table.user_id, users_table.value_1 FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[13, 14, 15, 16])))) foo4, (SELECT intermediate_result.user_id, intermediate_result.value_1 FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, value_1 integer)) foo5 WHERE ((foo1.user_id OPERATOR(pg_catalog.=) foo4.user_id) AND (foo1.user_id OPERATOR(pg_catalog.=) foo2.user_id) AND (foo1.user_id OPERATOR(pg_catalog.=) foo3.user_id) AND (foo1.user_id OPERATOR(pg_catalog.=) foo4.user_id) AND (foo2.user_id OPERATOR(pg_catalog.=) foo5.value_1))) foo_top @@ -359,7 +347,6 @@ SELECT true AS valid FROM explain_json_2($$ foo.user_id = bar.user_id) as bar_top ON (foo_top.user_id = bar_top.user_id); $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT users_table.user_id FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.value_2) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4]))) DEBUG: generating subplan XXX_2 for subquery SELECT users_table.user_id FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.value_2) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4]))) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((SELECT foo.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) foo, (SELECT users_table.user_id FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[5, 6, 7, 8])))) bar WHERE (foo.user_id OPERATOR(pg_catalog.=) bar.user_id)) foo_top JOIN (SELECT foo.user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) foo, (SELECT users_table.user_id FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[5, 6, 7, 8])))) bar WHERE (foo.user_id OPERATOR(pg_catalog.=) bar.user_id)) bar_top ON ((foo_top.user_id OPERATOR(pg_catalog.=) bar_top.user_id))) @@ -394,7 +381,6 @@ SELECT true AS valid FROM explain_json_2($$ ON (foo_top.value_2 = bar_top.user_id); $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT foo.user_id FROM (SELECT DISTINCT users_table.user_id FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[9, 10, 11, 12])))) foo, (SELECT DISTINCT users_table.user_id FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[13, 14, 15, 16])))) bar WHERE (foo.user_id OPERATOR(pg_catalog.=) bar.user_id) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((SELECT foo.user_id, foo.value_2 FROM (SELECT DISTINCT users_table.user_id, users_table.value_2 FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4])))) foo, (SELECT DISTINCT users_table.user_id FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[5, 6, 7, 8])))) bar WHERE (foo.user_id OPERATOR(pg_catalog.=) bar.user_id)) foo_top JOIN (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) bar_top ON ((foo_top.value_2 OPERATOR(pg_catalog.=) bar_top.user_id))) valid @@ -426,7 +412,6 @@ SELECT true AS valid FROM explain_json_2($$ foo.user_id = bar.user_id) as bar_top ON (foo_top.value_2 = bar_top.user_id); $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT DISTINCT users_table.user_id FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.value_2) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[13, 14, 15, 16]))) DEBUG: generating subplan XXX_2 for subquery SELECT foo.user_id FROM (SELECT DISTINCT users_table.user_id FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[9, 10, 11, 12])))) foo, (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) bar WHERE (foo.user_id OPERATOR(pg_catalog.=) bar.user_id) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((SELECT foo.user_id, foo.value_2 FROM (SELECT DISTINCT users_table.user_id, users_table.value_2 FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4])))) foo, (SELECT DISTINCT users_table.user_id FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[5, 6, 7, 8])))) bar WHERE (foo.user_id OPERATOR(pg_catalog.=) bar.user_id)) foo_top JOIN (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) bar_top ON ((foo_top.value_2 OPERATOR(pg_catalog.=) bar_top.user_id))) @@ -448,7 +433,6 @@ SELECT true AS valid FROM explain_json_2($$ WHERE foo.my_users = users_table.user_id) as mid_level_query ) as bar; $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT events_table.user_id AS my_users FROM public.events_table, public.users_table WHERE (events_table.event_type OPERATOR(pg_catalog.=) users_table.user_id) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT mid_level_query.user_id FROM (SELECT DISTINCT users_table.user_id FROM public.users_table, (SELECT intermediate_result.my_users FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(my_users integer)) foo WHERE (foo.my_users OPERATOR(pg_catalog.=) users_table.user_id)) mid_level_query) bar valid @@ -468,7 +452,6 @@ SELECT true AS valid FROM explain_json_2($$ WHERE foo.my_users = users_table.user_id) as mid_level_query ) as bar; $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT events_table.event_type AS my_users, random() AS random FROM public.events_table, public.users_table WHERE (events_table.user_id OPERATOR(pg_catalog.=) users_table.user_id) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT mid_level_query.user_id FROM (SELECT DISTINCT users_table.user_id FROM public.users_table, (SELECT intermediate_result.my_users, intermediate_result.random FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(my_users integer, random double precision)) foo WHERE (foo.my_users OPERATOR(pg_catalog.=) users_table.user_id)) mid_level_query) bar valid @@ -492,7 +475,6 @@ DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS c WHERE foo.my_users = users_table.user_id) as mid_level_query ) as bar; $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT events_table.user_id, random() AS random FROM public.users_table, public.events_table WHERE (users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT mid_level_query.user_id FROM (SELECT DISTINCT users_table.user_id FROM public.users_table, (SELECT events_table.user_id AS my_users FROM public.events_table, (SELECT intermediate_result.user_id, intermediate_result.random FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, random double precision)) selected_users WHERE (events_table.event_type OPERATOR(pg_catalog.=) selected_users.user_id)) foo WHERE (foo.my_users OPERATOR(pg_catalog.=) users_table.user_id)) mid_level_query) bar valid @@ -526,7 +508,6 @@ SELECT true AS valid FROM explain_json_2($$ ) as bar; $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT value_2 FROM public.events_table DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT mid_level_query.user_id FROM (SELECT DISTINCT users_table.user_id FROM public.users_table, (SELECT events_table.user_id AS my_users FROM public.events_table, (SELECT events_table_1.user_id FROM public.users_table users_table_1, public.events_table events_table_1 WHERE ((users_table_1.user_id OPERATOR(pg_catalog.=) events_table_1.user_id) AND (users_table_1.user_id OPERATOR(pg_catalog.=) ANY (SELECT intermediate_result.value_2 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value_2 integer))))) selected_users WHERE (events_table.user_id OPERATOR(pg_catalog.=) selected_users.user_id)) foo WHERE (foo.my_users OPERATOR(pg_catalog.=) users_table.user_id)) mid_level_query) bar valid @@ -548,7 +529,6 @@ WHERE users_table, events_table WHERE users_table.user_id = events_table.value_2 AND event_type IN (5,6));$$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT users_table.user_id FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.value_2) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[5, 6]))) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM public.users_table WHERE (value_1 OPERATOR(pg_catalog.=) ANY (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer))) valid @@ -565,7 +545,6 @@ SELECT count(*) FROM q1, (SELECT users_table, events_table WHERE users_table.user_id = events_table.value_2 AND event_type IN (1,2,3,4)) as bar WHERE bar.user_id = q1.user_id ;$$); -DEBUG: function does not have co-located tables DEBUG: CTE q1 is going to be inlined via distributed planning DEBUG: generating subplan XXX_1 for subquery SELECT users_table.user_id, random() AS random FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.value_2) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4]))) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT users_table.user_id FROM public.users_table) q1, (SELECT intermediate_result.user_id, intermediate_result.random FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, random double precision)) bar WHERE (bar.user_id OPERATOR(pg_catalog.=) q1.user_id) @@ -583,7 +562,6 @@ SELECT true AS valid FROM explain_json_2($$ users_table, events_table WHERE users_table.user_id = events_table.user_id AND event_type IN (1,2,3,4)) as bar WHERE bar.user_id = q1.user_id ;$$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for CTE q1: SELECT user_id FROM public.users_table DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) q1, (SELECT users_table.user_id, random() AS random FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4])))) bar WHERE (bar.user_id OPERATOR(pg_catalog.=) q1.user_id) valid @@ -595,7 +573,6 @@ DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS c SELECT true AS valid FROM explain_json_2($$ (SELECT users_table.user_id FROM users_table, events_table WHERE users_table.user_id = events_table.value_2 AND event_type IN (1,2,3,4)) UNION (SELECT users_table.user_id FROM users_table, events_table WHERE users_table.user_id = events_table.user_id AND event_type IN (5,6,7,8));$$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT users_table.user_id FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.value_2) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4]))) DEBUG: generating subplan XXX_2 for subquery SELECT users_table.user_id FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[5, 6, 7, 8]))) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) UNION SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) @@ -632,7 +609,6 @@ FROM ( ) q ORDER BY 2 DESC, 1; $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT users_table.user_id FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.value_2) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4]))) DEBUG: push down of limit count: 5 DEBUG: generating subplan XXX_2 for subquery SELECT user_id FROM public.users_table WHERE ((value_2 OPERATOR(pg_catalog.>=) 5) AND (EXISTS (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)))) LIMIT 5 @@ -654,7 +630,6 @@ SELECT true AS valid FROM explain_json_2($$ FROM (users_table u1 JOIN users_table u2 using(value_1)) a JOIN (SELECT value_1, random() FROM users_table) as u3 USING (value_1); $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT value_1, random() AS random FROM public.users_table DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((public.users_table u1 JOIN public.users_table u2 USING (value_1)) a(value_1, user_id, "time", value_2, value_3, value_4, user_id_1, time_1, value_2_1, value_3_1, value_4_1) JOIN (SELECT intermediate_result.value_1, intermediate_result.random FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value_1 integer, random double precision)) u3 USING (value_1)) ERROR: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns @@ -667,7 +642,6 @@ SELECT true AS valid FROM explain_json_2($$ FROM (SELECT * FROM users_table u1 JOIN users_table u2 using(value_1)) a JOIN (SELECT value_1, random() FROM users_table) as u3 USING (value_1); $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT u1.value_1, u1.user_id, u1."time", u1.value_2, u1.value_3, u1.value_4, u2.user_id, u2."time", u2.value_2, u2.value_3, u2.value_4 FROM (public.users_table u1 JOIN public.users_table u2 USING (value_1)) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((SELECT intermediate_result.value_1, intermediate_result.user_id, intermediate_result."time", intermediate_result.value_2, intermediate_result.value_3, intermediate_result.value_4, intermediate_result.user_id_1 AS user_id, intermediate_result.time_1 AS "time", intermediate_result.value_2_1 AS value_2, intermediate_result.value_3_1 AS value_3, intermediate_result.value_4_1 AS value_4 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value_1 integer, user_id integer, "time" timestamp without time zone, value_2 integer, value_3 double precision, value_4 bigint, user_id_1 integer, time_1 timestamp without time zone, value_2_1 integer, value_3_1 double precision, value_4_1 bigint)) a(value_1, user_id, "time", value_2, value_3, value_4, user_id_1, time_1, value_2_1, value_3_1, value_4_1) JOIN (SELECT users_table.value_1, random() AS random FROM public.users_table) u3 USING (value_1)) valid @@ -687,7 +661,6 @@ SELECT true AS valid FROM explain_json_2($$ events_table using (value_2); $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT value_2, random() AS random FROM public.users_table DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((SELECT intermediate_result.value_2, intermediate_result.random FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value_2 integer, random double precision)) u1 JOIN public.events_table USING (value_2)) valid @@ -706,7 +679,6 @@ SELECT true AS valid FROM explain_json_2($$ (SELECT value_2, random() FROM users_table) as u2 USING(value_2); $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT value_2, random() AS random FROM public.users_table DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((SELECT users_table.value_2, random() AS random FROM public.users_table) u1 LEFT JOIN (SELECT intermediate_result.value_2, intermediate_result.random FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value_2 integer, random double precision)) u2 USING (value_2)) valid @@ -725,7 +697,6 @@ SELECT true AS valid FROM explain_json_2($$ (SELECT value_2, random() FROM users_table) as u2 USING(value_2); $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT value_2, random() AS random FROM public.users_table DEBUG: recursively planning left side of the right join since the outer side is a recurring rel DEBUG: recursively planning the distributed subquery since it is part of a distributed join node that is outer joined with a recurring rel @@ -752,7 +723,6 @@ SELECT true AS valid FROM explain_json_2($$ (SELECT value_1 FROM users_table) as foo ON (a.user_id = foo.value_1) ); $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT user_id FROM public.users_table DEBUG: generating subplan XXX_2 for subquery SELECT user_id FROM public.users_table DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) UNION SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) @@ -777,7 +747,6 @@ SELECT true AS valid FROM explain_json_2($$ users_table as foo ON (a.user_id = foo.value_1) ); $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT user_id FROM public.users_table DEBUG: generating subplan XXX_2 for subquery SELECT user_id FROM public.users_table DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) UNION SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer) @@ -811,7 +780,6 @@ SELECT true AS valid FROM explain_json_2($$ ON(foo.user_id = bar.value_1) ); $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT value_1 FROM public.users_table DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT foo.user_id, a.user_id, bar.value_1 FROM (((SELECT users_table.user_id FROM public.users_table) foo JOIN (SELECT users_table.user_id FROM public.users_table WHERE (users_table.user_id OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4])) UNION SELECT users_table.user_id FROM public.users_table WHERE (users_table.user_id OPERATOR(pg_catalog.=) ANY (ARRAY[5, 6, 7, 8]))) a ON ((a.user_id OPERATOR(pg_catalog.=) foo.user_id))) JOIN (SELECT intermediate_result.value_1 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value_1 integer)) bar ON ((foo.user_id OPERATOR(pg_catalog.=) bar.value_1))) valid @@ -851,7 +819,6 @@ SELECT true AS valid FROM explain_json_2($$ WHERE non_colocated_subquery.value_2 != non_colocated_subquery_2.cnt $$); -DEBUG: function does not have co-located tables DEBUG: CTE non_colocated_subquery is going to be inlined via distributed planning DEBUG: CTE non_colocated_subquery_2 is going to be inlined via distributed planning DEBUG: generating subplan XXX_1 for subquery SELECT users_table.value_2 FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[5, 6, 7, 8]))) @@ -876,7 +843,6 @@ SELECT true AS valid FROM explain_json_2($$ AND foo.value_2 = baz.value_2 $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT users_table_local.value_2 FROM non_colocated_subquery.users_table_local, non_colocated_subquery.events_table_local WHERE ((users_table_local.user_id OPERATOR(pg_catalog.=) events_table_local.user_id) AND (events_table_local.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[5, 6, 7, 8]))) DEBUG: generating subplan XXX_2 for subquery SELECT users_table.value_2 FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[9, 10, 11, 12]))) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT users_table.value_2 FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4])))) foo, (SELECT intermediate_result.value_2 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value_2 integer)) bar, (SELECT intermediate_result.value_2 FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(value_2 integer)) baz WHERE ((foo.value_2 OPERATOR(pg_catalog.=) bar.value_2) AND (foo.value_2 OPERATOR(pg_catalog.=) baz.value_2)) @@ -912,7 +878,6 @@ SELECT true AS valid FROM explain_json_2($$ AND foo.user_id IN (SELECT users_table.user_id FROM users_table, events_table WHERE users_table.user_id = events_table.user_id AND event_type IN (1,2)) $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT value_1, value_2 FROM public.users_table DEBUG: generating subplan XXX_2 for subquery SELECT value_1 FROM public.users_table WHERE (value_2 OPERATOR(pg_catalog.<) 1) DEBUG: generating subplan XXX_3 for subquery SELECT value_2 FROM public.users_table WHERE (value_1 OPERATOR(pg_catalog.<) 2) @@ -935,7 +900,6 @@ SELECT true AS valid FROM explain_json_2($$ users_table_ref.user_id = foo.user_id AND foo.user_id = bar.value_2; $$); -DEBUG: function does not have co-located tables DEBUG: generating subplan XXX_1 for subquery SELECT user_id, value_2 FROM public.events_table DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM public.users_reference_table users_table_ref, (SELECT users_table.user_id FROM public.users_table) foo, (SELECT intermediate_result.user_id, intermediate_result.value_2 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, value_2 integer)) bar WHERE ((users_table_ref.user_id OPERATOR(pg_catalog.=) foo.user_id) AND (foo.user_id OPERATOR(pg_catalog.=) bar.value_2)) valid @@ -984,7 +948,6 @@ JOIN LATERAL WHERE user_id = users_table.user_id) AS bar LEFT JOIN users_table u2 ON u2.user_id = bar.value_2) AS foo ON TRUE $$); -DEBUG: function does not have co-located tables DEBUG: Router planner cannot handle multi-shard select queries DEBUG: skipping recursive planning for the subquery since it contains references to outer queries DEBUG: skipping recursive planning for the subquery since it contains references to outer queries diff --git a/src/test/regress/expected/single_node_enterprise.out b/src/test/regress/expected/single_node_enterprise.out index f8d725beb..305a02b8e 100644 --- a/src/test/regress/expected/single_node_enterprise.out +++ b/src/test/regress/expected/single_node_enterprise.out @@ -59,6 +59,8 @@ BEGIN END; $$; SELECT create_distributed_function('notice(text)'); +NOTICE: procedure single_node_ent.notice is already distributed +DETAIL: Citus distributes procedures with CREATE [PROCEDURE|FUNCTION|AGGREGATE] commands create_distributed_function --------------------------------------------------------------------- diff --git a/src/test/regress/expected/subquery_and_cte.out b/src/test/regress/expected/subquery_and_cte.out index f3f12b975..4360bb69e 100644 --- a/src/test/regress/expected/subquery_and_cte.out +++ b/src/test/regress/expected/subquery_and_cte.out @@ -126,7 +126,6 @@ WITH cte1 AS MATERIALIZED (SELECT id, value FROM func()) UPDATE dist_table dt SET value = cte1.value FROM cte1 WHERE dt.id = 1; DEBUG: generating subplan XXX_1 for CTE cte1: SELECT id, value FROM subquery_and_ctes.func() func(id, value) -DEBUG: function does not have co-located tables DEBUG: Plan XXX query after replacing subqueries and CTEs: UPDATE subquery_and_ctes.dist_table dt SET value = cte1.value FROM (SELECT intermediate_result.id, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(id integer, value integer)) cte1 WHERE (dt.id OPERATOR(pg_catalog.=) 1) -- CTEs are recursively planned, and subquery foo is also recursively planned -- final plan becomes a real-time plan since we also have events_table in the diff --git a/src/test/regress/spec/isolation_extension_commands.spec b/src/test/regress/spec/isolation_extension_commands.spec index 98a8eb6dc..0b81e8adf 100644 --- a/src/test/regress/spec/isolation_extension_commands.spec +++ b/src/test/regress/spec/isolation_extension_commands.spec @@ -41,13 +41,18 @@ step "s1-create-extension-with-schema2" step "s1-print" { - select count(*) from pg_catalog.pg_dist_object ; + select pg_identify_object_as_address(classid, objid, objsubid)::text as obj_repr from pg_dist_object order by obj_repr; select extname, extversion, nspname from pg_extension, pg_namespace where pg_namespace.oid=pg_extension.extnamespace and extname='seg'; SELECT run_command_on_workers($$select extname from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select extversion from pg_extension where extname='seg'$$); SELECT run_command_on_workers($$select nspname from pg_extension, pg_namespace where extname='seg' and pg_extension.extnamespace=pg_namespace.oid$$); } +step "s1-cleanup-node-1" +{ + SELECT run_command_on_workers($$drop extension if exists seg$$); +} + session "s2" step "s2-begin" @@ -121,4 +126,4 @@ permutation "s2-add-node-1" "s2-begin" "s2-drop-extension" "s1-remove-node-1" "s permutation "s2-begin" "s2-create-extension-with-schema1" "s1-add-node-1" "s2-commit" "s1-print" permutation "s2-drop-extension" "s2-add-node-1" "s2-create-extension-with-schema2" "s2-begin" "s2-alter-extension-version-13" "s1-remove-node-1" "s2-commit" "s1-print" permutation "s2-drop-extension" "s2-add-node-1" "s2-begin" "s2-create-extension-version-11" "s1-remove-node-1" "s2-commit" "s1-print" -permutation "s2-drop-extension" "s2-add-node-1" "s2-create-extension-version-11" "s2-remove-node-1" "s2-begin" "s2-drop-extension" "s1-add-node-1" "s2-commit" "s1-print" +permutation "s2-drop-extension" "s2-add-node-1" "s2-create-extension-version-11" "s2-remove-node-1" "s2-begin" "s2-drop-extension" "s1-add-node-1" "s2-commit" "s1-print" "s1-cleanup-node-1" diff --git a/src/test/regress/sql/multi_drop_extension.sql b/src/test/regress/sql/multi_drop_extension.sql index b1c569bfd..29da58dc4 100644 --- a/src/test/regress/sql/multi_drop_extension.sql +++ b/src/test/regress/sql/multi_drop_extension.sql @@ -34,8 +34,13 @@ BEGIN; alter table other_schema.l3 add constraint fkey foreign key (a) references l1(a); - -- show that works fine - drop schema public cascade; + -- Commented out because it fails due to the issue documented in + -- https://github.com/citusdata/citus/issues/6901. + -- + -- This wasn't the case before https://github.com/citusdata/citus/pull/6900. + -- This is because, we were not marking the schemas as distributed when there + -- are no worker nodes in the cluster before that PR. + -- drop schema public cascade; ROLLBACK; CREATE EXTENSION citus; From 8ff9dde4b3c7413c24fc1d63e639849f55ae88b7 Mon Sep 17 00:00:00 2001 From: Onur Tirtir Date: Wed, 17 May 2023 15:05:08 +0300 Subject: [PATCH 044/118] Prevent pushing down INSERT .. SELECT queries that we shouldn't (and allow some more) (#6752) Previously INSERT .. SELECT planner were pushing down some queries that should not be pushed down due to wrong colocation checks. It was checking whether one of the table in SELECT part and target table are colocated. But now, we check colocation for all tables in SELECT part and the target table. Another problem with INSERT .. SELECT planner was that some queries, which is valid to be pushed down, were not pushed down due to unnecessary checks which are currently supported. e.g. UNION check. As solution, we reused the pushdown planner checks for INSERT .. SELECT planner. DESCRIPTION: Fixes a bug that causes incorrectly pushing down some INSERT .. SELECT queries that we shouldn't DESCRIPTION: Prevents unnecessarily pulling the data into coordinator for some INSERT .. SELECT queries DESCRIPTION: Drops support for pushing down INSERT .. SELECT with append table as target Fixes #6749. Fixes #1428. Fixes #6920. --------- Co-authored-by: aykutbozkurt --- .../planner/insert_select_planner.c | 217 ++----------- .../expected/coordinator_shouldhaveshards.out | 5 +- .../coordinator_shouldhaveshards_0.out | 5 +- .../expected/insert_select_repartition.out | 81 +++-- .../expected/insert_select_repartition_0.out | 81 +++-- .../insert_select_single_shard_table.out | 122 ++++---- .../expected/intermediate_result_pruning.out | 10 +- .../intermediate_result_pruning_0.out | 10 +- .../expected/isolation_select_vs_all.out | 160 ---------- .../regress/expected/multi_insert_select.out | 287 ++++++++++++++++-- .../expected/multi_insert_select_0.out | 287 ++++++++++++++++-- .../expected/multi_insert_select_conflict.out | 18 +- .../multi_insert_select_conflict_0.out | 18 +- ...lti_insert_select_non_pushable_queries.out | 39 ++- .../multi_router_planner_fast_path.out | 18 +- .../mx_coordinator_shouldhaveshards.out | 20 +- .../mx_coordinator_shouldhaveshards_0.out | 20 +- .../expected/query_single_shard_table.out | 36 ++- .../regress/expected/recurring_outer_join.out | 9 +- src/test/regress/expected/with_dml.out | 4 +- src/test/regress/expected/with_modifying.out | 3 +- src/test/regress/multi_1_schedule | 7 +- src/test/regress/multi_schedule | 7 +- .../regress/spec/isolation_select_vs_all.spec | 8 - .../regress/sql/insert_select_repartition.sql | 11 + .../sql/insert_select_single_shard_table.sql | 6 + src/test/regress/sql/multi_insert_select.sql | 120 +++++++- ...lti_insert_select_non_pushable_queries.sql | 16 + 28 files changed, 1036 insertions(+), 589 deletions(-) diff --git a/src/backend/distributed/planner/insert_select_planner.c b/src/backend/distributed/planner/insert_select_planner.c index cae71845b..06e446783 100644 --- a/src/backend/distributed/planner/insert_select_planner.c +++ b/src/backend/distributed/planner/insert_select_planner.c @@ -73,9 +73,9 @@ static List * CreateTargetListForCombineQuery(List *targetList); static DeferredErrorMessage * DistributedInsertSelectSupported(Query *queryTree, RangeTblEntry *insertRte, RangeTblEntry *subqueryRte, - bool allReferenceTables); -static DeferredErrorMessage * MultiTaskRouterSelectQuerySupported(Query *query); -static bool HasUnsupportedDistinctOn(Query *query); + bool allReferenceTables, + PlannerRestrictionContext * + plannerRestrictionContext); static DeferredErrorMessage * InsertPartitionColumnMatchesSelect(Query *query, RangeTblEntry *insertRte, RangeTblEntry * @@ -292,7 +292,8 @@ CreateDistributedInsertSelectPlan(Query *originalQuery, distributedPlan->planningError = DistributedInsertSelectSupported(originalQuery, insertRte, subqueryRte, - allReferenceTables); + allReferenceTables, + plannerRestrictionContext); if (distributedPlan->planningError) { return distributedPlan; @@ -613,7 +614,8 @@ CreateTargetListForCombineQuery(List *targetList) */ static DeferredErrorMessage * DistributedInsertSelectSupported(Query *queryTree, RangeTblEntry *insertRte, - RangeTblEntry *subqueryRte, bool allReferenceTables) + RangeTblEntry *subqueryRte, bool allReferenceTables, + PlannerRestrictionContext *plannerRestrictionContext) { Oid selectPartitionColumnTableId = InvalidOid; Oid targetRelationId = insertRte->relid; @@ -687,8 +689,16 @@ DistributedInsertSelectSupported(Query *queryTree, RangeTblEntry *insertRte, NULL, NULL); } - /* we don't support LIMIT, OFFSET and WINDOW functions */ - DeferredErrorMessage *error = MultiTaskRouterSelectQuerySupported(subquery); + /* first apply toplevel pushdown checks to SELECT query */ + DeferredErrorMessage *error = DeferErrorIfUnsupportedSubqueryPushdown(subquery, + plannerRestrictionContext); + if (error) + { + return error; + } + + /* then apply subquery pushdown checks to SELECT query */ + error = DeferErrorIfCannotPushdownSubquery(subquery, false); if (error) { return error; @@ -730,27 +740,6 @@ DistributedInsertSelectSupported(Query *queryTree, RangeTblEntry *insertRte, "table", NULL, NULL); } - if (!HasDistributionKey(targetRelationId) || - subqueryRteListProperties->hasSingleShardDistTable) - { - /* - * XXX: Better to check this regardless of the fact that the target table - * has a distribution column or not. - */ - List *distributedRelationIdList = DistributedRelationIdList(subquery); - distributedRelationIdList = lappend_oid(distributedRelationIdList, - targetRelationId); - - if (!AllDistributedRelationsInListColocated(distributedRelationIdList)) - { - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - "distributed INSERT ... SELECT cannot reference a " - "distributed table without a shard key together " - "with non-colocated distributed tables", - NULL, NULL); - } - } - if (HasDistributionKey(targetRelationId)) { /* ensure that INSERT's partition column comes from SELECT's partition column */ @@ -760,22 +749,22 @@ DistributedInsertSelectSupported(Query *queryTree, RangeTblEntry *insertRte, { return error; } - - /* - * We expect partition column values come from colocated tables. Note that we - * skip this check from the reference table case given that all reference tables - * are already (and by default) co-located. - */ - if (!TablesColocated(insertRte->relid, selectPartitionColumnTableId)) - { - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - "INSERT target table and the source relation of the SELECT partition " - "column value must be colocated in distributed INSERT ... SELECT", - NULL, NULL); - } } } + /* All tables in source list and target table should be colocated. */ + List *distributedRelationIdList = DistributedRelationIdList(subquery); + distributedRelationIdList = lappend_oid(distributedRelationIdList, + targetRelationId); + + if (!AllDistributedRelationsInListColocated(distributedRelationIdList)) + { + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "INSERT target relation and all source relations of the " + "SELECT must be colocated in distributed INSERT ... SELECT", + NULL, NULL); + } + return NULL; } @@ -1131,152 +1120,6 @@ ReorderInsertSelectTargetLists(Query *originalQuery, RangeTblEntry *insertRte, } -/* - * MultiTaskRouterSelectQuerySupported returns NULL if the query may be used - * as the source for an INSERT ... SELECT or returns a description why not. - */ -static DeferredErrorMessage * -MultiTaskRouterSelectQuerySupported(Query *query) -{ - List *queryList = NIL; - ListCell *queryCell = NULL; - StringInfo errorDetail = NULL; - bool hasUnsupportedDistinctOn = false; - - ExtractQueryWalker((Node *) query, &queryList); - foreach(queryCell, queryList) - { - Query *subquery = (Query *) lfirst(queryCell); - - Assert(subquery->commandType == CMD_SELECT); - - /* pushing down rtes without relations yields (shardCount * expectedRows) */ - if (HasEmptyJoinTree(subquery)) - { - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - "Subqueries without relations are not allowed in " - "distributed INSERT ... SELECT queries", - NULL, NULL); - } - - /* pushing down limit per shard would yield wrong results */ - if (subquery->limitCount != NULL) - { - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - "LIMIT clauses are not allowed in distributed INSERT " - "... SELECT queries", - NULL, NULL); - } - - /* pushing down limit offest per shard would yield wrong results */ - if (subquery->limitOffset != NULL) - { - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - "OFFSET clauses are not allowed in distributed " - "INSERT ... SELECT queries", - NULL, NULL); - } - - /* group clause list must include partition column */ - if (subquery->groupClause) - { - List *groupClauseList = subquery->groupClause; - List *targetEntryList = subquery->targetList; - List *groupTargetEntryList = GroupTargetEntryList(groupClauseList, - targetEntryList); - bool groupOnPartitionColumn = TargetListOnPartitionColumn(subquery, - groupTargetEntryList); - if (!groupOnPartitionColumn) - { - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - "Group by list without distribution column is " - "not allowed in distributed INSERT ... " - "SELECT queries", - NULL, NULL); - } - } - - /* - * We support window functions when the window function - * is partitioned on distribution column. - */ - if (subquery->windowClause && !SafeToPushdownWindowFunction(subquery, - &errorDetail)) - { - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, errorDetail->data, NULL, - NULL); - } - - if (subquery->setOperations != NULL) - { - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - "Set operations are not allowed in distributed " - "INSERT ... SELECT queries", - NULL, NULL); - } - - /* - * We currently do not support grouping sets since it could generate NULL - * results even after the restrictions are applied to the query. A solution - * would be to add the whole query into a subquery and add the restrictions - * on that subquery. - */ - if (subquery->groupingSets != NULL) - { - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - "grouping sets are not allowed in distributed " - "INSERT ... SELECT queries", - NULL, NULL); - } - - /* - * We don't support DISTINCT ON clauses on non-partition columns. - */ - hasUnsupportedDistinctOn = HasUnsupportedDistinctOn(subquery); - if (hasUnsupportedDistinctOn) - { - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - "DISTINCT ON (non-partition column) clauses are not " - "allowed in distributed INSERT ... SELECT queries", - NULL, NULL); - } - } - - return NULL; -} - - -/* - * HasUnsupportedDistinctOn returns true if the query has distinct on and - * distinct targets do not contain partition column. - */ -static bool -HasUnsupportedDistinctOn(Query *query) -{ - ListCell *distinctCell = NULL; - - if (!query->hasDistinctOn) - { - return false; - } - - foreach(distinctCell, query->distinctClause) - { - SortGroupClause *distinctClause = lfirst(distinctCell); - TargetEntry *distinctEntry = get_sortgroupclause_tle(distinctClause, - query->targetList); - - bool skipOuterVars = true; - if (IsPartitionColumn(distinctEntry->expr, query, skipOuterVars)) - { - return false; - } - } - - return true; -} - - /* * InsertPartitionColumnMatchesSelect returns NULL the partition column in the * table targeted by INSERTed matches with the any of the SELECTed table's diff --git a/src/test/regress/expected/coordinator_shouldhaveshards.out b/src/test/regress/expected/coordinator_shouldhaveshards.out index 3307e6bb6..46d5bf6a9 100644 --- a/src/test/regress/expected/coordinator_shouldhaveshards.out +++ b/src/test/regress/expected/coordinator_shouldhaveshards.out @@ -908,7 +908,7 @@ key FROM a JOIN table_2 USING (key) GROUP BY key HAVING (max(table_2.value) >= (SELECT value FROM a)); -DEBUG: Group by list without distribution column is not allowed in distributed INSERT ... SELECT queries +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: generating subplan XXX_1 for CTE a: SELECT key, value FROM coordinator_shouldhaveshards.table_1 ORDER BY key, value DESC LIMIT 1 DEBUG: push down of limit count: 1 DEBUG: generating subplan XXX_2 for subquery SELECT int4(count(*)) AS auto_coerced_by_citus_0, (a.key)::text AS auto_coerced_by_citus_1 FROM ((SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) a JOIN coordinator_shouldhaveshards.table_2 USING (key)) GROUP BY a.key HAVING (max(table_2.value) OPERATOR(pg_catalog.>=) (SELECT a_1.value FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) a_1)) @@ -939,7 +939,8 @@ inserts AS ( ) SELECT count(*) FROM inserts; DEBUG: generating subplan XXX_1 for CTE stats: SELECT count(key) AS m FROM coordinator_shouldhaveshards.table_1 DEBUG: generating subplan XXX_2 for CTE inserts: INSERT INTO coordinator_shouldhaveshards.table_2 (key, value) SELECT table_1.key, count(*) AS count FROM coordinator_shouldhaveshards.table_1 WHERE (table_1.key OPERATOR(pg_catalog.>=) (SELECT stats.m FROM (SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m bigint)) stats)) GROUP BY table_1.key HAVING (count(*) OPERATOR(pg_catalog.<=) (SELECT stats.m FROM (SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m bigint)) stats)) LIMIT 1 RETURNING table_2.key, table_2.value -DEBUG: LIMIT clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Limit clause is currently unsupported when a subquery references a column from another query DEBUG: push down of limit count: 1 DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) inserts DEBUG: Subplan XXX_1 will be written to local file diff --git a/src/test/regress/expected/coordinator_shouldhaveshards_0.out b/src/test/regress/expected/coordinator_shouldhaveshards_0.out index 9b81a6a72..4c9dc0d18 100644 --- a/src/test/regress/expected/coordinator_shouldhaveshards_0.out +++ b/src/test/regress/expected/coordinator_shouldhaveshards_0.out @@ -908,7 +908,7 @@ key FROM a JOIN table_2 USING (key) GROUP BY key HAVING (max(table_2.value) >= (SELECT value FROM a)); -DEBUG: Group by list without distribution column is not allowed in distributed INSERT ... SELECT queries +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: generating subplan XXX_1 for CTE a: SELECT key, value FROM coordinator_shouldhaveshards.table_1 ORDER BY key, value DESC LIMIT 1 DEBUG: push down of limit count: 1 DEBUG: generating subplan XXX_2 for subquery SELECT int4(count(*)) AS auto_coerced_by_citus_0, (a.key)::text AS auto_coerced_by_citus_1 FROM ((SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) a JOIN coordinator_shouldhaveshards.table_2 USING (key)) GROUP BY a.key HAVING (max(table_2.value) OPERATOR(pg_catalog.>=) (SELECT a_1.value FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) a_1)) @@ -939,7 +939,8 @@ inserts AS ( ) SELECT count(*) FROM inserts; DEBUG: generating subplan XXX_1 for CTE stats: SELECT count(key) AS m FROM coordinator_shouldhaveshards.table_1 DEBUG: generating subplan XXX_2 for CTE inserts: INSERT INTO coordinator_shouldhaveshards.table_2 (key, value) SELECT key, count(*) AS count FROM coordinator_shouldhaveshards.table_1 WHERE (key OPERATOR(pg_catalog.>=) (SELECT stats.m FROM (SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m bigint)) stats)) GROUP BY key HAVING (count(*) OPERATOR(pg_catalog.<=) (SELECT stats.m FROM (SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m bigint)) stats)) LIMIT 1 RETURNING table_2.key, table_2.value -DEBUG: LIMIT clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Limit clause is currently unsupported when a subquery references a column from another query DEBUG: push down of limit count: 1 DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) inserts DEBUG: Subplan XXX_1 will be written to local file diff --git a/src/test/regress/expected/insert_select_repartition.out b/src/test/regress/expected/insert_select_repartition.out index bb77e9d47..b97a82b63 100644 --- a/src/test/regress/expected/insert_select_repartition.out +++ b/src/test/regress/expected/insert_select_repartition.out @@ -549,7 +549,7 @@ SELECT create_distributed_table('target_table', 'a'); INSERT INTO source_table SELECT floor(i/4), i*i FROM generate_series(1, 20) i; SET client_min_messages TO DEBUG1; INSERT INTO target_table SELECT a, max(b) FROM source_table GROUP BY a; -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: performing repartitioned INSERT ... SELECT RESET client_min_messages; SELECT * FROM target_table ORDER BY a; @@ -622,40 +622,40 @@ INSERT INTO target_table WHERE a BETWEEN $1 AND $2 GROUP BY a; SET client_min_messages TO DEBUG1; EXECUTE insert_plan(0, 2); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: performing repartitioned INSERT ... SELECT EXECUTE insert_plan(0, 2); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: performing repartitioned INSERT ... SELECT EXECUTE insert_plan(0, 2); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: performing repartitioned INSERT ... SELECT EXECUTE insert_plan(0, 2); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: performing repartitioned INSERT ... SELECT EXECUTE insert_plan(0, 2); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: performing repartitioned INSERT ... SELECT EXECUTE insert_plan(0, 2); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: performing repartitioned INSERT ... SELECT EXECUTE insert_plan(2, 4); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: performing repartitioned INSERT ... SELECT EXECUTE insert_plan(2, 4); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: performing repartitioned INSERT ... SELECT EXECUTE insert_plan(2, 4); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: performing repartitioned INSERT ... SELECT EXECUTE insert_plan(2, 4); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: performing repartitioned INSERT ... SELECT EXECUTE insert_plan(2, 4); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: performing repartitioned INSERT ... SELECT EXECUTE insert_plan(2, 4); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: performing repartitioned INSERT ... SELECT RESET client_min_messages; SELECT a, count(*), count(distinct b) distinct_values FROM target_table GROUP BY a ORDER BY a; @@ -680,25 +680,25 @@ INSERT INTO target_table WHERE a=$1 GROUP BY a; SET client_min_messages TO DEBUG1; EXECUTE insert_plan(0); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Collecting INSERT ... SELECT results on coordinator EXECUTE insert_plan(0); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Collecting INSERT ... SELECT results on coordinator EXECUTE insert_plan(0); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Collecting INSERT ... SELECT results on coordinator EXECUTE insert_plan(0); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Collecting INSERT ... SELECT results on coordinator EXECUTE insert_plan(0); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Collecting INSERT ... SELECT results on coordinator EXECUTE insert_plan(0); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Collecting INSERT ... SELECT results on coordinator EXECUTE insert_plan(0); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Collecting INSERT ... SELECT results on coordinator RESET client_min_messages; SELECT a, count(*), count(distinct b) distinct_values FROM target_table GROUP BY a ORDER BY a; @@ -761,10 +761,10 @@ WITH r AS ( INSERT INTO target_table SELECT * FROM source_table RETURNING * ) INSERT INTO target_table SELECT source_table.a, max(source_table.b) FROM source_table NATURAL JOIN r GROUP BY source_table.a; -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: only SELECT, UPDATE, or DELETE common table expressions may be router planned DEBUG: generating subplan XXX_1 for CTE r: INSERT INTO insert_select_repartition.target_table (a, b) SELECT source_table.a, source_table.b FROM insert_select_repartition.source_table RETURNING target_table.a, target_table.b -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Router planner cannot handle multi-shard select queries DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT a, max AS b FROM (SELECT source_table.a, max(source_table.b) AS max FROM (insert_select_repartition.source_table JOIN (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) r USING (a, b)) GROUP BY source_table.a) citus_insert_select_subquery DEBUG: Router planner cannot handle multi-shard select queries @@ -1015,7 +1015,7 @@ SELECT create_distributed_table('target_table', 'a'); INSERT INTO source_table SELECT i, i * i FROM generate_series(1, 10) i; SET client_min_messages TO DEBUG2; INSERT INTO target_table SELECT a AS aa, b AS aa, 1 AS aa, 2 AS aa FROM source_table; -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Router planner cannot handle multi-shard select queries DEBUG: performing repartitioned INSERT ... SELECT DEBUG: partitioning SELECT query by column index 0 with name 'a' @@ -1049,7 +1049,7 @@ EXPLAIN (costs off) INSERT INTO target_table SELECT a AS aa, b AS aa, 1 AS aa, 2 SET client_min_messages TO DEBUG2; INSERT INTO target_table SELECT a AS aa, b AS aa, 1 AS aa, 2 AS aa FROM source_table; -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Router planner cannot handle multi-shard select queries DEBUG: Collecting INSERT ... SELECT results on coordinator RESET client_min_messages; @@ -1104,7 +1104,7 @@ EXPLAIN (costs off) INSERT INTO test(y, x) SELECT a.x, b.y FROM test a JOIN test SET client_min_messages TO DEBUG1; INSERT INTO test(y, x) SELECT a.x, b.y FROM test a JOIN test b USING (y); -DEBUG: cannot perform distributed INSERT INTO ... SELECT because the partition columns in the source table and subquery do not match +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: Collecting INSERT ... SELECT results on coordinator RESET client_min_messages; SELECT count(*) FROM test; @@ -1133,7 +1133,7 @@ EXPLAIN (costs off) INSERT INTO test SELECT a.* FROM test a JOIN test b USING (y SET client_min_messages TO DEBUG1; INSERT INTO test SELECT a.* FROM test a JOIN test b USING (y); -DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: Collecting INSERT ... SELECT results on coordinator RESET client_min_messages; SELECT count(*) FROM test; @@ -1219,7 +1219,7 @@ ON CONFLICT(c1, c2, c3, c4, c5, c6) DO UPDATE SET cardinality = enriched.cardinality + excluded.cardinality, sum = enriched.sum + excluded.sum; -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Router planner cannot handle multi-shard select queries DEBUG: performing repartitioned INSERT ... SELECT DEBUG: partitioning SELECT query by column index 0 with name 'c1' @@ -1302,6 +1302,33 @@ explain (costs off) insert into table_with_user_sequences select y, x from table -> Seq Scan on table_with_user_sequences_4213652 table_with_user_sequences (8 rows) +CREATE TABLE dist_table_1(id int); +SELECT create_distributed_table('dist_table_1','id'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE dist_table_2(id int); +SELECT create_distributed_table('dist_table_2','id'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- verify that insert select with union can be repartitioned. We cannot push down the query +-- since UNION clause has no FROM clause at top level query. +SELECT public.coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_1(id) SELECT id FROM dist_table_1 UNION SELECT id FROM dist_table_2; +$$); + coordinator_plan +--------------------------------------------------------------------- + Custom Scan (Citus INSERT ... SELECT) + INSERT/SELECT method: repartition + -> Custom Scan (Citus Adaptive) + Task Count: 4 +(4 rows) + -- clean-up SET client_min_messages TO WARNING; DROP SCHEMA insert_select_repartition CASCADE; diff --git a/src/test/regress/expected/insert_select_repartition_0.out b/src/test/regress/expected/insert_select_repartition_0.out index 0aec4f49a..5bcb894cc 100644 --- a/src/test/regress/expected/insert_select_repartition_0.out +++ b/src/test/regress/expected/insert_select_repartition_0.out @@ -549,7 +549,7 @@ SELECT create_distributed_table('target_table', 'a'); INSERT INTO source_table SELECT floor(i/4), i*i FROM generate_series(1, 20) i; SET client_min_messages TO DEBUG1; INSERT INTO target_table SELECT a, max(b) FROM source_table GROUP BY a; -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: performing repartitioned INSERT ... SELECT RESET client_min_messages; SELECT * FROM target_table ORDER BY a; @@ -622,40 +622,40 @@ INSERT INTO target_table WHERE a BETWEEN $1 AND $2 GROUP BY a; SET client_min_messages TO DEBUG1; EXECUTE insert_plan(0, 2); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: performing repartitioned INSERT ... SELECT EXECUTE insert_plan(0, 2); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: performing repartitioned INSERT ... SELECT EXECUTE insert_plan(0, 2); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: performing repartitioned INSERT ... SELECT EXECUTE insert_plan(0, 2); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: performing repartitioned INSERT ... SELECT EXECUTE insert_plan(0, 2); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: performing repartitioned INSERT ... SELECT EXECUTE insert_plan(0, 2); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: performing repartitioned INSERT ... SELECT EXECUTE insert_plan(2, 4); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: performing repartitioned INSERT ... SELECT EXECUTE insert_plan(2, 4); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: performing repartitioned INSERT ... SELECT EXECUTE insert_plan(2, 4); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: performing repartitioned INSERT ... SELECT EXECUTE insert_plan(2, 4); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: performing repartitioned INSERT ... SELECT EXECUTE insert_plan(2, 4); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: performing repartitioned INSERT ... SELECT EXECUTE insert_plan(2, 4); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: performing repartitioned INSERT ... SELECT RESET client_min_messages; SELECT a, count(*), count(distinct b) distinct_values FROM target_table GROUP BY a ORDER BY a; @@ -680,25 +680,25 @@ INSERT INTO target_table WHERE a=$1 GROUP BY a; SET client_min_messages TO DEBUG1; EXECUTE insert_plan(0); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Collecting INSERT ... SELECT results on coordinator EXECUTE insert_plan(0); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Collecting INSERT ... SELECT results on coordinator EXECUTE insert_plan(0); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Collecting INSERT ... SELECT results on coordinator EXECUTE insert_plan(0); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Collecting INSERT ... SELECT results on coordinator EXECUTE insert_plan(0); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Collecting INSERT ... SELECT results on coordinator EXECUTE insert_plan(0); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Collecting INSERT ... SELECT results on coordinator EXECUTE insert_plan(0); -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Collecting INSERT ... SELECT results on coordinator RESET client_min_messages; SELECT a, count(*), count(distinct b) distinct_values FROM target_table GROUP BY a ORDER BY a; @@ -761,10 +761,10 @@ WITH r AS ( INSERT INTO target_table SELECT * FROM source_table RETURNING * ) INSERT INTO target_table SELECT source_table.a, max(source_table.b) FROM source_table NATURAL JOIN r GROUP BY source_table.a; -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: only SELECT, UPDATE, or DELETE common table expressions may be router planned DEBUG: generating subplan XXX_1 for CTE r: INSERT INTO insert_select_repartition.target_table (a, b) SELECT a, b FROM insert_select_repartition.source_table RETURNING target_table.a, target_table.b -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Router planner cannot handle multi-shard select queries DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT a, max AS b FROM (SELECT source_table.a, max(source_table.b) AS max FROM (insert_select_repartition.source_table JOIN (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) r USING (a, b)) GROUP BY source_table.a) citus_insert_select_subquery DEBUG: Router planner cannot handle multi-shard select queries @@ -1015,7 +1015,7 @@ SELECT create_distributed_table('target_table', 'a'); INSERT INTO source_table SELECT i, i * i FROM generate_series(1, 10) i; SET client_min_messages TO DEBUG2; INSERT INTO target_table SELECT a AS aa, b AS aa, 1 AS aa, 2 AS aa FROM source_table; -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Router planner cannot handle multi-shard select queries DEBUG: performing repartitioned INSERT ... SELECT DEBUG: partitioning SELECT query by column index 0 with name 'a' @@ -1049,7 +1049,7 @@ EXPLAIN (costs off) INSERT INTO target_table SELECT a AS aa, b AS aa, 1 AS aa, 2 SET client_min_messages TO DEBUG2; INSERT INTO target_table SELECT a AS aa, b AS aa, 1 AS aa, 2 AS aa FROM source_table; -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Router planner cannot handle multi-shard select queries DEBUG: Collecting INSERT ... SELECT results on coordinator RESET client_min_messages; @@ -1104,7 +1104,7 @@ EXPLAIN (costs off) INSERT INTO test(y, x) SELECT a.x, b.y FROM test a JOIN test SET client_min_messages TO DEBUG1; INSERT INTO test(y, x) SELECT a.x, b.y FROM test a JOIN test b USING (y); -DEBUG: cannot perform distributed INSERT INTO ... SELECT because the partition columns in the source table and subquery do not match +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: Collecting INSERT ... SELECT results on coordinator RESET client_min_messages; SELECT count(*) FROM test; @@ -1133,7 +1133,7 @@ EXPLAIN (costs off) INSERT INTO test SELECT a.* FROM test a JOIN test b USING (y SET client_min_messages TO DEBUG1; INSERT INTO test SELECT a.* FROM test a JOIN test b USING (y); -DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: Collecting INSERT ... SELECT results on coordinator RESET client_min_messages; SELECT count(*) FROM test; @@ -1219,7 +1219,7 @@ ON CONFLICT(c1, c2, c3, c4, c5, c6) DO UPDATE SET cardinality = enriched.cardinality + excluded.cardinality, sum = enriched.sum + excluded.sum; -DEBUG: INSERT target table and the source relation of the SELECT partition column value must be colocated in distributed INSERT ... SELECT +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Router planner cannot handle multi-shard select queries DEBUG: performing repartitioned INSERT ... SELECT DEBUG: partitioning SELECT query by column index 0 with name 'c1' @@ -1302,6 +1302,33 @@ explain (costs off) insert into table_with_user_sequences select y, x from table -> Seq Scan on table_with_user_sequences_4213652 table_with_user_sequences (8 rows) +CREATE TABLE dist_table_1(id int); +SELECT create_distributed_table('dist_table_1','id'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE dist_table_2(id int); +SELECT create_distributed_table('dist_table_2','id'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- verify that insert select with union can be repartitioned. We cannot push down the query +-- since UNION clause has no FROM clause at top level query. +SELECT public.coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_1(id) SELECT id FROM dist_table_1 UNION SELECT id FROM dist_table_2; +$$); + coordinator_plan +--------------------------------------------------------------------- + Custom Scan (Citus INSERT ... SELECT) + INSERT/SELECT method: repartition + -> Custom Scan (Citus Adaptive) + Task Count: 4 +(4 rows) + -- clean-up SET client_min_messages TO WARNING; DROP SCHEMA insert_select_repartition CASCADE; diff --git a/src/test/regress/expected/insert_select_single_shard_table.out b/src/test/regress/expected/insert_select_single_shard_table.out index 68391abb5..219e7d5d9 100644 --- a/src/test/regress/expected/insert_select_single_shard_table.out +++ b/src/test/regress/expected/insert_select_single_shard_table.out @@ -118,73 +118,82 @@ SET client_min_messages TO DEBUG2; -- different table types together with single-shard tables. -- use a single-shard table INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1; -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: cannot perform distributed INSERT INTO ... SELECT because the partition columns in the source table and subquery do not match +DETAIL: The target table's partition column should correspond to a partition column in the subquery. DEBUG: Distributed planning for a fast-path router query DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator -- use a reference table INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN reference_table USING (a); -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: cannot perform distributed INSERT INTO ... SELECT because the partition columns in the source table and subquery do not match +DETAIL: The target table's partition column should correspond to a partition column in the subquery. DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 RIGHT JOIN reference_table USING (b) WHERE reference_table.a >= 1 AND reference_table.a <= 5; -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: cannot perform a lateral outer join when a distributed subquery references a reference table DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 LEFT JOIN reference_table USING (b); -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: cannot perform distributed INSERT INTO ... SELECT because the partition columns in the source table and subquery do not match +DETAIL: The target table's partition column should correspond to a partition column in the subquery. DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 INTERSECT SELECT * FROM reference_table; -DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Intersect and Except are currently unsupported DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator -- use a colocated single-shard table INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN nullkey_c1_t2 USING (b); -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: cannot perform distributed INSERT INTO ... SELECT because the partition columns in the source table and subquery do not match +DETAIL: The target table's partition column should correspond to a partition column in the subquery. DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 FULL JOIN nullkey_c1_t2 USING (a); -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: cannot perform distributed INSERT INTO ... SELECT because the partition columns in the source table and subquery do not match +DETAIL: The target table's partition column should correspond to a partition column in the subquery. DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 FULL JOIN matview USING (a); -DEBUG: distributed INSERT ... SELECT cannot select from a local relation when inserting into a distributed table +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables DETAIL: Local tables cannot be used in distributed queries. INSERT INTO distributed_table_c1_t1 SELECT * FROM nullkey_c1_t1 UNION SELECT * FROM nullkey_c1_t2; -DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot perform distributed INSERT INTO ... SELECT because the partition columns in the source table and subquery do not match +DETAIL: The target table's partition column should correspond to a partition column in the subquery. DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator -- use a non-colocated single-shard table INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 LEFT JOIN nullkey_c2_t1 USING (a); -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: cannot perform distributed INSERT INTO ... SELECT because the partition columns in the source table and subquery do not match +DETAIL: The target table's partition column should correspond to a partition column in the subquery. ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables DETAIL: router planner does not support queries that reference non-colocated distributed tables INSERT INTO distributed_table_c1_t1 SELECT * FROM nullkey_c1_t1 UNION SELECT * FROM nullkey_c2_t1; -DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot perform distributed INSERT INTO ... SELECT because the partition columns in the source table and subquery do not match +DETAIL: The target table's partition column should correspond to a partition column in the subquery. ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables DETAIL: router planner does not support queries that reference non-colocated distributed tables -- use a distributed table that is colocated with the target table INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a); -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables DETAIL: router planner does not support queries that reference non-colocated distributed tables INSERT INTO distributed_table_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a); -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables DETAIL: router planner does not support queries that reference non-colocated distributed tables INSERT INTO distributed_table_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (b); -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables DETAIL: router planner does not support queries that reference non-colocated distributed tables INSERT INTO distributed_table_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a) WHERE distributed_table_c1_t2.a = 1; -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables DETAIL: router planner does not support queries that reference non-colocated distributed tables -- use a distributed table that is not colocated with the target table INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 JOIN distributed_table_c2_t1 USING (a); -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables DETAIL: router planner does not support queries that reference non-colocated distributed tables -- use a citus local table @@ -199,12 +208,14 @@ ERROR: queries that reference a distributed table without a shard key can only DETAIL: Local tables cannot be used in distributed queries. -- use append / range distributed tables INSERT INTO range_table SELECT * FROM nullkey_c1_t1; -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: cannot perform distributed INSERT INTO ... SELECT because the partition columns in the source table and subquery do not match +DETAIL: The target table's partition column should correspond to a partition column in the subquery. DEBUG: Distributed planning for a fast-path router query DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO append_table SELECT * FROM nullkey_c1_t1; -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: cannot perform distributed INSERT INTO ... SELECT because the partition columns in the source table and subquery do not match +DETAIL: The target table's partition column should correspond to a partition column in the subquery. ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables DETAIL: INSERT ... SELECT into an append-distributed table is not supported SELECT avg(a), avg(b) FROM distributed_table_c1_t1 ORDER BY 1, 2; @@ -236,11 +247,12 @@ DEBUG: only reference tables may be queried when targeting a reference table wi DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 UNION SELECT * FROM reference_table; -DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Reference tables are not supported with union operator DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO reference_table SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 LEFT JOIN reference_table USING (b) WHERE b IN (SELECT b FROM matview); -DEBUG: only reference tables may be queried when targeting a reference table with distributed INSERT ... SELECT +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables DETAIL: Local tables cannot be used in distributed queries. -- use a colocated single-shard table @@ -259,19 +271,19 @@ ERROR: queries that reference a distributed table without a shard key can only DETAIL: router planner does not support queries that reference non-colocated distributed tables -- use a distributed table INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a); -DEBUG: only reference tables may be queried when targeting a reference table with distributed INSERT ... SELECT +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables DETAIL: router planner does not support queries that reference non-colocated distributed tables INSERT INTO reference_table SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a); -DEBUG: only reference tables may be queried when targeting a reference table with distributed INSERT ... SELECT +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables DETAIL: router planner does not support queries that reference non-colocated distributed tables INSERT INTO reference_table SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (b); -DEBUG: only reference tables may be queried when targeting a reference table with distributed INSERT ... SELECT +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables DETAIL: router planner does not support queries that reference non-colocated distributed tables INSERT INTO reference_table SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a) WHERE distributed_table_c1_t2.a = 1; -DEBUG: only reference tables may be queried when targeting a reference table with distributed INSERT ... SELECT +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables DETAIL: router planner does not support queries that reference non-colocated distributed tables -- use a citus local table @@ -371,20 +383,20 @@ ERROR: queries that reference a distributed table without a shard key can only DETAIL: Local tables cannot be used in distributed queries. -- use a distributed table INSERT INTO nullkey_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM distributed_table_c1_t2; -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Router planner cannot handle multi-shard select queries DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO nullkey_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM distributed_table_c1_t2 JOIN reference_table USING (a); -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Router planner cannot handle multi-shard select queries DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO nullkey_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM distributed_table_c1_t2 JOIN nullkey_c1_t1 USING (a); -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables DETAIL: router planner does not support queries that reference non-colocated distributed tables -- use a non-colocated single-shard table INSERT INTO nullkey_c2_t1 SELECT q.* FROM (SELECT reference_table.* FROM reference_table LEFT JOIN nullkey_c1_t1 USING (a)) q JOIN nullkey_c1_t2 USING (a); -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: cannot perform a lateral outer join when a distributed subquery references a reference table DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator -- use a materialized view @@ -392,11 +404,11 @@ INSERT INTO nullkey_c1_t1 SELECT * FROM matview; DEBUG: distributed INSERT ... SELECT can only select from distributed tables DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO nullkey_c1_t1 SELECT reference_table.a, reference_table.b FROM reference_table JOIN matview ON (reference_table.a = matview.a); -DEBUG: distributed INSERT ... SELECT cannot select from a local relation when inserting into a distributed table +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO nullkey_c1_t1 SELECT q.* FROM (SELECT reference_table.* FROM reference_table JOIN nullkey_c1_t1 USING (a)) q JOIN matview USING (a); -DEBUG: distributed INSERT ... SELECT cannot select from a local relation when inserting into a distributed table +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables DETAIL: Local tables cannot be used in distributed queries. -- use append / range distributed tables @@ -405,7 +417,7 @@ DEBUG: Router planner cannot handle multi-shard select queries DEBUG: Router planner cannot handle multi-shard select queries DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO nullkey_c1_t1 SELECT * FROM append_table; -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Router planner does not support append-partitioned tables. DEBUG: Collecting INSERT ... SELECT results on coordinator SELECT avg(a), avg(b) FROM nullkey_c1_t1 ORDER BY 1, 2; @@ -462,8 +474,7 @@ cte_2 AS ( ) INSERT INTO distributed_table_c1_t1 SELECT cte_1.* FROM cte_1 JOIN cte_2 USING (a) JOIN distributed_table_c1_t2 USING (a) ORDER BY 1,2; -DEBUG: cannot perform distributed INSERT INTO ... SELECT because the partition columns in the source table and subquery do not match -DETAIL: The target table's partition column should correspond to a partition column in the subquery. +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: CTE cte_1 is going to be inlined via distributed planning DEBUG: CTE cte_2 is going to be inlined via distributed planning ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables @@ -479,7 +490,8 @@ cte_2 AS ( ) INSERT INTO distributed_table_c1_t1 SELECT * FROM cte_1 UNION SELECT * FROM cte_2 EXCEPT SELECT * FROM reference_table; -DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: CTEs in subqueries are currently unsupported DEBUG: CTE cte_1 is going to be inlined via distributed planning DEBUG: CTE cte_2 is going to be inlined via distributed planning DEBUG: Creating router plan @@ -491,7 +503,8 @@ JOIN ( SELECT b FROM nullkey_c1_t2 ORDER BY b DESC LIMIT 1 ) t2 ON t1.b < t2.b; -DEBUG: LIMIT clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Limit clause is currently unsupported when a subquery references a column from another query DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO distributed_table_c1_t1 (a, b) @@ -503,7 +516,9 @@ WITH cte AS ( ) SELECT d1, COALESCE(d2, a) FROM cte WHERE d1 IS NOT NULL AND d2 IS NOT NULL; DEBUG: CTE cte is going to be inlined via distributed planning -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: cannot perform distributed INSERT INTO ... SELECT because the partition columns in the source table and subquery do not match +DETAIL: Subquery contains an expression that is not a simple column reference in the same position as the target table's partition column. +HINT: Ensure the target table's partition column has a corresponding simple column reference to a distributed table's partition column in the subquery. DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO citus_local_table (a, b) @@ -523,7 +538,8 @@ LEFT JOIN ( FROM nullkey_c1_t1 ) t2 ON t1.b = t2.b WHERE t2.rn > 0; -DEBUG: Window functions without PARTITION BY on distribution column is currently unsupported +DEBUG: cannot push down this subquery +DETAIL: Window functions without PARTITION BY on distribution column is currently unsupported DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO nullkey_c1_t1 (a, b) @@ -537,7 +553,7 @@ JOIN ( ) q ) t2 ON t1.b = t2.b WHERE t2.rn > 2; -DEBUG: Window functions without PARTITION BY on distribution column is currently unsupported +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables DETAIL: router planner does not support queries that reference non-colocated distributed tables INSERT INTO distributed_table_c1_t1 (a, b) @@ -551,21 +567,23 @@ JOIN ( ) q ) t2 ON t1.b = t2.b WHERE t2.sum_val > 2; -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: cannot perform distributed INSERT INTO ... SELECT because the partition columns in the source table and subquery do not match +DETAIL: The target table's partition column should correspond to a partition column in the subquery. DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator +-- Temporaryly reduce the verbosity to avoid noise +-- in the output of the next query. +SET client_min_messages TO DEBUG1; -- MultiTaskRouterSelectQuerySupported() is unnecessarily restrictive -- about pushing down queries with DISTINCT ON clause even if the table -- doesn't have a shard key. See https://github.com/citusdata/citus/pull/6752. INSERT INTO nullkey_c1_t1 SELECT DISTINCT ON (a) a, b FROM nullkey_c1_t2; -DEBUG: DISTINCT ON (non-partition column) clauses are not allowed in distributed INSERT ... SELECT queries -DEBUG: Distributed planning for a fast-path router query -DEBUG: Creating router plan -DEBUG: Collecting INSERT ... SELECT results on coordinator +SET client_min_messages TO DEBUG2; -- Similarly, we could push down the following query as well. see -- https://github.com/citusdata/citus/pull/6831. INSERT INTO nullkey_c1_t1 SELECT b, SUM(a) OVER (ORDER BY b) AS sum_val FROM nullkey_c1_t1; -DEBUG: Window functions without PARTITION BY on distribution column is currently unsupported +DEBUG: cannot push down this subquery +DETAIL: Window functions without PARTITION BY on distribution column is currently unsupported DEBUG: Distributed planning for a fast-path router query DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator @@ -576,7 +594,7 @@ JOIN reference_table AS t3 ON (t2.a = t3.a) WHERE NOT EXISTS ( SELECT 1 FROM nullkey_c1_t2 AS t1 WHERE t1.b = t3.b ); -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO distributed_table_c1_t1 @@ -585,7 +603,7 @@ FROM nullkey_c1_t1 AS t1 WHERE t1.a NOT IN ( SELECT DISTINCT t2.a FROM distributed_table_c1_t2 AS t2 ); -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables DETAIL: Router planner cannot handle multi-shard select queries INSERT INTO distributed_table_c1_t1 @@ -598,7 +616,8 @@ JOIN ( SELECT a FROM nullkey_c1_t2 ) AS t2 ) AS t3 ON t1.a = t3.a; -DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot perform distributed INSERT INTO ... SELECT because the partition columns in the source table and subquery do not match +DETAIL: The target table's partition column should correspond to a partition column in the subquery. DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator -- Temporaryly reduce the verbosity to avoid noise @@ -617,7 +636,7 @@ WHERE t1.a IN ( ) AS t4 ON t3.a = t4.a ) AS t2 ); -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: correlated subqueries are not supported when the FROM clause contains a reference table DEBUG: generating subplan XXX_1 for subquery SELECT a FROM (SELECT t3.a FROM ((SELECT distributed_table_c1_t1.a FROM insert_select_single_shard_table.distributed_table_c1_t1 WHERE (distributed_table_c1_t1.b OPERATOR(pg_catalog.>) 4)) t3 JOIN (SELECT distributed_table_c1_t2.a FROM insert_select_single_shard_table.distributed_table_c1_t2 WHERE (distributed_table_c1_t2.b OPERATOR(pg_catalog.<) 7)) t4 ON ((t3.a OPERATOR(pg_catalog.=) t4.a)))) t2 DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT a, b FROM insert_select_single_shard_table.reference_table t1 WHERE (a OPERATOR(pg_catalog.=) ANY (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer))) DEBUG: Collecting INSERT ... SELECT results on coordinator @@ -736,16 +755,17 @@ DEBUG: distributed statement: INSERT INTO insert_select_single_shard_table.null SET client_min_messages TO DEBUG1; INSERT INTO distributed_table_c1_t1 AS t1 (a, b) SELECT t3.a, t3.b FROM nullkey_c1_t2 t2 JOIN reference_table t3 ON (t2.a = t3.a) ON CONFLICT (a, b) DO UPDATE SET b = t1.b + 10; -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: cannot perform distributed INSERT INTO ... SELECT because the partition columns in the source table and subquery do not match +DETAIL: The target table's partition column should correspond to a partition column in the subquery. DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO nullkey_c1_t1 AS t1 (a, b) SELECT t3.a, t3.b FROM distributed_table_c1_t1 t2 JOIN reference_table t3 ON (t2.a = t3.a) ON CONFLICT (a) DO UPDATE SET a = t1.a + 10; -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Collecting INSERT ... SELECT results on coordinator -- This also fails due to https://github.com/citusdata/citus/issues/6826. INSERT INTO nullkey_c1_t1 AS t1 (a, b) SELECT t3.a, t3.b FROM distributed_table_c1_t1 t2 JOIN reference_table t3 ON (t2.a = t3.a) WHERE t2.a = 3 ON CONFLICT (a) DO UPDATE SET a = (SELECT max(b)+1 FROM distributed_table_c1_t1 WHERE a = 3); -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: Collecting INSERT ... SELECT results on coordinator ERROR: cannot execute a distributed query from a query on a shard DETAIL: Executing a distributed query in a function call that may be pushed to a remote node can lead to incorrect results. diff --git a/src/test/regress/expected/intermediate_result_pruning.out b/src/test/regress/expected/intermediate_result_pruning.out index f6cf8c1e1..e178765a8 100644 --- a/src/test/regress/expected/intermediate_result_pruning.out +++ b/src/test/regress/expected/intermediate_result_pruning.out @@ -762,7 +762,8 @@ ROLLBACK; -- We use offset 1 to make sure the result needs to be pulled to the coordinator, offset 0 would be optimized away INSERT INTO table_1 SELECT * FROM table_2 OFFSET 1; -DEBUG: OFFSET clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Offset clause is currently unsupported when a subquery references a column from another query DEBUG: Collecting INSERT ... SELECT results on coordinator -- INSERT .. SELECT via coordinator which has intermediate result, -- and can be pruned to a single worker because the final query is on @@ -793,7 +794,7 @@ INSERT INTO table_1 SELECT * FROM cte_1 UNION SELECT * FROM cte_2); -DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT table_1.key FROM intermediate_result_pruning.table_1 WHERE (table_1.key OPERATOR(pg_catalog.=) 1) INTERSECT SELECT table_1.key FROM intermediate_result_pruning.table_1 WHERE (table_1.key OPERATOR(pg_catalog.=) 2) DEBUG: generating subplan XXX_1 for subquery SELECT key FROM intermediate_result_pruning.table_1 WHERE (key OPERATOR(pg_catalog.=) 1) DEBUG: generating subplan XXX_2 for subquery SELECT key FROM intermediate_result_pruning.table_1 WHERE (key OPERATOR(pg_catalog.=) 2) @@ -830,7 +831,7 @@ INSERT INTO table_1 ) foo where table_2.key != 1 AND foo.key = table_2.value::int; -DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT table_1.key FROM intermediate_result_pruning.table_1 WHERE (table_1.key OPERATOR(pg_catalog.=) 1) INTERSECT SELECT table_1.key FROM intermediate_result_pruning.table_1 WHERE (table_1.key OPERATOR(pg_catalog.=) 2) DEBUG: generating subplan XXX_1 for subquery SELECT key FROM intermediate_result_pruning.table_1 WHERE (key OPERATOR(pg_catalog.=) 1) DEBUG: generating subplan XXX_2 for subquery SELECT key FROM intermediate_result_pruning.table_1 WHERE (key OPERATOR(pg_catalog.=) 2) @@ -1054,7 +1055,8 @@ inserts AS MATERIALIZED ( ) SELECT count(*) FROM inserts; DEBUG: generating subplan XXX_1 for CTE stats: SELECT count(key) AS m FROM intermediate_result_pruning.table_3 DEBUG: generating subplan XXX_2 for CTE inserts: INSERT INTO intermediate_result_pruning.table_2 (key, value) SELECT table_1.key, count(*) AS count FROM intermediate_result_pruning.table_1 WHERE (table_1.key OPERATOR(pg_catalog.>) (SELECT stats.m FROM (SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m bigint)) stats)) GROUP BY table_1.key HAVING (count(*) OPERATOR(pg_catalog.<) (SELECT stats.m FROM (SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m bigint)) stats)) LIMIT 1 RETURNING table_2.key, table_2.value -DEBUG: LIMIT clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Limit clause is currently unsupported when a subquery references a column from another query DEBUG: push down of limit count: 1 DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) inserts DEBUG: Subplan XXX_1 will be written to local file diff --git a/src/test/regress/expected/intermediate_result_pruning_0.out b/src/test/regress/expected/intermediate_result_pruning_0.out index 4ae6b8e16..ec4b489d0 100644 --- a/src/test/regress/expected/intermediate_result_pruning_0.out +++ b/src/test/regress/expected/intermediate_result_pruning_0.out @@ -762,7 +762,8 @@ ROLLBACK; -- We use offset 1 to make sure the result needs to be pulled to the coordinator, offset 0 would be optimized away INSERT INTO table_1 SELECT * FROM table_2 OFFSET 1; -DEBUG: OFFSET clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Offset clause is currently unsupported when a subquery references a column from another query DEBUG: Collecting INSERT ... SELECT results on coordinator -- INSERT .. SELECT via coordinator which has intermediate result, -- and can be pruned to a single worker because the final query is on @@ -793,7 +794,7 @@ INSERT INTO table_1 SELECT * FROM cte_1 UNION SELECT * FROM cte_2); -DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT table_1.key FROM intermediate_result_pruning.table_1 WHERE (table_1.key OPERATOR(pg_catalog.=) 1) INTERSECT SELECT table_1.key FROM intermediate_result_pruning.table_1 WHERE (table_1.key OPERATOR(pg_catalog.=) 2) DEBUG: generating subplan XXX_1 for subquery SELECT key FROM intermediate_result_pruning.table_1 WHERE (key OPERATOR(pg_catalog.=) 1) DEBUG: generating subplan XXX_2 for subquery SELECT key FROM intermediate_result_pruning.table_1 WHERE (key OPERATOR(pg_catalog.=) 2) @@ -830,7 +831,7 @@ INSERT INTO table_1 ) foo where table_2.key != 1 AND foo.key = table_2.value::int; -DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT table_1.key FROM intermediate_result_pruning.table_1 WHERE (table_1.key OPERATOR(pg_catalog.=) 1) INTERSECT SELECT table_1.key FROM intermediate_result_pruning.table_1 WHERE (table_1.key OPERATOR(pg_catalog.=) 2) DEBUG: generating subplan XXX_1 for subquery SELECT key FROM intermediate_result_pruning.table_1 WHERE (key OPERATOR(pg_catalog.=) 1) DEBUG: generating subplan XXX_2 for subquery SELECT key FROM intermediate_result_pruning.table_1 WHERE (key OPERATOR(pg_catalog.=) 2) @@ -1054,7 +1055,8 @@ inserts AS MATERIALIZED ( ) SELECT count(*) FROM inserts; DEBUG: generating subplan XXX_1 for CTE stats: SELECT count(key) AS m FROM intermediate_result_pruning.table_3 DEBUG: generating subplan XXX_2 for CTE inserts: INSERT INTO intermediate_result_pruning.table_2 (key, value) SELECT key, count(*) AS count FROM intermediate_result_pruning.table_1 WHERE (key OPERATOR(pg_catalog.>) (SELECT stats.m FROM (SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m bigint)) stats)) GROUP BY key HAVING (count(*) OPERATOR(pg_catalog.<) (SELECT stats.m FROM (SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m bigint)) stats)) LIMIT 1 RETURNING table_2.key, table_2.value -DEBUG: LIMIT clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Limit clause is currently unsupported when a subquery references a column from another query DEBUG: push down of limit count: 1 DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) inserts DEBUG: Subplan XXX_1 will be written to local file diff --git a/src/test/regress/expected/isolation_select_vs_all.out b/src/test/regress/expected/isolation_select_vs_all.out index 0485dba78..21be42915 100644 --- a/src/test/regress/expected/isolation_select_vs_all.out +++ b/src/test/regress/expected/isolation_select_vs_all.out @@ -341,29 +341,6 @@ count (1 row) -starting permutation: s1-initialize s1-begin s1-router-select s2-insert-select s1-commit s1-select-count -master_create_empty_shard ---------------------------------------------------------------------- - 6780300 -(1 row) - -step s1-initialize: COPY select_append FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH (format 'csv', append_to_shard xxxxx); -step s1-begin: BEGIN; -step s1-router-select: SELECT * FROM select_append WHERE id = 1; -id|data|int_data ---------------------------------------------------------------------- - 1| b | 1 -(1 row) - -step s2-insert-select: INSERT INTO select_append SELECT * FROM select_append; -step s1-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM select_append; -count ---------------------------------------------------------------------- - 10 -(1 row) - - starting permutation: s1-initialize s1-begin s1-router-select s2-update s1-commit s1-select-count master_create_empty_shard --------------------------------------------------------------------- @@ -770,29 +747,6 @@ count (1 row) -starting permutation: s1-initialize s1-begin s1-insert-select s2-router-select s1-commit s1-select-count -master_create_empty_shard ---------------------------------------------------------------------- - 6780300 -(1 row) - -step s1-initialize: COPY select_append FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH (format 'csv', append_to_shard xxxxx); -step s1-begin: BEGIN; -step s1-insert-select: INSERT INTO select_append SELECT * FROM select_append; -step s2-router-select: SELECT * FROM select_append WHERE id = 1; -id|data|int_data ---------------------------------------------------------------------- - 1| b | 1 -(1 row) - -step s1-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM select_append; -count ---------------------------------------------------------------------- - 10 -(1 row) - - starting permutation: s1-initialize s1-begin s1-update s2-router-select s1-commit s1-select-count master_create_empty_shard --------------------------------------------------------------------- @@ -1162,33 +1116,6 @@ count (1 row) -starting permutation: s1-initialize s1-begin s1-real-time-select s2-insert-select s1-commit s1-select-count -master_create_empty_shard ---------------------------------------------------------------------- - 6780300 -(1 row) - -step s1-initialize: COPY select_append FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH (format 'csv', append_to_shard xxxxx); -step s1-begin: BEGIN; -step s1-real-time-select: SELECT * FROM select_append ORDER BY 1, 2; -id|data|int_data ---------------------------------------------------------------------- - 0| a | 0 - 1| b | 1 - 2| c | 2 - 3| d | 3 - 4| e | 4 -(5 rows) - -step s2-insert-select: INSERT INTO select_append SELECT * FROM select_append; -step s1-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM select_append; -count ---------------------------------------------------------------------- - 10 -(1 row) - - starting permutation: s1-initialize s1-begin s1-real-time-select s2-update s1-commit s1-select-count master_create_empty_shard --------------------------------------------------------------------- @@ -1621,33 +1548,6 @@ count (1 row) -starting permutation: s1-initialize s1-begin s1-insert-select s2-real-time-select s1-commit s1-select-count -master_create_empty_shard ---------------------------------------------------------------------- - 6780300 -(1 row) - -step s1-initialize: COPY select_append FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH (format 'csv', append_to_shard xxxxx); -step s1-begin: BEGIN; -step s1-insert-select: INSERT INTO select_append SELECT * FROM select_append; -step s2-real-time-select: SELECT * FROM select_append ORDER BY 1, 2; -id|data|int_data ---------------------------------------------------------------------- - 0| a | 0 - 1| b | 1 - 2| c | 2 - 3| d | 3 - 4| e | 4 -(5 rows) - -step s1-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM select_append; -count ---------------------------------------------------------------------- - 10 -(1 row) - - starting permutation: s1-initialize s1-begin s1-update s2-real-time-select s1-commit s1-select-count master_create_empty_shard --------------------------------------------------------------------- @@ -2034,36 +1934,6 @@ count (1 row) -starting permutation: s1-initialize s1-begin s1-adaptive-select s2-insert-select s1-commit s1-select-count -master_create_empty_shard ---------------------------------------------------------------------- - 6780300 -(1 row) - -step s1-initialize: COPY select_append FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH (format 'csv', append_to_shard xxxxx); -step s1-begin: BEGIN; -step s1-adaptive-select: - SET citus.enable_repartition_joins TO ON; - SELECT * FROM select_append AS t1 JOIN select_append AS t2 ON t1.id = t2.int_data ORDER BY 1, 2, 3, 4; - -id|data|int_data|id|data|int_data ---------------------------------------------------------------------- - 0| a | 0| 0| a | 0 - 1| b | 1| 1| b | 1 - 2| c | 2| 2| c | 2 - 3| d | 3| 3| d | 3 - 4| e | 4| 4| e | 4 -(5 rows) - -step s2-insert-select: INSERT INTO select_append SELECT * FROM select_append; -step s1-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM select_append; -count ---------------------------------------------------------------------- - 10 -(1 row) - - starting permutation: s1-initialize s1-begin s1-adaptive-select s2-update s1-commit s1-select-count master_create_empty_shard --------------------------------------------------------------------- @@ -2538,36 +2408,6 @@ count (1 row) -starting permutation: s1-initialize s1-begin s1-insert-select s2-adaptive-select s1-commit s1-select-count -master_create_empty_shard ---------------------------------------------------------------------- - 6780300 -(1 row) - -step s1-initialize: COPY select_append FROM PROGRAM 'echo 0, a, 0 && echo 1, b, 1 && echo 2, c, 2 && echo 3, d, 3 && echo 4, e, 4' WITH (format 'csv', append_to_shard xxxxx); -step s1-begin: BEGIN; -step s1-insert-select: INSERT INTO select_append SELECT * FROM select_append; -step s2-adaptive-select: - SET citus.enable_repartition_joins TO ON; - SELECT * FROM select_append AS t1 JOIN select_append AS t2 ON t1.id = t2.int_data ORDER BY 1, 2, 3, 4; - -id|data|int_data|id|data|int_data ---------------------------------------------------------------------- - 0| a | 0| 0| a | 0 - 1| b | 1| 1| b | 1 - 2| c | 2| 2| c | 2 - 3| d | 3| 3| d | 3 - 4| e | 4| 4| e | 4 -(5 rows) - -step s1-commit: COMMIT; -step s1-select-count: SELECT COUNT(*) FROM select_append; -count ---------------------------------------------------------------------- - 10 -(1 row) - - starting permutation: s1-initialize s1-begin s1-update s2-adaptive-select s1-commit s1-select-count master_create_empty_shard --------------------------------------------------------------------- diff --git a/src/test/regress/expected/multi_insert_select.out b/src/test/regress/expected/multi_insert_select.out index ed836d398..abebf314e 100644 --- a/src/test/regress/expected/multi_insert_select.out +++ b/src/test/regress/expected/multi_insert_select.out @@ -620,7 +620,8 @@ INSERT INTO agg_events (value_1_agg, user_id) DISTINCT ON (value_1) value_1, user_id FROM raw_events_first; -DEBUG: DISTINCT ON (non-partition column) clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Distinct on columns without partition column is currently unsupported DEBUG: Router planner cannot handle multi-shard select queries DEBUG: Collecting INSERT ... SELECT results on coordinator SELECT user_id, value_1_agg FROM agg_events ORDER BY 1,2; @@ -686,7 +687,7 @@ DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT user_id, v1_a DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator ROLLBACK; --- We don't support CTEs that are referenced in the target list +-- We do support CTEs that are referenced in the target list INSERT INTO agg_events WITH sub_cte AS (SELECT 1) SELECT @@ -694,15 +695,11 @@ INSERT INTO agg_events FROM raw_events_first; DEBUG: CTE sub_cte is going to be inlined via distributed planning -DEBUG: Subqueries without relations are not allowed in distributed INSERT ... SELECT queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: performing repartitioned INSERT ... SELECT -DEBUG: partitioning SELECT query by column index 0 with name 'user_id' -DEBUG: distributed statement: INSERT INTO multi_insert_select.agg_events_13300008 AS citus_table_alias (user_id, value_1_agg) SELECT intermediate_result.user_id, intermediate_result.value_1_agg FROM read_intermediate_results('{repartitioned_results_xxxxx_from_13300000_to_0}'::text[], 'binary'::citus_copy_format) intermediate_result(user_id integer, value_1_agg integer) -DEBUG: distributed statement: INSERT INTO multi_insert_select.agg_events_13300009 AS citus_table_alias (user_id, value_1_agg) SELECT intermediate_result.user_id, intermediate_result.value_1_agg FROM read_intermediate_results('{repartitioned_results_xxxxx_from_13300001_to_1}'::text[], 'binary'::citus_copy_format) intermediate_result(user_id integer, value_1_agg integer) -DEBUG: distributed statement: INSERT INTO multi_insert_select.agg_events_13300010 AS citus_table_alias (user_id, value_1_agg) SELECT intermediate_result.user_id, intermediate_result.value_1_agg FROM read_intermediate_results('{repartitioned_results_xxxxx_from_13300002_to_2}'::text[], 'binary'::citus_copy_format) intermediate_result(user_id integer, value_1_agg integer) -DEBUG: distributed statement: INSERT INTO multi_insert_select.agg_events_13300011 AS citus_table_alias (user_id, value_1_agg) SELECT intermediate_result.user_id, intermediate_result.value_1_agg FROM read_intermediate_results('{repartitioned_results_xxxxx_from_13300003_to_3}'::text[], 'binary'::citus_copy_format) intermediate_result(user_id integer, value_1_agg integer) --- We support set operations via the coordinator +DEBUG: distributed statement: INSERT INTO multi_insert_select.agg_events_13300008 AS citus_table_alias (user_id, value_1_agg) SELECT raw_events_first.user_id, (SELECT sub_cte."?column?" FROM (SELECT 1) sub_cte("?column?")) FROM multi_insert_select.raw_events_first_13300000 raw_events_first WHERE (raw_events_first.user_id IS NOT NULL) +DEBUG: distributed statement: INSERT INTO multi_insert_select.agg_events_13300009 AS citus_table_alias (user_id, value_1_agg) SELECT raw_events_first.user_id, (SELECT sub_cte."?column?" FROM (SELECT 1) sub_cte("?column?")) FROM multi_insert_select.raw_events_first_13300001 raw_events_first WHERE (raw_events_first.user_id IS NOT NULL) +DEBUG: distributed statement: INSERT INTO multi_insert_select.agg_events_13300010 AS citus_table_alias (user_id, value_1_agg) SELECT raw_events_first.user_id, (SELECT sub_cte."?column?" FROM (SELECT 1) sub_cte("?column?")) FROM multi_insert_select.raw_events_first_13300002 raw_events_first WHERE (raw_events_first.user_id IS NOT NULL) +DEBUG: distributed statement: INSERT INTO multi_insert_select.agg_events_13300011 AS citus_table_alias (user_id, value_1_agg) SELECT raw_events_first.user_id, (SELECT sub_cte."?column?" FROM (SELECT 1) sub_cte("?column?")) FROM multi_insert_select.raw_events_first_13300003 raw_events_first WHERE (raw_events_first.user_id IS NOT NULL) +-- We support set operations BEGIN; INSERT INTO raw_events_first(user_id) @@ -711,14 +708,10 @@ SELECT FROM ((SELECT user_id FROM raw_events_first) UNION (SELECT user_id FROM raw_events_second)) as foo; -DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: performing repartitioned INSERT ... SELECT -DEBUG: partitioning SELECT query by column index 0 with name 'user_id' -DEBUG: distributed statement: INSERT INTO multi_insert_select.raw_events_first_13300000 AS citus_table_alias (user_id) SELECT intermediate_result.user_id FROM read_intermediate_results('{repartitioned_results_xxxxx_from_13300004_to_0}'::text[], 'binary'::citus_copy_format) intermediate_result(user_id integer) -DEBUG: distributed statement: INSERT INTO multi_insert_select.raw_events_first_13300001 AS citus_table_alias (user_id) SELECT intermediate_result.user_id FROM read_intermediate_results('{repartitioned_results_xxxxx_from_13300005_to_1}'::text[], 'binary'::citus_copy_format) intermediate_result(user_id integer) -DEBUG: distributed statement: INSERT INTO multi_insert_select.raw_events_first_13300002 AS citus_table_alias (user_id) SELECT intermediate_result.user_id FROM read_intermediate_results('{repartitioned_results_xxxxx_from_13300006_to_2}'::text[], 'binary'::citus_copy_format) intermediate_result(user_id integer) -DEBUG: distributed statement: INSERT INTO multi_insert_select.raw_events_first_13300003 AS citus_table_alias (user_id) SELECT intermediate_result.user_id FROM read_intermediate_results('{repartitioned_results_xxxxx_from_13300007_to_3}'::text[], 'binary'::citus_copy_format) intermediate_result(user_id integer) +DEBUG: distributed statement: INSERT INTO multi_insert_select.raw_events_first_13300000 AS citus_table_alias (user_id) SELECT foo.user_id FROM (SELECT raw_events_first.user_id FROM multi_insert_select.raw_events_first_13300000 raw_events_first UNION SELECT raw_events_second.user_id FROM multi_insert_select.raw_events_second_13300004 raw_events_second) foo WHERE (foo.user_id IS NOT NULL) +DEBUG: distributed statement: INSERT INTO multi_insert_select.raw_events_first_13300001 AS citus_table_alias (user_id) SELECT foo.user_id FROM (SELECT raw_events_first.user_id FROM multi_insert_select.raw_events_first_13300001 raw_events_first UNION SELECT raw_events_second.user_id FROM multi_insert_select.raw_events_second_13300005 raw_events_second) foo WHERE (foo.user_id IS NOT NULL) +DEBUG: distributed statement: INSERT INTO multi_insert_select.raw_events_first_13300002 AS citus_table_alias (user_id) SELECT foo.user_id FROM (SELECT raw_events_first.user_id FROM multi_insert_select.raw_events_first_13300002 raw_events_first UNION SELECT raw_events_second.user_id FROM multi_insert_select.raw_events_second_13300006 raw_events_second) foo WHERE (foo.user_id IS NOT NULL) +DEBUG: distributed statement: INSERT INTO multi_insert_select.raw_events_first_13300003 AS citus_table_alias (user_id) SELECT foo.user_id FROM (SELECT raw_events_first.user_id FROM multi_insert_select.raw_events_first_13300003 raw_events_first UNION SELECT raw_events_second.user_id FROM multi_insert_select.raw_events_second_13300007 raw_events_second) foo WHERE (foo.user_id IS NOT NULL) ROLLBACK; -- We do support set operations through recursive planning BEGIN; @@ -727,7 +720,7 @@ INSERT INTO raw_events_first(user_id) (SELECT user_id FROM raw_events_first) INTERSECT (SELECT user_id FROM raw_events_first); -DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: Router planner cannot handle multi-shard select queries DEBUG: Router planner cannot handle multi-shard select queries DEBUG: generating subplan XXX_1 for subquery SELECT user_id FROM multi_insert_select.raw_events_first @@ -747,7 +740,7 @@ SELECT FROM ((SELECT user_id FROM raw_events_first WHERE user_id = 15) EXCEPT (SELECT user_id FROM raw_events_second where user_id = 17)) as foo; -DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator -- some supported LEFT joins @@ -765,10 +758,16 @@ DEBUG: distributed statement: INSERT INTO multi_insert_select.agg_events_133000 raw_events_second.user_id FROM reference_table LEFT JOIN raw_events_second ON reference_table.user_id = raw_events_second.user_id; -DEBUG: distributed statement: INSERT INTO multi_insert_select.agg_events_13300008 AS citus_table_alias (user_id) SELECT raw_events_second.user_id FROM (multi_insert_select.reference_table_13300012 reference_table LEFT JOIN multi_insert_select.raw_events_second_13300004 raw_events_second ON ((reference_table.user_id OPERATOR(pg_catalog.=) raw_events_second.user_id))) WHERE (raw_events_second.user_id IS NOT NULL) -DEBUG: distributed statement: INSERT INTO multi_insert_select.agg_events_13300009 AS citus_table_alias (user_id) SELECT raw_events_second.user_id FROM (multi_insert_select.reference_table_13300012 reference_table LEFT JOIN multi_insert_select.raw_events_second_13300005 raw_events_second ON ((reference_table.user_id OPERATOR(pg_catalog.=) raw_events_second.user_id))) WHERE (raw_events_second.user_id IS NOT NULL) -DEBUG: distributed statement: INSERT INTO multi_insert_select.agg_events_13300010 AS citus_table_alias (user_id) SELECT raw_events_second.user_id FROM (multi_insert_select.reference_table_13300012 reference_table LEFT JOIN multi_insert_select.raw_events_second_13300006 raw_events_second ON ((reference_table.user_id OPERATOR(pg_catalog.=) raw_events_second.user_id))) WHERE (raw_events_second.user_id IS NOT NULL) -DEBUG: distributed statement: INSERT INTO multi_insert_select.agg_events_13300011 AS citus_table_alias (user_id) SELECT raw_events_second.user_id FROM (multi_insert_select.reference_table_13300012 reference_table LEFT JOIN multi_insert_select.raw_events_second_13300007 raw_events_second ON ((reference_table.user_id OPERATOR(pg_catalog.=) raw_events_second.user_id))) WHERE (raw_events_second.user_id IS NOT NULL) +DEBUG: cannot perform a lateral outer join when a distributed subquery references a reference table +DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: recursively planning right side of the left join since the outer side is a recurring rel +DEBUG: recursively planning distributed relation "raw_events_second" since it is part of a distributed join node that is outer joined with a recurring rel +DEBUG: Wrapping relation "raw_events_second" to a subquery +DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: generating subplan XXX_1 for subquery SELECT user_id FROM multi_insert_select.raw_events_second WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT raw_events_second.user_id FROM (multi_insert_select.reference_table LEFT JOIN (SELECT raw_events_second_1.user_id, NULL::timestamp without time zone AS "time", NULL::integer AS value_1, NULL::integer AS value_2, NULL::double precision AS value_3, NULL::bigint AS value_4 FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) raw_events_second_1) raw_events_second ON ((reference_table.user_id OPERATOR(pg_catalog.=) raw_events_second.user_id))) +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO agg_events (user_id) SELECT raw_events_first.user_id @@ -1126,7 +1125,8 @@ FROM (SELECT SUM(raw_events_second.value_4) AS v4, raw_events_second WHERE raw_events_first.user_id = raw_events_second.user_id GROUP BY raw_events_second.value_3) AS foo; -DEBUG: Group by list without distribution column is not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Group by list without partition column is currently unsupported when a subquery references a column from another query DEBUG: Router planner cannot handle multi-shard select queries DEBUG: Router planner cannot handle multi-shard select queries DEBUG: join prunable for intervals [-2147483648,-1073741825] and [-1073741824,-1] @@ -1281,7 +1281,7 @@ FROM (SELECT SUM(raw_events_second.value_4) AS v4, GROUP BY raw_events_second.value_1 HAVING SUM(raw_events_second.value_4) > 10) AS foo2 ) as f2 ON (f.id = f2.id); -DEBUG: Group by list without distribution column is not allowed in distributed INSERT ... SELECT queries +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: Router planner cannot handle multi-shard select queries DEBUG: Router planner cannot handle multi-shard select queries DEBUG: join prunable for intervals [-2147483648,-1073741825] and [-1073741824,-1] @@ -1327,7 +1327,7 @@ FROM (SELECT SUM(raw_events_second.value_4) AS v4, GROUP BY raw_events_second.value_1 HAVING SUM(raw_events_second.value_4) > 10) AS foo2 ) as f2 ON (f.id = f2.id); -DEBUG: Group by list without distribution column is not allowed in distributed INSERT ... SELECT queries +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: Router planner cannot handle multi-shard select queries DEBUG: Router planner cannot handle multi-shard select queries DEBUG: join prunable for intervals [-2147483648,-1073741825] and [-1073741824,-1] @@ -3242,5 +3242,236 @@ returning text_col_1; string (1 row) +CREATE TABLE dist_table_3( +dist_col bigint, +int_col integer +); +SELECT create_distributed_table('dist_table_3', 'dist_col'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- dist_table_2 and dist_table_3 are non-colocated source tables. Repartitioning is also not possible due to +-- different types for distribution columns. Citus would not be able to handle this complex insert select. +INSERT INTO dist_table_1 SELECT dist_table_2.dist_col FROM dist_table_2 JOIN dist_table_3 USING(dist_col); +ERROR: complex joins are only supported when all distributed tables are joined on their distribution columns with equal operator +CREATE TABLE dist_table_4( +dist_col integer, +int_col integer +); +SELECT create_distributed_table('dist_table_4', 'dist_col'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- Even if target table distribution column is colocated with dist_table_2's distributed column, source tables dist_table_2 and dist_table_4 +-- are non-colocated. Hence, SELECT part of the query should be pulled to coordinator. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_1 SELECT dist_table_2.dist_col FROM dist_table_2 JOIN dist_table_4 ON dist_table_2.dist_col = dist_table_4.int_col; +$$); + coordinator_plan +--------------------------------------------------------------------- + Custom Scan (Citus INSERT ... SELECT) + INSERT/SELECT method: pull to coordinator + -> Custom Scan (Citus Adaptive) + Task Count: 4 +(4 rows) + +-- For INSERT SELECT, when a lateral query references an outer query, push-down is possible even if limit clause exists in the lateral query. +-- It is because subquery with limit does not need to be merged at coordinator as it is a lateral query. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_1 SELECT d1.dist_col FROM dist_table_1 d1 LEFT JOIN LATERAL (SELECT * FROM dist_table_2 d2 WHERE d1.dist_col = d2.dist_col LIMIT 3) dummy USING(dist_col); +$$); + coordinator_plan +--------------------------------------------------------------------- + Custom Scan (Citus Adaptive) + Task Count: 4 +(2 rows) + +-- For INSERT SELECT, when push-down is NOT possible when limit clause exists in a subquery at SELECT part of INSERT SELECT. +-- It is because the subquery with limit needs to be merged at coordinator. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_1 SELECT d1.dist_col FROM dist_table_1 d1 LEFT JOIN (SELECT * FROM dist_table_2 LIMIT 3) dummy USING(dist_col); +$$); + coordinator_plan +--------------------------------------------------------------------- + Custom Scan (Citus INSERT ... SELECT) + INSERT/SELECT method: repartition + -> Custom Scan (Citus Adaptive) + -> Distributed Subplan XXX_1 + -> Limit + -> Custom Scan (Citus Adaptive) + Task Count: 4 +(7 rows) + +CREATE TABLE dist_table_5(id int, id2 int); +SELECT create_distributed_table('dist_table_5','id'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE dist_table_6(id int, id2 int); +SELECT create_distributed_table('dist_table_6','id'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- verify that insert select with union can be pushed down since UNION clause has FROM clause at top level query. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_5(id) SELECT id FROM (SELECT id FROM dist_table_5 UNION SELECT id FROM dist_table_6) dummy; +$$); + coordinator_plan +--------------------------------------------------------------------- + Custom Scan (Citus Adaptive) + Task Count: 4 +(2 rows) + +-- verify that insert select with sublink can be pushed down when tables are colocated. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_5 SELECT id, (SELECT id FROM dist_table_5 WHERE dist_table_5.id = dist_table_6.id) FROM dist_table_6; +$$); + coordinator_plan +--------------------------------------------------------------------- + Custom Scan (Citus Adaptive) + Task Count: 4 +(2 rows) + +CREATE TABLE ref_table_1(id int); +SELECT create_reference_table('ref_table_1'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +-- verify that insert select with sublink cannot be pushed down when from clause does not contain any distributed relation. +INSERT INTO dist_table_5 SELECT id, (SELECT id FROM dist_table_5 WHERE dist_table_5.id = ref_table_1.id) FROM ref_table_1; +ERROR: correlated subqueries are not supported when the FROM clause contains a reference table +-- verify that insert select cannot be pushed down when we have recurring range table in from clause. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_5 SELECT id, (SELECT id FROM ref_table_1 WHERE id = 1) FROM ref_table_1; +$$); + coordinator_plan +--------------------------------------------------------------------- + Custom Scan (Citus INSERT ... SELECT) + INSERT/SELECT method: pull to coordinator + -> Custom Scan (Citus Adaptive) + Task Count: 1 +(4 rows) + +-- verify that insert select cannot be pushed down when we have reference table in outside of outer join. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_5 SELECT a.id FROM dist_table_5 a LEFT JOIN ref_table_1 b ON (true) RIGHT JOIN ref_table_1 c ON (true); +$$); + coordinator_plan +--------------------------------------------------------------------- + Custom Scan (Citus INSERT ... SELECT) + INSERT/SELECT method: pull to coordinator + -> Custom Scan (Citus Adaptive) + -> Distributed Subplan XXX_1 + -> Custom Scan (Citus Adaptive) + Task Count: 4 +(6 rows) + +-- verify that insert select cannot be pushed down when it has a recurring outer join in a subquery. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_5 SELECT id FROM ref_table_1 LEFT JOIN dist_table_5 USING(id); +$$); + coordinator_plan +--------------------------------------------------------------------- + Custom Scan (Citus INSERT ... SELECT) + INSERT/SELECT method: pull to coordinator + -> Custom Scan (Citus Adaptive) + -> Distributed Subplan XXX_1 + -> Custom Scan (Citus Adaptive) + Task Count: 4 +(6 rows) + +CREATE TABLE loc_table_1(id int); +-- verify that insert select cannot be pushed down when it contains join between local and distributed tables. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_5 SELECT id FROM dist_table_5 JOIN loc_table_1 USING(id); +$$); + coordinator_plan +--------------------------------------------------------------------- + Custom Scan (Citus INSERT ... SELECT) + INSERT/SELECT method: repartition + -> Custom Scan (Citus Adaptive) + -> Distributed Subplan XXX_1 + -> Seq Scan on loc_table_1 + Task Count: 4 +(6 rows) + +CREATE VIEW view_1 AS + SELECT id FROM dist_table_6; +CREATE MATERIALIZED VIEW view_2 AS + SELECT id FROM dist_table_6; +-- verify that insert select cannot be pushed down when it contains view. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_5 SELECT * FROM view_1; +$$); + coordinator_plan +--------------------------------------------------------------------- + Custom Scan (Citus Adaptive) + Task Count: 4 +(2 rows) + +-- verify that insert select cannot be pushed down when it contains materialized view. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_5 SELECT * FROM view_2; +$$); + coordinator_plan +--------------------------------------------------------------------- + Custom Scan (Citus INSERT ... SELECT) + INSERT/SELECT method: pull to coordinator + -> Seq Scan on view_2 +(3 rows) + +CREATE TABLE append_table(id integer, data text, int_data int); +SELECT create_distributed_table('append_table', 'id', 'append'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT master_create_empty_shard('append_table'); + master_create_empty_shard +--------------------------------------------------------------------- + 13300096 +(1 row) + +-- verify that insert select push down for append tables are not supported. +INSERT INTO append_table SELECT * FROM append_table; +ERROR: INSERT ... SELECT into an append-distributed table is not supported +-- verify that CTEs at top level of INSERT SELECT, that can normally be inlined, would not be inlined by INSERT SELECT pushdown planner +-- and handled by pull to coordinator. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) WITH cte_1 AS (SELECT id FROM dist_table_5 WHERE id = 5) + INSERT INTO dist_table_5 + SELECT id FROM dist_table_5 JOIN cte_1 USING(id); +$$); + coordinator_plan +--------------------------------------------------------------------- + Custom Scan (Citus INSERT ... SELECT) + INSERT/SELECT method: pull to coordinator + -> Custom Scan (Citus Adaptive) + Task Count: 1 +(4 rows) + +-- verify that CTEs at top level of SELECT part, would be inlined by Postgres and pushed down by INSERT SELECT planner. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_5 + WITH cte_1 AS (SELECT id FROM dist_table_5 WHERE id = 5) + SELECT id FROM dist_table_5 JOIN cte_1 USING(id); +$$); + coordinator_plan +--------------------------------------------------------------------- + Custom Scan (Citus Adaptive) + Task Count: 1 +(2 rows) + SET client_min_messages TO ERROR; DROP SCHEMA multi_insert_select CASCADE; diff --git a/src/test/regress/expected/multi_insert_select_0.out b/src/test/regress/expected/multi_insert_select_0.out index efc845e88..ee2341759 100644 --- a/src/test/regress/expected/multi_insert_select_0.out +++ b/src/test/regress/expected/multi_insert_select_0.out @@ -620,7 +620,8 @@ INSERT INTO agg_events (value_1_agg, user_id) DISTINCT ON (value_1) value_1, user_id FROM raw_events_first; -DEBUG: DISTINCT ON (non-partition column) clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Distinct on columns without partition column is currently unsupported DEBUG: Router planner cannot handle multi-shard select queries DEBUG: Collecting INSERT ... SELECT results on coordinator SELECT user_id, value_1_agg FROM agg_events ORDER BY 1,2; @@ -686,7 +687,7 @@ DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT user_id, v1_a DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator ROLLBACK; --- We don't support CTEs that are referenced in the target list +-- We do support CTEs that are referenced in the target list INSERT INTO agg_events WITH sub_cte AS (SELECT 1) SELECT @@ -694,15 +695,11 @@ INSERT INTO agg_events FROM raw_events_first; DEBUG: CTE sub_cte is going to be inlined via distributed planning -DEBUG: Subqueries without relations are not allowed in distributed INSERT ... SELECT queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: performing repartitioned INSERT ... SELECT -DEBUG: partitioning SELECT query by column index 0 with name 'user_id' -DEBUG: distributed statement: INSERT INTO multi_insert_select.agg_events_13300008 AS citus_table_alias (user_id, value_1_agg) SELECT user_id, value_1_agg FROM read_intermediate_results('{repartitioned_results_xxxxx_from_13300000_to_0}'::text[], 'binary'::citus_copy_format) intermediate_result(user_id integer, value_1_agg integer) -DEBUG: distributed statement: INSERT INTO multi_insert_select.agg_events_13300009 AS citus_table_alias (user_id, value_1_agg) SELECT user_id, value_1_agg FROM read_intermediate_results('{repartitioned_results_xxxxx_from_13300001_to_1}'::text[], 'binary'::citus_copy_format) intermediate_result(user_id integer, value_1_agg integer) -DEBUG: distributed statement: INSERT INTO multi_insert_select.agg_events_13300010 AS citus_table_alias (user_id, value_1_agg) SELECT user_id, value_1_agg FROM read_intermediate_results('{repartitioned_results_xxxxx_from_13300002_to_2}'::text[], 'binary'::citus_copy_format) intermediate_result(user_id integer, value_1_agg integer) -DEBUG: distributed statement: INSERT INTO multi_insert_select.agg_events_13300011 AS citus_table_alias (user_id, value_1_agg) SELECT user_id, value_1_agg FROM read_intermediate_results('{repartitioned_results_xxxxx_from_13300003_to_3}'::text[], 'binary'::citus_copy_format) intermediate_result(user_id integer, value_1_agg integer) --- We support set operations via the coordinator +DEBUG: distributed statement: INSERT INTO multi_insert_select.agg_events_13300008 AS citus_table_alias (user_id, value_1_agg) SELECT user_id, (SELECT sub_cte."?column?" FROM (SELECT 1) sub_cte("?column?")) FROM multi_insert_select.raw_events_first_13300000 raw_events_first WHERE (user_id IS NOT NULL) +DEBUG: distributed statement: INSERT INTO multi_insert_select.agg_events_13300009 AS citus_table_alias (user_id, value_1_agg) SELECT user_id, (SELECT sub_cte."?column?" FROM (SELECT 1) sub_cte("?column?")) FROM multi_insert_select.raw_events_first_13300001 raw_events_first WHERE (user_id IS NOT NULL) +DEBUG: distributed statement: INSERT INTO multi_insert_select.agg_events_13300010 AS citus_table_alias (user_id, value_1_agg) SELECT user_id, (SELECT sub_cte."?column?" FROM (SELECT 1) sub_cte("?column?")) FROM multi_insert_select.raw_events_first_13300002 raw_events_first WHERE (user_id IS NOT NULL) +DEBUG: distributed statement: INSERT INTO multi_insert_select.agg_events_13300011 AS citus_table_alias (user_id, value_1_agg) SELECT user_id, (SELECT sub_cte."?column?" FROM (SELECT 1) sub_cte("?column?")) FROM multi_insert_select.raw_events_first_13300003 raw_events_first WHERE (user_id IS NOT NULL) +-- We support set operations BEGIN; INSERT INTO raw_events_first(user_id) @@ -711,14 +708,10 @@ SELECT FROM ((SELECT user_id FROM raw_events_first) UNION (SELECT user_id FROM raw_events_second)) as foo; -DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries -DEBUG: Router planner cannot handle multi-shard select queries -DEBUG: performing repartitioned INSERT ... SELECT -DEBUG: partitioning SELECT query by column index 0 with name 'user_id' -DEBUG: distributed statement: INSERT INTO multi_insert_select.raw_events_first_13300000 AS citus_table_alias (user_id) SELECT user_id FROM read_intermediate_results('{repartitioned_results_xxxxx_from_13300004_to_0}'::text[], 'binary'::citus_copy_format) intermediate_result(user_id integer) -DEBUG: distributed statement: INSERT INTO multi_insert_select.raw_events_first_13300001 AS citus_table_alias (user_id) SELECT user_id FROM read_intermediate_results('{repartitioned_results_xxxxx_from_13300005_to_1}'::text[], 'binary'::citus_copy_format) intermediate_result(user_id integer) -DEBUG: distributed statement: INSERT INTO multi_insert_select.raw_events_first_13300002 AS citus_table_alias (user_id) SELECT user_id FROM read_intermediate_results('{repartitioned_results_xxxxx_from_13300006_to_2}'::text[], 'binary'::citus_copy_format) intermediate_result(user_id integer) -DEBUG: distributed statement: INSERT INTO multi_insert_select.raw_events_first_13300003 AS citus_table_alias (user_id) SELECT user_id FROM read_intermediate_results('{repartitioned_results_xxxxx_from_13300007_to_3}'::text[], 'binary'::citus_copy_format) intermediate_result(user_id integer) +DEBUG: distributed statement: INSERT INTO multi_insert_select.raw_events_first_13300000 AS citus_table_alias (user_id) SELECT user_id FROM (SELECT raw_events_first.user_id FROM multi_insert_select.raw_events_first_13300000 raw_events_first UNION SELECT raw_events_second.user_id FROM multi_insert_select.raw_events_second_13300004 raw_events_second) foo WHERE (user_id IS NOT NULL) +DEBUG: distributed statement: INSERT INTO multi_insert_select.raw_events_first_13300001 AS citus_table_alias (user_id) SELECT user_id FROM (SELECT raw_events_first.user_id FROM multi_insert_select.raw_events_first_13300001 raw_events_first UNION SELECT raw_events_second.user_id FROM multi_insert_select.raw_events_second_13300005 raw_events_second) foo WHERE (user_id IS NOT NULL) +DEBUG: distributed statement: INSERT INTO multi_insert_select.raw_events_first_13300002 AS citus_table_alias (user_id) SELECT user_id FROM (SELECT raw_events_first.user_id FROM multi_insert_select.raw_events_first_13300002 raw_events_first UNION SELECT raw_events_second.user_id FROM multi_insert_select.raw_events_second_13300006 raw_events_second) foo WHERE (user_id IS NOT NULL) +DEBUG: distributed statement: INSERT INTO multi_insert_select.raw_events_first_13300003 AS citus_table_alias (user_id) SELECT user_id FROM (SELECT raw_events_first.user_id FROM multi_insert_select.raw_events_first_13300003 raw_events_first UNION SELECT raw_events_second.user_id FROM multi_insert_select.raw_events_second_13300007 raw_events_second) foo WHERE (user_id IS NOT NULL) ROLLBACK; -- We do support set operations through recursive planning BEGIN; @@ -727,7 +720,7 @@ INSERT INTO raw_events_first(user_id) (SELECT user_id FROM raw_events_first) INTERSECT (SELECT user_id FROM raw_events_first); -DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: Router planner cannot handle multi-shard select queries DEBUG: Router planner cannot handle multi-shard select queries DEBUG: generating subplan XXX_1 for subquery SELECT user_id FROM multi_insert_select.raw_events_first @@ -747,7 +740,7 @@ SELECT FROM ((SELECT user_id FROM raw_events_first WHERE user_id = 15) EXCEPT (SELECT user_id FROM raw_events_second where user_id = 17)) as foo; -DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator -- some supported LEFT joins @@ -765,10 +758,16 @@ DEBUG: distributed statement: INSERT INTO multi_insert_select.agg_events_133000 raw_events_second.user_id FROM reference_table LEFT JOIN raw_events_second ON reference_table.user_id = raw_events_second.user_id; -DEBUG: distributed statement: INSERT INTO multi_insert_select.agg_events_13300008 AS citus_table_alias (user_id) SELECT raw_events_second.user_id FROM (multi_insert_select.reference_table_13300012 reference_table LEFT JOIN multi_insert_select.raw_events_second_13300004 raw_events_second ON ((reference_table.user_id OPERATOR(pg_catalog.=) raw_events_second.user_id))) WHERE (raw_events_second.user_id IS NOT NULL) -DEBUG: distributed statement: INSERT INTO multi_insert_select.agg_events_13300009 AS citus_table_alias (user_id) SELECT raw_events_second.user_id FROM (multi_insert_select.reference_table_13300012 reference_table LEFT JOIN multi_insert_select.raw_events_second_13300005 raw_events_second ON ((reference_table.user_id OPERATOR(pg_catalog.=) raw_events_second.user_id))) WHERE (raw_events_second.user_id IS NOT NULL) -DEBUG: distributed statement: INSERT INTO multi_insert_select.agg_events_13300010 AS citus_table_alias (user_id) SELECT raw_events_second.user_id FROM (multi_insert_select.reference_table_13300012 reference_table LEFT JOIN multi_insert_select.raw_events_second_13300006 raw_events_second ON ((reference_table.user_id OPERATOR(pg_catalog.=) raw_events_second.user_id))) WHERE (raw_events_second.user_id IS NOT NULL) -DEBUG: distributed statement: INSERT INTO multi_insert_select.agg_events_13300011 AS citus_table_alias (user_id) SELECT raw_events_second.user_id FROM (multi_insert_select.reference_table_13300012 reference_table LEFT JOIN multi_insert_select.raw_events_second_13300007 raw_events_second ON ((reference_table.user_id OPERATOR(pg_catalog.=) raw_events_second.user_id))) WHERE (raw_events_second.user_id IS NOT NULL) +DEBUG: cannot perform a lateral outer join when a distributed subquery references a reference table +DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: recursively planning right side of the left join since the outer side is a recurring rel +DEBUG: recursively planning distributed relation "raw_events_second" since it is part of a distributed join node that is outer joined with a recurring rel +DEBUG: Wrapping relation "raw_events_second" to a subquery +DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: generating subplan XXX_1 for subquery SELECT user_id FROM multi_insert_select.raw_events_second WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT raw_events_second.user_id FROM (multi_insert_select.reference_table LEFT JOIN (SELECT raw_events_second_1.user_id, NULL::timestamp without time zone AS "time", NULL::integer AS value_1, NULL::integer AS value_2, NULL::double precision AS value_3, NULL::bigint AS value_4 FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) raw_events_second_1) raw_events_second ON ((reference_table.user_id OPERATOR(pg_catalog.=) raw_events_second.user_id))) +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO agg_events (user_id) SELECT raw_events_first.user_id @@ -1126,7 +1125,8 @@ FROM (SELECT SUM(raw_events_second.value_4) AS v4, raw_events_second WHERE raw_events_first.user_id = raw_events_second.user_id GROUP BY raw_events_second.value_3) AS foo; -DEBUG: Group by list without distribution column is not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Group by list without partition column is currently unsupported when a subquery references a column from another query DEBUG: Router planner cannot handle multi-shard select queries DEBUG: Router planner cannot handle multi-shard select queries DEBUG: join prunable for intervals [-2147483648,-1073741825] and [-1073741824,-1] @@ -1281,7 +1281,7 @@ FROM (SELECT SUM(raw_events_second.value_4) AS v4, GROUP BY raw_events_second.value_1 HAVING SUM(raw_events_second.value_4) > 10) AS foo2 ) as f2 ON (f.id = f2.id); -DEBUG: Group by list without distribution column is not allowed in distributed INSERT ... SELECT queries +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: Router planner cannot handle multi-shard select queries DEBUG: Router planner cannot handle multi-shard select queries DEBUG: join prunable for intervals [-2147483648,-1073741825] and [-1073741824,-1] @@ -1327,7 +1327,7 @@ FROM (SELECT SUM(raw_events_second.value_4) AS v4, GROUP BY raw_events_second.value_1 HAVING SUM(raw_events_second.value_4) > 10) AS foo2 ) as f2 ON (f.id = f2.id); -DEBUG: Group by list without distribution column is not allowed in distributed INSERT ... SELECT queries +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: Router planner cannot handle multi-shard select queries DEBUG: Router planner cannot handle multi-shard select queries DEBUG: join prunable for intervals [-2147483648,-1073741825] and [-1073741824,-1] @@ -3242,5 +3242,236 @@ returning text_col_1; string (1 row) +CREATE TABLE dist_table_3( +dist_col bigint, +int_col integer +); +SELECT create_distributed_table('dist_table_3', 'dist_col'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- dist_table_2 and dist_table_3 are non-colocated source tables. Repartitioning is also not possible due to +-- different types for distribution columns. Citus would not be able to handle this complex insert select. +INSERT INTO dist_table_1 SELECT dist_table_2.dist_col FROM dist_table_2 JOIN dist_table_3 USING(dist_col); +ERROR: complex joins are only supported when all distributed tables are joined on their distribution columns with equal operator +CREATE TABLE dist_table_4( +dist_col integer, +int_col integer +); +SELECT create_distributed_table('dist_table_4', 'dist_col'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- Even if target table distribution column is colocated with dist_table_2's distributed column, source tables dist_table_2 and dist_table_4 +-- are non-colocated. Hence, SELECT part of the query should be pulled to coordinator. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_1 SELECT dist_table_2.dist_col FROM dist_table_2 JOIN dist_table_4 ON dist_table_2.dist_col = dist_table_4.int_col; +$$); + coordinator_plan +--------------------------------------------------------------------- + Custom Scan (Citus INSERT ... SELECT) + INSERT/SELECT method: pull to coordinator + -> Custom Scan (Citus Adaptive) + Task Count: 4 +(4 rows) + +-- For INSERT SELECT, when a lateral query references an outer query, push-down is possible even if limit clause exists in the lateral query. +-- It is because subquery with limit does not need to be merged at coordinator as it is a lateral query. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_1 SELECT d1.dist_col FROM dist_table_1 d1 LEFT JOIN LATERAL (SELECT * FROM dist_table_2 d2 WHERE d1.dist_col = d2.dist_col LIMIT 3) dummy USING(dist_col); +$$); + coordinator_plan +--------------------------------------------------------------------- + Custom Scan (Citus Adaptive) + Task Count: 4 +(2 rows) + +-- For INSERT SELECT, when push-down is NOT possible when limit clause exists in a subquery at SELECT part of INSERT SELECT. +-- It is because the subquery with limit needs to be merged at coordinator. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_1 SELECT d1.dist_col FROM dist_table_1 d1 LEFT JOIN (SELECT * FROM dist_table_2 LIMIT 3) dummy USING(dist_col); +$$); + coordinator_plan +--------------------------------------------------------------------- + Custom Scan (Citus INSERT ... SELECT) + INSERT/SELECT method: repartition + -> Custom Scan (Citus Adaptive) + -> Distributed Subplan XXX_1 + -> Limit + -> Custom Scan (Citus Adaptive) + Task Count: 4 +(7 rows) + +CREATE TABLE dist_table_5(id int, id2 int); +SELECT create_distributed_table('dist_table_5','id'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE dist_table_6(id int, id2 int); +SELECT create_distributed_table('dist_table_6','id'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- verify that insert select with union can be pushed down since UNION clause has FROM clause at top level query. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_5(id) SELECT id FROM (SELECT id FROM dist_table_5 UNION SELECT id FROM dist_table_6) dummy; +$$); + coordinator_plan +--------------------------------------------------------------------- + Custom Scan (Citus Adaptive) + Task Count: 4 +(2 rows) + +-- verify that insert select with sublink can be pushed down when tables are colocated. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_5 SELECT id, (SELECT id FROM dist_table_5 WHERE dist_table_5.id = dist_table_6.id) FROM dist_table_6; +$$); + coordinator_plan +--------------------------------------------------------------------- + Custom Scan (Citus Adaptive) + Task Count: 4 +(2 rows) + +CREATE TABLE ref_table_1(id int); +SELECT create_reference_table('ref_table_1'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +-- verify that insert select with sublink cannot be pushed down when from clause does not contain any distributed relation. +INSERT INTO dist_table_5 SELECT id, (SELECT id FROM dist_table_5 WHERE dist_table_5.id = ref_table_1.id) FROM ref_table_1; +ERROR: correlated subqueries are not supported when the FROM clause contains a reference table +-- verify that insert select cannot be pushed down when we have recurring range table in from clause. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_5 SELECT id, (SELECT id FROM ref_table_1 WHERE id = 1) FROM ref_table_1; +$$); + coordinator_plan +--------------------------------------------------------------------- + Custom Scan (Citus INSERT ... SELECT) + INSERT/SELECT method: pull to coordinator + -> Custom Scan (Citus Adaptive) + Task Count: 1 +(4 rows) + +-- verify that insert select cannot be pushed down when we have reference table in outside of outer join. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_5 SELECT a.id FROM dist_table_5 a LEFT JOIN ref_table_1 b ON (true) RIGHT JOIN ref_table_1 c ON (true); +$$); + coordinator_plan +--------------------------------------------------------------------- + Custom Scan (Citus INSERT ... SELECT) + INSERT/SELECT method: pull to coordinator + -> Custom Scan (Citus Adaptive) + -> Distributed Subplan XXX_1 + -> Custom Scan (Citus Adaptive) + Task Count: 4 +(6 rows) + +-- verify that insert select cannot be pushed down when it has a recurring outer join in a subquery. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_5 SELECT id FROM ref_table_1 LEFT JOIN dist_table_5 USING(id); +$$); + coordinator_plan +--------------------------------------------------------------------- + Custom Scan (Citus INSERT ... SELECT) + INSERT/SELECT method: pull to coordinator + -> Custom Scan (Citus Adaptive) + -> Distributed Subplan XXX_1 + -> Custom Scan (Citus Adaptive) + Task Count: 4 +(6 rows) + +CREATE TABLE loc_table_1(id int); +-- verify that insert select cannot be pushed down when it contains join between local and distributed tables. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_5 SELECT id FROM dist_table_5 JOIN loc_table_1 USING(id); +$$); + coordinator_plan +--------------------------------------------------------------------- + Custom Scan (Citus INSERT ... SELECT) + INSERT/SELECT method: repartition + -> Custom Scan (Citus Adaptive) + -> Distributed Subplan XXX_1 + -> Seq Scan on loc_table_1 + Task Count: 4 +(6 rows) + +CREATE VIEW view_1 AS + SELECT id FROM dist_table_6; +CREATE MATERIALIZED VIEW view_2 AS + SELECT id FROM dist_table_6; +-- verify that insert select cannot be pushed down when it contains view. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_5 SELECT * FROM view_1; +$$); + coordinator_plan +--------------------------------------------------------------------- + Custom Scan (Citus Adaptive) + Task Count: 4 +(2 rows) + +-- verify that insert select cannot be pushed down when it contains materialized view. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_5 SELECT * FROM view_2; +$$); + coordinator_plan +--------------------------------------------------------------------- + Custom Scan (Citus INSERT ... SELECT) + INSERT/SELECT method: pull to coordinator + -> Seq Scan on view_2 +(3 rows) + +CREATE TABLE append_table(id integer, data text, int_data int); +SELECT create_distributed_table('append_table', 'id', 'append'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT master_create_empty_shard('append_table'); + master_create_empty_shard +--------------------------------------------------------------------- + 13300096 +(1 row) + +-- verify that insert select push down for append tables are not supported. +INSERT INTO append_table SELECT * FROM append_table; +ERROR: INSERT ... SELECT into an append-distributed table is not supported +-- verify that CTEs at top level of INSERT SELECT, that can normally be inlined, would not be inlined by INSERT SELECT pushdown planner +-- and handled by pull to coordinator. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) WITH cte_1 AS (SELECT id FROM dist_table_5 WHERE id = 5) + INSERT INTO dist_table_5 + SELECT id FROM dist_table_5 JOIN cte_1 USING(id); +$$); + coordinator_plan +--------------------------------------------------------------------- + Custom Scan (Citus INSERT ... SELECT) + INSERT/SELECT method: pull to coordinator + -> Custom Scan (Citus Adaptive) + Task Count: 1 +(4 rows) + +-- verify that CTEs at top level of SELECT part, would be inlined by Postgres and pushed down by INSERT SELECT planner. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_5 + WITH cte_1 AS (SELECT id FROM dist_table_5 WHERE id = 5) + SELECT id FROM dist_table_5 JOIN cte_1 USING(id); +$$); + coordinator_plan +--------------------------------------------------------------------- + Custom Scan (Citus Adaptive) + Task Count: 1 +(2 rows) + SET client_min_messages TO ERROR; DROP SCHEMA multi_insert_select CASCADE; diff --git a/src/test/regress/expected/multi_insert_select_conflict.out b/src/test/regress/expected/multi_insert_select_conflict.out index df7bdc9b9..f344a8b79 100644 --- a/src/test/regress/expected/multi_insert_select_conflict.out +++ b/src/test/regress/expected/multi_insert_select_conflict.out @@ -106,7 +106,8 @@ FROM ( LIMIT 5 ) as foo ON CONFLICT DO NOTHING; -DEBUG: LIMIT clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Limit clause is currently unsupported when a subquery references a column from another query DEBUG: push down of limit count: 5 DEBUG: generating subplan XXX_1 for subquery SELECT col_1, col_2, col_3 FROM on_conflict.source_table_1 LIMIT 5 DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT col_1, col_2 FROM (SELECT intermediate_result.col_1, intermediate_result.col_2, intermediate_result.col_3 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(col_1 integer, col_2 integer, col_3 integer)) foo @@ -127,7 +128,8 @@ WITH inserted_table AS ( ON CONFLICT(col_1) DO UPDATE SET col_2 = EXCLUDED.col_2 RETURNING * ) SELECT * FROM inserted_table ORDER BY 1; DEBUG: generating subplan XXX_1 for CTE inserted_table: INSERT INTO on_conflict.target_table (col_1, col_2) SELECT foo.col_1, foo.col_2 FROM (SELECT source_table_1.col_1, source_table_1.col_2, source_table_1.col_3 FROM on_conflict.source_table_1 LIMIT 5) foo ON CONFLICT(col_1) DO UPDATE SET col_2 = excluded.col_2 RETURNING target_table.col_1, target_table.col_2 -DEBUG: LIMIT clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Limit clause is currently unsupported when a subquery references a column from another query DEBUG: push down of limit count: 5 DEBUG: generating subplan XXX_1 for subquery SELECT col_1, col_2, col_3 FROM on_conflict.source_table_1 LIMIT 5 DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT col_1, col_2 FROM (SELECT intermediate_result.col_1, intermediate_result.col_2, intermediate_result.col_3 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(col_1 integer, col_2 integer, col_3 integer)) foo @@ -163,7 +165,8 @@ WITH inserted_table AS ( ON CONFLICT(col_1) DO UPDATE SET col_2 = 0 RETURNING * ) SELECT * FROM inserted_table ORDER BY 1; DEBUG: generating subplan XXX_1 for CTE inserted_table: INSERT INTO on_conflict.target_table (col_1, col_2) SELECT foo.col_1, foo.col_2 FROM ((SELECT source_table_1.col_1, source_table_1.col_2, source_table_1.col_3 FROM on_conflict.source_table_1 LIMIT 5) UNION (SELECT source_table_2.col_1, source_table_2.col_2, source_table_2.col_3 FROM on_conflict.source_table_2 LIMIT 5)) foo ON CONFLICT(col_1) DO UPDATE SET col_2 = 0 RETURNING target_table.col_1, target_table.col_2 -DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Limit clause is currently unsupported when a subquery references a column from another query DEBUG: push down of limit count: 5 DEBUG: generating subplan XXX_1 for subquery SELECT col_1, col_2, col_3 FROM on_conflict.source_table_1 LIMIT 5 DEBUG: push down of limit count: 5 @@ -498,7 +501,8 @@ FROM ( LIMIT 5) ) as foo ON CONFLICT(col_1) DO UPDATE SET col_2 = 0; -DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Limit clause is currently unsupported when a subquery references a column from another query DEBUG: push down of limit count: 5 DEBUG: generating subplan XXX_1 for subquery SELECT col_1, col_2, col_3 FROM on_conflict.source_table_1 LIMIT 5 DEBUG: push down of limit count: 5 @@ -560,7 +564,8 @@ DEBUG: Collecting INSERT ... SELECT results on coordinator -- intermediate result file SET citus.max_adaptive_executor_pool_size TO 1; INSERT INTO target_table SELECT * FROM target_table LIMIT 10000 ON CONFLICT(col_1) DO UPDATE SET col_2 = EXCLUDED.col_2 + 1; -DEBUG: LIMIT clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Limit clause is currently unsupported when a subquery references a column from another query DEBUG: push down of limit count: 10000 DEBUG: Collecting INSERT ... SELECT results on coordinator SELECT DISTINCT col_2 FROM target_table; @@ -572,7 +577,8 @@ SELECT DISTINCT col_2 FROM target_table; WITH cte_1 AS (INSERT INTO target_table SELECT * FROM target_table LIMIT 10000 ON CONFLICT(col_1) DO UPDATE SET col_2 = EXCLUDED.col_2 + 1 RETURNING *) SELECT DISTINCT col_2 FROM cte_1; DEBUG: generating subplan XXX_1 for CTE cte_1: INSERT INTO on_conflict.target_table (col_1, col_2) SELECT target_table_1.col_1, target_table_1.col_2 FROM on_conflict.target_table target_table_1 LIMIT 10000 ON CONFLICT(col_1) DO UPDATE SET col_2 = (excluded.col_2 OPERATOR(pg_catalog.+) 1) RETURNING target_table.col_1, target_table.col_2 -DEBUG: LIMIT clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Limit clause is currently unsupported when a subquery references a column from another query DEBUG: push down of limit count: 10000 DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT DISTINCT col_2 FROM (SELECT intermediate_result.col_1, intermediate_result.col_2 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(col_1 integer, col_2 integer)) cte_1 DEBUG: Collecting INSERT ... SELECT results on coordinator diff --git a/src/test/regress/expected/multi_insert_select_conflict_0.out b/src/test/regress/expected/multi_insert_select_conflict_0.out index b8f926d30..42b5aed31 100644 --- a/src/test/regress/expected/multi_insert_select_conflict_0.out +++ b/src/test/regress/expected/multi_insert_select_conflict_0.out @@ -106,7 +106,8 @@ FROM ( LIMIT 5 ) as foo ON CONFLICT DO NOTHING; -DEBUG: LIMIT clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Limit clause is currently unsupported when a subquery references a column from another query DEBUG: push down of limit count: 5 DEBUG: generating subplan XXX_1 for subquery SELECT col_1, col_2, col_3 FROM on_conflict.source_table_1 LIMIT 5 DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT col_1, col_2 FROM (SELECT intermediate_result.col_1, intermediate_result.col_2, intermediate_result.col_3 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(col_1 integer, col_2 integer, col_3 integer)) foo @@ -127,7 +128,8 @@ WITH inserted_table AS ( ON CONFLICT(col_1) DO UPDATE SET col_2 = EXCLUDED.col_2 RETURNING * ) SELECT * FROM inserted_table ORDER BY 1; DEBUG: generating subplan XXX_1 for CTE inserted_table: INSERT INTO on_conflict.target_table (col_1, col_2) SELECT col_1, col_2 FROM (SELECT source_table_1.col_1, source_table_1.col_2, source_table_1.col_3 FROM on_conflict.source_table_1 LIMIT 5) foo ON CONFLICT(col_1) DO UPDATE SET col_2 = excluded.col_2 RETURNING target_table.col_1, target_table.col_2 -DEBUG: LIMIT clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Limit clause is currently unsupported when a subquery references a column from another query DEBUG: push down of limit count: 5 DEBUG: generating subplan XXX_1 for subquery SELECT col_1, col_2, col_3 FROM on_conflict.source_table_1 LIMIT 5 DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT col_1, col_2 FROM (SELECT intermediate_result.col_1, intermediate_result.col_2, intermediate_result.col_3 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(col_1 integer, col_2 integer, col_3 integer)) foo @@ -163,7 +165,8 @@ WITH inserted_table AS ( ON CONFLICT(col_1) DO UPDATE SET col_2 = 0 RETURNING * ) SELECT * FROM inserted_table ORDER BY 1; DEBUG: generating subplan XXX_1 for CTE inserted_table: INSERT INTO on_conflict.target_table (col_1, col_2) SELECT col_1, col_2 FROM ((SELECT source_table_1.col_1, source_table_1.col_2, source_table_1.col_3 FROM on_conflict.source_table_1 LIMIT 5) UNION (SELECT source_table_2.col_1, source_table_2.col_2, source_table_2.col_3 FROM on_conflict.source_table_2 LIMIT 5)) foo ON CONFLICT(col_1) DO UPDATE SET col_2 = 0 RETURNING target_table.col_1, target_table.col_2 -DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Limit clause is currently unsupported when a subquery references a column from another query DEBUG: push down of limit count: 5 DEBUG: generating subplan XXX_1 for subquery SELECT col_1, col_2, col_3 FROM on_conflict.source_table_1 LIMIT 5 DEBUG: push down of limit count: 5 @@ -498,7 +501,8 @@ FROM ( LIMIT 5) ) as foo ON CONFLICT(col_1) DO UPDATE SET col_2 = 0; -DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Limit clause is currently unsupported when a subquery references a column from another query DEBUG: push down of limit count: 5 DEBUG: generating subplan XXX_1 for subquery SELECT col_1, col_2, col_3 FROM on_conflict.source_table_1 LIMIT 5 DEBUG: push down of limit count: 5 @@ -560,7 +564,8 @@ DEBUG: Collecting INSERT ... SELECT results on coordinator -- intermediate result file SET citus.max_adaptive_executor_pool_size TO 1; INSERT INTO target_table SELECT * FROM target_table LIMIT 10000 ON CONFLICT(col_1) DO UPDATE SET col_2 = EXCLUDED.col_2 + 1; -DEBUG: LIMIT clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Limit clause is currently unsupported when a subquery references a column from another query DEBUG: push down of limit count: 10000 DEBUG: Collecting INSERT ... SELECT results on coordinator SELECT DISTINCT col_2 FROM target_table; @@ -572,7 +577,8 @@ SELECT DISTINCT col_2 FROM target_table; WITH cte_1 AS (INSERT INTO target_table SELECT * FROM target_table LIMIT 10000 ON CONFLICT(col_1) DO UPDATE SET col_2 = EXCLUDED.col_2 + 1 RETURNING *) SELECT DISTINCT col_2 FROM cte_1; DEBUG: generating subplan XXX_1 for CTE cte_1: INSERT INTO on_conflict.target_table (col_1, col_2) SELECT col_1, col_2 FROM on_conflict.target_table LIMIT 10000 ON CONFLICT(col_1) DO UPDATE SET col_2 = (excluded.col_2 OPERATOR(pg_catalog.+) 1) RETURNING target_table.col_1, target_table.col_2 -DEBUG: LIMIT clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Limit clause is currently unsupported when a subquery references a column from another query DEBUG: push down of limit count: 10000 DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT DISTINCT col_2 FROM (SELECT intermediate_result.col_1, intermediate_result.col_2 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(col_1 integer, col_2 integer)) cte_1 DEBUG: Collecting INSERT ... SELECT results on coordinator diff --git a/src/test/regress/expected/multi_insert_select_non_pushable_queries.out b/src/test/regress/expected/multi_insert_select_non_pushable_queries.out index fc3f62385..c4a537277 100644 --- a/src/test/regress/expected/multi_insert_select_non_pushable_queries.out +++ b/src/test/regress/expected/multi_insert_select_non_pushable_queries.out @@ -3,6 +3,8 @@ -- Vanilla funnel query --------------------------------------------------------------------- --------------------------------------------------------------------- +CREATE SCHEMA multi_insert_select_non_pushable_queries; +SET search_path = multi_insert_select_non_pushable_queries,public; -- not pushable since the JOIN is not an equi join INSERT INTO agg_results_third (user_id, value_1_agg) SELECT user_id, array_length(events_table, 1) @@ -147,7 +149,7 @@ FROM ( GROUP BY t1.user_id, hasdone_event ) t GROUP BY user_id, hasdone_event RETURNING user_id, value_1_agg, value_2_agg; -DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: generating subplan XXX_1 for subquery SELECT u.user_id, 'step=>1'::text AS event, e."time" FROM public.users_table u, public.events_table e WHERE ((u.user_id OPERATOR(pg_catalog.=) e.user_id) AND (u.user_id OPERATOR(pg_catalog.>=) 10) AND (u.user_id OPERATOR(pg_catalog.<=) 25) AND (e.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[100, 101, 102]))) DEBUG: generating subplan XXX_2 for subquery SELECT u.user_id, 'step=>2'::text AS event, e."time" FROM public.users_table u, public.events_table e WHERE ((u.user_id OPERATOR(pg_catalog.=) e.user_id) AND (u.user_id OPERATOR(pg_catalog.>=) 10) AND (u.user_id OPERATOR(pg_catalog.<=) 25) AND (e.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[103, 104, 105]))) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT intermediate_result.user_id, intermediate_result.event, intermediate_result."time" FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, event text, "time" timestamp without time zone) UNION SELECT intermediate_result.user_id, intermediate_result.event, intermediate_result."time" FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, event text, "time" timestamp without time zone) @@ -305,7 +307,7 @@ GROUP BY ORDER BY count_pay RETURNING user_id, value_1_agg, value_2_agg; -DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: generating subplan XXX_1 for subquery SELECT users_table.user_id, 'action=>1'::text AS event, events_table."time" FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (users_table.user_id OPERATOR(pg_catalog.>=) 10) AND (users_table.user_id OPERATOR(pg_catalog.<=) 70) AND (events_table.event_type OPERATOR(pg_catalog.>) 10) AND (events_table.event_type OPERATOR(pg_catalog.<) 12)) DEBUG: generating subplan XXX_2 for subquery SELECT users_table.user_id, 'action=>2'::text AS event, events_table."time" FROM public.users_table, public.events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) events_table.user_id) AND (users_table.user_id OPERATOR(pg_catalog.>=) 10) AND (users_table.user_id OPERATOR(pg_catalog.<=) 70) AND (events_table.event_type OPERATOR(pg_catalog.>) 12) AND (events_table.event_type OPERATOR(pg_catalog.<) 14)) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT intermediate_result.user_id, intermediate_result.event, intermediate_result."time" FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, event text, "time" timestamp without time zone) UNION SELECT intermediate_result.user_id, intermediate_result.event, intermediate_result."time" FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, event text, "time" timestamp without time zone) @@ -808,3 +810,36 @@ FROM ( GROUP BY user_id ) AS shard_union ORDER BY user_lastseen DESC; +CREATE TABLE dist_table_1(id int); +SELECT create_distributed_table('dist_table_1','id'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE dist_table_2(id int, id2 int); +SELECT create_distributed_table('dist_table_2','id2'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- verify that insert select with union can be pulled to coordinator. We cannot push down the query +-- since UNION clause has no FROM clause at top level query. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_1(id) SELECT id FROM dist_table_1 UNION SELECT id FROM dist_table_2; +$$); + coordinator_plan +--------------------------------------------------------------------- + Custom Scan (Citus INSERT ... SELECT) + INSERT/SELECT method: pull to coordinator + -> Custom Scan (Citus Adaptive) + -> Distributed Subplan XXX_1 + -> Custom Scan (Citus Adaptive) + Task Count: 4 +(6 rows) + +DROP SCHEMA multi_insert_select_non_pushable_queries CASCADE; +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to table dist_table_1 +drop cascades to table dist_table_2 diff --git a/src/test/regress/expected/multi_router_planner_fast_path.out b/src/test/regress/expected/multi_router_planner_fast_path.out index f2f18266b..474d4a107 100644 --- a/src/test/regress/expected/multi_router_planner_fast_path.out +++ b/src/test/regress/expected/multi_router_planner_fast_path.out @@ -1876,32 +1876,38 @@ PREPARE insert_sel(int, int) AS INSERT INTO articles_hash SELECT * FROM articles_hash WHERE author_id = $2 AND word_count = $1 OFFSET 0; EXECUTE insert_sel(1,1); -DEBUG: OFFSET clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Offset clause is currently unsupported when a subquery references a column from another query DEBUG: Deferred pruning for a fast-path router query DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator EXECUTE insert_sel(1,1); -DEBUG: OFFSET clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Offset clause is currently unsupported when a subquery references a column from another query DEBUG: Deferred pruning for a fast-path router query DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator EXECUTE insert_sel(1,1); -DEBUG: OFFSET clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Offset clause is currently unsupported when a subquery references a column from another query DEBUG: Deferred pruning for a fast-path router query DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator EXECUTE insert_sel(1,1); -DEBUG: OFFSET clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Offset clause is currently unsupported when a subquery references a column from another query DEBUG: Deferred pruning for a fast-path router query DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator EXECUTE insert_sel(1,1); -DEBUG: OFFSET clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Offset clause is currently unsupported when a subquery references a column from another query DEBUG: Deferred pruning for a fast-path router query DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator EXECUTE insert_sel(1,1); -DEBUG: OFFSET clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Offset clause is currently unsupported when a subquery references a column from another query DEBUG: Deferred pruning for a fast-path router query DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator diff --git a/src/test/regress/expected/mx_coordinator_shouldhaveshards.out b/src/test/regress/expected/mx_coordinator_shouldhaveshards.out index 438e1dcdd..547300460 100644 --- a/src/test/regress/expected/mx_coordinator_shouldhaveshards.out +++ b/src/test/regress/expected/mx_coordinator_shouldhaveshards.out @@ -89,7 +89,7 @@ key FROM a JOIN table_2 USING (key) GROUP BY key HAVING (max(table_2.value) >= (SELECT value FROM a)); -DEBUG: Group by list without distribution column is not allowed in distributed INSERT ... SELECT queries +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: generating subplan XXX_1 for CTE a: SELECT key, value FROM mx_coordinator_shouldhaveshards.table_1 ORDER BY key, value DESC LIMIT 1 DEBUG: push down of limit count: 1 DEBUG: generating subplan XXX_2 for subquery SELECT int4(count(*)) AS auto_coerced_by_citus_0, (a.key)::text AS auto_coerced_by_citus_1 FROM ((SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) a JOIN mx_coordinator_shouldhaveshards.table_2 USING (key)) GROUP BY a.key HAVING (max(table_2.value) OPERATOR(pg_catalog.>=) (SELECT a_1.value FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) a_1)) @@ -114,7 +114,8 @@ inserts AS ( ) SELECT count(*) FROM inserts; DEBUG: generating subplan XXX_1 for CTE stats: SELECT count(key) AS m FROM mx_coordinator_shouldhaveshards.table_1 DEBUG: generating subplan XXX_2 for CTE inserts: INSERT INTO mx_coordinator_shouldhaveshards.table_2 (key, value) SELECT table_1.key, count(*) AS count FROM mx_coordinator_shouldhaveshards.table_1 WHERE (table_1.key OPERATOR(pg_catalog.>=) (SELECT stats.m FROM (SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m bigint)) stats)) GROUP BY table_1.key HAVING (count(*) OPERATOR(pg_catalog.<=) (SELECT stats.m FROM (SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m bigint)) stats)) LIMIT 1 RETURNING table_2.key, table_2.value -DEBUG: LIMIT clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Limit clause is currently unsupported when a subquery references a column from another query DEBUG: push down of limit count: 1 DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) inserts DEBUG: Subplan XXX_1 will be written to local file @@ -150,7 +151,7 @@ key FROM a JOIN table_2_rep USING (key) GROUP BY key HAVING (max(table_2_rep.value) >= (SELECT value FROM a)); -DEBUG: Group by list without distribution column is not allowed in distributed INSERT ... SELECT queries +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: generating subplan XXX_1 for CTE a: SELECT key, value FROM mx_coordinator_shouldhaveshards.table_1_rep ORDER BY key, value DESC LIMIT 1 DEBUG: push down of limit count: 1 DEBUG: generating subplan XXX_2 for subquery SELECT int4(count(*)) AS auto_coerced_by_citus_0, (a.key)::text AS auto_coerced_by_citus_1 FROM ((SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) a JOIN mx_coordinator_shouldhaveshards.table_2_rep USING (key)) GROUP BY a.key HAVING (max(table_2_rep.value) OPERATOR(pg_catalog.>=) (SELECT a_1.value FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) a_1)) @@ -175,7 +176,8 @@ inserts AS ( ) SELECT count(*) FROM inserts; DEBUG: generating subplan XXX_1 for CTE stats: SELECT count(key) AS m FROM mx_coordinator_shouldhaveshards.table_1_rep DEBUG: generating subplan XXX_2 for CTE inserts: INSERT INTO mx_coordinator_shouldhaveshards.table_2_rep (key, value) SELECT table_1_rep.key, count(*) AS count FROM mx_coordinator_shouldhaveshards.table_1_rep WHERE (table_1_rep.key OPERATOR(pg_catalog.>=) (SELECT stats.m FROM (SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m bigint)) stats)) GROUP BY table_1_rep.key HAVING (count(*) OPERATOR(pg_catalog.<=) (SELECT stats.m FROM (SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m bigint)) stats)) LIMIT 1 RETURNING table_2_rep.key, table_2_rep.value -DEBUG: LIMIT clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Limit clause is currently unsupported when a subquery references a column from another query DEBUG: push down of limit count: 1 DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) inserts DEBUG: Subplan XXX_1 will be written to local file @@ -215,7 +217,7 @@ key FROM a JOIN table_2 USING (key) GROUP BY key HAVING (max(table_2.value) >= (SELECT value FROM a)); -DEBUG: Group by list without distribution column is not allowed in distributed INSERT ... SELECT queries +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: generating subplan XXX_1 for CTE a: SELECT key, value FROM mx_coordinator_shouldhaveshards.table_1 ORDER BY key, value DESC LIMIT 1 DEBUG: push down of limit count: 1 DEBUG: generating subplan XXX_2 for subquery SELECT int4(count(*)) AS auto_coerced_by_citus_0, (a.key)::text AS auto_coerced_by_citus_1 FROM ((SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) a JOIN mx_coordinator_shouldhaveshards.table_2 USING (key)) GROUP BY a.key HAVING (max(table_2.value) OPERATOR(pg_catalog.>=) (SELECT a_1.value FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) a_1)) @@ -240,7 +242,8 @@ inserts AS ( ) SELECT count(*) FROM inserts; DEBUG: generating subplan XXX_1 for CTE stats: SELECT count(key) AS m FROM mx_coordinator_shouldhaveshards.table_1 DEBUG: generating subplan XXX_2 for CTE inserts: INSERT INTO mx_coordinator_shouldhaveshards.table_2 (key, value) SELECT table_1.key, count(*) AS count FROM mx_coordinator_shouldhaveshards.table_1 WHERE (table_1.key OPERATOR(pg_catalog.>=) (SELECT stats.m FROM (SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m bigint)) stats)) GROUP BY table_1.key HAVING (count(*) OPERATOR(pg_catalog.<=) (SELECT stats.m FROM (SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m bigint)) stats)) LIMIT 1 RETURNING table_2.key, table_2.value -DEBUG: LIMIT clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Limit clause is currently unsupported when a subquery references a column from another query DEBUG: push down of limit count: 1 DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) inserts DEBUG: Subplan XXX_1 will be written to local file @@ -276,7 +279,7 @@ key FROM a JOIN table_2_rep USING (key) GROUP BY key HAVING (max(table_2_rep.value) >= (SELECT value FROM a)); -DEBUG: Group by list without distribution column is not allowed in distributed INSERT ... SELECT queries +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: generating subplan XXX_1 for CTE a: SELECT key, value FROM mx_coordinator_shouldhaveshards.table_1_rep ORDER BY key, value DESC LIMIT 1 DEBUG: push down of limit count: 1 DEBUG: generating subplan XXX_2 for subquery SELECT int4(count(*)) AS auto_coerced_by_citus_0, (a.key)::text AS auto_coerced_by_citus_1 FROM ((SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) a JOIN mx_coordinator_shouldhaveshards.table_2_rep USING (key)) GROUP BY a.key HAVING (max(table_2_rep.value) OPERATOR(pg_catalog.>=) (SELECT a_1.value FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) a_1)) @@ -301,7 +304,8 @@ inserts AS ( ) SELECT count(*) FROM inserts; DEBUG: generating subplan XXX_1 for CTE stats: SELECT count(key) AS m FROM mx_coordinator_shouldhaveshards.table_1_rep DEBUG: generating subplan XXX_2 for CTE inserts: INSERT INTO mx_coordinator_shouldhaveshards.table_2_rep (key, value) SELECT table_1_rep.key, count(*) AS count FROM mx_coordinator_shouldhaveshards.table_1_rep WHERE (table_1_rep.key OPERATOR(pg_catalog.>=) (SELECT stats.m FROM (SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m bigint)) stats)) GROUP BY table_1_rep.key HAVING (count(*) OPERATOR(pg_catalog.<=) (SELECT stats.m FROM (SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m bigint)) stats)) LIMIT 1 RETURNING table_2_rep.key, table_2_rep.value -DEBUG: LIMIT clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Limit clause is currently unsupported when a subquery references a column from another query DEBUG: push down of limit count: 1 DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) inserts DEBUG: Subplan XXX_1 will be written to local file diff --git a/src/test/regress/expected/mx_coordinator_shouldhaveshards_0.out b/src/test/regress/expected/mx_coordinator_shouldhaveshards_0.out index 398229fbb..15cd69068 100644 --- a/src/test/regress/expected/mx_coordinator_shouldhaveshards_0.out +++ b/src/test/regress/expected/mx_coordinator_shouldhaveshards_0.out @@ -89,7 +89,7 @@ key FROM a JOIN table_2 USING (key) GROUP BY key HAVING (max(table_2.value) >= (SELECT value FROM a)); -DEBUG: Group by list without distribution column is not allowed in distributed INSERT ... SELECT queries +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: generating subplan XXX_1 for CTE a: SELECT key, value FROM mx_coordinator_shouldhaveshards.table_1 ORDER BY key, value DESC LIMIT 1 DEBUG: push down of limit count: 1 DEBUG: generating subplan XXX_2 for subquery SELECT int4(count(*)) AS auto_coerced_by_citus_0, (a.key)::text AS auto_coerced_by_citus_1 FROM ((SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) a JOIN mx_coordinator_shouldhaveshards.table_2 USING (key)) GROUP BY a.key HAVING (max(table_2.value) OPERATOR(pg_catalog.>=) (SELECT a_1.value FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) a_1)) @@ -114,7 +114,8 @@ inserts AS ( ) SELECT count(*) FROM inserts; DEBUG: generating subplan XXX_1 for CTE stats: SELECT count(key) AS m FROM mx_coordinator_shouldhaveshards.table_1 DEBUG: generating subplan XXX_2 for CTE inserts: INSERT INTO mx_coordinator_shouldhaveshards.table_2 (key, value) SELECT key, count(*) AS count FROM mx_coordinator_shouldhaveshards.table_1 WHERE (key OPERATOR(pg_catalog.>=) (SELECT stats.m FROM (SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m bigint)) stats)) GROUP BY key HAVING (count(*) OPERATOR(pg_catalog.<=) (SELECT stats.m FROM (SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m bigint)) stats)) LIMIT 1 RETURNING table_2.key, table_2.value -DEBUG: LIMIT clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Limit clause is currently unsupported when a subquery references a column from another query DEBUG: push down of limit count: 1 DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) inserts DEBUG: Subplan XXX_1 will be written to local file @@ -150,7 +151,7 @@ key FROM a JOIN table_2_rep USING (key) GROUP BY key HAVING (max(table_2_rep.value) >= (SELECT value FROM a)); -DEBUG: Group by list without distribution column is not allowed in distributed INSERT ... SELECT queries +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: generating subplan XXX_1 for CTE a: SELECT key, value FROM mx_coordinator_shouldhaveshards.table_1_rep ORDER BY key, value DESC LIMIT 1 DEBUG: push down of limit count: 1 DEBUG: generating subplan XXX_2 for subquery SELECT int4(count(*)) AS auto_coerced_by_citus_0, (a.key)::text AS auto_coerced_by_citus_1 FROM ((SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) a JOIN mx_coordinator_shouldhaveshards.table_2_rep USING (key)) GROUP BY a.key HAVING (max(table_2_rep.value) OPERATOR(pg_catalog.>=) (SELECT a_1.value FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) a_1)) @@ -175,7 +176,8 @@ inserts AS ( ) SELECT count(*) FROM inserts; DEBUG: generating subplan XXX_1 for CTE stats: SELECT count(key) AS m FROM mx_coordinator_shouldhaveshards.table_1_rep DEBUG: generating subplan XXX_2 for CTE inserts: INSERT INTO mx_coordinator_shouldhaveshards.table_2_rep (key, value) SELECT key, count(*) AS count FROM mx_coordinator_shouldhaveshards.table_1_rep WHERE (key OPERATOR(pg_catalog.>=) (SELECT stats.m FROM (SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m bigint)) stats)) GROUP BY key HAVING (count(*) OPERATOR(pg_catalog.<=) (SELECT stats.m FROM (SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m bigint)) stats)) LIMIT 1 RETURNING table_2_rep.key, table_2_rep.value -DEBUG: LIMIT clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Limit clause is currently unsupported when a subquery references a column from another query DEBUG: push down of limit count: 1 DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) inserts DEBUG: Subplan XXX_1 will be written to local file @@ -215,7 +217,7 @@ key FROM a JOIN table_2 USING (key) GROUP BY key HAVING (max(table_2.value) >= (SELECT value FROM a)); -DEBUG: Group by list without distribution column is not allowed in distributed INSERT ... SELECT queries +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: generating subplan XXX_1 for CTE a: SELECT key, value FROM mx_coordinator_shouldhaveshards.table_1 ORDER BY key, value DESC LIMIT 1 DEBUG: push down of limit count: 1 DEBUG: generating subplan XXX_2 for subquery SELECT int4(count(*)) AS auto_coerced_by_citus_0, (a.key)::text AS auto_coerced_by_citus_1 FROM ((SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) a JOIN mx_coordinator_shouldhaveshards.table_2 USING (key)) GROUP BY a.key HAVING (max(table_2.value) OPERATOR(pg_catalog.>=) (SELECT a_1.value FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) a_1)) @@ -240,7 +242,8 @@ inserts AS ( ) SELECT count(*) FROM inserts; DEBUG: generating subplan XXX_1 for CTE stats: SELECT count(key) AS m FROM mx_coordinator_shouldhaveshards.table_1 DEBUG: generating subplan XXX_2 for CTE inserts: INSERT INTO mx_coordinator_shouldhaveshards.table_2 (key, value) SELECT key, count(*) AS count FROM mx_coordinator_shouldhaveshards.table_1 WHERE (key OPERATOR(pg_catalog.>=) (SELECT stats.m FROM (SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m bigint)) stats)) GROUP BY key HAVING (count(*) OPERATOR(pg_catalog.<=) (SELECT stats.m FROM (SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m bigint)) stats)) LIMIT 1 RETURNING table_2.key, table_2.value -DEBUG: LIMIT clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Limit clause is currently unsupported when a subquery references a column from another query DEBUG: push down of limit count: 1 DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) inserts DEBUG: Subplan XXX_1 will be written to local file @@ -276,7 +279,7 @@ key FROM a JOIN table_2_rep USING (key) GROUP BY key HAVING (max(table_2_rep.value) >= (SELECT value FROM a)); -DEBUG: Group by list without distribution column is not allowed in distributed INSERT ... SELECT queries +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: generating subplan XXX_1 for CTE a: SELECT key, value FROM mx_coordinator_shouldhaveshards.table_1_rep ORDER BY key, value DESC LIMIT 1 DEBUG: push down of limit count: 1 DEBUG: generating subplan XXX_2 for subquery SELECT int4(count(*)) AS auto_coerced_by_citus_0, (a.key)::text AS auto_coerced_by_citus_1 FROM ((SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) a JOIN mx_coordinator_shouldhaveshards.table_2_rep USING (key)) GROUP BY a.key HAVING (max(table_2_rep.value) OPERATOR(pg_catalog.>=) (SELECT a_1.value FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) a_1)) @@ -301,7 +304,8 @@ inserts AS ( ) SELECT count(*) FROM inserts; DEBUG: generating subplan XXX_1 for CTE stats: SELECT count(key) AS m FROM mx_coordinator_shouldhaveshards.table_1_rep DEBUG: generating subplan XXX_2 for CTE inserts: INSERT INTO mx_coordinator_shouldhaveshards.table_2_rep (key, value) SELECT key, count(*) AS count FROM mx_coordinator_shouldhaveshards.table_1_rep WHERE (key OPERATOR(pg_catalog.>=) (SELECT stats.m FROM (SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m bigint)) stats)) GROUP BY key HAVING (count(*) OPERATOR(pg_catalog.<=) (SELECT stats.m FROM (SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m bigint)) stats)) LIMIT 1 RETURNING table_2_rep.key, table_2_rep.value -DEBUG: LIMIT clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Limit clause is currently unsupported when a subquery references a column from another query DEBUG: push down of limit count: 1 DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT intermediate_result.key, intermediate_result.value FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(key integer, value text)) inserts DEBUG: Subplan XXX_1 will be written to local file diff --git a/src/test/regress/expected/query_single_shard_table.out b/src/test/regress/expected/query_single_shard_table.out index 6e1fe1529..ff04ad50e 100644 --- a/src/test/regress/expected/query_single_shard_table.out +++ b/src/test/regress/expected/query_single_shard_table.out @@ -827,7 +827,7 @@ INSERT INTO nullkey_c1_t1 SELECT * FROM nullkey_c1_t2; SET client_min_messages TO DEBUG2; -- between two non-colocated single-shard tables INSERT INTO nullkey_c1_t1 SELECT * FROM nullkey_c2_t1; -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Distributed planning for a fast-path router query DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator @@ -848,7 +848,7 @@ INSERT INTO nullkey_c1_t1 SELECT * FROM reference_table; SET client_min_messages TO DEBUG2; INSERT INTO nullkey_c1_t1 SELECT * FROM distributed_table; -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Router planner cannot handle multi-shard select queries DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO nullkey_c1_t1 SELECT * FROM citus_local_table; @@ -865,7 +865,8 @@ DEBUG: Distributed planning for a fast-path router query DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO distributed_table SELECT * FROM nullkey_c1_t1; -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: cannot perform distributed INSERT INTO ... SELECT because the partition columns in the source table and subquery do not match +DETAIL: The target table's partition column should correspond to a partition column in the subquery. DEBUG: Distributed planning for a fast-path router query DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator @@ -1197,6 +1198,8 @@ SELECT COALESCE(raw_events_first.user_id, users_ref_table.user_id) FROM raw_events_first RIGHT JOIN (users_ref_table LEFT JOIN raw_events_second ON users_ref_table.user_id = raw_events_second.user_id) ON raw_events_first.user_id = users_ref_table.user_id; +DEBUG: cannot perform a lateral outer join when a distributed subquery references a reference table +DEBUG: Collecting INSERT ... SELECT results on coordinator -- using a full join INSERT INTO agg_events (user_id, value_1_agg) SELECT t1.user_id AS col1, @@ -1227,6 +1230,8 @@ FROM users_ref_table WHERE NOT EXISTS (SELECT 1 FROM raw_events_second WHERE raw_events_second.user_id = users_ref_table.user_id); +DEBUG: correlated subqueries are not supported when the FROM clause contains a reference table +DEBUG: Collecting INSERT ... SELECT results on coordinator -- using inner join INSERT INTO agg_events (user_id) SELECT raw_events_first.user_id @@ -1247,13 +1252,16 @@ WHERE raw_events_first.value_1 IN (10, 11,12) OR users_ref_table.user_id IN (1,2 -- Below "limit / offset clause" test and some others are examples of this. -- limit / offset clause INSERT INTO agg_events (user_id) SELECT raw_events_first.user_id FROM raw_events_first LIMIT 1; -DEBUG: LIMIT clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Limit clause is currently unsupported when a subquery references a column from another query DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO agg_events (user_id) SELECT raw_events_first.user_id FROM raw_events_first OFFSET 1; -DEBUG: OFFSET clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Offset clause is currently unsupported when a subquery references a column from another query DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO agg_events (user_id) SELECT users_ref_table.user_id FROM users_ref_table LIMIT 1; -DEBUG: LIMIT clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Limit clause is currently unsupported when a subquery references a column from another query DEBUG: Collecting INSERT ... SELECT results on coordinator -- using a materialized cte WITH cte AS MATERIALIZED @@ -1265,9 +1273,15 @@ DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO raw_events_second WITH cte AS MATERIALIZED (SELECT * FROM raw_events_first) SELECT user_id * 1000, time, value_1, value_2, value_3, value_4 FROM cte; +DEBUG: cannot push down this subquery +DETAIL: CTEs in subqueries are currently unsupported +DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO raw_events_second (user_id) WITH cte AS MATERIALIZED (SELECT * FROM users_ref_table) SELECT user_id FROM cte; +DEBUG: cannot push down this subquery +DETAIL: CTEs in subqueries are currently unsupported +DEBUG: Collecting INSERT ... SELECT results on coordinator -- using a regular cte WITH cte AS (SELECT * FROM raw_events_first) INSERT INTO raw_events_second @@ -1286,8 +1300,6 @@ INSERT INTO agg_events FROM raw_events_first; DEBUG: CTE sub_cte is going to be inlined via distributed planning -DEBUG: Subqueries without relations are not allowed in distributed INSERT ... SELECT queries -DEBUG: Collecting INSERT ... SELECT results on coordinator -- we still support complex joins via INSERT's cte list .. WITH cte AS ( SELECT DISTINCT(reference_table.a) AS a, 1 AS b @@ -1311,7 +1323,7 @@ WITH cte AS ( ) SELECT (a+5)*2, b FROM cte; DEBUG: CTE cte is going to be inlined via distributed planning -DEBUG: distributed INSERT ... SELECT cannot reference a distributed table without a shard key together with non-colocated distributed tables +DEBUG: cannot perform a lateral outer join when a distributed subquery references a reference table DEBUG: recursively planning left side of the right join since the outer side is a recurring rel DEBUG: recursively planning distributed relation "distributed_table" since it is part of a distributed join node that is outer joined with a recurring rel DEBUG: Wrapping relation "distributed_table" to a subquery @@ -1323,13 +1335,15 @@ INSERT INTO raw_events_first(user_id) (SELECT user_id FROM raw_events_first) INTERSECT (SELECT user_id FROM raw_events_first); -DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Intersect and Except are currently unsupported DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO raw_events_first(user_id) (SELECT user_id FROM users_ref_table) INTERSECT (SELECT user_id FROM raw_events_first); -DEBUG: Set operations are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Intersect and Except are currently unsupported DEBUG: Collecting INSERT ... SELECT results on coordinator -- group by clause inside subquery INSERT INTO agg_events diff --git a/src/test/regress/expected/recurring_outer_join.out b/src/test/regress/expected/recurring_outer_join.out index 3cd7cc6dc..aa8cb906d 100644 --- a/src/test/regress/expected/recurring_outer_join.out +++ b/src/test/regress/expected/recurring_outer_join.out @@ -1969,8 +1969,7 @@ BEGIN; FROM ref_1 t1 LEFT JOIN dist_1 t2 ON (t1.a = t2.a); -DEBUG: cannot perform distributed INSERT INTO ... SELECT because the partition columns in the source table and subquery do not match -DETAIL: The target table's partition column should correspond to a partition column in the subquery. +DEBUG: cannot perform a lateral outer join when a distributed subquery references a reference table DEBUG: recursively planning right side of the left join since the outer side is a recurring rel DEBUG: recursively planning distributed relation "dist_1" "t2" since it is part of a distributed join node that is outer joined with a recurring rel DEBUG: Wrapping relation "dist_1" "t2" to a subquery @@ -1986,9 +1985,7 @@ BEGIN; JOIN (ref_1 t2 LEFT JOIN dist_1 t3 USING(a)) t4 ON (t1.a = t4.a); -DEBUG: cannot perform distributed INSERT INTO ... SELECT because the partition columns in the source table and subquery do not match -DETAIL: Subquery contains an operator in the same position as the target table's partition column. -HINT: Ensure the target table's partition column has a corresponding simple column reference to a distributed table's partition column in the subquery. +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: recursively planning right side of the left join since the outer side is a recurring rel DEBUG: recursively planning distributed relation "dist_1" "t3" since it is part of a distributed join node that is outer joined with a recurring rel DEBUG: Wrapping relation "dist_1" "t3" to a subquery @@ -2005,7 +2002,7 @@ BEGIN; JOIN (ref_1 t2 LEFT JOIN dist_1 t3 USING(a)) t4 ON (t1.a = t4.a); -DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: recursively planning right side of the left join since the outer side is a recurring rel DEBUG: recursively planning distributed relation "dist_1" "t3" since it is part of a distributed join node that is outer joined with a recurring rel DEBUG: Wrapping relation "dist_1" "t3" to a subquery diff --git a/src/test/regress/expected/with_dml.out b/src/test/regress/expected/with_dml.out index b5141db33..f2743a8d9 100644 --- a/src/test/regress/expected/with_dml.out +++ b/src/test/regress/expected/with_dml.out @@ -103,9 +103,7 @@ WITH ids_to_insert AS INSERT INTO distributed_table SELECT DISTINCT ids_to_insert.tenant_id FROM ids_to_insert, distributed_table WHERE distributed_table.tenant_id < ids_to_insert.tenant_id; -DEBUG: cannot perform distributed INSERT INTO ... SELECT because the partition columns in the source table and subquery do not match -DETAIL: Subquery contains an expression that is not a simple column reference in the same position as the target table's partition column. -HINT: Ensure the target table's partition column has a corresponding simple column reference to a distributed table's partition column in the subquery. +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: CTE ids_to_insert is going to be inlined via distributed planning DEBUG: generating subplan XXX_1 for subquery SELECT (((tenant_id)::integer OPERATOR(pg_catalog.*) 100))::text AS tenant_id FROM with_dml.distributed_table WHERE (dept OPERATOR(pg_catalog.>) 7) DEBUG: generating subplan XXX_2 for subquery SELECT DISTINCT ids_to_insert.tenant_id FROM (SELECT intermediate_result.tenant_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(tenant_id text)) ids_to_insert, with_dml.distributed_table WHERE (distributed_table.tenant_id OPERATOR(pg_catalog.<) ids_to_insert.tenant_id) diff --git a/src/test/regress/expected/with_modifying.out b/src/test/regress/expected/with_modifying.out index 9f62271d5..997c62f93 100644 --- a/src/test/regress/expected/with_modifying.out +++ b/src/test/regress/expected/with_modifying.out @@ -956,7 +956,8 @@ WITH first_query AS (INSERT INTO modify_table (id) VALUES (10001)), SET client_min_messages TO debug2; -- pushed down without the insert WITH mb AS (UPDATE modify_table SET val = 3 WHERE id = 3 RETURNING NULL) INSERT INTO modify_table WITH ma AS (SELECT * FROM modify_table LIMIT 10) SELECT count(*) FROM mb; -DEBUG: LIMIT clauses are not allowed in distributed INSERT ... SELECT queries +DEBUG: cannot push down this subquery +DETAIL: Limit clause is currently unsupported when a subquery references a column from another query DEBUG: Creating router plan DEBUG: query has a single distribution column value: 3 DEBUG: Collecting INSERT ... SELECT results on coordinator diff --git a/src/test/regress/multi_1_schedule b/src/test/regress/multi_1_schedule index b5943f899..0936a9625 100644 --- a/src/test/regress/multi_1_schedule +++ b/src/test/regress/multi_1_schedule @@ -65,7 +65,12 @@ test: multi_remove_node_reference_table test: multi_create_table test: multi_create_table_superuser test: multi_master_protocol multi_load_data multi_load_data_superuser multi_behavioral_analytics_create_table -test: multi_behavioral_analytics_basics multi_behavioral_analytics_single_shard_queries multi_insert_select_non_pushable_queries multi_insert_select multi_behavioral_analytics_create_table_superuser +test: multi_behavioral_analytics_basics multi_behavioral_analytics_single_shard_queries multi_behavioral_analytics_create_table_superuser + +# We don't parallelize the following test with the ones above because they're +# not idempotent and hence causing flaky test detection check to fail. +test: multi_insert_select_non_pushable_queries multi_insert_select + test: multi_shard_update_delete recursive_dml_with_different_planners_executors test: insert_select_repartition window_functions dml_recursive multi_insert_select_window test: multi_insert_select_conflict citus_table_triggers alter_table_single_shard_table diff --git a/src/test/regress/multi_schedule b/src/test/regress/multi_schedule index a78ee6088..6dcf41266 100644 --- a/src/test/regress/multi_schedule +++ b/src/test/regress/multi_schedule @@ -12,7 +12,12 @@ test: replicated_table_disable_node test: multi_create_table test: multi_create_table_superuser test: multi_create_table_constraints multi_master_protocol multi_load_data multi_load_data_superuser multi_behavioral_analytics_create_table -test: multi_behavioral_analytics_basics multi_behavioral_analytics_single_shard_queries multi_insert_select_non_pushable_queries multi_insert_select multi_behavioral_analytics_create_table_superuser +test: multi_behavioral_analytics_basics multi_behavioral_analytics_single_shard_queries multi_behavioral_analytics_create_table_superuser + +# We don't parallelize the following test with the ones above because they're +# not idempotent and hence causing flaky test detection check to fail. +test: multi_insert_select_non_pushable_queries multi_insert_select + test: multi_shard_update_delete recursive_dml_with_different_planners_executors test: insert_select_repartition window_functions dml_recursive multi_insert_select_window test: multi_insert_select_conflict citus_table_triggers diff --git a/src/test/regress/spec/isolation_select_vs_all.spec b/src/test/regress/spec/isolation_select_vs_all.spec index a3b65ca77..3611e627e 100644 --- a/src/test/regress/spec/isolation_select_vs_all.spec +++ b/src/test/regress/spec/isolation_select_vs_all.spec @@ -35,7 +35,6 @@ step "s1-adaptive-select" SELECT * FROM select_append AS t1 JOIN select_append AS t2 ON t1.id = t2.int_data ORDER BY 1, 2, 3, 4; } step "s1-insert" { INSERT INTO select_append VALUES(0, 'k', 0); } -step "s1-insert-select" { INSERT INTO select_append SELECT * FROM select_append; } step "s1-update" { UPDATE select_append SET data = 'l' WHERE id = 0; } step "s1-delete" { DELETE FROM select_append WHERE id = 1; } step "s1-truncate" { TRUNCATE select_append; } @@ -65,7 +64,6 @@ step "s2-adaptive-select" SELECT * FROM select_append AS t1 JOIN select_append AS t2 ON t1.id = t2.int_data ORDER BY 1, 2, 3, 4; } step "s2-insert" { INSERT INTO select_append VALUES(0, 'k', 0); } -step "s2-insert-select" { INSERT INTO select_append SELECT * FROM select_append; } step "s2-update" { UPDATE select_append SET data = 'l' WHERE id = 0; } step "s2-delete" { DELETE FROM select_append WHERE id = 1; } step "s2-truncate" { TRUNCATE select_append; } @@ -101,7 +99,6 @@ permutation "s1-initialize" "s1-begin" "s1-adaptive-select" "s2-adaptive-select" // permutations - router SELECT first permutation "s1-initialize" "s1-begin" "s1-router-select" "s2-insert" "s1-commit" "s1-select-count" -permutation "s1-initialize" "s1-begin" "s1-router-select" "s2-insert-select" "s1-commit" "s1-select-count" permutation "s1-initialize" "s1-begin" "s1-router-select" "s2-update" "s1-commit" "s1-select-count" permutation "s1-initialize" "s1-begin" "s1-router-select" "s2-delete" "s1-commit" "s1-select-count" permutation "s1-initialize" "s1-begin" "s1-router-select" "s2-truncate" "s1-commit" "s1-select-count" @@ -119,7 +116,6 @@ permutation "s1-drop" "s1-create-non-distributed-table" "s1-begin" "s1-router-se // permutations - router SELECT second permutation "s1-initialize" "s1-begin" "s1-insert" "s2-router-select" "s1-commit" "s1-select-count" -permutation "s1-initialize" "s1-begin" "s1-insert-select" "s2-router-select" "s1-commit" "s1-select-count" permutation "s1-initialize" "s1-begin" "s1-update" "s2-router-select" "s1-commit" "s1-select-count" permutation "s1-initialize" "s1-begin" "s1-delete" "s2-router-select" "s1-commit" "s1-select-count" permutation "s1-initialize" "s1-begin" "s1-truncate" "s2-router-select" "s1-commit" "s1-select-count" @@ -136,7 +132,6 @@ permutation "s1-drop" "s1-create-non-distributed-table" "s1-begin" "s1-distribut // permutations - real-time SELECT first permutation "s1-initialize" "s1-begin" "s1-real-time-select" "s2-insert" "s1-commit" "s1-select-count" -permutation "s1-initialize" "s1-begin" "s1-real-time-select" "s2-insert-select" "s1-commit" "s1-select-count" permutation "s1-initialize" "s1-begin" "s1-real-time-select" "s2-update" "s1-commit" "s1-select-count" permutation "s1-initialize" "s1-begin" "s1-real-time-select" "s2-delete" "s1-commit" "s1-select-count" permutation "s1-initialize" "s1-begin" "s1-real-time-select" "s2-truncate" "s1-commit" "s1-select-count" @@ -153,7 +148,6 @@ permutation "s1-drop" "s1-create-non-distributed-table" "s1-begin" "s1-real-time // permutations - real-time SELECT second permutation "s1-initialize" "s1-begin" "s1-insert" "s2-real-time-select" "s1-commit" "s1-select-count" -permutation "s1-initialize" "s1-begin" "s1-insert-select" "s2-real-time-select" "s1-commit" "s1-select-count" permutation "s1-initialize" "s1-begin" "s1-update" "s2-real-time-select" "s1-commit" "s1-select-count" permutation "s1-initialize" "s1-begin" "s1-delete" "s2-real-time-select" "s1-commit" "s1-select-count" permutation "s1-initialize" "s1-begin" "s1-truncate" "s2-real-time-select" "s1-commit" "s1-select-count" @@ -169,7 +163,6 @@ permutation "s1-drop" "s1-create-non-distributed-table" "s1-begin" "s1-distribut // permutations - adaptive SELECT first permutation "s1-initialize" "s1-begin" "s1-adaptive-select" "s2-insert" "s1-commit" "s1-select-count" -permutation "s1-initialize" "s1-begin" "s1-adaptive-select" "s2-insert-select" "s1-commit" "s1-select-count" permutation "s1-initialize" "s1-begin" "s1-adaptive-select" "s2-update" "s1-commit" "s1-select-count" permutation "s1-initialize" "s1-begin" "s1-adaptive-select" "s2-delete" "s1-commit" "s1-select-count" permutation "s1-initialize" "s1-begin" "s1-adaptive-select" "s2-truncate" "s1-commit" "s1-select-count" @@ -186,7 +179,6 @@ permutation "s1-drop" "s1-create-non-distributed-table" "s1-begin" "s1-adaptive- // permutations - adaptive SELECT second permutation "s1-initialize" "s1-begin" "s1-insert" "s2-adaptive-select" "s1-commit" "s1-select-count" -permutation "s1-initialize" "s1-begin" "s1-insert-select" "s2-adaptive-select" "s1-commit" "s1-select-count" permutation "s1-initialize" "s1-begin" "s1-update" "s2-adaptive-select" "s1-commit" "s1-select-count" permutation "s1-initialize" "s1-begin" "s1-delete" "s2-adaptive-select" "s1-commit" "s1-select-count" permutation "s1-initialize" "s1-begin" "s1-truncate" "s2-adaptive-select" "s1-commit" "s1-select-count" diff --git a/src/test/regress/sql/insert_select_repartition.sql b/src/test/regress/sql/insert_select_repartition.sql index 526b6eff5..4d13a83f4 100644 --- a/src/test/regress/sql/insert_select_repartition.sql +++ b/src/test/regress/sql/insert_select_repartition.sql @@ -647,6 +647,17 @@ insert into table_with_user_sequences values (1,1); select create_distributed_table('table_with_user_sequences','x'); explain (costs off) insert into table_with_user_sequences select y, x from table_with_user_sequences; +CREATE TABLE dist_table_1(id int); +SELECT create_distributed_table('dist_table_1','id'); +CREATE TABLE dist_table_2(id int); +SELECT create_distributed_table('dist_table_2','id'); + +-- verify that insert select with union can be repartitioned. We cannot push down the query +-- since UNION clause has no FROM clause at top level query. +SELECT public.coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_1(id) SELECT id FROM dist_table_1 UNION SELECT id FROM dist_table_2; +$$); + -- clean-up SET client_min_messages TO WARNING; DROP SCHEMA insert_select_repartition CASCADE; diff --git a/src/test/regress/sql/insert_select_single_shard_table.sql b/src/test/regress/sql/insert_select_single_shard_table.sql index f428752ec..6593ab90b 100644 --- a/src/test/regress/sql/insert_select_single_shard_table.sql +++ b/src/test/regress/sql/insert_select_single_shard_table.sql @@ -325,11 +325,17 @@ JOIN ( ) t2 ON t1.b = t2.b WHERE t2.sum_val > 2; +-- Temporaryly reduce the verbosity to avoid noise +-- in the output of the next query. +SET client_min_messages TO DEBUG1; + -- MultiTaskRouterSelectQuerySupported() is unnecessarily restrictive -- about pushing down queries with DISTINCT ON clause even if the table -- doesn't have a shard key. See https://github.com/citusdata/citus/pull/6752. INSERT INTO nullkey_c1_t1 SELECT DISTINCT ON (a) a, b FROM nullkey_c1_t2; +SET client_min_messages TO DEBUG2; + -- Similarly, we could push down the following query as well. see -- https://github.com/citusdata/citus/pull/6831. INSERT INTO nullkey_c1_t1 SELECT b, SUM(a) OVER (ORDER BY b) AS sum_val FROM nullkey_c1_t1; diff --git a/src/test/regress/sql/multi_insert_select.sql b/src/test/regress/sql/multi_insert_select.sql index b14affddf..4d202041f 100644 --- a/src/test/regress/sql/multi_insert_select.sql +++ b/src/test/regress/sql/multi_insert_select.sql @@ -536,7 +536,7 @@ INSERT INTO agg_events fist_table_agg; ROLLBACK; --- We don't support CTEs that are referenced in the target list +-- We do support CTEs that are referenced in the target list INSERT INTO agg_events WITH sub_cte AS (SELECT 1) SELECT @@ -544,7 +544,7 @@ INSERT INTO agg_events FROM raw_events_first; --- We support set operations via the coordinator +-- We support set operations BEGIN; INSERT INTO @@ -2341,5 +2341,121 @@ join dist_table_2 t2 using (dist_col) limit 1 returning text_col_1; +CREATE TABLE dist_table_3( +dist_col bigint, +int_col integer +); + +SELECT create_distributed_table('dist_table_3', 'dist_col'); + +-- dist_table_2 and dist_table_3 are non-colocated source tables. Repartitioning is also not possible due to +-- different types for distribution columns. Citus would not be able to handle this complex insert select. +INSERT INTO dist_table_1 SELECT dist_table_2.dist_col FROM dist_table_2 JOIN dist_table_3 USING(dist_col); + +CREATE TABLE dist_table_4( +dist_col integer, +int_col integer +); +SELECT create_distributed_table('dist_table_4', 'dist_col'); + +-- Even if target table distribution column is colocated with dist_table_2's distributed column, source tables dist_table_2 and dist_table_4 +-- are non-colocated. Hence, SELECT part of the query should be pulled to coordinator. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_1 SELECT dist_table_2.dist_col FROM dist_table_2 JOIN dist_table_4 ON dist_table_2.dist_col = dist_table_4.int_col; +$$); + +-- For INSERT SELECT, when a lateral query references an outer query, push-down is possible even if limit clause exists in the lateral query. +-- It is because subquery with limit does not need to be merged at coordinator as it is a lateral query. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_1 SELECT d1.dist_col FROM dist_table_1 d1 LEFT JOIN LATERAL (SELECT * FROM dist_table_2 d2 WHERE d1.dist_col = d2.dist_col LIMIT 3) dummy USING(dist_col); +$$); + +-- For INSERT SELECT, when push-down is NOT possible when limit clause exists in a subquery at SELECT part of INSERT SELECT. +-- It is because the subquery with limit needs to be merged at coordinator. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_1 SELECT d1.dist_col FROM dist_table_1 d1 LEFT JOIN (SELECT * FROM dist_table_2 LIMIT 3) dummy USING(dist_col); +$$); + +CREATE TABLE dist_table_5(id int, id2 int); +SELECT create_distributed_table('dist_table_5','id'); +CREATE TABLE dist_table_6(id int, id2 int); +SELECT create_distributed_table('dist_table_6','id'); + +-- verify that insert select with union can be pushed down since UNION clause has FROM clause at top level query. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_5(id) SELECT id FROM (SELECT id FROM dist_table_5 UNION SELECT id FROM dist_table_6) dummy; +$$); + +-- verify that insert select with sublink can be pushed down when tables are colocated. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_5 SELECT id, (SELECT id FROM dist_table_5 WHERE dist_table_5.id = dist_table_6.id) FROM dist_table_6; +$$); + +CREATE TABLE ref_table_1(id int); +SELECT create_reference_table('ref_table_1'); + +-- verify that insert select with sublink cannot be pushed down when from clause does not contain any distributed relation. +INSERT INTO dist_table_5 SELECT id, (SELECT id FROM dist_table_5 WHERE dist_table_5.id = ref_table_1.id) FROM ref_table_1; + +-- verify that insert select cannot be pushed down when we have recurring range table in from clause. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_5 SELECT id, (SELECT id FROM ref_table_1 WHERE id = 1) FROM ref_table_1; +$$); + +-- verify that insert select cannot be pushed down when we have reference table in outside of outer join. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_5 SELECT a.id FROM dist_table_5 a LEFT JOIN ref_table_1 b ON (true) RIGHT JOIN ref_table_1 c ON (true); +$$); + +-- verify that insert select cannot be pushed down when it has a recurring outer join in a subquery. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_5 SELECT id FROM ref_table_1 LEFT JOIN dist_table_5 USING(id); +$$); + +CREATE TABLE loc_table_1(id int); + +-- verify that insert select cannot be pushed down when it contains join between local and distributed tables. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_5 SELECT id FROM dist_table_5 JOIN loc_table_1 USING(id); +$$); + +CREATE VIEW view_1 AS + SELECT id FROM dist_table_6; + +CREATE MATERIALIZED VIEW view_2 AS + SELECT id FROM dist_table_6; + +-- verify that insert select cannot be pushed down when it contains view. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_5 SELECT * FROM view_1; +$$); + +-- verify that insert select cannot be pushed down when it contains materialized view. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_5 SELECT * FROM view_2; +$$); + +CREATE TABLE append_table(id integer, data text, int_data int); +SELECT create_distributed_table('append_table', 'id', 'append'); +SELECT master_create_empty_shard('append_table'); + +-- verify that insert select push down for append tables are not supported. +INSERT INTO append_table SELECT * FROM append_table; + +-- verify that CTEs at top level of INSERT SELECT, that can normally be inlined, would not be inlined by INSERT SELECT pushdown planner +-- and handled by pull to coordinator. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) WITH cte_1 AS (SELECT id FROM dist_table_5 WHERE id = 5) + INSERT INTO dist_table_5 + SELECT id FROM dist_table_5 JOIN cte_1 USING(id); +$$); + +-- verify that CTEs at top level of SELECT part, would be inlined by Postgres and pushed down by INSERT SELECT planner. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_5 + WITH cte_1 AS (SELECT id FROM dist_table_5 WHERE id = 5) + SELECT id FROM dist_table_5 JOIN cte_1 USING(id); +$$); + SET client_min_messages TO ERROR; DROP SCHEMA multi_insert_select CASCADE; diff --git a/src/test/regress/sql/multi_insert_select_non_pushable_queries.sql b/src/test/regress/sql/multi_insert_select_non_pushable_queries.sql index b4654144b..539024141 100644 --- a/src/test/regress/sql/multi_insert_select_non_pushable_queries.sql +++ b/src/test/regress/sql/multi_insert_select_non_pushable_queries.sql @@ -4,6 +4,9 @@ ------------------------------------ ------------------------------------ +CREATE SCHEMA multi_insert_select_non_pushable_queries; +SET search_path = multi_insert_select_non_pushable_queries,public; + -- not pushable since the JOIN is not an equi join INSERT INTO agg_results_third (user_id, value_1_agg) SELECT user_id, array_length(events_table, 1) @@ -716,3 +719,16 @@ FROM ( GROUP BY user_id ) AS shard_union ORDER BY user_lastseen DESC; + +CREATE TABLE dist_table_1(id int); +SELECT create_distributed_table('dist_table_1','id'); +CREATE TABLE dist_table_2(id int, id2 int); +SELECT create_distributed_table('dist_table_2','id2'); + +-- verify that insert select with union can be pulled to coordinator. We cannot push down the query +-- since UNION clause has no FROM clause at top level query. +SELECT coordinator_plan($$ + EXPLAIN (COSTS FALSE) INSERT INTO dist_table_1(id) SELECT id FROM dist_table_1 UNION SELECT id FROM dist_table_2; +$$); + +DROP SCHEMA multi_insert_select_non_pushable_queries CASCADE; From 6a83290d91411298bda0231cb6fe7a619cc05b86 Mon Sep 17 00:00:00 2001 From: Hanefi Onaldi Date: Thu, 18 May 2023 12:45:39 +0300 Subject: [PATCH 045/118] Add ORDER BY clauses to some flaky tests (#6931) I observed a flaky test output [here](https://app.circleci.com/pipelines/github/citusdata/citus/32692/workflows/32464a22-7fd6-440a-9ff7-cfa62f9ff58a/jobs/1126144) and added `ORDER BY` clauses to similar queries in the failing test file. ```diff SELECT pg_identify_object_as_address(classid, objid, objsubid) from pg_catalog.pg_dist_object where objid IN('viewsc.prop_view3'::regclass::oid, 'viewsc.prop_view4'::regclass::oid); pg_identify_object_as_address --------------------------------- - (view,"{viewsc,prop_view3}",{}) (view,"{viewsc,prop_view4}",{}) + (view,"{viewsc,prop_view3}",{}) (2 rows) ``` --- src/test/regress/expected/citus_local_tables_mx.out | 6 +++--- src/test/regress/sql/citus_local_tables_mx.sql | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/regress/expected/citus_local_tables_mx.out b/src/test/regress/expected/citus_local_tables_mx.out index c1539ad7c..363e87e58 100644 --- a/src/test/regress/expected/citus_local_tables_mx.out +++ b/src/test/regress/expected/citus_local_tables_mx.out @@ -805,11 +805,11 @@ SELECT run_command_on_workers($$SELECT COUNT(*) FROM pg_views WHERE viewname LIK (localhost,57638,t,2) (2 rows) -SELECT pg_identify_object_as_address(classid, objid, objsubid) from pg_catalog.pg_dist_object where objid IN('viewsc.prop_view'::regclass::oid, 'viewsc.prop_view2'::regclass::oid); +SELECT pg_identify_object_as_address(classid, objid, objsubid) from pg_catalog.pg_dist_object where objid IN('viewsc.prop_view'::regclass::oid, 'viewsc.prop_view2'::regclass::oid) ORDER BY 1; pg_identify_object_as_address --------------------------------------------------------------------- - (view,"{viewsc,prop_view2}",{}) (view,"{viewsc,prop_view}",{}) + (view,"{viewsc,prop_view2}",{}) (2 rows) -- drop views @@ -857,7 +857,7 @@ SELECT run_command_on_workers($$SELECT COUNT(*) FROM pg_views WHERE viewname LIK (localhost,57638,t,2) (2 rows) -SELECT pg_identify_object_as_address(classid, objid, objsubid) from pg_catalog.pg_dist_object where objid IN('viewsc.prop_view3'::regclass::oid, 'viewsc.prop_view4'::regclass::oid); +SELECT pg_identify_object_as_address(classid, objid, objsubid) from pg_catalog.pg_dist_object where objid IN('viewsc.prop_view3'::regclass::oid, 'viewsc.prop_view4'::regclass::oid) ORDER BY 1; pg_identify_object_as_address --------------------------------------------------------------------- (view,"{viewsc,prop_view3}",{}) diff --git a/src/test/regress/sql/citus_local_tables_mx.sql b/src/test/regress/sql/citus_local_tables_mx.sql index 86307cacb..2bb79a802 100644 --- a/src/test/regress/sql/citus_local_tables_mx.sql +++ b/src/test/regress/sql/citus_local_tables_mx.sql @@ -422,7 +422,7 @@ SELECT citus_add_local_table_to_metadata('view_tbl_1'); SELECT viewname, definition FROM pg_views WHERE viewname LIKE 'prop_view%' ORDER BY viewname; SELECT run_command_on_workers($$SELECT COUNT(*) FROM pg_views WHERE viewname LIKE 'prop_view%';$$); -SELECT pg_identify_object_as_address(classid, objid, objsubid) from pg_catalog.pg_dist_object where objid IN('viewsc.prop_view'::regclass::oid, 'viewsc.prop_view2'::regclass::oid); +SELECT pg_identify_object_as_address(classid, objid, objsubid) from pg_catalog.pg_dist_object where objid IN('viewsc.prop_view'::regclass::oid, 'viewsc.prop_view2'::regclass::oid) ORDER BY 1; -- drop views DROP VIEW viewsc.prop_view; DROP VIEW viewsc.prop_view2; @@ -441,7 +441,7 @@ SELECT run_command_on_workers($$SELECT COUNT(*) FROM pg_views WHERE viewname LIK SELECT citus_add_local_table_to_metadata('view_tbl_2'); -- verify both views are distributed SELECT run_command_on_workers($$SELECT COUNT(*) FROM pg_views WHERE viewname LIKE 'prop_view%';$$); -SELECT pg_identify_object_as_address(classid, objid, objsubid) from pg_catalog.pg_dist_object where objid IN('viewsc.prop_view3'::regclass::oid, 'viewsc.prop_view4'::regclass::oid); +SELECT pg_identify_object_as_address(classid, objid, objsubid) from pg_catalog.pg_dist_object where objid IN('viewsc.prop_view3'::regclass::oid, 'viewsc.prop_view4'::regclass::oid) ORDER BY 1; -- test with fkey cascading create table ref_tb(a int primary key); From f9a5be59b9e73de042472d51bd2f7ba14c790a06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emel=20=C5=9Eim=C5=9Fek?= Date: Thu, 18 May 2023 23:46:32 +0300 Subject: [PATCH 046/118] Run replicate_reference_tables background task as superuser. (#6930) DESCRIPTION: Fixes a bug in background shard rebalancer where the replicate reference tables task fails if the current user is not a superuser. This change is to be backported to earlier releases. We should fix the permissions for replicate_reference_tables on main branch such that it can be run by non-superuser roles. Fixes #6925. Fixes #6926. --- .../distributed/operations/shard_rebalancer.c | 9 ++- .../distributed/utils/reference_table_utils.c | 2 +- src/test/regress/bin/normalize.sed | 2 +- src/test/regress/citus_tests/run_test.py | 8 +++ .../regress/expected/background_rebalance.out | 55 +++++++++++++++++++ .../regress/expected/shard_rebalancer.out | 8 +-- src/test/regress/operations_schedule | 2 +- src/test/regress/sql/background_rebalance.sql | 21 +++++++ 8 files changed, 97 insertions(+), 10 deletions(-) diff --git a/src/backend/distributed/operations/shard_rebalancer.c b/src/backend/distributed/operations/shard_rebalancer.c index 082e0a8b5..0bb27934d 100644 --- a/src/backend/distributed/operations/shard_rebalancer.c +++ b/src/backend/distributed/operations/shard_rebalancer.c @@ -2174,7 +2174,10 @@ RebalanceTableShardsBackground(RebalanceOptions *options, Oid shardReplicationMo quote_literal_cstr(shardTranferModeLabel)); int32 nodesInvolved[] = { 0 }; - BackgroundTask *task = ScheduleBackgroundTask(jobId, GetUserId(), buf.data, 0, + + /* replicate_reference_tables permissions require superuser */ + Oid superUserId = CitusExtensionOwner(); + BackgroundTask *task = ScheduleBackgroundTask(jobId, superUserId, buf.data, 0, NULL, 0, nodesInvolved); replicateRefTablesTaskId = task->taskid; } @@ -2277,7 +2280,7 @@ UpdateShardPlacement(PlacementUpdateEvent *placementUpdateEvent, if (updateType == PLACEMENT_UPDATE_MOVE) { appendStringInfo(placementUpdateCommand, - "SELECT citus_move_shard_placement(%ld,%u,%u,%s)", + "SELECT pg_catalog.citus_move_shard_placement(%ld,%u,%u,%s)", shardId, sourceNode->nodeId, targetNode->nodeId, @@ -2286,7 +2289,7 @@ UpdateShardPlacement(PlacementUpdateEvent *placementUpdateEvent, else if (updateType == PLACEMENT_UPDATE_COPY) { appendStringInfo(placementUpdateCommand, - "SELECT citus_copy_shard_placement(%ld,%u,%u,%s)", + "SELECT pg_catalog.citus_copy_shard_placement(%ld,%u,%u,%s)", shardId, sourceNode->nodeId, targetNode->nodeId, diff --git a/src/backend/distributed/utils/reference_table_utils.c b/src/backend/distributed/utils/reference_table_utils.c index 687ce02a7..314044ab5 100644 --- a/src/backend/distributed/utils/reference_table_utils.c +++ b/src/backend/distributed/utils/reference_table_utils.c @@ -420,7 +420,7 @@ CopyShardPlacementToWorkerNodeQuery(ShardPlacement *sourceShardPlacement, "auto"; appendStringInfo(queryString, - "SELECT citus_copy_shard_placement(" + "SELECT pg_catalog.citus_copy_shard_placement(" UINT64_FORMAT ", %d, %d, " "transfer_mode := %s)", sourceShardPlacement->shardId, diff --git a/src/test/regress/bin/normalize.sed b/src/test/regress/bin/normalize.sed index 731ae659a..56e40ac51 100644 --- a/src/test/regress/bin/normalize.sed +++ b/src/test/regress/bin/normalize.sed @@ -257,7 +257,7 @@ s/pg_cancel_backend\('[0-9]+'::bigint\)/pg_cancel_backend('xxxxx'::bigint)/g s/issuing SELECT pg_cancel_backend\([0-9]+::integer\)/issuing SELECT pg_cancel_backend(xxxxx::integer)/g # shard_rebalancer output for flaky nodeIds -s/issuing SELECT citus_copy_shard_placement\(43[0-9]+,[0-9]+,[0-9]+,'block_writes'\)/issuing SELECT citus_copy_shard_placement(43xxxx,xx,xx,'block_writes')/g +s/issuing SELECT pg_catalog.citus_copy_shard_placement\(43[0-9]+,[0-9]+,[0-9]+,'block_writes'\)/issuing SELECT pg_catalog.citus_copy_shard_placement(43xxxx,xx,xx,'block_writes')/g # node id in run_command_on_all_nodes warning s/Error on node with node id [0-9]+/Error on node with node id xxxxx/g diff --git a/src/test/regress/citus_tests/run_test.py b/src/test/regress/citus_tests/run_test.py index 6ccdc40c9..3249c98a7 100755 --- a/src/test/regress/citus_tests/run_test.py +++ b/src/test/regress/citus_tests/run_test.py @@ -115,6 +115,14 @@ DEPS = { "multi_mx_function_table_reference", ], ), + "background_rebalance": TestDeps( + None, + [ + "multi_test_helpers", + "multi_cluster_management", + ], + worker_count=3, + ), "background_rebalance_parallel": TestDeps( None, [ diff --git a/src/test/regress/expected/background_rebalance.out b/src/test/regress/expected/background_rebalance.out index e4495ccf9..1b8dfdd08 100644 --- a/src/test/regress/expected/background_rebalance.out +++ b/src/test/regress/expected/background_rebalance.out @@ -310,6 +310,61 @@ SELECT public.wait_until_metadata_sync(30000); (1 row) +-- make sure a non-super user can rebalance when there are reference tables to replicate +CREATE TABLE ref_table(a int primary key); +SELECT create_reference_table('ref_table'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +-- add a new node to trigger replicate_reference_tables task +SELECT 1 FROM citus_add_node('localhost', :worker_3_port); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +SET ROLE non_super_user_rebalance; +SELECT 1 FROM citus_rebalance_start(shard_transfer_mode := 'force_logical'); +NOTICE: Scheduled 1 moves as job xxx +DETAIL: Rebalance scheduled as background job +HINT: To monitor progress, run: SELECT * FROM citus_rebalance_status(); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +-- wait for success +SELECT citus_rebalance_wait(); + citus_rebalance_wait +--------------------------------------------------------------------- + +(1 row) + +SELECT state, details from citus_rebalance_status(); + state | details +--------------------------------------------------------------------- + finished | {"tasks": [], "task_state_counts": {"done": 2}} +(1 row) + +RESET ROLE; +-- reset the the number of nodes by removing the previously added node +SELECT 1 FROM citus_drain_node('localhost', :worker_3_port); +NOTICE: Moving shard xxxxx from localhost:xxxxx to localhost:xxxxx ... + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +CALL citus_cleanup_orphaned_resources(); +NOTICE: cleaned up 1 orphaned resources +SELECT 1 FROM citus_remove_node('localhost', :worker_3_port); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + SET client_min_messages TO WARNING; DROP SCHEMA background_rebalance CASCADE; DROP USER non_super_user_rebalance; diff --git a/src/test/regress/expected/shard_rebalancer.out b/src/test/regress/expected/shard_rebalancer.out index 23f1f7373..0ae2193e5 100644 --- a/src/test/regress/expected/shard_rebalancer.out +++ b/src/test/regress/expected/shard_rebalancer.out @@ -222,7 +222,7 @@ NOTICE: issuing SET LOCAL citus.shard_count TO '4'; DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing SET LOCAL citus.shard_replication_factor TO '2'; DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing SELECT citus_copy_shard_placement(43xxxx,xx,xx,'block_writes') +NOTICE: issuing SELECT pg_catalog.citus_copy_shard_placement(43xxxx,xx,xx,'block_writes') DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing COMMIT DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx @@ -245,7 +245,7 @@ NOTICE: issuing SET LOCAL citus.shard_count TO '4'; DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing SET LOCAL citus.shard_replication_factor TO '2'; DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing SELECT citus_copy_shard_placement(43xxxx,xx,xx,'block_writes') +NOTICE: issuing SELECT pg_catalog.citus_copy_shard_placement(43xxxx,xx,xx,'block_writes') DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing COMMIT DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx @@ -268,7 +268,7 @@ NOTICE: issuing SET LOCAL citus.shard_count TO '4'; DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing SET LOCAL citus.shard_replication_factor TO '2'; DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing SELECT citus_copy_shard_placement(43xxxx,xx,xx,'block_writes') +NOTICE: issuing SELECT pg_catalog.citus_copy_shard_placement(43xxxx,xx,xx,'block_writes') DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing COMMIT DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx @@ -291,7 +291,7 @@ NOTICE: issuing SET LOCAL citus.shard_count TO '4'; DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing SET LOCAL citus.shard_replication_factor TO '2'; DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing SELECT citus_copy_shard_placement(43xxxx,xx,xx,'block_writes') +NOTICE: issuing SELECT pg_catalog.citus_copy_shard_placement(43xxxx,xx,xx,'block_writes') DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing COMMIT DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx diff --git a/src/test/regress/operations_schedule b/src/test/regress/operations_schedule index f5e77c835..6dbc303c2 100644 --- a/src/test/regress/operations_schedule +++ b/src/test/regress/operations_schedule @@ -1,10 +1,10 @@ test: multi_test_helpers multi_test_helpers_superuser test: multi_cluster_management test: multi_test_catalog_views +test: worker_copy_table_to_node test: shard_rebalancer_unit test: shard_rebalancer test: background_rebalance -test: worker_copy_table_to_node test: background_rebalance_parallel test: foreign_key_to_reference_shard_rebalance test: multi_move_mx diff --git a/src/test/regress/sql/background_rebalance.sql b/src/test/regress/sql/background_rebalance.sql index 59b296576..86a592d64 100644 --- a/src/test/regress/sql/background_rebalance.sql +++ b/src/test/regress/sql/background_rebalance.sql @@ -110,6 +110,27 @@ SELECT public.wait_for_resource_cleanup(); SELECT 1 FROM citus_remove_node('localhost', :master_port); SELECT public.wait_until_metadata_sync(30000); +-- make sure a non-super user can rebalance when there are reference tables to replicate +CREATE TABLE ref_table(a int primary key); +SELECT create_reference_table('ref_table'); + +-- add a new node to trigger replicate_reference_tables task +SELECT 1 FROM citus_add_node('localhost', :worker_3_port); + +SET ROLE non_super_user_rebalance; +SELECT 1 FROM citus_rebalance_start(shard_transfer_mode := 'force_logical'); + +-- wait for success +SELECT citus_rebalance_wait(); +SELECT state, details from citus_rebalance_status(); + +RESET ROLE; + +-- reset the the number of nodes by removing the previously added node +SELECT 1 FROM citus_drain_node('localhost', :worker_3_port); +CALL citus_cleanup_orphaned_resources(); +SELECT 1 FROM citus_remove_node('localhost', :worker_3_port); + SET client_min_messages TO WARNING; DROP SCHEMA background_rebalance CASCADE; DROP USER non_super_user_rebalance; From 02f815ce1fc0484d6a5bb09cf81a42309d274959 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emel=20=C5=9Eim=C5=9Fek?= Date: Tue, 23 May 2023 14:33:22 +0300 Subject: [PATCH 047/118] Disable local execution when Explain Analyze is requested for a query. (#6892) DESCRIPTION: Fixes a crash when explain analyze is requested for a query that is normally locally executed. When explain analyze is requested for a query, a task with two queries is created. Those two queries are 1. Wrapped Query --> `SELECT ... FROM worker_save_query_explain_analyze(, )` 2. Fetch Query -->` SELECT explain_analyze_output, execution_duration FROM worker_last_saved_explain_analyze();` When the query is locally executed a task with multiple queries causes a crash in production. See the Assert at https://github.com/citusdata/citus/blob/57455dc64dba521514c3bd85b2aab7f3cb2eecf8/src/backend/distributed/executor/tuple_destination.c#:~:text=Assert(task%2D%3EqueryCount%20%3D%3D%201)%3B This becomes a critical issue when auto_explain extension is used. When auto_explain extension is enabled, explain analyze is automatically requested for every query. One possible solution could be not to create two queries for a locally executed query. The fetch part may not have to be a query since the values are available in local variables. Until we enable local execution for explain analyze, it is best to disable local execution. Fixes #6777. --- .../distributed/executor/adaptive_executor.c | 10 ++++-- src/test/regress/citus_tests/run_test.py | 3 ++ src/test/regress/expected/multi_explain.out | 29 +++++++++++++--- src/test/regress/multi_schedule | 3 +- src/test/regress/sql/multi_explain.sql | 33 ++++++++++++++++--- 5 files changed, 67 insertions(+), 11 deletions(-) diff --git a/src/backend/distributed/executor/adaptive_executor.c b/src/backend/distributed/executor/adaptive_executor.c index 22963c458..d88cdbea8 100644 --- a/src/backend/distributed/executor/adaptive_executor.c +++ b/src/backend/distributed/executor/adaptive_executor.c @@ -805,6 +805,8 @@ AdaptiveExecutor(CitusScanState *scanState) TupleDestination *defaultTupleDest = CreateTupleStoreTupleDest(scanState->tuplestorestate, tupleDescriptor); + bool localExecutionSupported = true; + if (RequestedForExplainAnalyze(scanState)) { /* @@ -814,6 +816,12 @@ AdaptiveExecutor(CitusScanState *scanState) UseCoordinatedTransaction(); taskList = ExplainAnalyzeTaskList(taskList, defaultTupleDest, tupleDescriptor, paramListInfo); + + /* + * Multiple queries per task is not supported with local execution. See the Assert in + * TupleDestDestReceiverReceive. + */ + localExecutionSupported = false; } bool hasDependentJobs = job->dependentJobList != NIL; @@ -836,8 +844,6 @@ AdaptiveExecutor(CitusScanState *scanState) TransactionProperties xactProperties = DecideTransactionPropertiesForTaskList( distributedPlan->modLevel, taskList, excludeFromXact); - bool localExecutionSupported = true; - /* * In some rare cases, we have prepared statements that pass a parameter * and never used in the query, mark such parameters' type as Invalid(0), diff --git a/src/test/regress/citus_tests/run_test.py b/src/test/regress/citus_tests/run_test.py index 3249c98a7..ddd0d454e 100755 --- a/src/test/regress/citus_tests/run_test.py +++ b/src/test/regress/citus_tests/run_test.py @@ -103,6 +103,9 @@ DEPS = { "single_node_enterprise": TestDeps(None), "single_node": TestDeps(None), "single_node_truncate": TestDeps(None), + "multi_explain": TestDeps( + "base_schedule", ["multi_insert_select_non_pushable_queries"] + ), "multi_extension": TestDeps(None, repeatable=False), "multi_test_helpers": TestDeps(None), "multi_insert_select": TestDeps("base_schedule"), diff --git a/src/test/regress/expected/multi_explain.out b/src/test/regress/expected/multi_explain.out index eee7a6236..7b533a642 100644 --- a/src/test/regress/expected/multi_explain.out +++ b/src/test/regress/expected/multi_explain.out @@ -8,7 +8,7 @@ SET citus.enable_repartition_joins to ON; -- Ensure tuple data in explain analyze output is the same on all PG versions SET citus.enable_binary_protocol = TRUE; -- Function that parses explain output as JSON -CREATE FUNCTION explain_json(query text) +CREATE OR REPLACE FUNCTION explain_json(query text) RETURNS jsonb AS $BODY$ DECLARE @@ -18,7 +18,7 @@ BEGIN RETURN result; END; $BODY$ LANGUAGE plpgsql; -CREATE FUNCTION explain_analyze_json(query text) +CREATE OR REPLACE FUNCTION explain_analyze_json(query text) RETURNS jsonb AS $BODY$ DECLARE @@ -29,7 +29,7 @@ BEGIN END; $BODY$ LANGUAGE plpgsql; -- Function that parses explain output as XML -CREATE FUNCTION explain_xml(query text) +CREATE OR REPLACE FUNCTION explain_xml(query text) RETURNS xml AS $BODY$ DECLARE @@ -40,7 +40,7 @@ BEGIN END; $BODY$ LANGUAGE plpgsql; -- Function that parses explain output as XML -CREATE FUNCTION explain_analyze_xml(query text) +CREATE OR REPLACE FUNCTION explain_analyze_xml(query text) RETURNS xml AS $BODY$ DECLARE @@ -1276,6 +1276,7 @@ CREATE TABLE lineitem_clone (LIKE lineitem); EXPLAIN (COSTS FALSE) SELECT avg(l_linenumber) FROM lineitem_clone; Aggregate -> Seq Scan on lineitem_clone +DROP TABLE lineitem_clone; -- ensure distributed plans don't break EXPLAIN (COSTS FALSE) SELECT avg(l_linenumber) FROM lineitem; Aggregate @@ -2884,6 +2885,7 @@ Custom Scan (Citus Adaptive) (actual rows=0 loops=1) Filter: ((id IS NOT NULL) AND (name = ANY (ARRAY[$1, $2]))) Rows Removed by Filter: 1 deallocate distributed_insert_select; +DROP TABLE simple; -- prepared cte BEGIN; PREPARE cte_query AS @@ -2976,6 +2978,7 @@ Custom Scan (Citus Adaptive) (actual rows=0 loops=1) Node: host=localhost port=xxxxx dbname=regression -> Seq Scan on users_table_2_570028 users_table_2 (actual rows=0 loops=1) Filter: ((value_1)::text = '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000X'::text) +DROP TABLE users_table_2; -- sorted explain analyze output CREATE TABLE explain_analyze_execution_time (a int); INSERT INTO explain_analyze_execution_time VALUES (2); @@ -3140,5 +3143,23 @@ Custom Scan (Citus Adaptive) (actual rows=0 loops=1) -> Update on tbl_570036 tbl (actual rows=0 loops=1) -> Seq Scan on tbl_570036 tbl (actual rows=0 loops=1) Filter: (a = 1) +-- check when auto explain + analyze is enabled, we do not allow local execution. +CREATE SCHEMA test_auto_explain; +SET search_path TO 'test_auto_explain'; +SELECT citus_set_coordinator_host('localhost'); + +CREATE TABLE test_ref_table (key int PRIMARY KEY); +SELECT create_reference_table('test_ref_table'); + +LOAD 'auto_explain'; +SET auto_explain.log_min_duration = 0; +set auto_explain.log_analyze to true; +-- the following should not be locally executed since explain analyze is on +select * from test_ref_table; +DROP SCHEMA test_auto_explain CASCADE; +select master_remove_node('localhost', :master_port); + +SELECT public.wait_until_metadata_sync(30000); + SET client_min_messages TO ERROR; DROP SCHEMA multi_explain CASCADE; diff --git a/src/test/regress/multi_schedule b/src/test/regress/multi_schedule index 6dcf41266..67fb48fa2 100644 --- a/src/test/regress/multi_schedule +++ b/src/test/regress/multi_schedule @@ -72,7 +72,8 @@ test: tableam # Miscellaneous tests to check our query planning behavior # ---------- test: multi_deparse_shard_query multi_distributed_transaction_id intermediate_results limit_intermediate_size -test: multi_explain hyperscale_tutorial partitioned_intermediate_results distributed_intermediate_results multi_real_time_transaction +test: multi_explain +test: hyperscale_tutorial partitioned_intermediate_results distributed_intermediate_results multi_real_time_transaction test: multi_basic_queries cross_join multi_complex_expressions multi_subquery multi_subquery_complex_queries multi_subquery_behavioral_analytics test: multi_subquery_complex_reference_clause multi_subquery_window_functions multi_view multi_sql_function multi_prepare_sql test: sql_procedure multi_function_in_join row_types materialized_view diff --git a/src/test/regress/sql/multi_explain.sql b/src/test/regress/sql/multi_explain.sql index 4429e46e7..931fb02bc 100644 --- a/src/test/regress/sql/multi_explain.sql +++ b/src/test/regress/sql/multi_explain.sql @@ -13,7 +13,7 @@ SET citus.enable_repartition_joins to ON; SET citus.enable_binary_protocol = TRUE; -- Function that parses explain output as JSON -CREATE FUNCTION explain_json(query text) +CREATE OR REPLACE FUNCTION explain_json(query text) RETURNS jsonb AS $BODY$ DECLARE @@ -24,7 +24,7 @@ BEGIN END; $BODY$ LANGUAGE plpgsql; -CREATE FUNCTION explain_analyze_json(query text) +CREATE OR REPLACE FUNCTION explain_analyze_json(query text) RETURNS jsonb AS $BODY$ DECLARE @@ -36,7 +36,7 @@ END; $BODY$ LANGUAGE plpgsql; -- Function that parses explain output as XML -CREATE FUNCTION explain_xml(query text) +CREATE OR REPLACE FUNCTION explain_xml(query text) RETURNS xml AS $BODY$ DECLARE @@ -48,7 +48,7 @@ END; $BODY$ LANGUAGE plpgsql; -- Function that parses explain output as XML -CREATE FUNCTION explain_analyze_xml(query text) +CREATE OR REPLACE FUNCTION explain_analyze_xml(query text) RETURNS xml AS $BODY$ DECLARE @@ -559,6 +559,7 @@ EXPLAIN (COSTS FALSE, FORMAT YAML) -- ensure local plans display correctly CREATE TABLE lineitem_clone (LIKE lineitem); EXPLAIN (COSTS FALSE) SELECT avg(l_linenumber) FROM lineitem_clone; +DROP TABLE lineitem_clone; -- ensure distributed plans don't break EXPLAIN (COSTS FALSE) SELECT avg(l_linenumber) FROM lineitem; @@ -1048,6 +1049,8 @@ EXPLAIN :default_explain_flags EXECUTE distributed_insert_select('x', 'y'); EXPLAIN :default_analyze_flags EXECUTE distributed_insert_select('x', 'y'); deallocate distributed_insert_select; +DROP TABLE simple; + -- prepared cte BEGIN; PREPARE cte_query AS @@ -1079,6 +1082,8 @@ EXPLAIN :default_analyze_flags execute p4(20,20); -- simple test to confirm we can fetch long (>4KB) plans EXPLAIN (ANALYZE, COSTS OFF, TIMING OFF, SUMMARY OFF) SELECT * FROM users_table_2 WHERE value_1::text = '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000X'; +DROP TABLE users_table_2; + -- sorted explain analyze output CREATE TABLE explain_analyze_execution_time (a int); INSERT INTO explain_analyze_execution_time VALUES (2); @@ -1142,5 +1147,25 @@ PREPARE q2(int_wrapper_type) AS WITH a AS (UPDATE tbl SET b = $1 WHERE a = 1 RET EXPLAIN (COSTS false) EXECUTE q2('(1)'); EXPLAIN :default_analyze_flags EXECUTE q2('(1)'); +-- check when auto explain + analyze is enabled, we do not allow local execution. +CREATE SCHEMA test_auto_explain; +SET search_path TO 'test_auto_explain'; + +SELECT citus_set_coordinator_host('localhost'); + +CREATE TABLE test_ref_table (key int PRIMARY KEY); +SELECT create_reference_table('test_ref_table'); + +LOAD 'auto_explain'; +SET auto_explain.log_min_duration = 0; +set auto_explain.log_analyze to true; + +-- the following should not be locally executed since explain analyze is on +select * from test_ref_table; + +DROP SCHEMA test_auto_explain CASCADE; +select master_remove_node('localhost', :master_port); +SELECT public.wait_until_metadata_sync(30000); + SET client_min_messages TO ERROR; DROP SCHEMA multi_explain CASCADE; From 350a0f64173306c7dadcfabba21979a7ca865bcd Mon Sep 17 00:00:00 2001 From: Jelte Fennema Date: Tue, 23 May 2023 14:38:54 +0200 Subject: [PATCH 048/118] Support running Citus upgrade tests with run_test.py (#6832) Citus upgrade tests require some additional logic to run, because we have a before and after schedule and we need to swap the Citus version in-between. This adds that logic to `run_test.py`. In passing this makes running upgrade tests locally multiple times faster by caching tarballs. --- .flake8 | 1 + src/test/regress/Makefile | 2 +- src/test/regress/citus_tests/common.py | 69 +++++++++++++------ src/test/regress/citus_tests/config.py | 6 +- src/test/regress/citus_tests/run_test.py | 67 +++++++++++++----- .../regress/citus_tests/upgrade/__init__.py | 1 + .../citus_tests/upgrade/citus_upgrade_test.py | 65 +++++++++-------- .../upgrade/generate_citus_tarballs.sh | 36 +++------- 8 files changed, 144 insertions(+), 103 deletions(-) diff --git a/.flake8 b/.flake8 index 18feeb500..74457e31e 100644 --- a/.flake8 +++ b/.flake8 @@ -4,3 +4,4 @@ extend-ignore = E203 # black will truncate to 88 characters usually, but long string literals it # might keep. That's fine in most cases unless it gets really excessive. max-line-length = 150 +exclude = .git,__pycache__,vendor,tmp_* diff --git a/src/test/regress/Makefile b/src/test/regress/Makefile index 51d6fe86a..4bdc7a1b8 100644 --- a/src/test/regress/Makefile +++ b/src/test/regress/Makefile @@ -295,7 +295,7 @@ check-citus-upgrade-mixed-local: all clean-upgrade-artifacts --mixed clean-upgrade-artifacts: - rm -rf $(citus_abs_srcdir)/tmp_citus_tarballs/ $(citus_abs_srcdir)/tmp_citus_upgrade/ /tmp/citus_copy/ + rm -rf $(citus_abs_srcdir)/tmp_citus_upgrade/ /tmp/citus_copy/ clean distclean maintainer-clean: rm -rf input/ output/ diff --git a/src/test/regress/citus_tests/common.py b/src/test/regress/citus_tests/common.py index d0ac688f9..9cf18cc89 100644 --- a/src/test/regress/citus_tests/common.py +++ b/src/test/regress/citus_tests/common.py @@ -48,6 +48,54 @@ TIMEOUT_DEFAULT = timedelta(seconds=int(os.getenv("PG_TEST_TIMEOUT_DEFAULT", "10 FORCE_PORTS = os.getenv("PG_FORCE_PORTS", "NO").lower() not in ("no", "0", "n", "") REGRESS_DIR = pathlib.Path(os.path.realpath(__file__)).parent.parent +REPO_ROOT = REGRESS_DIR.parent.parent.parent +CI = os.environ.get("CI") == "true" + + +def eprint(*args, **kwargs): + """eprint prints to stderr""" + + print(*args, file=sys.stderr, **kwargs) + + +def run(command, *args, check=True, shell=True, silent=False, **kwargs): + """run runs the given command and prints it to stderr""" + + if not silent: + eprint(f"+ {command} ") + if silent: + kwargs.setdefault("stdout", subprocess.DEVNULL) + return subprocess.run(command, *args, check=check, shell=shell, **kwargs) + + +def capture(command, *args, **kwargs): + """runs the given command and returns its output as a string""" + return run(command, *args, stdout=subprocess.PIPE, text=True, **kwargs).stdout + + +PG_CONFIG = os.environ.get("PG_CONFIG", "pg_config") +PG_BINDIR = capture([PG_CONFIG, "--bindir"], shell=False).rstrip() +os.environ["PATH"] = PG_BINDIR + os.pathsep + os.environ["PATH"] + + +def get_pg_major_version(): + full_version_string = run( + "initdb --version", stdout=subprocess.PIPE, encoding="utf-8", silent=True + ).stdout + major_version_string = re.search("[0-9]+", full_version_string) + assert major_version_string is not None + return int(major_version_string.group(0)) + + +PG_MAJOR_VERSION = get_pg_major_version() + +OLDEST_SUPPORTED_CITUS_VERSION_MATRIX = { + 13: "9.5.0", + 14: "10.2.0", + 15: "11.1.5", +} + +OLDEST_SUPPORTED_CITUS_VERSION = OLDEST_SUPPORTED_CITUS_VERSION_MATRIX[PG_MAJOR_VERSION] def initialize_temp_dir(temp_dir): @@ -357,27 +405,6 @@ def initialize_citus_cluster(bindir, datadir, settings, config): config.setup_steps() -def eprint(*args, **kwargs): - """eprint prints to stderr""" - - print(*args, file=sys.stderr, **kwargs) - - -def run(command, *args, check=True, shell=True, silent=False, **kwargs): - """run runs the given command and prints it to stderr""" - - if not silent: - eprint(f"+ {command} ") - if silent: - kwargs.setdefault("stdout", subprocess.DEVNULL) - return subprocess.run(command, *args, check=check, shell=shell, **kwargs) - - -def capture(command, *args, **kwargs): - """runs the given command and returns its output as a string""" - return run(command, *args, stdout=subprocess.PIPE, text=True, **kwargs).stdout - - def sudo(command, *args, shell=True, **kwargs): """ A version of run that prefixes the command with sudo when the process is diff --git a/src/test/regress/citus_tests/config.py b/src/test/regress/citus_tests/config.py index 0973ac58a..a6499a42e 100644 --- a/src/test/regress/citus_tests/config.py +++ b/src/test/regress/citus_tests/config.py @@ -179,10 +179,10 @@ class CitusDefaultClusterConfig(CitusBaseClusterConfig): class CitusUpgradeConfig(CitusBaseClusterConfig): - def __init__(self, arguments): + def __init__(self, arguments, pre_tar, post_tar): super().__init__(arguments) - self.pre_tar_path = arguments["--citus-pre-tar"] - self.post_tar_path = arguments["--citus-post-tar"] + self.pre_tar_path = pre_tar + self.post_tar_path = post_tar self.temp_dir = "./tmp_citus_upgrade" self.new_settings = {"citus.enable_version_checks": "false"} self.user = SUPER_USER_NAME diff --git a/src/test/regress/citus_tests/run_test.py b/src/test/regress/citus_tests/run_test.py index ddd0d454e..79bac0adf 100755 --- a/src/test/regress/citus_tests/run_test.py +++ b/src/test/regress/citus_tests/run_test.py @@ -13,9 +13,10 @@ from contextlib import contextmanager from typing import Optional import common -from common import REGRESS_DIR, capture, run +from common import OLDEST_SUPPORTED_CITUS_VERSION, PG_BINDIR, REGRESS_DIR, capture, run +from upgrade import generate_citus_tarball, run_citus_upgrade_tests -from config import ARBITRARY_SCHEDULE_NAMES, MASTER_VERSION, CitusBaseClusterConfig +from config import ARBITRARY_SCHEDULE_NAMES, CitusBaseClusterConfig, CitusUpgradeConfig def main(): @@ -75,11 +76,19 @@ class TestDeps: schedule: Optional[str] direct_extra_tests: list[str] - def __init__(self, schedule, extra_tests=None, repeatable=True, worker_count=2): + def __init__( + self, + schedule, + extra_tests=None, + repeatable=True, + worker_count=2, + citus_upgrade_infra=False, + ): self.schedule = schedule self.direct_extra_tests = extra_tests or [] self.repeatable = repeatable self.worker_count = worker_count + self.citus_upgrade_infra = citus_upgrade_infra def extra_tests(self): all_deps = OrderedDict() @@ -176,26 +185,29 @@ def run_regress_test(test_name, args): with tmp_schedule(test_name, dependencies, schedule_line, args) as schedule: if "upgrade" in original_schedule: - run_schedule_with_python(schedule) + run_schedule_with_python(test_name, schedule, dependencies) else: run_schedule_with_multiregress(test_name, schedule, dependencies, args) -def run_schedule_with_python(schedule): - bindir = capture("pg_config --bindir").rstrip() +def run_schedule_with_python(test_name, schedule, dependencies): pgxs_path = pathlib.Path(capture("pg_config --pgxs").rstrip()) os.chdir(REGRESS_DIR) os.environ["PATH"] = str(REGRESS_DIR / "bin") + os.pathsep + os.environ["PATH"] os.environ["PG_REGRESS_DIFF_OPTS"] = "-dU10 -w" - os.environ["CITUS_OLD_VERSION"] = f"v{MASTER_VERSION}.0" - args = { + fake_config_args = { "--pgxsdir": str(pgxs_path.parent.parent.parent), - "--bindir": bindir, + "--bindir": PG_BINDIR, + "--mixed": False, } - config = CitusBaseClusterConfig(args) + if dependencies.citus_upgrade_infra: + run_single_citus_upgrade_test(test_name, schedule, fake_config_args) + return + + config = CitusBaseClusterConfig(fake_config_args) common.initialize_temp_dir(config.temp_dir) common.initialize_citus_cluster( config.bindir, config.datadir, config.settings, config @@ -205,6 +217,29 @@ def run_schedule_with_python(schedule): ) +def run_single_citus_upgrade_test(test_name, schedule, fake_config_args): + os.environ["CITUS_OLD_VERSION"] = f"v{OLDEST_SUPPORTED_CITUS_VERSION}" + citus_tarball_path = generate_citus_tarball(f"v{OLDEST_SUPPORTED_CITUS_VERSION}") + config = CitusUpgradeConfig(fake_config_args, citus_tarball_path, None) + + # Before tests are a simple case, because no actual upgrade is needed + if "_before" in test_name: + run_citus_upgrade_tests(config, schedule, None) + return + + before_schedule_name = f"{schedule}_before" + before_schedule_path = REGRESS_DIR / before_schedule_name + + before_test_name = test_name.replace("_after", "_before") + with open(before_schedule_path, "w") as before_schedule_file: + before_schedule_file.write(f"test: {before_test_name}\n") + + try: + run_citus_upgrade_tests(config, before_schedule_name, schedule) + finally: + os.remove(before_schedule_path) + + def run_schedule_with_multiregress(test_name, schedule, dependencies, args): worker_count = needed_worker_count(test_name, dependencies) @@ -249,15 +284,6 @@ def default_base_schedule(test_schedule, args): if "operations" in test_schedule: return "minimal_schedule" - if "after_citus_upgrade" in test_schedule: - print( - f"WARNING: After citus upgrade schedule ({test_schedule}) is not supported." - ) - sys.exit(0) - - if "citus_upgrade" in test_schedule: - return None - if "pg_upgrade" in test_schedule: return "minimal_pg_upgrade_schedule" @@ -316,6 +342,9 @@ def test_dependencies(test_name, test_schedule, schedule_line, args): if test_name in DEPS: return DEPS[test_name] + if "citus_upgrade" in test_schedule: + return TestDeps(None, citus_upgrade_infra=True) + if schedule_line_is_upgrade_after(schedule_line): # upgrade_xxx_after tests always depend on upgrade_xxx_before test_names = schedule_line.split()[1:] diff --git a/src/test/regress/citus_tests/upgrade/__init__.py b/src/test/regress/citus_tests/upgrade/__init__.py index e69de29bb..672b7a32c 100644 --- a/src/test/regress/citus_tests/upgrade/__init__.py +++ b/src/test/regress/citus_tests/upgrade/__init__.py @@ -0,0 +1 @@ +from .citus_upgrade_test import generate_citus_tarball, run_citus_upgrade_tests # noqa diff --git a/src/test/regress/citus_tests/upgrade/citus_upgrade_test.py b/src/test/regress/citus_tests/upgrade/citus_upgrade_test.py index 3ea51d5d9..87b00c83c 100755 --- a/src/test/regress/citus_tests/upgrade/citus_upgrade_test.py +++ b/src/test/regress/citus_tests/upgrade/citus_upgrade_test.py @@ -13,7 +13,7 @@ Options: --mixed Run the verification phase with one node not upgraded. """ -import atexit +import multiprocessing import os import re import subprocess @@ -27,6 +27,7 @@ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) # ignore E402 because these imports require addition to path import common # noqa: E402 import utils # noqa: E402 +from common import CI, PG_MAJOR_VERSION, REPO_ROOT, run # noqa: E402 from utils import USER # noqa: E402 from config import ( # noqa: E402 @@ -41,6 +42,12 @@ from config import ( # noqa: E402 def main(config): + before_upgrade_schedule = get_before_upgrade_schedule(config.mixed_mode) + after_upgrade_schedule = get_after_upgrade_schedule(config.mixed_mode) + run_citus_upgrade_tests(config, before_upgrade_schedule, after_upgrade_schedule) + + +def run_citus_upgrade_tests(config, before_upgrade_schedule, after_upgrade_schedule): install_citus(config.pre_tar_path) common.initialize_temp_dir(config.temp_dir) common.initialize_citus_cluster( @@ -48,23 +55,28 @@ def main(config): ) report_initial_version(config) - before_upgrade_schedule = get_before_upgrade_schedule(config.mixed_mode) run_test_on_coordinator(config, before_upgrade_schedule) remove_citus(config.pre_tar_path) + if after_upgrade_schedule is None: + return + install_citus(config.post_tar_path) restart_databases(config.bindir, config.datadir, config.mixed_mode, config) run_alter_citus(config.bindir, config.mixed_mode, config) verify_upgrade(config, config.mixed_mode, config.node_name_to_ports.values()) - after_upgrade_schedule = get_after_upgrade_schedule(config.mixed_mode) run_test_on_coordinator(config, after_upgrade_schedule) remove_citus(config.post_tar_path) def install_citus(tar_path): - with utils.cd("/"): - subprocess.run(["tar", "xvf", tar_path], check=True) + if tar_path: + with utils.cd("/"): + run(["tar", "xvf", tar_path], shell=False) + else: + with utils.cd(REPO_ROOT): + run(f"make -j{multiprocessing.cpu_count()} -s install") def report_initial_version(config): @@ -90,8 +102,9 @@ def run_test_on_coordinator(config, schedule): def remove_citus(tar_path): - with utils.cd("/"): - remove_tar_files(tar_path) + if tar_path: + with utils.cd("/"): + remove_tar_files(tar_path) def remove_tar_files(tar_path): @@ -171,43 +184,29 @@ def get_after_upgrade_schedule(mixed_mode): return AFTER_CITUS_UPGRADE_COORD_SCHEDULE -# IsRunningOnLocalMachine returns true if the upgrade test is run on -# local machine, in which case the old citus version will be installed -# and it will be upgraded to the current code. -def IsRunningOnLocalMachine(arguments): - return arguments["--citus-old-version"] - - -def generate_citus_tarballs(citus_version): +def generate_citus_tarball(citus_version): tmp_dir = "tmp_citus_tarballs" citus_old_tarpath = os.path.abspath( - os.path.join(tmp_dir, "install-citus{}.tar".format(citus_version)) - ) - citus_new_tarpath = os.path.abspath( - os.path.join(tmp_dir, "install-citusmaster.tar") + os.path.join(tmp_dir, f"install-pg{PG_MAJOR_VERSION}-citus{citus_version}.tar") ) common.initialize_temp_dir_if_not_exists(tmp_dir) dirpath = os.path.dirname(os.path.realpath(__file__)) local_script_path = os.path.join(dirpath, "generate_citus_tarballs.sh") with utils.cd(tmp_dir): - subprocess.check_call([local_script_path, citus_version]) + subprocess.check_call([local_script_path, str(PG_MAJOR_VERSION), citus_version]) - return [citus_old_tarpath, citus_new_tarpath] + return citus_old_tarpath if __name__ == "__main__": args = docopt(__doc__, version="citus_upgrade_test") - if IsRunningOnLocalMachine(args): - citus_tarball_paths = generate_citus_tarballs(args["--citus-old-version"]) - args["--citus-pre-tar"] = citus_tarball_paths[0] - args["--citus-post-tar"] = citus_tarball_paths[1] - config = CitusUpgradeConfig(args) - atexit.register( - common.stop_databases, - config.bindir, - config.datadir, - config.node_name_to_ports, - config.name, - ) + if not CI: + citus_tarball_path = generate_citus_tarball(args["--citus-old-version"]) + config = CitusUpgradeConfig(args, citus_tarball_path, None) + else: + config = CitusUpgradeConfig( + args, args["--citus-pre-tar"], args["--citus-post-tar"] + ) + main(config) diff --git a/src/test/regress/citus_tests/upgrade/generate_citus_tarballs.sh b/src/test/regress/citus_tests/upgrade/generate_citus_tarballs.sh index 440154aa3..08636adb3 100755 --- a/src/test/regress/citus_tests/upgrade/generate_citus_tarballs.sh +++ b/src/test/regress/citus_tests/upgrade/generate_citus_tarballs.sh @@ -2,7 +2,8 @@ set -euxo pipefail -citus_old_version=$1 +pg_version=$1 +citus_old_version=$2 base="$(pwd)" @@ -17,38 +18,22 @@ install_citus_and_tar() { cd "${installdir}" && find . -type f -print >"${builddir}/files.lst" - tar cvf "${basedir}/install-citus${citus_version}.tar" $(cat "${builddir}"/files.lst) - mv "${basedir}/install-citus${citus_version}.tar" "${base}/install-citus${citus_version}.tar" + tar cvf "${basedir}/install-pg${pg_version}-citus${citus_version}.tar" $(cat "${builddir}"/files.lst) + mv "${basedir}/install-pg${pg_version}-citus${citus_version}.tar" "${base}/install-pg${pg_version}-citus${citus_version}.tar" cd "${builddir}" && rm -rf install files.lst && make clean } -build_current() { - citus_version="$1" - basedir="${base}/${citus_version}" - - mkdir -p "${basedir}" - citus_repo=$(git rev-parse --show-toplevel) - - cd "$citus_repo" && cp -R . /tmp/citus_copy - # https://stackoverflow.com/questions/957928/is-there-a-way-to-get-the-git-root-directory-in-one-command - mv /tmp/citus_copy "${basedir}/citus_${citus_version}" - builddir="${basedir}/build" - cd "${basedir}" - - citus_dir=${basedir}/citus_$citus_version - - make -C "${citus_dir}" clean - cd "${citus_dir}" - ./configure --without-libcurl - - install_citus_and_tar -} - build_ext() { citus_version="$1" + # If tarball already exsists we're good + if [ -f "${base}/install-pg${pg_version}-citus${citus_version}.tar" ]; then + return + fi + basedir="${base}/${citus_version}" + rm -rf "${basedir}" mkdir -p "${basedir}" cd "${basedir}" citus_dir=${basedir}/citus_$citus_version @@ -58,5 +43,4 @@ build_ext() { install_citus_and_tar } -build_current "master" build_ext "${citus_old_version}" From 2c7beee56246157eec797eccaad525ad059b7293 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20Ozan=20Akg=C3=BCl?= Date: Tue, 23 May 2023 17:44:07 +0300 Subject: [PATCH 049/118] Fix citus.tenant_stats_limit test by setting it to 2 (#6899) citus.tenant_stats_limit was set to 2 when we were adding tests for it. Then we changed it to 10, making the tests incorrect. This PR fixes that without breaking other tests. --- src/test/regress/expected/citus_stat_tenants.out | 14 ++++++-------- src/test/regress/pg_regress_multi.pl | 2 +- src/test/regress/sql/citus_stat_tenants.sql | 10 +++++----- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/test/regress/expected/citus_stat_tenants.out b/src/test/regress/expected/citus_stat_tenants.out index c1f07ccaa..102f5e9a3 100644 --- a/src/test/regress/expected/citus_stat_tenants.out +++ b/src/test/regress/expected/citus_stat_tenants.out @@ -224,10 +224,8 @@ SELECT tenant_attribute, query_count_in_this_period, score FROM citus_stat_tenan bcde | 3 | 3000000000 2 | 1 | 1000000000 3 | 1 | 1000000000 - 4 | 1 | 1000000000 - cdef | 1 | 1000000000 defg | 1 | 1000000000 -(7 rows) +(5 rows) -- test period passing SELECT citus_stat_tenants_reset(); @@ -439,7 +437,7 @@ SELECT count(*)>=0 FROM dist_tbl_text WHERE a = U&'\0061\0308bc'; (1 row) \c - - - :worker_1_port -SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants ORDER BY tenant_attribute; +SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants(true) ORDER BY tenant_attribute; tenant_attribute | read_count_in_this_period | read_count_in_last_period | query_count_in_this_period | query_count_in_last_period --------------------------------------------------------------------- /*bcde | 1 | 0 | 1 | 0 @@ -456,7 +454,7 @@ SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, q \c - - - :worker_2_port SET search_path TO citus_stat_tenants; -SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants ORDER BY tenant_attribute; +SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants(true) ORDER BY tenant_attribute; tenant_attribute | read_count_in_this_period | read_count_in_last_period | query_count_in_this_period | query_count_in_last_period --------------------------------------------------------------------- /*bcde | 1 | 0 | 1 | 0 @@ -507,7 +505,7 @@ DELETE FROM dist_tbl_text WHERE a = '/b*c/de'; DELETE FROM dist_tbl_text WHERE a = '/bcde'; DELETE FROM dist_tbl_text WHERE a = U&'\0061\0308bc'; DELETE FROM dist_tbl_text WHERE a = 'bcde*'; -SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants_local ORDER BY tenant_attribute; +SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants_local(true) ORDER BY tenant_attribute; tenant_attribute | read_count_in_this_period | read_count_in_last_period | query_count_in_this_period | query_count_in_last_period --------------------------------------------------------------------- /b*c/de | 1 | 0 | 2 | 0 @@ -590,7 +588,7 @@ EXECUTE dist_tbl_text_select_plan('bcde*'); t (1 row) -SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants_local ORDER BY tenant_attribute; +SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants_local(true) ORDER BY tenant_attribute; tenant_attribute | read_count_in_this_period | read_count_in_last_period | query_count_in_this_period | query_count_in_last_period --------------------------------------------------------------------- /b*c/de | 4 | 0 | 5 | 0 @@ -676,7 +674,7 @@ EXECUTE dist_tbl_text_select_plan('bcde*'); \c - - - :worker_2_port SET search_path TO citus_stat_tenants; -SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants ORDER BY tenant_attribute; +SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants(true) ORDER BY tenant_attribute; tenant_attribute | read_count_in_this_period | read_count_in_last_period | query_count_in_this_period | query_count_in_last_period --------------------------------------------------------------------- /b*c/de | 7 | 0 | 8 | 0 diff --git a/src/test/regress/pg_regress_multi.pl b/src/test/regress/pg_regress_multi.pl index 7b02ca5cc..4cc022198 100755 --- a/src/test/regress/pg_regress_multi.pl +++ b/src/test/regress/pg_regress_multi.pl @@ -489,7 +489,7 @@ push(@pgOptions, "citus.enable_manual_changes_to_shards=on"); push(@pgOptions, "citus.allow_unsafe_locks_from_workers=on"); push(@pgOptions, "citus.stat_statements_track = 'all'"); push(@pgOptions, "citus.enable_change_data_capture=on"); -push(@pgOptions, "citus.stat_tenants_limit = 10"); +push(@pgOptions, "citus.stat_tenants_limit = 2"); push(@pgOptions, "citus.stat_tenants_track = 'ALL'"); # Some tests look at shards in pg_class, make sure we can usually see them: diff --git a/src/test/regress/sql/citus_stat_tenants.sql b/src/test/regress/sql/citus_stat_tenants.sql index af44c7f1e..61d9f19b3 100644 --- a/src/test/regress/sql/citus_stat_tenants.sql +++ b/src/test/regress/sql/citus_stat_tenants.sql @@ -158,11 +158,11 @@ SELECT count(*)>=0 FROM dist_tbl_text WHERE a = 'bcde*/'; SELECT count(*)>=0 FROM dist_tbl_text WHERE a = U&'\0061\0308bc'; \c - - - :worker_1_port -SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants ORDER BY tenant_attribute; +SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants(true) ORDER BY tenant_attribute; \c - - - :worker_2_port SET search_path TO citus_stat_tenants; -SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants ORDER BY tenant_attribute; +SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants(true) ORDER BY tenant_attribute; SELECT citus_stat_tenants_reset(); @@ -179,7 +179,7 @@ DELETE FROM dist_tbl_text WHERE a = '/bcde'; DELETE FROM dist_tbl_text WHERE a = U&'\0061\0308bc'; DELETE FROM dist_tbl_text WHERE a = 'bcde*'; -SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants_local ORDER BY tenant_attribute; +SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants_local(true) ORDER BY tenant_attribute; -- test local cached queries & prepared statements @@ -198,7 +198,7 @@ EXECUTE dist_tbl_text_select_plan('/bcde'); EXECUTE dist_tbl_text_select_plan(U&'\0061\0308bc'); EXECUTE dist_tbl_text_select_plan('bcde*'); -SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants_local ORDER BY tenant_attribute; +SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants_local(true) ORDER BY tenant_attribute; \c - - - :master_port SET search_path TO citus_stat_tenants; @@ -221,7 +221,7 @@ EXECUTE dist_tbl_text_select_plan('bcde*'); \c - - - :worker_2_port SET search_path TO citus_stat_tenants; -SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants ORDER BY tenant_attribute; +SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants(true) ORDER BY tenant_attribute; \c - - - :master_port SET search_path TO citus_stat_tenants; From 246b054a7d06b0dd27584fe854ccfd3f9a7f6c61 Mon Sep 17 00:00:00 2001 From: Onur Tirtir Date: Fri, 26 May 2023 10:49:58 +0300 Subject: [PATCH 050/118] Add support for schema-based-sharding via a GUC (#6866) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DESCRIPTION: Adds citus.enable_schema_based_sharding GUC that allows sharding the database based on schemas when enabled. * Refactor the logic that automatically creates Citus managed tables * Refactor CreateSingleShardTable() to allow specifying colocation id instead * Add support for schema-based-sharding via a GUC ### What this PR is about: Add **citus.enable_schema_based_sharding GUC** to enable schema-based sharding. Each schema created while this GUC is ON will be considered as a tenant schema. Later on, regardless of whether the GUC is ON or OFF, any table created in a tenant schema will be converted to a single shard distributed table (without a shard key). All the tenant tables that belong to a particular schema will be co-located with each other and will have a shard count of 1. We introduce a new metadata table --pg_dist_tenant_schema-- to do the bookkeeping for tenant schemas: ```sql psql> \d pg_dist_tenant_schema Table "pg_catalog.pg_dist_tenant_schema" ┌───────────────┬─────────┬───────────┬──────────┬─────────┐ │ Column │ Type │ Collation │ Nullable │ Default │ ├───────────────┼─────────┼───────────┼──────────┼─────────┤ │ schemaid │ oid │ │ not null │ │ │ colocationid │ integer │ │ not null │ │ └───────────────┴─────────┴───────────┴──────────┴─────────┘ Indexes: "pg_dist_tenant_schema_pkey" PRIMARY KEY, btree (schemaid) "pg_dist_tenant_schema_unique_colocationid_index" UNIQUE, btree (colocationid) psql> table pg_dist_tenant_schema; ┌───────────┬───────────────┐ │ schemaid │ colocationid │ ├───────────┼───────────────┤ │ 41963 │ 91 │ │ 41962 │ 90 │ └───────────┴───────────────┘ (2 rows) ``` Colocation id column of pg_dist_tenant_schema can never be NULL even for the tenant schemas that don't have a tenant table yet. This is because, we assign colocation ids to tenant schemas as soon as they are created. That way, we can keep associating tenant schemas with particular colocation groups even if all the tenant tables of a tenant schema are dropped and recreated later on. When a tenant schema is dropped, we delete the corresponding row from pg_dist_tenant_schema. In that case, we delete the corresponding colocation group from pg_dist_colocation as well. ### Future work for 12.0 release: We're building schema-based sharding on top of the infrastructure that adds support for creating distributed tables without a shard key (https://github.com/citusdata/citus/pull/6867). However, not all the operations that can be done on distributed tables without a shard key necessarily make sense (in the same way) in the context of schema-based sharding. For example, we need to think about what happens if user attempts altering schema of a tenant table. We will tackle such scenarios in a future PR. We will also add a new UDF --citus.schema_tenant_set() or such-- to allow users to use an existing schema as a tenant schema, and another one --citus.schema_tenant_unset() or such-- to stop using a schema as a tenant schema in future PRs. --- configure | 1 + .../citus_add_local_table_to_metadata.c | 36 + .../commands/create_distributed_table.c | 77 +- .../commands/distribute_object_ops.c | 4 +- .../commands/drop_distributed_table.c | 22 +- src/backend/distributed/commands/schema.c | 66 +- .../commands/schema_based_sharding.c | 244 +++ src/backend/distributed/commands/table.c | 168 +- .../distributed/commands/utility_hook.c | 95 +- .../connection/placement_connection.c | 2 +- .../distributed/metadata/metadata_cache.c | 36 + .../distributed/metadata/metadata_sync.c | 177 +++ src/backend/distributed/shared_library_init.c | 14 + .../distributed/sql/citus--11.3-1--12.0-1.sql | 22 + .../sql/downgrades/citus--12.0-1--11.3-1.sql | 24 +- .../sql/udfs/citus_drop_trigger/12.0-1.sql | 68 + .../sql/udfs/citus_drop_trigger/latest.sql | 13 +- .../udfs/citus_finish_pg_upgrade/12.0-1.sql | 160 ++ .../udfs/citus_finish_pg_upgrade/latest.sql | 2 + .../12.0-1.sql | 8 + .../latest.sql | 8 + .../12.0-1.sql | 8 + .../latest.sql | 8 + .../12.0-1.sql | 7 + .../latest.sql | 7 + .../udfs/citus_prepare_pg_upgrade/12.0-1.sql | 82 + .../udfs/citus_prepare_pg_upgrade/latest.sql | 3 + .../distributed/utils/colocation_utils.c | 35 +- .../utils/tenant_schema_metadata.c | 239 +++ src/include/distributed/colocation_utils.h | 1 + src/include/distributed/commands.h | 14 +- src/include/distributed/metadata_cache.h | 3 + src/include/distributed/metadata_sync.h | 4 + src/include/distributed/metadata_utility.h | 33 +- .../distributed/pg_dist_tenant_schema.h | 41 + .../distributed/tenant_schema_metadata.h | 32 + src/test/regress/after_pg_upgrade_schedule | 2 +- src/test/regress/before_pg_upgrade_schedule | 2 +- src/test/regress/citus_tests/run_test.py | 1 + .../expected/auto_undist_citus_local.out | 5 +- .../expected/create_single_shard_table.out | 4 +- .../isolation_schema_based_sharding.out | 37 + src/test/regress/expected/multi_extension.out | 68 +- .../regress/expected/multi_metadata_sync.out | 133 +- .../expected/multi_metadata_sync_0.out | 133 +- .../expected/schema_based_sharding.out | 1368 +++++++++++++++++ src/test/regress/expected/single_node.out | 22 + src/test/regress/expected/single_node_0.out | 22 + .../expected/upgrade_list_citus_objects.out | 6 +- .../upgrade_schema_based_sharding_after.out | 98 ++ .../upgrade_schema_based_sharding_before.out | 10 + src/test/regress/isolation_schedule | 1 + src/test/regress/multi_1_schedule | 1 + .../spec/isolation_schema_based_sharding.spec | 42 + .../regress/sql/auto_undist_citus_local.sql | 7 +- .../regress/sql/create_single_shard_table.sql | 4 +- src/test/regress/sql/multi_extension.sql | 48 + .../regress/sql/schema_based_sharding.sql | 941 ++++++++++++ src/test/regress/sql/single_node.sql | 17 + .../upgrade_schema_based_sharding_after.sql | 70 + .../upgrade_schema_based_sharding_before.sql | 14 + 61 files changed, 4552 insertions(+), 268 deletions(-) create mode 100644 src/backend/distributed/commands/schema_based_sharding.c create mode 100644 src/backend/distributed/sql/udfs/citus_drop_trigger/12.0-1.sql create mode 100644 src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/12.0-1.sql create mode 100644 src/backend/distributed/sql/udfs/citus_internal_add_tenant_schema/12.0-1.sql create mode 100644 src/backend/distributed/sql/udfs/citus_internal_add_tenant_schema/latest.sql create mode 100644 src/backend/distributed/sql/udfs/citus_internal_delete_tenant_schema/12.0-1.sql create mode 100644 src/backend/distributed/sql/udfs/citus_internal_delete_tenant_schema/latest.sql create mode 100644 src/backend/distributed/sql/udfs/citus_internal_unregister_tenant_schema_globally/12.0-1.sql create mode 100644 src/backend/distributed/sql/udfs/citus_internal_unregister_tenant_schema_globally/latest.sql create mode 100644 src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/12.0-1.sql create mode 100644 src/backend/distributed/utils/tenant_schema_metadata.c create mode 100644 src/include/distributed/pg_dist_tenant_schema.h create mode 100644 src/include/distributed/tenant_schema_metadata.h create mode 100644 src/test/regress/expected/isolation_schema_based_sharding.out create mode 100644 src/test/regress/expected/schema_based_sharding.out create mode 100644 src/test/regress/expected/upgrade_schema_based_sharding_after.out create mode 100644 src/test/regress/expected/upgrade_schema_based_sharding_before.out create mode 100644 src/test/regress/spec/isolation_schema_based_sharding.spec create mode 100644 src/test/regress/sql/schema_based_sharding.sql create mode 100644 src/test/regress/sql/upgrade_schema_based_sharding_after.sql create mode 100644 src/test/regress/sql/upgrade_schema_based_sharding_before.sql diff --git a/configure b/configure index 65e89799f..73a841bee 100755 --- a/configure +++ b/configure @@ -6160,3 +6160,4 @@ if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi + diff --git a/src/backend/distributed/commands/citus_add_local_table_to_metadata.c b/src/backend/distributed/commands/citus_add_local_table_to_metadata.c index 41ec7e1b1..4b2b76995 100644 --- a/src/backend/distributed/commands/citus_add_local_table_to_metadata.c +++ b/src/backend/distributed/commands/citus_add_local_table_to_metadata.c @@ -18,6 +18,7 @@ */ #include "postgres.h" +#include "miscadmin.h" #include "access/genam.h" #include "access/htup_details.h" @@ -1500,3 +1501,38 @@ FinalizeCitusLocalTableCreation(Oid relationId) InvalidateForeignKeyGraph(); } } + + +/* + * ShouldAddNewTableToMetadata takes a relationId and returns true if we need to add a + * newly created table to metadata, false otherwise. + * For partitions and temporary tables, ShouldAddNewTableToMetadata returns false. + * For other tables created, returns true, if we are on a coordinator that is added + * as worker, and ofcourse, if the GUC use_citus_managed_tables is set to on. + */ +bool +ShouldAddNewTableToMetadata(Oid relationId) +{ + if (get_rel_persistence(relationId) == RELPERSISTENCE_TEMP || + PartitionTableNoLock(relationId)) + { + /* + * Shouldn't add table to metadata if it's a temp table, or a partition. + * Creating partitions of a table that is added to metadata is already handled. + */ + return false; + } + + if (AddAllLocalTablesToMetadata && !IsBinaryUpgrade && + IsCoordinator() && CoordinatorAddedAsWorkerNode()) + { + /* + * We have verified that the GUC is set to true, and we are not upgrading, + * and we are on the coordinator that is added as worker node. + * So return true here, to add this newly created table to metadata. + */ + return true; + } + + return false; +} diff --git a/src/backend/distributed/commands/create_distributed_table.c b/src/backend/distributed/commands/create_distributed_table.c index e55196100..71fe0b9e3 100644 --- a/src/backend/distributed/commands/create_distributed_table.c +++ b/src/backend/distributed/commands/create_distributed_table.c @@ -111,8 +111,8 @@ typedef struct { int shardCount; bool shardCountIsStrict; - char *colocateWithTableName; char *distributionColumnName; + ColocationParam colocationParam; } DistributedTableParams; @@ -296,7 +296,11 @@ create_distributed_table(PG_FUNCTION_ARGS) "when the distribution column is null "))); } - CreateSingleShardTable(relationId, colocateWithTableName); + ColocationParam colocationParam = { + .colocationParamType = COLOCATE_WITH_TABLE_LIKE_OPT, + .colocateWithTableName = colocateWithTableName, + }; + CreateSingleShardTable(relationId, colocationParam); } PG_RETURN_VOID(); @@ -1006,7 +1010,10 @@ CreateDistributedTable(Oid relationId, char *distributionColumnName, } DistributedTableParams distributedTableParams = { - .colocateWithTableName = colocateWithTableName, + .colocationParam = { + .colocateWithTableName = colocateWithTableName, + .colocationParamType = COLOCATE_WITH_TABLE_LIKE_OPT + }, .shardCount = shardCount, .shardCountIsStrict = shardCountIsStrict, .distributionColumnName = distributionColumnName @@ -1031,10 +1038,10 @@ CreateReferenceTable(Oid relationId) * single shard distributed table that doesn't have a shard key. */ void -CreateSingleShardTable(Oid relationId, char *colocateWithTableName) +CreateSingleShardTable(Oid relationId, ColocationParam colocationParam) { DistributedTableParams distributedTableParams = { - .colocateWithTableName = colocateWithTableName, + .colocationParam = colocationParam, .shardCount = 1, .shardCountIsStrict = true, .distributionColumnName = NULL @@ -1155,9 +1162,23 @@ CreateCitusTable(Oid relationId, CitusTableType tableType, * ColocationIdForNewTable assumes caller acquires lock on relationId. In our case, * our caller already acquired lock on relationId. */ - uint32 colocationId = ColocationIdForNewTable(relationId, tableType, - distributedTableParams, - distributionColumn); + uint32 colocationId = INVALID_COLOCATION_ID; + if (distributedTableParams && + distributedTableParams->colocationParam.colocationParamType == + COLOCATE_WITH_COLOCATION_ID) + { + colocationId = distributedTableParams->colocationParam.colocationId; + } + else + { + /* + * ColocationIdForNewTable assumes caller acquires lock on relationId. In our case, + * our caller already acquired lock on relationId. + */ + colocationId = ColocationIdForNewTable(relationId, tableType, + distributedTableParams, + distributionColumn); + } EnsureRelationCanBeDistributed(relationId, distributionColumn, citusTableParams.distributionMethod, @@ -1257,7 +1278,10 @@ CreateCitusTable(Oid relationId, CitusTableType tableType, MemoryContextReset(citusPartitionContext); DistributedTableParams childDistributedTableParams = { - .colocateWithTableName = parentRelationName, + .colocationParam = { + .colocationParamType = COLOCATE_WITH_TABLE_LIKE_OPT, + .colocateWithTableName = parentRelationName, + }, .shardCount = distributedTableParams->shardCount, .shardCountIsStrict = false, .distributionColumnName = distributedTableParams->distributionColumnName, @@ -1308,30 +1332,39 @@ DecideCitusTableParams(CitusTableType tableType, { case HASH_DISTRIBUTED: { + Assert(distributedTableParams->colocationParam.colocationParamType == + COLOCATE_WITH_TABLE_LIKE_OPT); + citusTableParams.distributionMethod = DISTRIBUTE_BY_HASH; citusTableParams.replicationModel = DecideDistTableReplicationModel(DISTRIBUTE_BY_HASH, - distributedTableParams-> + distributedTableParams->colocationParam. colocateWithTableName); break; } case APPEND_DISTRIBUTED: { + Assert(distributedTableParams->colocationParam.colocationParamType == + COLOCATE_WITH_TABLE_LIKE_OPT); + citusTableParams.distributionMethod = DISTRIBUTE_BY_APPEND; citusTableParams.replicationModel = DecideDistTableReplicationModel(APPEND_DISTRIBUTED, - distributedTableParams-> + distributedTableParams->colocationParam. colocateWithTableName); break; } case RANGE_DISTRIBUTED: { + Assert(distributedTableParams->colocationParam.colocationParamType == + COLOCATE_WITH_TABLE_LIKE_OPT); + citusTableParams.distributionMethod = DISTRIBUTE_BY_RANGE; citusTableParams.replicationModel = DecideDistTableReplicationModel(RANGE_DISTRIBUTED, - distributedTableParams-> + distributedTableParams->colocationParam. colocateWithTableName); break; } @@ -1768,7 +1801,11 @@ ColocationIdForNewTable(Oid relationId, CitusTableType tableType, if (tableType == APPEND_DISTRIBUTED || tableType == RANGE_DISTRIBUTED) { - if (!IsColocateWithDefault(distributedTableParams->colocateWithTableName)) + Assert(distributedTableParams->colocationParam.colocationParamType == + COLOCATE_WITH_TABLE_LIKE_OPT); + char *colocateWithTableName = + distributedTableParams->colocationParam.colocateWithTableName; + if (!IsColocateWithDefault(colocateWithTableName)) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot distribute relation"), @@ -1795,8 +1832,13 @@ ColocationIdForNewTable(Oid relationId, CitusTableType tableType, Oid distributionColumnCollation = distributionColumn ? get_typcollation(distributionColumnType) : InvalidOid; + Assert(distributedTableParams->colocationParam.colocationParamType == + COLOCATE_WITH_TABLE_LIKE_OPT); + char *colocateWithTableName = + distributedTableParams->colocationParam.colocateWithTableName; + /* get an advisory lock to serialize concurrent default group creations */ - if (IsColocateWithDefault(distributedTableParams->colocateWithTableName)) + if (IsColocateWithDefault(colocateWithTableName)) { AcquireColocationDefaultLock(); } @@ -1808,10 +1850,9 @@ ColocationIdForNewTable(Oid relationId, CitusTableType tableType, distributedTableParams->shardCount, distributedTableParams-> shardCountIsStrict, - distributedTableParams-> colocateWithTableName); - if (IsColocateWithDefault(distributedTableParams->colocateWithTableName) && + if (IsColocateWithDefault(colocateWithTableName) && (colocationId != INVALID_COLOCATION_ID)) { /* @@ -1824,7 +1865,7 @@ ColocationIdForNewTable(Oid relationId, CitusTableType tableType, if (colocationId == INVALID_COLOCATION_ID) { - if (IsColocateWithDefault(distributedTableParams->colocateWithTableName)) + if (IsColocateWithDefault(colocateWithTableName)) { /* * Generate a new colocation ID and insert a pg_dist_colocation @@ -1835,7 +1876,7 @@ ColocationIdForNewTable(Oid relationId, CitusTableType tableType, distributionColumnType, distributionColumnCollation); } - else if (IsColocateWithNone(distributedTableParams->colocateWithTableName)) + else if (IsColocateWithNone(colocateWithTableName)) { /* * Generate a new colocation ID and insert a pg_dist_colocation diff --git a/src/backend/distributed/commands/distribute_object_ops.c b/src/backend/distributed/commands/distribute_object_ops.c index 017cb6537..ce8d16275 100644 --- a/src/backend/distributed/commands/distribute_object_ops.c +++ b/src/backend/distributed/commands/distribute_object_ops.c @@ -294,8 +294,8 @@ static DistributeObjectOps Any_CreateForeignServer = { static DistributeObjectOps Any_CreateSchema = { .deparse = DeparseCreateSchemaStmt, .qualify = NULL, - .preprocess = PreprocessCreateSchemaStmt, - .postprocess = NULL, + .preprocess = NULL, + .postprocess = PostprocessCreateSchemaStmt, .operationType = DIST_OPS_CREATE, .address = CreateSchemaStmtObjectAddress, .markDistributed = true, diff --git a/src/backend/distributed/commands/drop_distributed_table.c b/src/backend/distributed/commands/drop_distributed_table.c index 1e214adf3..24dd8e892 100644 --- a/src/backend/distributed/commands/drop_distributed_table.c +++ b/src/backend/distributed/commands/drop_distributed_table.c @@ -90,7 +90,27 @@ master_remove_partition_metadata(PG_FUNCTION_ARGS) DeletePartitionRow(relationId); - DeleteColocationGroupIfNoTablesBelong(colocationId); + /* + * We want to keep using the same colocation group for the tenant even if + * all the tables that belong to it are dropped and new tables are created + * for the tenant etc. For this reason, if a colocation group belongs to a + * tenant schema, we don't delete the colocation group even if there are no + * tables that belong to it. + * + * We do the same if system catalog cannot find the schema of the table + * because this means that the whole schema is dropped. + * + * In that case, we want to delete the colocation group regardless of + * whether the schema is a tenant schema or not. Even more, calling + * IsTenantSchema() with InvalidOid would cause an error, hence we check + * whether the schema is valid beforehand. + */ + bool missingOk = true; + Oid schemaId = get_namespace_oid(schemaName, missingOk); + if (!OidIsValid(schemaId) || !IsTenantSchema(schemaId)) + { + DeleteColocationGroupIfNoTablesBelong(colocationId); + } PG_RETURN_VOID(); } diff --git a/src/backend/distributed/commands/schema.c b/src/backend/distributed/commands/schema.c index 66189f39e..f1d1472c5 100644 --- a/src/backend/distributed/commands/schema.c +++ b/src/backend/distributed/commands/schema.c @@ -19,6 +19,7 @@ #include "catalog/namespace.h" #include "catalog/pg_class.h" #include "catalog/pg_namespace.h" +#include "distributed/colocation_utils.h" #include "distributed/commands.h" #include #include "distributed/commands/utility_hook.h" @@ -33,6 +34,7 @@ #include "distributed/resource_lock.h" #include #include +#include "distributed/tenant_schema_metadata.h" #include "distributed/version_compat.h" #include "nodes/parsenodes.h" #include "utils/fmgroids.h" @@ -45,16 +47,18 @@ static List * FilterDistributedSchemas(List *schemas); static bool SchemaHasDistributedTableWithFKey(char *schemaName); static bool ShouldPropagateCreateSchemaStmt(void); static List * GetGrantCommandsFromCreateSchemaStmt(Node *node); +static bool CreateSchemaStmtCreatesTable(CreateSchemaStmt *stmt); /* - * PreprocessCreateSchemaStmt is called during the planning phase for + * PostprocessCreateSchemaStmt is called during the planning phase for * CREATE SCHEMA .. */ List * -PreprocessCreateSchemaStmt(Node *node, const char *queryString, - ProcessUtilityContext processUtilityContext) +PostprocessCreateSchemaStmt(Node *node, const char *queryString) { + CreateSchemaStmt *createSchemaStmt = castNode(CreateSchemaStmt, node); + if (!ShouldPropagateCreateSchemaStmt()) { return NIL; @@ -74,6 +78,38 @@ PreprocessCreateSchemaStmt(Node *node, const char *queryString, commands = list_concat(commands, GetGrantCommandsFromCreateSchemaStmt(node)); + if (ShouldUseSchemaBasedSharding(createSchemaStmt->schemaname)) + { + /* for now, we don't allow creating tenant tables when creating the schema itself */ + if (CreateSchemaStmtCreatesTable(createSchemaStmt)) + { + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot create tenant table in CREATE " + "SCHEMA statement"), + errhint("Use CREATE TABLE statement to create " + "tenant tables."))); + } + + bool missingOk = false; + Oid schemaId = get_namespace_oid(createSchemaStmt->schemaname, missingOk); + + /* + * Register the tenant schema on the coordinator and save the command + * to register it on the workers. + */ + int shardCount = 1; + int replicationFactor = 1; + Oid distributionColumnType = InvalidOid; + Oid distributionColumnCollation = InvalidOid; + uint32 colocationId = CreateColocationGroup( + shardCount, replicationFactor, distributionColumnType, + distributionColumnCollation); + + InsertTenantSchemaLocally(schemaId, colocationId); + + commands = lappend(commands, TenantSchemaInsertCommand(schemaId, colocationId)); + } + commands = lappend(commands, ENABLE_DDL_PROPAGATION); return NodeDDLTaskList(NON_COORDINATOR_NODES, commands); @@ -402,3 +438,27 @@ GetGrantCommandsFromCreateSchemaStmt(Node *node) return commands; } + + +/* + * CreateSchemaStmtCreatesTable returns true if given CreateSchemaStmt + * creates a table using "schema_element" list. + */ +static bool +CreateSchemaStmtCreatesTable(CreateSchemaStmt *stmt) +{ + Node *element = NULL; + foreach_ptr(element, stmt->schemaElts) + { + /* + * CREATE TABLE AS and CREATE FOREIGN TABLE commands cannot be + * used as schema_elements anyway, so we don't need to check them. + */ + if (IsA(element, CreateStmt)) + { + return true; + } + } + + return false; +} diff --git a/src/backend/distributed/commands/schema_based_sharding.c b/src/backend/distributed/commands/schema_based_sharding.c new file mode 100644 index 000000000..678835821 --- /dev/null +++ b/src/backend/distributed/commands/schema_based_sharding.c @@ -0,0 +1,244 @@ +/*------------------------------------------------------------------------- + * schema_based_sharding.c + * + * Routines for schema-based sharding. + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" +#include "miscadmin.h" +#include "catalog/pg_namespace_d.h" +#include "commands/extension.h" +#include "distributed/argutils.h" +#include "distributed/backend_data.h" +#include "distributed/colocation_utils.h" +#include "distributed/commands.h" +#include "distributed/metadata_sync.h" +#include "distributed/multi_partitioning_utils.h" +#include "distributed/tenant_schema_metadata.h" +#include "distributed/metadata/distobject.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/syscache.h" + + +PG_FUNCTION_INFO_V1(citus_internal_unregister_tenant_schema_globally); + + +/* controlled via citus.enable_schema_based_sharding GUC */ +bool EnableSchemaBasedSharding = false; + + +/* + * ShouldUseSchemaBasedSharding returns true if schema given name should be + * used as a tenant schema. + */ +bool +ShouldUseSchemaBasedSharding(char *schemaName) +{ + if (!EnableSchemaBasedSharding) + { + return false; + } + + if (IsBinaryUpgrade) + { + return false; + } + + /* + * Citus utility hook skips processing CREATE SCHEMA commands while an + * extension is being created. For this reason, we don't expect to get + * here while an extension is being created. + */ + Assert(!creating_extension); + + /* + * CREATE SCHEMA commands issued by internal backends are not meant to + * create tenant schemas but to sync metadata. + * + * On workers, Citus utility hook skips processing CREATE SCHEMA commands + * because we temporarily disable DDL propagation on workers when sending + * CREATE SCHEMA commands. For this reason, right now this check is a bit + * redundant but we prefer to keep it here to be on the safe side. + */ + if (IsCitusInternalBackend() || IsRebalancerInternalBackend()) + { + return false; + } + + /* + * Not do an oid comparison based on PG_PUBLIC_NAMESPACE because + * we want to treat "public" schema in the same way even if it's + * recreated. + */ + if (strcmp(schemaName, "public") == 0) + { + return false; + } + + return true; +} + + +/* + * ShouldCreateTenantSchemaTable returns true if we should create a tenant + * schema table for given relationId. + */ +bool +ShouldCreateTenantSchemaTable(Oid relationId) +{ + if (IsBinaryUpgrade) + { + return false; + } + + /* + * CREATE TABLE commands issued by internal backends are not meant to + * create tenant tables but to sync metadata. + */ + if (IsCitusInternalBackend() || IsRebalancerInternalBackend()) + { + return false; + } + + Oid schemaId = get_rel_namespace(relationId); + return IsTenantSchema(schemaId); +} + + +/* + * CreateTenantSchemaTable creates a tenant table with given relationId. + * + * This means creating a single shard distributed table without a shard + * key and colocating it with the other tables in its schema. + */ +void +CreateTenantSchemaTable(Oid relationId) +{ + if (!IsCoordinator()) + { + /* + * We don't support creating tenant tables from workers. We could + * let ShouldCreateTenantSchemaTable() to return false to allow users + * to create a local table as usual but that would be confusing because + * it might sound like we allow creating tenant tables from workers. + * For this reason, we prefer to throw an error instead. + * + * Indeed, CreateSingleShardTable() would already do so but we + * prefer to throw an error with a more meaningful message, rather + * than saying "operation is not allowed on this node". + */ + ereport(ERROR, (errmsg("cannot create a tenant table from a worker node"), + errhint("Connect to the coordinator node and try again."))); + } + + if (IsForeignTable(relationId)) + { + /* throw an error that is nicer than the one CreateSingleShardTable() would throw */ + ereport(ERROR, (errmsg("cannot create a tenant table from a foreign table"))); + } + else if (PartitionTable(relationId)) + { + ErrorIfIllegalPartitioningInTenantSchema(PartitionParentOid(relationId), + relationId); + } + + /* + * We don't expect this to happen because ShouldCreateTenantSchemaTable() + * should've already verified that; but better to check. + */ + Oid schemaId = get_rel_namespace(relationId); + uint32 colocationId = SchemaIdGetTenantColocationId(schemaId); + if (colocationId == INVALID_COLOCATION_ID) + { + ereport(ERROR, (errmsg("schema \"%s\" is not a tenant schema", + get_namespace_name(schemaId)))); + } + + ColocationParam colocationParam = { + .colocationParamType = COLOCATE_WITH_COLOCATION_ID, + .colocationId = colocationId, + }; + CreateSingleShardTable(relationId, colocationParam); +} + + +/* + * ErrorIfIllegalPartitioningInTenantSchema throws an error if the + * partitioning relationship between the parent and the child is illegal + * because they are in different schemas while one of them is a tenant table. + */ +void +ErrorIfIllegalPartitioningInTenantSchema(Oid parentRelationId, Oid partitionRelationId) +{ + Oid partitionSchemaId = get_rel_namespace(partitionRelationId); + Oid parentSchemaId = get_rel_namespace(parentRelationId); + + bool partitionIsTenantTable = IsTenantSchema(partitionSchemaId); + bool parentIsTenantTable = IsTenantSchema(parentSchemaId); + + bool illegalPartitioning = false; + if (partitionIsTenantTable != parentIsTenantTable) + { + illegalPartitioning = true; + } + else if (partitionIsTenantTable && parentIsTenantTable) + { + illegalPartitioning = (parentSchemaId != partitionSchemaId); + } + + if (illegalPartitioning) + { + ereport(ERROR, (errmsg("partitioning with tenant tables is not " + "supported when the parent and the child " + "are in different schemas"))); + } +} + + +/* + * citus_internal_unregister_tenant_schema_globally removes given schema from + * the tenant schema metadata table, deletes the colocation group of the schema + * and sends the command to do the same on the workers. + */ +Datum +citus_internal_unregister_tenant_schema_globally(PG_FUNCTION_ARGS) +{ + PG_ENSURE_ARGNOTNULL(0, "schema_id"); + Oid schemaId = PG_GETARG_OID(0); + + PG_ENSURE_ARGNOTNULL(1, "schema_name"); + text *schemaName = PG_GETARG_TEXT_PP(1); + char *schemaNameStr = text_to_cstring(schemaName); + + /* + * Skip on workers because we expect this to be called from the coordinator + * only via drop hook. + */ + if (!IsCoordinator()) + { + PG_RETURN_VOID(); + } + + /* make sure that the schema is dropped already */ + HeapTuple namespaceTuple = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(schemaId)); + if (HeapTupleIsValid(namespaceTuple)) + { + ReleaseSysCache(namespaceTuple); + + ereport(ERROR, (errmsg("schema is expected to be already dropped " + "because this function is only expected to " + "be called from Citus drop hook"))); + } + + uint32 tenantSchemaColocationId = SchemaIdGetTenantColocationId(schemaId); + + DeleteTenantSchemaLocally(schemaId); + SendCommandToWorkersWithMetadata(TenantSchemaDeleteCommand(schemaNameStr)); + + DeleteColocationGroup(tenantSchemaColocationId); + + PG_RETURN_VOID(); +} diff --git a/src/backend/distributed/commands/table.c b/src/backend/distributed/commands/table.c index 87a6a11b8..2286c292a 100644 --- a/src/backend/distributed/commands/table.c +++ b/src/backend/distributed/commands/table.c @@ -229,6 +229,17 @@ PostprocessCreateTableStmt(CreateStmt *createStatement, const char *queryString) { PostprocessCreateTableStmtForeignKeys(createStatement); + bool missingOk = false; + Oid relationId = RangeVarGetRelid(createStatement->relation, NoLock, missingOk); + Oid schemaId = get_rel_namespace(relationId); + if (createStatement->ofTypename && IsTenantSchema(schemaId)) + { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot create a tenant table by using CREATE TABLE " + "OF syntax"))); + } + if (createStatement->inhRelations != NIL) { if (createStatement->partbound != NULL) @@ -239,15 +250,31 @@ PostprocessCreateTableStmt(CreateStmt *createStatement, const char *queryString) else { /* process CREATE TABLE ... INHERITS ... */ + + if (IsTenantSchema(schemaId)) + { + ereport(ERROR, (errmsg("tenant tables cannot inherit or " + "be inherited"))); + } + RangeVar *parentRelation = NULL; foreach_ptr(parentRelation, createStatement->inhRelations) { - bool missingOk = false; Oid parentRelationId = RangeVarGetRelid(parentRelation, NoLock, missingOk); Assert(parentRelationId != InvalidOid); - if (IsCitusTable(parentRelationId)) + /* + * Throw a better error message if the user tries to inherit a + * tenant table or if the user tries to inherit from a tenant + * table. + */ + if (IsTenantSchema(get_rel_namespace(parentRelationId))) + { + ereport(ERROR, (errmsg("tenant tables cannot inherit or " + "be inherited"))); + } + else if (IsCitusTable(parentRelationId)) { /* here we error out if inheriting a distributed table */ ereport(ERROR, (errmsg("non-distributed tables cannot inherit " @@ -282,6 +309,15 @@ PostprocessCreateTableStmtForeignKeys(CreateStmt *createStatement) bool missingOk = false; Oid relationId = RangeVarGetRelid(createStatement->relation, NoLock, missingOk); + if (ShouldCreateTenantSchemaTable(relationId)) + { + /* + * Avoid unnecessarily adding the table into metadata if we will + * distribute it as a tenant table later. + */ + return; + } + /* * As we are just creating the table, we cannot have foreign keys that our * relation is referenced. So we use INCLUDE_REFERENCING_CONSTRAINTS here. @@ -378,6 +414,8 @@ PostprocessCreateTableStmtPartitionOf(CreateStmt *createStatement, const } } + ErrorIfIllegalPartitioningInTenantSchema(PartitionParentOid(relationId), relationId); + /* * If a partition is being created and if its parent is a distributed * table, we will distribute this table as well. @@ -385,9 +423,8 @@ PostprocessCreateTableStmtPartitionOf(CreateStmt *createStatement, const if (IsCitusTable(parentRelationId)) { /* - * We can create Citus local tables and single-shard distributed tables - * right away, without switching to sequential mode, because they are going to - * have only one shard. + * We can create Citus local tables right away, without switching to + * sequential mode, because they are going to have only one shard. */ if (IsCitusTableType(parentRelationId, CITUS_LOCAL_TABLE)) { @@ -396,25 +433,7 @@ PostprocessCreateTableStmtPartitionOf(CreateStmt *createStatement, const return; } - char *parentRelationName = generate_qualified_relation_name(parentRelationId); - - if (IsCitusTableType(parentRelationId, SINGLE_SHARD_DISTRIBUTED)) - { - CreateSingleShardTable(relationId, parentRelationName); - return; - } - - Var *parentDistributionColumn = DistPartitionKeyOrError(parentRelationId); - char *distributionColumnName = - ColumnToColumnName(parentRelationId, (Node *) parentDistributionColumn); - char parentDistributionMethod = DISTRIBUTE_BY_HASH; - - SwitchToSequentialAndLocalExecutionIfPartitionNameTooLong(parentRelationId, - relationId); - - CreateDistributedTable(relationId, distributionColumnName, - parentDistributionMethod, ShardCount, false, - parentRelationName); + DistributePartitionUsingParent(parentRelationId, relationId); } } @@ -477,6 +496,9 @@ PreprocessAlterTableStmtAttachPartition(AlterTableStmt *alterTableStatement, return NIL; } + ErrorIfIllegalPartitioningInTenantSchema(parentRelationId, + partitionRelationId); + if (!IsCitusTable(parentRelationId)) { /* @@ -612,13 +634,26 @@ DistributePartitionUsingParent(Oid parentCitusRelationId, Oid partitionRelationI { char *parentRelationName = generate_qualified_relation_name(parentCitusRelationId); - if (!HasDistributionKey(parentCitusRelationId)) + /* + * We can create tenant tables and single shard tables right away, without + * switching to sequential mode, because they are going to have only one shard. + */ + if (ShouldCreateTenantSchemaTable(partitionRelationId)) + { + CreateTenantSchemaTable(partitionRelationId); + return; + } + else if (!HasDistributionKey(parentCitusRelationId)) { /* * If the parent is null key distributed, we should distribute the partition * with null distribution key as well. */ - CreateSingleShardTable(partitionRelationId, parentRelationName); + ColocationParam colocationParam = { + .colocationParamType = COLOCATE_WITH_TABLE_LIKE_OPT, + .colocateWithTableName = parentRelationName, + }; + CreateSingleShardTable(partitionRelationId, colocationParam); return; } @@ -4056,3 +4091,84 @@ ErrorIfTableHasIdentityColumn(Oid relationId) relation_close(relation, NoLock); } + + +/* + * ConvertNewTableIfNecessary converts the given table to a tenant schema + * table or a Citus managed table if necessary. + * + * Input node is expected to be a CreateStmt or a CreateTableAsStmt. + */ +void +ConvertNewTableIfNecessary(Node *createStmt) +{ + /* + * Need to increment command counter so that next command + * can see the new table. + */ + CommandCounterIncrement(); + + if (IsA(createStmt, CreateTableAsStmt)) + { + CreateTableAsStmt *createTableAsStmt = (CreateTableAsStmt *) createStmt; + + bool missingOk = false; + Oid createdRelationId = RangeVarGetRelid(createTableAsStmt->into->rel, + NoLock, missingOk); + + if (ShouldCreateTenantSchemaTable(createdRelationId)) + { + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot create a tenant table using " + "CREATE TABLE AS or SELECT INTO " + "statements"))); + } + + /* + * We simply ignore the tables created by using that syntax when using + * Citus managed tables. + */ + return; + } + + CreateStmt *baseCreateTableStmt = (CreateStmt *) createStmt; + + bool missingOk = false; + Oid createdRelationId = RangeVarGetRelid(baseCreateTableStmt->relation, + NoLock, missingOk); + + /* not try to convert the table if it already exists and IF NOT EXISTS syntax is used */ + if (baseCreateTableStmt->if_not_exists && IsCitusTable(createdRelationId)) + { + return; + } + + /* + * Check ShouldCreateTenantSchemaTable() before ShouldAddNewTableToMetadata() + * because we don't want to unnecessarily add the table into metadata + * (as a Citus managed table) before distributing it as a tenant table. + */ + if (ShouldCreateTenantSchemaTable(createdRelationId)) + { + /* + * We skip creating tenant schema table if the table is a partition + * table because in that case PostprocessCreateTableStmt() should've + * already created a tenant schema table from the partition table. + */ + if (!PartitionTable(createdRelationId)) + { + CreateTenantSchemaTable(createdRelationId); + } + } + else if (ShouldAddNewTableToMetadata(createdRelationId)) + { + /* + * Here we set autoConverted to false, since the user explicitly + * wants these tables to be added to metadata, by setting the + * GUC use_citus_managed_tables to true. + */ + bool autoConverted = false; + bool cascade = true; + CreateCitusLocalTable(createdRelationId, cascade, autoConverted); + } +} diff --git a/src/backend/distributed/commands/utility_hook.c b/src/backend/distributed/commands/utility_hook.c index 1eb6f7321..212091569 100644 --- a/src/backend/distributed/commands/utility_hook.c +++ b/src/backend/distributed/commands/utility_hook.c @@ -116,7 +116,6 @@ static void PostStandardProcessUtility(Node *parsetree); static void DecrementUtilityHookCountersIfNecessary(Node *parsetree); static bool IsDropSchemaOrDB(Node *parsetree); static bool ShouldCheckUndistributeCitusLocalTables(void); -static bool ShouldAddNewTableToMetadata(Node *parsetree); /* * ProcessUtilityParseTree is a convenience method to create a PlannedStmt out of @@ -344,26 +343,32 @@ multi_ProcessUtility(PlannedStmt *pstmt, } ResetConstraintDropped(); + /* + * We're only interested in top-level CREATE TABLE commands + * to create a tenant schema table or a Citus managed table. + */ if (context == PROCESS_UTILITY_TOPLEVEL && - ShouldAddNewTableToMetadata(parsetree)) + (IsA(parsetree, CreateStmt) || + IsA(parsetree, CreateForeignTableStmt) || + IsA(parsetree, CreateTableAsStmt))) { - /* - * Here we need to increment command counter so that next command - * can see the new table. - */ - CommandCounterIncrement(); - CreateStmt *createTableStmt = (CreateStmt *) parsetree; - Oid relationId = RangeVarGetRelid(createTableStmt->relation, - NoLock, false); + Node *createStmt = NULL; + if (IsA(parsetree, CreateTableAsStmt)) + { + createStmt = parsetree; + } + else + { + /* + * Not directly cast to CreateStmt to guard against the case where + * the definition of CreateForeignTableStmt changes in future. + */ + createStmt = + IsA(parsetree, CreateStmt) ? parsetree : + (Node *) &(((CreateForeignTableStmt *) parsetree)->base); + } - /* - * Here we set autoConverted to false, since the user explicitly - * wants these tables to be added to metadata, by setting the - * GUC use_citus_managed_tables to true. - */ - bool autoConverted = false; - bool cascade = true; - CreateCitusLocalTable(relationId, cascade, autoConverted); + ConvertNewTableIfNecessary(createStmt); } } @@ -1060,60 +1065,6 @@ ShouldCheckUndistributeCitusLocalTables(void) } -/* - * ShouldAddNewTableToMetadata takes a Node* and returns true if we need to add a - * newly created table to metadata, false otherwise. - * This function checks whether the given Node* is a CREATE TABLE statement. - * For partitions and temporary tables, ShouldAddNewTableToMetadata returns false. - * For other tables created, returns true, if we are on a coordinator that is added - * as worker, and ofcourse, if the GUC use_citus_managed_tables is set to on. - */ -static bool -ShouldAddNewTableToMetadata(Node *parsetree) -{ - CreateStmt *createTableStmt; - - if (IsA(parsetree, CreateStmt)) - { - createTableStmt = (CreateStmt *) parsetree; - } - else if (IsA(parsetree, CreateForeignTableStmt)) - { - CreateForeignTableStmt *createForeignTableStmt = - (CreateForeignTableStmt *) parsetree; - createTableStmt = (CreateStmt *) &(createForeignTableStmt->base); - } - else - { - /* if the command is not CREATE [FOREIGN] TABLE, we can early return false */ - return false; - } - - if (createTableStmt->relation->relpersistence == RELPERSISTENCE_TEMP || - createTableStmt->partbound != NULL) - { - /* - * Shouldn't add table to metadata if it's a temp table, or a partition. - * Creating partitions of a table that is added to metadata is already handled. - */ - return false; - } - - if (AddAllLocalTablesToMetadata && !IsBinaryUpgrade && - IsCoordinator() && CoordinatorAddedAsWorkerNode()) - { - /* - * We have verified that the GUC is set to true, and we are not upgrading, - * and we are on the coordinator that is added as worker node. - * So return true here, to add this newly created table to metadata. - */ - return true; - } - - return false; -} - - /* * NotifyUtilityHookConstraintDropped sets ConstraintDropped to true to tell us * last command dropped a table constraint. diff --git a/src/backend/distributed/connection/placement_connection.c b/src/backend/distributed/connection/placement_connection.c index 225bf9708..cc7962e37 100644 --- a/src/backend/distributed/connection/placement_connection.c +++ b/src/backend/distributed/connection/placement_connection.c @@ -958,7 +958,7 @@ ResetShardPlacementAssociation(struct MultiConnection *connection) /* - * ResetPlacementConnectionManagement() disassociates connections from + * ResetPlacementConnectionManagement() dissociates connections from * placements and shards. This will be called at the end of XACT_EVENT_COMMIT * and XACT_EVENT_ABORT. */ diff --git a/src/backend/distributed/metadata/metadata_cache.c b/src/backend/distributed/metadata/metadata_cache.c index cfcaa4e65..ae81e6d8e 100644 --- a/src/backend/distributed/metadata/metadata_cache.c +++ b/src/backend/distributed/metadata/metadata_cache.c @@ -178,6 +178,7 @@ typedef struct MetadataCacheData Oid distColocationRelationId; Oid distColocationConfigurationIndexId; Oid distPartitionRelationId; + Oid distTenantSchemaRelationId; Oid distPartitionLogicalRelidIndexId; Oid distPartitionColocationidIndexId; Oid distShardLogicalRelidIndexId; @@ -188,6 +189,8 @@ typedef struct MetadataCacheData Oid distPlacementGroupidIndexId; Oid distTransactionRelationId; Oid distTransactionGroupIndexId; + Oid distTenantSchemaPrimaryKeyIndexId; + Oid distTenantSchemaUniqueColocationIdIndexId; Oid citusCatalogNamespaceId; Oid copyFormatTypeId; Oid readIntermediateResultFuncId; @@ -2843,6 +2846,39 @@ DistColocationConfigurationIndexId(void) } +/* return oid of pg_dist_tenant_schema relation */ +Oid +DistTenantSchemaRelationId(void) +{ + CachedRelationLookup("pg_dist_tenant_schema", + &MetadataCache.distTenantSchemaRelationId); + + return MetadataCache.distTenantSchemaRelationId; +} + + +/* return oid of pg_dist_tenant_schema_pkey index */ +Oid +DistTenantSchemaPrimaryKeyIndexId(void) +{ + CachedRelationLookup("pg_dist_tenant_schema_pkey", + &MetadataCache.distTenantSchemaPrimaryKeyIndexId); + + return MetadataCache.distTenantSchemaPrimaryKeyIndexId; +} + + +/* return oid of pg_dist_tenant_schema_unique_colocationid_index index */ +Oid +DistTenantSchemaUniqueColocationIdIndexId(void) +{ + CachedRelationLookup("pg_dist_tenant_schema_unique_colocationid_index", + &MetadataCache.distTenantSchemaUniqueColocationIdIndexId); + + return MetadataCache.distTenantSchemaUniqueColocationIdIndexId; +} + + /* return oid of pg_dist_partition relation */ Oid DistPartitionRelationId(void) diff --git a/src/backend/distributed/metadata/metadata_sync.c b/src/backend/distributed/metadata/metadata_sync.c index f9c80942a..558644a82 100644 --- a/src/backend/distributed/metadata/metadata_sync.c +++ b/src/backend/distributed/metadata/metadata_sync.c @@ -40,6 +40,7 @@ #include "distributed/backend_data.h" #include "distributed/citus_ruleutils.h" #include "distributed/colocation_utils.h" +#include "distributed/tenant_schema_metadata.h" #include "distributed/commands.h" #include "distributed/deparser.h" #include "distributed/distribution_column.h" @@ -60,6 +61,7 @@ #include "distributed/pg_dist_colocation.h" #include "distributed/pg_dist_node.h" #include "distributed/pg_dist_shard.h" +#include "distributed/pg_dist_tenant_schema.h" #include "distributed/relation_access_tracking.h" #include "distributed/remote_commands.h" #include "distributed/resource_lock.h" @@ -144,6 +146,8 @@ static char * ColocationGroupCreateCommand(uint32 colocationId, int shardCount, Oid distributionColumnType, Oid distributionColumnCollation); static char * ColocationGroupDeleteCommand(uint32 colocationId); +static char * RemoteSchemaIdExpressionById(Oid schemaId); +static char * RemoteSchemaIdExpressionByName(char *schemaName); static char * RemoteTypeIdExpression(Oid typeId); static char * RemoteCollationIdExpression(Oid colocationId); @@ -170,6 +174,8 @@ PG_FUNCTION_INFO_V1(citus_internal_update_relation_colocation); PG_FUNCTION_INFO_V1(citus_internal_add_object_metadata); PG_FUNCTION_INFO_V1(citus_internal_add_colocation_metadata); PG_FUNCTION_INFO_V1(citus_internal_delete_colocation_metadata); +PG_FUNCTION_INFO_V1(citus_internal_add_tenant_schema); +PG_FUNCTION_INFO_V1(citus_internal_delete_tenant_schema); static bool got_SIGTERM = false; @@ -3788,6 +3794,52 @@ citus_internal_delete_colocation_metadata(PG_FUNCTION_ARGS) } +/* + * citus_internal_add_tenant_schema is an internal UDF to + * call InsertTenantSchemaLocally on a remote node. + * + * None of the parameters are allowed to be NULL. To set the colocation + * id to NULL in metadata, use INVALID_COLOCATION_ID. + */ +Datum +citus_internal_add_tenant_schema(PG_FUNCTION_ARGS) +{ + CheckCitusVersion(ERROR); + + PG_ENSURE_ARGNOTNULL(0, "schema_id"); + Oid schemaId = PG_GETARG_OID(0); + + PG_ENSURE_ARGNOTNULL(1, "colocation_id"); + uint32 colocationId = PG_GETARG_INT32(1); + + InsertTenantSchemaLocally(schemaId, colocationId); + + PG_RETURN_VOID(); +} + + +/* + * citus_internal_delete_tenant_schema is an internal UDF to + * call DeleteTenantSchemaLocally on a remote node. + * + * The schemaId parameter is not allowed to be NULL. Morever, input schema is + * expected to be dropped already because this function is called from Citus + * drop hook and only used to clean up metadata after the schema is dropped. + */ +Datum +citus_internal_delete_tenant_schema(PG_FUNCTION_ARGS) +{ + CheckCitusVersion(ERROR); + + PG_ENSURE_ARGNOTNULL(0, "schema_id"); + Oid schemaId = PG_GETARG_OID(0); + + DeleteTenantSchemaLocally(schemaId); + + PG_RETURN_VOID(); +} + + /* * SyncNewColocationGroup synchronizes a new pg_dist_colocation entry to a worker. */ @@ -3937,6 +3989,72 @@ ColocationGroupDeleteCommand(uint32 colocationId) } +/* + * TenantSchemaInsertCommand returns a command to call + * citus_internal_add_tenant_schema(). + */ +char * +TenantSchemaInsertCommand(Oid schemaId, uint32 colocationId) +{ + StringInfo command = makeStringInfo(); + appendStringInfo(command, + "SELECT pg_catalog.citus_internal_add_tenant_schema(%s, %u)", + RemoteSchemaIdExpressionById(schemaId), colocationId); + + return command->data; +} + + +/* + * TenantSchemaDeleteCommand returns a command to call + * citus_internal_delete_tenant_schema(). + */ +char * +TenantSchemaDeleteCommand(char *schemaName) +{ + StringInfo command = makeStringInfo(); + appendStringInfo(command, + "SELECT pg_catalog.citus_internal_delete_tenant_schema(%s)", + RemoteSchemaIdExpressionByName(schemaName)); + + return command->data; +} + + +/* + * RemoteSchemaIdExpressionById returns an expression in text form that + * can be used to obtain the OID of the schema with given schema id on a + * different node when included in a query string. + */ +static char * +RemoteSchemaIdExpressionById(Oid schemaId) +{ + char *schemaName = get_namespace_name(schemaId); + if (schemaName == NULL) + { + ereport(ERROR, (errmsg("schema with OID %u does not exist", schemaId))); + } + + return RemoteSchemaIdExpressionByName(schemaName); +} + + +/* + * RemoteSchemaIdExpressionByName returns an expression in text form that + * can be used to obtain the OID of the schema with given schema name on a + * different node when included in a query string. + */ +static char * +RemoteSchemaIdExpressionByName(char *schemaName) +{ + StringInfo regnamespaceExpr = makeStringInfo(); + appendStringInfo(regnamespaceExpr, "%s::regnamespace", + quote_literal_cstr(quote_identifier(schemaName))); + + return regnamespaceExpr->data; +} + + /* * SetMetadataSyncNodesFromNodeList sets list of nodes that needs to be metadata * synced among given node list into metadataSyncContext. @@ -4331,6 +4449,14 @@ SyncDistributedObjects(MetadataSyncContext *context) SendDistTableMetadataCommands(context); SendDistObjectCommands(context); + /* + * Commands to insert pg_dist_tenant_schema entries. + * + * Need to be done after syncing distributed objects because the schemas + * need to exist on the worker. + */ + SendTenantSchemaMetadataCommands(context); + /* * After creating each table, handle the inter table relationship between * those tables. @@ -4403,6 +4529,10 @@ SendMetadataDeletionCommands(MetadataSyncContext *context) /* remove pg_dist_colocation entries */ SendOrCollectCommandListToActivatedNodes(context, list_make1(DELETE_ALL_COLOCATION)); + + /* remove pg_dist_tenant_schema entries */ + SendOrCollectCommandListToActivatedNodes(context, + list_make1(DELETE_ALL_TENANT_SCHEMAS)); } @@ -4502,6 +4632,53 @@ SendColocationMetadataCommands(MetadataSyncContext *context) } +/* + * SendTenantSchemaMetadataCommands sends tenant schema metadata entries with + * transactional or nontransactional mode according to transactionMode inside + * metadataSyncContext. + */ +void +SendTenantSchemaMetadataCommands(MetadataSyncContext *context) +{ + ScanKeyData scanKey[1]; + int scanKeyCount = 0; + + Relation pgDistTenantSchema = table_open(DistTenantSchemaRelationId(), + AccessShareLock); + SysScanDesc scanDesc = systable_beginscan(pgDistTenantSchema, InvalidOid, false, NULL, + scanKeyCount, scanKey); + + MemoryContext oldContext = MemoryContextSwitchTo(context->context); + HeapTuple heapTuple = NULL; + while (true) + { + ResetMetadataSyncMemoryContext(context); + + heapTuple = systable_getnext(scanDesc); + if (!HeapTupleIsValid(heapTuple)) + { + break; + } + + Form_pg_dist_tenant_schema tenantSchemaForm = + (Form_pg_dist_tenant_schema) GETSTRUCT(heapTuple); + + StringInfo insertTenantSchemaCommand = makeStringInfo(); + appendStringInfo(insertTenantSchemaCommand, + "SELECT pg_catalog.citus_internal_add_tenant_schema(%s, %u)", + RemoteSchemaIdExpressionById(tenantSchemaForm->schemaid), + tenantSchemaForm->colocationid); + + List *commandList = list_make1(insertTenantSchemaCommand->data); + SendOrCollectCommandListToActivatedNodes(context, commandList); + } + MemoryContextSwitchTo(oldContext); + + systable_endscan(scanDesc); + table_close(pgDistTenantSchema, AccessShareLock); +} + + /* * SendDependencyCreationCommands sends dependency creation commands to workers * with transactional or nontransactional mode according to transactionMode diff --git a/src/backend/distributed/shared_library_init.c b/src/backend/distributed/shared_library_init.c index d20eeeb08..24cd7acd6 100644 --- a/src/backend/distributed/shared_library_init.c +++ b/src/backend/distributed/shared_library_init.c @@ -1381,6 +1381,20 @@ RegisterCitusConfigVariables(void) GUC_NO_SHOW_ALL, NULL, NULL, NULL); + DefineCustomBoolVariable( + "citus.enable_schema_based_sharding", + gettext_noop("Enables schema based sharding."), + gettext_noop("The schemas created while this is ON will be automatically " + "associated with individual colocation groups such that the " + "tables created in those schemas will be automatically " + "converted to colocated distributed tables without a shard " + "key."), + &EnableSchemaBasedSharding, + false, + PGC_USERSET, + GUC_STANDARD, + NULL, NULL, NULL); + DefineCustomBoolVariable( "citus.enable_single_hash_repartition_joins", gettext_noop("Enables single hash repartitioning between hash " diff --git a/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql b/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql index 56bbdfb0d..9f96b01cc 100644 --- a/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql +++ b/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql @@ -1,3 +1,25 @@ -- citus--11.3-1--12.0-1 -- bump version to 12.0-1 + +CREATE TABLE citus.pg_dist_tenant_schema ( + schemaid oid NOT NULL, + colocationid int NOT NULL, + CONSTRAINT pg_dist_tenant_schema_pkey PRIMARY KEY (schemaid), + CONSTRAINT pg_dist_tenant_schema_unique_colocationid_index UNIQUE (colocationid) +); + +ALTER TABLE citus.pg_dist_tenant_schema SET SCHEMA pg_catalog; + +GRANT SELECT ON pg_catalog.pg_dist_tenant_schema TO public; + +-- udfs used to modify pg_dist_tenant_schema on workers, to sync metadata +#include "udfs/citus_internal_add_tenant_schema/12.0-1.sql" +#include "udfs/citus_internal_delete_tenant_schema/12.0-1.sql" + +#include "udfs/citus_prepare_pg_upgrade/12.0-1.sql" +#include "udfs/citus_finish_pg_upgrade/12.0-1.sql" + +-- udfs used to modify pg_dist_tenant_schema globally via drop trigger +#include "udfs/citus_internal_unregister_tenant_schema_globally/12.0-1.sql" +#include "udfs/citus_drop_trigger/12.0-1.sql" diff --git a/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql b/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql index 0ca58bafd..42eab88d2 100644 --- a/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql +++ b/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql @@ -1,8 +1,18 @@ -- citus--12.0-1--11.3-1 --- Throw an error if user has any distributed tables without a shard key. DO $$ BEGIN + -- Throw an error if user has created any tenant schemas. + IF EXISTS (SELECT 1 FROM pg_catalog.pg_dist_tenant_schema) + THEN + RAISE EXCEPTION 'cannot downgrade Citus because there are ' + 'tenant schemas created.' + USING HINT = 'To downgrade Citus to an older version, you should ' + 'first issue SELECT citus.schema_tenant_unset("%s") ' + 'for each tenant schema.'; + END IF; + + -- Throw an error if user has any distributed tables without a shard key. IF EXISTS ( SELECT 1 FROM pg_dist_partition WHERE repmodel != 't' AND partmethod = 'n' AND colocationid != 0) @@ -19,3 +29,15 @@ BEGIN END IF; END; $$ LANGUAGE plpgsql; + +DROP FUNCTION pg_catalog.citus_internal_add_tenant_schema(Oid, int); + +#include "../udfs/citus_prepare_pg_upgrade/11.2-1.sql" +#include "../udfs/citus_finish_pg_upgrade/11.2-1.sql" + +DROP FUNCTION pg_catalog.citus_internal_delete_tenant_schema(Oid); +DROP FUNCTION pg_catalog.citus_internal_unregister_tenant_schema_globally(Oid, text); + +#include "../udfs/citus_drop_trigger/10.2-1.sql" + +DROP TABLE pg_catalog.pg_dist_tenant_schema; diff --git a/src/backend/distributed/sql/udfs/citus_drop_trigger/12.0-1.sql b/src/backend/distributed/sql/udfs/citus_drop_trigger/12.0-1.sql new file mode 100644 index 000000000..494331d4d --- /dev/null +++ b/src/backend/distributed/sql/udfs/citus_drop_trigger/12.0-1.sql @@ -0,0 +1,68 @@ +CREATE OR REPLACE FUNCTION pg_catalog.citus_drop_trigger() + RETURNS event_trigger + LANGUAGE plpgsql + SET search_path = pg_catalog + AS $cdbdt$ +DECLARE + constraint_event_count INTEGER; + v_obj record; + dropped_table_is_a_partition boolean := false; +BEGIN + FOR v_obj IN SELECT * FROM pg_event_trigger_dropped_objects() + WHERE object_type IN ('table', 'foreign table') + LOOP + -- first drop the table and metadata on the workers + -- then drop all the shards on the workers + -- finally remove the pg_dist_partition entry on the coordinator + PERFORM master_remove_distributed_table_metadata_from_workers(v_obj.objid, v_obj.schema_name, v_obj.object_name); + + -- If both original and normal values are false, the dropped table was a partition + -- that was dropped as a result of its parent being dropped + -- NOTE: the other way around is not true: + -- the table being a partition doesn't imply both original and normal values are false + SELECT (v_obj.original = false AND v_obj.normal = false) INTO dropped_table_is_a_partition; + + -- The partition's shards will be dropped when dropping the parent's shards, so we can skip: + -- i.e. we call citus_drop_all_shards with drop_shards_metadata_only parameter set to true + IF dropped_table_is_a_partition + THEN + PERFORM citus_drop_all_shards(v_obj.objid, v_obj.schema_name, v_obj.object_name, drop_shards_metadata_only := true); + ELSE + PERFORM citus_drop_all_shards(v_obj.objid, v_obj.schema_name, v_obj.object_name, drop_shards_metadata_only := false); + END IF; + + PERFORM master_remove_partition_metadata(v_obj.objid, v_obj.schema_name, v_obj.object_name); + END LOOP; + + FOR v_obj IN SELECT * FROM pg_event_trigger_dropped_objects() + LOOP + -- Remove entries from pg_catalog.pg_dist_tenant_schema for all dropped tenant schemas. + -- Also delete the corresponding colocation group from pg_catalog.pg_dist_colocation. + -- + -- Although normally we automatically delete the colocation groups when they become empty, + -- we don't do so for the colocation groups that are created for tenant schemas. For this + -- reason, here we need to delete the colocation group when the tenant schema is dropped. + IF v_obj.object_type = 'schema' AND EXISTS (SELECT 1 FROM pg_catalog.pg_dist_tenant_schema WHERE schemaid = v_obj.objid) + THEN + PERFORM pg_catalog.citus_internal_unregister_tenant_schema_globally(v_obj.objid, v_obj.object_name); + END IF; + + -- remove entries from citus.pg_dist_object for all dropped root (objsubid = 0) objects + PERFORM master_unmark_object_distributed(v_obj.classid, v_obj.objid, v_obj.objsubid); + END LOOP; + + SELECT COUNT(*) INTO constraint_event_count + FROM pg_event_trigger_dropped_objects() + WHERE object_type IN ('table constraint'); + + IF constraint_event_count > 0 + THEN + -- Tell utility hook that a table constraint is dropped so we might + -- need to undistribute some of the citus local tables that are not + -- connected to any reference tables. + PERFORM notify_constraint_dropped(); + END IF; +END; +$cdbdt$; +COMMENT ON FUNCTION pg_catalog.citus_drop_trigger() + IS 'perform checks and actions at the end of DROP actions'; diff --git a/src/backend/distributed/sql/udfs/citus_drop_trigger/latest.sql b/src/backend/distributed/sql/udfs/citus_drop_trigger/latest.sql index a440766ed..494331d4d 100644 --- a/src/backend/distributed/sql/udfs/citus_drop_trigger/latest.sql +++ b/src/backend/distributed/sql/udfs/citus_drop_trigger/latest.sql @@ -34,9 +34,20 @@ BEGIN PERFORM master_remove_partition_metadata(v_obj.objid, v_obj.schema_name, v_obj.object_name); END LOOP; - -- remove entries from citus.pg_dist_object for all dropped root (objsubid = 0) objects FOR v_obj IN SELECT * FROM pg_event_trigger_dropped_objects() LOOP + -- Remove entries from pg_catalog.pg_dist_tenant_schema for all dropped tenant schemas. + -- Also delete the corresponding colocation group from pg_catalog.pg_dist_colocation. + -- + -- Although normally we automatically delete the colocation groups when they become empty, + -- we don't do so for the colocation groups that are created for tenant schemas. For this + -- reason, here we need to delete the colocation group when the tenant schema is dropped. + IF v_obj.object_type = 'schema' AND EXISTS (SELECT 1 FROM pg_catalog.pg_dist_tenant_schema WHERE schemaid = v_obj.objid) + THEN + PERFORM pg_catalog.citus_internal_unregister_tenant_schema_globally(v_obj.objid, v_obj.object_name); + END IF; + + -- remove entries from citus.pg_dist_object for all dropped root (objsubid = 0) objects PERFORM master_unmark_object_distributed(v_obj.classid, v_obj.objid, v_obj.objsubid); END LOOP; diff --git a/src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/12.0-1.sql b/src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/12.0-1.sql new file mode 100644 index 000000000..9b3943cf3 --- /dev/null +++ b/src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/12.0-1.sql @@ -0,0 +1,160 @@ +CREATE OR REPLACE FUNCTION pg_catalog.citus_finish_pg_upgrade() + RETURNS void + LANGUAGE plpgsql + SET search_path = pg_catalog + AS $cppu$ +DECLARE + table_name regclass; + command text; + trigger_name text; +BEGIN + + + IF substring(current_Setting('server_version'), '\d+')::int >= 14 THEN + EXECUTE $cmd$ + -- disable propagation to prevent EnsureCoordinator errors + -- the aggregate created here does not depend on Citus extension (yet) + -- since we add the dependency with the next command + SET citus.enable_ddl_propagation TO OFF; + CREATE AGGREGATE array_cat_agg(anycompatiblearray) (SFUNC = array_cat, STYPE = anycompatiblearray); + COMMENT ON AGGREGATE array_cat_agg(anycompatiblearray) + IS 'concatenate input arrays into a single array'; + RESET citus.enable_ddl_propagation; + $cmd$; + ELSE + EXECUTE $cmd$ + SET citus.enable_ddl_propagation TO OFF; + CREATE AGGREGATE array_cat_agg(anyarray) (SFUNC = array_cat, STYPE = anyarray); + COMMENT ON AGGREGATE array_cat_agg(anyarray) + IS 'concatenate input arrays into a single array'; + RESET citus.enable_ddl_propagation; + $cmd$; + END IF; + + -- + -- Citus creates the array_cat_agg but because of a compatibility + -- issue between pg13-pg14, we drop and create it during upgrade. + -- And as Citus creates it, there needs to be a dependency to the + -- Citus extension, so we create that dependency here. + -- We are not using: + -- ALTER EXENSION citus DROP/CREATE AGGREGATE array_cat_agg + -- because we don't have an easy way to check if the aggregate + -- exists with anyarray type or anycompatiblearray type. + + INSERT INTO pg_depend + SELECT + 'pg_proc'::regclass::oid as classid, + (SELECT oid FROM pg_proc WHERE proname = 'array_cat_agg') as objid, + 0 as objsubid, + 'pg_extension'::regclass::oid as refclassid, + (select oid from pg_extension where extname = 'citus') as refobjid, + 0 as refobjsubid , + 'e' as deptype; + + -- + -- restore citus catalog tables + -- + INSERT INTO pg_catalog.pg_dist_partition SELECT * FROM public.pg_dist_partition; + INSERT INTO pg_catalog.pg_dist_shard SELECT * FROM public.pg_dist_shard; + INSERT INTO pg_catalog.pg_dist_placement SELECT * FROM public.pg_dist_placement; + INSERT INTO pg_catalog.pg_dist_node_metadata SELECT * FROM public.pg_dist_node_metadata; + INSERT INTO pg_catalog.pg_dist_node SELECT * FROM public.pg_dist_node; + INSERT INTO pg_catalog.pg_dist_local_group SELECT * FROM public.pg_dist_local_group; + INSERT INTO pg_catalog.pg_dist_transaction SELECT * FROM public.pg_dist_transaction; + INSERT INTO pg_catalog.pg_dist_colocation SELECT * FROM public.pg_dist_colocation; + INSERT INTO pg_catalog.pg_dist_cleanup SELECT * FROM public.pg_dist_cleanup; + INSERT INTO pg_catalog.pg_dist_tenant_schema SELECT schemaname::regnamespace, colocationid FROM public.pg_dist_tenant_schema; + -- enterprise catalog tables + INSERT INTO pg_catalog.pg_dist_authinfo SELECT * FROM public.pg_dist_authinfo; + INSERT INTO pg_catalog.pg_dist_poolinfo SELECT * FROM public.pg_dist_poolinfo; + + INSERT INTO pg_catalog.pg_dist_rebalance_strategy SELECT + name, + default_strategy, + shard_cost_function::regprocedure::regproc, + node_capacity_function::regprocedure::regproc, + shard_allowed_on_node_function::regprocedure::regproc, + default_threshold, + minimum_threshold, + improvement_threshold + FROM public.pg_dist_rebalance_strategy; + + -- + -- drop backup tables + -- + DROP TABLE public.pg_dist_authinfo; + DROP TABLE public.pg_dist_colocation; + DROP TABLE public.pg_dist_local_group; + DROP TABLE public.pg_dist_node; + DROP TABLE public.pg_dist_node_metadata; + DROP TABLE public.pg_dist_partition; + DROP TABLE public.pg_dist_placement; + DROP TABLE public.pg_dist_poolinfo; + DROP TABLE public.pg_dist_shard; + DROP TABLE public.pg_dist_transaction; + DROP TABLE public.pg_dist_rebalance_strategy; + DROP TABLE public.pg_dist_cleanup; + DROP TABLE public.pg_dist_tenant_schema; + -- + -- reset sequences + -- + PERFORM setval('pg_catalog.pg_dist_shardid_seq', (SELECT MAX(shardid)+1 AS max_shard_id FROM pg_dist_shard), false); + PERFORM setval('pg_catalog.pg_dist_placement_placementid_seq', (SELECT MAX(placementid)+1 AS max_placement_id FROM pg_dist_placement), false); + PERFORM setval('pg_catalog.pg_dist_groupid_seq', (SELECT MAX(groupid)+1 AS max_group_id FROM pg_dist_node), false); + PERFORM setval('pg_catalog.pg_dist_node_nodeid_seq', (SELECT MAX(nodeid)+1 AS max_node_id FROM pg_dist_node), false); + PERFORM setval('pg_catalog.pg_dist_colocationid_seq', (SELECT MAX(colocationid)+1 AS max_colocation_id FROM pg_dist_colocation), false); + PERFORM setval('pg_catalog.pg_dist_operationid_seq', (SELECT MAX(operation_id)+1 AS max_operation_id FROM pg_dist_cleanup), false); + PERFORM setval('pg_catalog.pg_dist_cleanup_recordid_seq', (SELECT MAX(record_id)+1 AS max_record_id FROM pg_dist_cleanup), false); + PERFORM setval('pg_catalog.pg_dist_clock_logical_seq', (SELECT last_value FROM public.pg_dist_clock_logical_seq), false); + DROP TABLE public.pg_dist_clock_logical_seq; + + + + -- + -- register triggers + -- + FOR table_name IN SELECT logicalrelid FROM pg_catalog.pg_dist_partition JOIN pg_class ON (logicalrelid = oid) WHERE relkind <> 'f' + LOOP + trigger_name := 'truncate_trigger_' || table_name::oid; + command := 'create trigger ' || trigger_name || ' after truncate on ' || table_name || ' execute procedure pg_catalog.citus_truncate_trigger()'; + EXECUTE command; + command := 'update pg_trigger set tgisinternal = true where tgname = ' || quote_literal(trigger_name); + EXECUTE command; + END LOOP; + + -- + -- set dependencies + -- + INSERT INTO pg_depend + SELECT + 'pg_class'::regclass::oid as classid, + p.logicalrelid::regclass::oid as objid, + 0 as objsubid, + 'pg_extension'::regclass::oid as refclassid, + (select oid from pg_extension where extname = 'citus') as refobjid, + 0 as refobjsubid , + 'n' as deptype + FROM pg_catalog.pg_dist_partition p; + + -- set dependencies for columnar table access method + PERFORM columnar_internal.columnar_ensure_am_depends_catalog(); + + -- restore pg_dist_object from the stable identifiers + TRUNCATE pg_catalog.pg_dist_object; + INSERT INTO pg_catalog.pg_dist_object (classid, objid, objsubid, distribution_argument_index, colocationid) + SELECT + address.classid, + address.objid, + address.objsubid, + naming.distribution_argument_index, + naming.colocationid + FROM + public.pg_dist_object naming, + pg_catalog.pg_get_object_address(naming.type, naming.object_names, naming.object_args) address; + + DROP TABLE public.pg_dist_object; +END; +$cppu$; + +COMMENT ON FUNCTION pg_catalog.citus_finish_pg_upgrade() + IS 'perform tasks to restore citus settings from a location that has been prepared before pg_upgrade'; diff --git a/src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/latest.sql b/src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/latest.sql index 448937c28..9b3943cf3 100644 --- a/src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/latest.sql +++ b/src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/latest.sql @@ -63,6 +63,7 @@ BEGIN INSERT INTO pg_catalog.pg_dist_transaction SELECT * FROM public.pg_dist_transaction; INSERT INTO pg_catalog.pg_dist_colocation SELECT * FROM public.pg_dist_colocation; INSERT INTO pg_catalog.pg_dist_cleanup SELECT * FROM public.pg_dist_cleanup; + INSERT INTO pg_catalog.pg_dist_tenant_schema SELECT schemaname::regnamespace, colocationid FROM public.pg_dist_tenant_schema; -- enterprise catalog tables INSERT INTO pg_catalog.pg_dist_authinfo SELECT * FROM public.pg_dist_authinfo; INSERT INTO pg_catalog.pg_dist_poolinfo SELECT * FROM public.pg_dist_poolinfo; @@ -93,6 +94,7 @@ BEGIN DROP TABLE public.pg_dist_transaction; DROP TABLE public.pg_dist_rebalance_strategy; DROP TABLE public.pg_dist_cleanup; + DROP TABLE public.pg_dist_tenant_schema; -- -- reset sequences -- diff --git a/src/backend/distributed/sql/udfs/citus_internal_add_tenant_schema/12.0-1.sql b/src/backend/distributed/sql/udfs/citus_internal_add_tenant_schema/12.0-1.sql new file mode 100644 index 000000000..a97a2544d --- /dev/null +++ b/src/backend/distributed/sql/udfs/citus_internal_add_tenant_schema/12.0-1.sql @@ -0,0 +1,8 @@ +CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_add_tenant_schema(schema_id Oid, colocation_id int) + RETURNS void + LANGUAGE C + VOLATILE + AS 'MODULE_PATHNAME'; + +COMMENT ON FUNCTION pg_catalog.citus_internal_add_tenant_schema(Oid, int) IS + 'insert given tenant schema into pg_dist_tenant_schema with given colocation id'; diff --git a/src/backend/distributed/sql/udfs/citus_internal_add_tenant_schema/latest.sql b/src/backend/distributed/sql/udfs/citus_internal_add_tenant_schema/latest.sql new file mode 100644 index 000000000..a97a2544d --- /dev/null +++ b/src/backend/distributed/sql/udfs/citus_internal_add_tenant_schema/latest.sql @@ -0,0 +1,8 @@ +CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_add_tenant_schema(schema_id Oid, colocation_id int) + RETURNS void + LANGUAGE C + VOLATILE + AS 'MODULE_PATHNAME'; + +COMMENT ON FUNCTION pg_catalog.citus_internal_add_tenant_schema(Oid, int) IS + 'insert given tenant schema into pg_dist_tenant_schema with given colocation id'; diff --git a/src/backend/distributed/sql/udfs/citus_internal_delete_tenant_schema/12.0-1.sql b/src/backend/distributed/sql/udfs/citus_internal_delete_tenant_schema/12.0-1.sql new file mode 100644 index 000000000..839a5b18a --- /dev/null +++ b/src/backend/distributed/sql/udfs/citus_internal_delete_tenant_schema/12.0-1.sql @@ -0,0 +1,8 @@ +CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_delete_tenant_schema(schema_id Oid) + RETURNS void + LANGUAGE C + VOLATILE + AS 'MODULE_PATHNAME'; + +COMMENT ON FUNCTION pg_catalog.citus_internal_delete_tenant_schema(Oid) IS + 'delete given tenant schema from pg_dist_tenant_schema'; diff --git a/src/backend/distributed/sql/udfs/citus_internal_delete_tenant_schema/latest.sql b/src/backend/distributed/sql/udfs/citus_internal_delete_tenant_schema/latest.sql new file mode 100644 index 000000000..839a5b18a --- /dev/null +++ b/src/backend/distributed/sql/udfs/citus_internal_delete_tenant_schema/latest.sql @@ -0,0 +1,8 @@ +CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_delete_tenant_schema(schema_id Oid) + RETURNS void + LANGUAGE C + VOLATILE + AS 'MODULE_PATHNAME'; + +COMMENT ON FUNCTION pg_catalog.citus_internal_delete_tenant_schema(Oid) IS + 'delete given tenant schema from pg_dist_tenant_schema'; diff --git a/src/backend/distributed/sql/udfs/citus_internal_unregister_tenant_schema_globally/12.0-1.sql b/src/backend/distributed/sql/udfs/citus_internal_unregister_tenant_schema_globally/12.0-1.sql new file mode 100644 index 000000000..1863f1ddf --- /dev/null +++ b/src/backend/distributed/sql/udfs/citus_internal_unregister_tenant_schema_globally/12.0-1.sql @@ -0,0 +1,7 @@ +CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_unregister_tenant_schema_globally(schema_id Oid, schema_name text) + RETURNS void + LANGUAGE C + VOLATILE + AS 'MODULE_PATHNAME'; +COMMENT ON FUNCTION pg_catalog.citus_internal_unregister_tenant_schema_globally(schema_id Oid, schema_name text) IS + 'Delete a tenant schema and the corresponding colocation group from metadata tables.'; diff --git a/src/backend/distributed/sql/udfs/citus_internal_unregister_tenant_schema_globally/latest.sql b/src/backend/distributed/sql/udfs/citus_internal_unregister_tenant_schema_globally/latest.sql new file mode 100644 index 000000000..1863f1ddf --- /dev/null +++ b/src/backend/distributed/sql/udfs/citus_internal_unregister_tenant_schema_globally/latest.sql @@ -0,0 +1,7 @@ +CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_unregister_tenant_schema_globally(schema_id Oid, schema_name text) + RETURNS void + LANGUAGE C + VOLATILE + AS 'MODULE_PATHNAME'; +COMMENT ON FUNCTION pg_catalog.citus_internal_unregister_tenant_schema_globally(schema_id Oid, schema_name text) IS + 'Delete a tenant schema and the corresponding colocation group from metadata tables.'; diff --git a/src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/12.0-1.sql b/src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/12.0-1.sql new file mode 100644 index 000000000..a7c368930 --- /dev/null +++ b/src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/12.0-1.sql @@ -0,0 +1,82 @@ +CREATE OR REPLACE FUNCTION pg_catalog.citus_prepare_pg_upgrade() + RETURNS void + LANGUAGE plpgsql + SET search_path = pg_catalog + AS $cppu$ +BEGIN + + DELETE FROM pg_depend WHERE + objid IN (SELECT oid FROM pg_proc WHERE proname = 'array_cat_agg') AND + refobjid IN (select oid from pg_extension where extname = 'citus'); + -- + -- We are dropping the aggregates because postgres 14 changed + -- array_cat type from anyarray to anycompatiblearray. When + -- upgrading to pg14, specifically when running pg_restore on + -- array_cat_agg we would get an error. So we drop the aggregate + -- and create the right one on citus_finish_pg_upgrade. + + DROP AGGREGATE IF EXISTS array_cat_agg(anyarray); + DROP AGGREGATE IF EXISTS array_cat_agg(anycompatiblearray); + -- + -- Drop existing backup tables + -- + DROP TABLE IF EXISTS public.pg_dist_partition; + DROP TABLE IF EXISTS public.pg_dist_shard; + DROP TABLE IF EXISTS public.pg_dist_placement; + DROP TABLE IF EXISTS public.pg_dist_node_metadata; + DROP TABLE IF EXISTS public.pg_dist_node; + DROP TABLE IF EXISTS public.pg_dist_local_group; + DROP TABLE IF EXISTS public.pg_dist_transaction; + DROP TABLE IF EXISTS public.pg_dist_colocation; + DROP TABLE IF EXISTS public.pg_dist_authinfo; + DROP TABLE IF EXISTS public.pg_dist_poolinfo; + DROP TABLE IF EXISTS public.pg_dist_rebalance_strategy; + DROP TABLE IF EXISTS public.pg_dist_object; + DROP TABLE IF EXISTS public.pg_dist_cleanup; + DROP TABLE IF EXISTS public.pg_dist_tenant_schema; + DROP TABLE IF EXISTS public.pg_dist_clock_logical_seq; + + -- + -- backup citus catalog tables + -- + CREATE TABLE public.pg_dist_partition AS SELECT * FROM pg_catalog.pg_dist_partition; + CREATE TABLE public.pg_dist_shard AS SELECT * FROM pg_catalog.pg_dist_shard; + CREATE TABLE public.pg_dist_placement AS SELECT * FROM pg_catalog.pg_dist_placement; + CREATE TABLE public.pg_dist_node_metadata AS SELECT * FROM pg_catalog.pg_dist_node_metadata; + CREATE TABLE public.pg_dist_node AS SELECT * FROM pg_catalog.pg_dist_node; + CREATE TABLE public.pg_dist_local_group AS SELECT * FROM pg_catalog.pg_dist_local_group; + CREATE TABLE public.pg_dist_transaction AS SELECT * FROM pg_catalog.pg_dist_transaction; + CREATE TABLE public.pg_dist_colocation AS SELECT * FROM pg_catalog.pg_dist_colocation; + CREATE TABLE public.pg_dist_cleanup AS SELECT * FROM pg_catalog.pg_dist_cleanup; + -- save names of the tenant schemas instead of their oids because the oids might change after pg upgrade + CREATE TABLE public.pg_dist_tenant_schema AS SELECT schemaid::regnamespace::text AS schemaname, colocationid FROM pg_catalog.pg_dist_tenant_schema; + -- enterprise catalog tables + CREATE TABLE public.pg_dist_authinfo AS SELECT * FROM pg_catalog.pg_dist_authinfo; + CREATE TABLE public.pg_dist_poolinfo AS SELECT * FROM pg_catalog.pg_dist_poolinfo; + -- sequences + CREATE TABLE public.pg_dist_clock_logical_seq AS SELECT last_value FROM pg_catalog.pg_dist_clock_logical_seq; + CREATE TABLE public.pg_dist_rebalance_strategy AS SELECT + name, + default_strategy, + shard_cost_function::regprocedure::text, + node_capacity_function::regprocedure::text, + shard_allowed_on_node_function::regprocedure::text, + default_threshold, + minimum_threshold, + improvement_threshold + FROM pg_catalog.pg_dist_rebalance_strategy; + + -- store upgrade stable identifiers on pg_dist_object catalog + CREATE TABLE public.pg_dist_object AS SELECT + address.type, + address.object_names, + address.object_args, + objects.distribution_argument_index, + objects.colocationid + FROM pg_catalog.pg_dist_object objects, + pg_catalog.pg_identify_object_as_address(objects.classid, objects.objid, objects.objsubid) address; +END; +$cppu$; + +COMMENT ON FUNCTION pg_catalog.citus_prepare_pg_upgrade() + IS 'perform tasks to copy citus settings to a location that could later be restored after pg_upgrade is done'; diff --git a/src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/latest.sql b/src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/latest.sql index 8fc7d2ee4..a7c368930 100644 --- a/src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/latest.sql +++ b/src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/latest.sql @@ -33,6 +33,7 @@ BEGIN DROP TABLE IF EXISTS public.pg_dist_rebalance_strategy; DROP TABLE IF EXISTS public.pg_dist_object; DROP TABLE IF EXISTS public.pg_dist_cleanup; + DROP TABLE IF EXISTS public.pg_dist_tenant_schema; DROP TABLE IF EXISTS public.pg_dist_clock_logical_seq; -- @@ -47,6 +48,8 @@ BEGIN CREATE TABLE public.pg_dist_transaction AS SELECT * FROM pg_catalog.pg_dist_transaction; CREATE TABLE public.pg_dist_colocation AS SELECT * FROM pg_catalog.pg_dist_colocation; CREATE TABLE public.pg_dist_cleanup AS SELECT * FROM pg_catalog.pg_dist_cleanup; + -- save names of the tenant schemas instead of their oids because the oids might change after pg upgrade + CREATE TABLE public.pg_dist_tenant_schema AS SELECT schemaid::regnamespace::text AS schemaname, colocationid FROM pg_catalog.pg_dist_tenant_schema; -- enterprise catalog tables CREATE TABLE public.pg_dist_authinfo AS SELECT * FROM pg_catalog.pg_dist_authinfo; CREATE TABLE public.pg_dist_poolinfo AS SELECT * FROM pg_catalog.pg_dist_poolinfo; diff --git a/src/backend/distributed/utils/colocation_utils.c b/src/backend/distributed/utils/colocation_utils.c index 9cc5df8f9..015eb13df 100644 --- a/src/backend/distributed/utils/colocation_utils.c +++ b/src/backend/distributed/utils/colocation_utils.c @@ -20,6 +20,7 @@ #include "catalog/pg_type.h" #include "commands/sequence.h" #include "distributed/colocation_utils.h" +#include "distributed/commands.h" #include "distributed/listutils.h" #include "distributed/metadata_utility.h" #include "distributed/coordinator_protocol.h" @@ -30,6 +31,7 @@ #include "distributed/pg_dist_colocation.h" #include "distributed/resource_lock.h" #include "distributed/shardinterval_utils.h" +#include "distributed/tenant_schema_metadata.h" #include "distributed/version_compat.h" #include "distributed/utils/array_type.h" #include "distributed/worker_protocol.h" @@ -49,7 +51,6 @@ static bool HashPartitionedShardIntervalsEqual(ShardInterval *leftShardInterval, ShardInterval *rightShardInterval); static int CompareShardPlacementsByNode(const void *leftElement, const void *rightElement); -static void DeleteColocationGroup(uint32 colocationId); static uint32 CreateColocationGroupForRelation(Oid sourceRelationId); static void BreakColocation(Oid sourceRelationId); @@ -546,6 +547,13 @@ ColocationId(int shardCount, int replicationFactor, Oid distributionColumnType, Form_pg_dist_colocation colocationForm = (Form_pg_dist_colocation) GETSTRUCT(colocationTuple); + /* avoid chosing a colocation group that belongs to a tenant schema */ + if (IsTenantSchemaColocationGroup(colocationForm->colocationid)) + { + colocationTuple = systable_getnext(scanDescriptor); + continue; + } + if (colocationId == INVALID_COLOCATION_ID || colocationId > colocationForm->colocationid) { @@ -1258,9 +1266,9 @@ DeleteColocationGroupIfNoTablesBelong(uint32 colocationId) /* * DeleteColocationGroup deletes the colocation group from pg_dist_colocation - * throughout the cluster. + * throughout the cluster and dissociates the tenant schema if any. */ -static void +void DeleteColocationGroup(uint32 colocationId) { DeleteColocationGroupLocally(colocationId); @@ -1422,4 +1430,25 @@ EnsureTableCanBeColocatedWith(Oid relationId, char replicationModel, "%s and %s.", sourceRelationName, relationName))); } + + /* prevent colocating regular tables with tenant tables */ + Oid sourceRelationSchemaId = get_rel_namespace(sourceRelationId); + Oid targetRelationSchemaId = get_rel_namespace(relationId); + if (IsTenantSchema(sourceRelationSchemaId) && + sourceRelationSchemaId != targetRelationSchemaId) + { + char *relationName = get_rel_name(relationId); + char *sourceRelationName = get_rel_name(sourceRelationId); + char *sourceRelationSchemaName = get_namespace_name(sourceRelationSchemaId); + + ereport(ERROR, (errmsg("cannot colocate tables %s and %s", + sourceRelationName, relationName), + errdetail("Cannot colocate tables with tenant tables " + "by using colocate_with option."), + errhint("Consider using \"CREATE TABLE\" statement " + "to create this table as a tenant table in " + "the same schema to automatically colocate " + "it with %s.%s", + sourceRelationSchemaName, sourceRelationName))); + } } diff --git a/src/backend/distributed/utils/tenant_schema_metadata.c b/src/backend/distributed/utils/tenant_schema_metadata.c new file mode 100644 index 000000000..fa67f8875 --- /dev/null +++ b/src/backend/distributed/utils/tenant_schema_metadata.c @@ -0,0 +1,239 @@ +/*------------------------------------------------------------------------- + * + * tenant_schema_metadata.c + * + * This file contains functions to query and modify tenant schema metadata, + * which is used to track the schemas used for schema-based sharding in + * Citus. + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "access/genam.h" +#include "access/htup.h" +#include "access/table.h" +#include "distributed/colocation_utils.h" +#include "distributed/metadata_cache.h" +#include "distributed/metadata_sync.h" +#include "distributed/pg_dist_tenant_schema.h" +#include "distributed/tenant_schema_metadata.h" +#include "storage/lockdefs.h" +#include "utils/relcache.h" +#include "utils/fmgroids.h" + + +static Oid ColocationIdGetTenantSchemaId(uint32 colocationId); + + +/* + * IsTenantSchema returns true if there is a tenant schema with given schemaId. + */ +bool +IsTenantSchema(Oid schemaId) +{ + /* + * We don't allow creating tenant schemas when there is a version + * mismatch. Even more, SchemaIdGetTenantColocationId() would throw an + * error if the underlying pg_dist_tenant_schema metadata table has not + * been created yet, which is the case in older versions. For this reason, + * it's safe to assume that it cannot be a tenant schema when there is a + * version mismatch. + * + * But it's a bit tricky that we do the same when version checks are + * disabled because then CheckCitusVersion() returns true even if there + * is a version mismatch. And in that case, the tests that are trying to + * create tables (in multi_extension.sql) in older versions would + * fail when deciding whether we should create a tenant table or not. + * + * The downside of doing so is that, for example, we will skip deleting + * the tenant schema entry from pg_dist_tenant_schema when dropping a + * tenant schema while the version checks are disabled even if there was + * no version mismatch. But we're okay with that because we don't expect + * users to disable version checks anyway. + */ + if (!EnableVersionChecks || !CheckCitusVersion(DEBUG4)) + { + return false; + } + + return SchemaIdGetTenantColocationId(schemaId) != INVALID_COLOCATION_ID; +} + + +/* + * IsTenantSchemaColocationGroup returns true if there is a tenant schema + * that is associated with given colocation id. + */ +bool +IsTenantSchemaColocationGroup(uint32 colocationId) +{ + return OidIsValid(ColocationIdGetTenantSchemaId(colocationId)); +} + + +/* + * SchemaIdGetTenantColocationId returns the colocation id associated with + * the tenant schema with given id. + * + * Returns INVALID_COLOCATION_ID if there is no tenant schema with given id. + */ +uint32 +SchemaIdGetTenantColocationId(Oid schemaId) +{ + uint32 colocationId = INVALID_COLOCATION_ID; + + if (!OidIsValid(schemaId)) + { + ereport(ERROR, (errmsg("schema id is invalid"))); + } + + Relation pgDistTenantSchema = table_open(DistTenantSchemaRelationId(), + AccessShareLock); + ScanKeyData scanKey[1]; + ScanKeyInit(&scanKey[0], Anum_pg_dist_tenant_schema_schemaid, BTEqualStrategyNumber, + F_OIDEQ, ObjectIdGetDatum(schemaId)); + + bool indexOk = true; + SysScanDesc scanDescriptor = systable_beginscan(pgDistTenantSchema, + DistTenantSchemaPrimaryKeyIndexId(), + indexOk, NULL, 1, scanKey); + + HeapTuple heapTuple = systable_getnext_ordered(scanDescriptor, ForwardScanDirection); + if (HeapTupleIsValid(heapTuple)) + { + bool isNull = false; + colocationId = DatumGetUInt32( + heap_getattr(heapTuple, + Anum_pg_dist_tenant_schema_colocationid, + RelationGetDescr(pgDistTenantSchema), + &isNull)); + Assert(!isNull); + } + + systable_endscan(scanDescriptor); + table_close(pgDistTenantSchema, AccessShareLock); + + return colocationId; +} + + +/* + * ColocationIdGetTenantSchemaId returns the oid of the tenant schema that + * is associated with given colocation id. + * + * Returns InvalidOid if there is no such tenant schema. + */ +static Oid +ColocationIdGetTenantSchemaId(uint32 colocationId) +{ + if (colocationId == INVALID_COLOCATION_ID) + { + ereport(ERROR, (errmsg("colocation id is invalid"))); + } + + Relation pgDistTenantSchema = table_open(DistTenantSchemaRelationId(), + AccessShareLock); + ScanKeyData scanKey[1]; + ScanKeyInit(&scanKey[0], Anum_pg_dist_tenant_schema_colocationid, + BTEqualStrategyNumber, F_INT4EQ, UInt32GetDatum(colocationId)); + + bool indexOk = true; + SysScanDesc scanDescriptor = systable_beginscan(pgDistTenantSchema, + DistTenantSchemaUniqueColocationIdIndexId(), + indexOk, NULL, 1, scanKey); + + HeapTuple heapTuple = systable_getnext_ordered(scanDescriptor, ForwardScanDirection); + Oid schemaId = InvalidOid; + if (HeapTupleIsValid(heapTuple)) + { + bool isNull = false; + schemaId = heap_getattr(heapTuple, Anum_pg_dist_tenant_schema_schemaid, + RelationGetDescr(pgDistTenantSchema), &isNull); + Assert(!isNull); + } + + systable_endscan(scanDescriptor); + table_close(pgDistTenantSchema, AccessShareLock); + + return schemaId; +} + + +/* + * InsertTenantSchemaLocally inserts an entry into pg_dist_tenant_schema + * with given schemaId and colocationId. + * + * Throws a constraint violation error if there is already an entry with + * given schemaId, or if given colocation id is already associated with + * another tenant schema. + */ +void +InsertTenantSchemaLocally(Oid schemaId, uint32 colocationId) +{ + if (!OidIsValid(schemaId)) + { + ereport(ERROR, (errmsg("schema id is invalid"))); + } + + if (colocationId == INVALID_COLOCATION_ID) + { + ereport(ERROR, (errmsg("colocation id is invalid"))); + } + + Datum values[Natts_pg_dist_tenant_schema] = { 0 }; + bool isNulls[Natts_pg_dist_tenant_schema] = { 0 }; + + values[Anum_pg_dist_tenant_schema_schemaid - 1] = ObjectIdGetDatum(schemaId); + values[Anum_pg_dist_tenant_schema_colocationid - 1] = UInt32GetDatum(colocationId); + + Relation pgDistTenantSchema = table_open(DistTenantSchemaRelationId(), + RowExclusiveLock); + TupleDesc tupleDescriptor = RelationGetDescr(pgDistTenantSchema); + HeapTuple heapTuple = heap_form_tuple(tupleDescriptor, values, isNulls); + + CatalogTupleInsert(pgDistTenantSchema, heapTuple); + CommandCounterIncrement(); + + table_close(pgDistTenantSchema, NoLock); +} + + +/* + * DeleteTenantSchemaLocally deletes the entry for given schemaId from + * pg_dist_tenant_schema. + * + * Throws an error if there is no such tenant schema. + */ +void +DeleteTenantSchemaLocally(Oid schemaId) +{ + if (!OidIsValid(schemaId)) + { + ereport(ERROR, (errmsg("schema id is invalid"))); + } + + Relation pgDistTenantSchema = table_open(DistTenantSchemaRelationId(), + RowExclusiveLock); + ScanKeyData scanKey[1]; + ScanKeyInit(&scanKey[0], Anum_pg_dist_tenant_schema_schemaid, BTEqualStrategyNumber, + F_OIDEQ, ObjectIdGetDatum(schemaId)); + + bool indexOk = true; + SysScanDesc scanDescriptor = systable_beginscan(pgDistTenantSchema, + DistTenantSchemaPrimaryKeyIndexId(), + indexOk, NULL, 1, scanKey); + + HeapTuple heapTuple = systable_getnext_ordered(scanDescriptor, ForwardScanDirection); + if (!HeapTupleIsValid(heapTuple)) + { + ereport(ERROR, (errmsg("could not find tuple for tenant schema %u", schemaId))); + } + + CatalogTupleDelete(pgDistTenantSchema, &heapTuple->t_self); + CommandCounterIncrement(); + + systable_endscan(scanDescriptor); + table_close(pgDistTenantSchema, NoLock); +} diff --git a/src/include/distributed/colocation_utils.h b/src/include/distributed/colocation_utils.h index 9e6641cd3..c9fcf4776 100644 --- a/src/include/distributed/colocation_utils.h +++ b/src/include/distributed/colocation_utils.h @@ -48,6 +48,7 @@ extern void UpdateRelationColocationGroup(Oid distributedRelationId, uint32 colo bool localOnly); extern void DeleteColocationGroupIfNoTablesBelong(uint32 colocationId); extern List * ColocationGroupTableList(uint32 colocationId, uint32 count); +extern void DeleteColocationGroup(uint32 colocationId); extern void DeleteColocationGroupLocally(uint32 colocationId); extern uint32 FindColocateWithColocationId(Oid relationId, char replicationModel, Oid distributionColumnType, diff --git a/src/include/distributed/commands.h b/src/include/distributed/commands.h index dba63c659..fd67da301 100644 --- a/src/include/distributed/commands.h +++ b/src/include/distributed/commands.h @@ -23,6 +23,7 @@ extern bool AddAllLocalTablesToMetadata; +extern bool EnableSchemaBasedSharding; /* controlled via GUC, should be accessed via EnableLocalReferenceForeignKeys() */ extern bool EnableLocalReferenceForeignKeys; @@ -458,8 +459,7 @@ extern void UnmarkRolesDistributed(List *roles); extern List * FilterDistributedRoles(List *roles); /* schema.c - forward declarations */ -extern List * PreprocessCreateSchemaStmt(Node *node, const char *queryString, - ProcessUtilityContext processUtilityContext); +extern List * PostprocessCreateSchemaStmt(Node *node, const char *queryString); extern List * PreprocessDropSchemaStmt(Node *dropSchemaStatement, const char *queryString, ProcessUtilityContext processUtilityContext); @@ -586,6 +586,7 @@ extern char * GetAlterColumnWithNextvalDefaultCmd(Oid sequenceOid, Oid relationI extern void ErrorIfTableHasUnsupportedIdentityColumn(Oid relationId); extern void ErrorIfTableHasIdentityColumn(Oid relationId); +extern void ConvertNewTableIfNecessary(Node *createStmt); /* text_search.c - forward declarations */ extern List * GetCreateTextSearchConfigStatements(const ObjectAddress *address); @@ -770,6 +771,7 @@ extern void ExecuteForeignKeyCreateCommandList(List *ddlCommandList, /* create_citus_local_table.c */ extern void CreateCitusLocalTable(Oid relationId, bool cascadeViaForeignKeys, bool autoConverted); +extern bool ShouldAddNewTableToMetadata(Oid relationId); extern List * GetExplicitIndexOidList(Oid relationId); extern bool ShouldPropagateSetCommand(VariableSetStmt *setStmt); @@ -780,4 +782,12 @@ extern void CreateCitusLocalTablePartitionOf(CreateStmt *createStatement, extern void UpdateAutoConvertedForConnectedRelations(List *relationId, bool autoConverted); +/* schema_based_sharding.c */ +extern bool ShouldUseSchemaBasedSharding(char *schemaName); +extern bool ShouldCreateTenantSchemaTable(Oid relationId); +extern bool IsTenantSchema(Oid schemaId); +extern void ErrorIfIllegalPartitioningInTenantSchema(Oid parentRelationId, + Oid partitionRelationId); +extern void CreateTenantSchemaTable(Oid relationId); + #endif /*CITUS_COMMANDS_H */ diff --git a/src/include/distributed/metadata_cache.h b/src/include/distributed/metadata_cache.h index 5dfb80519..27a6eb632 100644 --- a/src/include/distributed/metadata_cache.h +++ b/src/include/distributed/metadata_cache.h @@ -241,6 +241,7 @@ extern Oid DistRebalanceStrategyRelationId(void); extern Oid DistLocalGroupIdRelationId(void); extern Oid DistObjectRelationId(void); extern Oid DistEnabledCustomAggregatesId(void); +extern Oid DistTenantSchemaRelationId(void); /* index oids */ extern Oid DistNodeNodeIdIndexId(void); @@ -263,6 +264,8 @@ extern Oid DistTransactionGroupIndexId(void); extern Oid DistPlacementGroupidIndexId(void); extern Oid DistObjectPrimaryKeyIndexId(void); extern Oid DistCleanupPrimaryKeyIndexId(void); +extern Oid DistTenantSchemaPrimaryKeyIndexId(void); +extern Oid DistTenantSchemaUniqueColocationIdIndexId(void); /* sequence oids */ extern Oid DistBackgroundJobJobIdSequenceId(void); diff --git a/src/include/distributed/metadata_sync.h b/src/include/distributed/metadata_sync.h index 227243cc1..3153e42d7 100644 --- a/src/include/distributed/metadata_sync.h +++ b/src/include/distributed/metadata_sync.h @@ -137,6 +137,8 @@ extern void SyncNewColocationGroupToNodes(uint32 colocationId, int shardCount, Oid distributionColumType, Oid distributionColumnCollation); extern void SyncDeleteColocationGroupToNodes(uint32 colocationId); +extern char * TenantSchemaInsertCommand(Oid schemaId, uint32 colocationId); +extern char * TenantSchemaDeleteCommand(char *schemaName); extern MetadataSyncContext * CreateMetadataSyncContext(List *nodeList, bool collectCommands, @@ -163,6 +165,7 @@ extern void SendNodeWideObjectsSyncCommands(MetadataSyncContext *context); extern void SendShellTableDeletionCommands(MetadataSyncContext *context); extern void SendMetadataDeletionCommands(MetadataSyncContext *context); extern void SendColocationMetadataCommands(MetadataSyncContext *context); +extern void SendTenantSchemaMetadataCommands(MetadataSyncContext *context); extern void SendDependencyCreationCommands(MetadataSyncContext *context); extern void SendDistTableMetadataCommands(MetadataSyncContext *context); extern void SendDistObjectCommands(MetadataSyncContext *context); @@ -174,6 +177,7 @@ extern void SendInterTableRelationshipCommands(MetadataSyncContext *context); #define DELETE_ALL_DISTRIBUTED_OBJECTS "DELETE FROM pg_catalog.pg_dist_object" #define DELETE_ALL_PARTITIONS "DELETE FROM pg_dist_partition" #define DELETE_ALL_COLOCATION "DELETE FROM pg_catalog.pg_dist_colocation" +#define DELETE_ALL_TENANT_SCHEMAS "DELETE FROM pg_catalog.pg_dist_tenant_schema" #define WORKER_DROP_ALL_SHELL_TABLES \ "CALL pg_catalog.worker_drop_all_shell_tables(%s)" #define CITUS_INTERNAL_MARK_NODE_NOT_SYNCED \ diff --git a/src/include/distributed/metadata_utility.h b/src/include/distributed/metadata_utility.h index 08d4896c1..ae9350770 100644 --- a/src/include/distributed/metadata_utility.h +++ b/src/include/distributed/metadata_utility.h @@ -201,6 +201,37 @@ typedef enum SizeQueryType TABLE_SIZE /* pg_table_size() */ } SizeQueryType; + +typedef enum +{ + COLOCATE_WITH_TABLE_LIKE_OPT, + COLOCATE_WITH_COLOCATION_ID +} ColocationParamType; + +/* + * Param used to specify the colocation target of a distributed table. It can + * be either a table name or a colocation id. + * + * When colocationParamType is COLOCATE_WITH_COLOCATION_ID, colocationId is + * expected to be a valid colocation id. When colocationParamType is set to + * COLOCATE_WITH_TABLE_LIKE_OPT, colocateWithTableName is expected to + * be a valid table name, "default" or "none". + * + * Among the functions used to create a Citus table, right now only + * CreateSingleShardTable() accepts a ColocationParam. + */ +typedef struct +{ + union + { + char *colocateWithTableName; + uint32 colocationId; + }; + + ColocationParamType colocationParamType; +} ColocationParam; + + typedef enum BackgroundJobStatus { BACKGROUND_JOB_STATUS_SCHEDULED, @@ -326,7 +357,7 @@ extern void DeletePartitionRow(Oid distributedRelationId); extern void DeleteShardRow(uint64 shardId); extern void UpdatePlacementGroupId(uint64 placementId, int groupId); extern void DeleteShardPlacementRow(uint64 placementId); -extern void CreateSingleShardTable(Oid relationId, char *colocateWithTableName); +extern void CreateSingleShardTable(Oid relationId, ColocationParam colocationParam); extern void CreateDistributedTable(Oid relationId, char *distributionColumnName, char distributionMethod, int shardCount, bool shardCountIsStrict, char *colocateWithTableName); diff --git a/src/include/distributed/pg_dist_tenant_schema.h b/src/include/distributed/pg_dist_tenant_schema.h new file mode 100644 index 000000000..8364853db --- /dev/null +++ b/src/include/distributed/pg_dist_tenant_schema.h @@ -0,0 +1,41 @@ +/*------------------------------------------------------------------------- + * + * pg_dist_tenant_schema.h + * definition of the system catalog for the schemas used for schema-based + * sharding in Citus. + * + *------------------------------------------------------------------------- + */ + +#ifndef PG_DIST_TENANT_SCHEMA_H +#define PG_DIST_TENANT_SCHEMA_H + +#include "postgres.h" + + +/* ---------------- + * pg_dist_tenant_schema definition. + * ---------------- + */ +typedef struct FormData_pg_dist_tenant_schema +{ + Oid schemaid; + uint32 colocationid; +} FormData_pg_dist_tenant_schema; + +/* ---------------- + * Form_pg_dist_tenant_schema corresponds to a pointer to a tuple with + * the format of pg_dist_tenant_schema relation. + * ---------------- + */ +typedef FormData_pg_dist_tenant_schema *Form_pg_dist_tenant_schema; + +/* ---------------- + * compiler constants for pg_dist_tenant_schema + * ---------------- + */ +#define Natts_pg_dist_tenant_schema 2 +#define Anum_pg_dist_tenant_schema_schemaid 1 +#define Anum_pg_dist_tenant_schema_colocationid 2 + +#endif /* PG_DIST_TENANT_SCHEMA_H */ diff --git a/src/include/distributed/tenant_schema_metadata.h b/src/include/distributed/tenant_schema_metadata.h new file mode 100644 index 000000000..1e5889c15 --- /dev/null +++ b/src/include/distributed/tenant_schema_metadata.h @@ -0,0 +1,32 @@ +/*------------------------------------------------------------------------- + * + * tenant_schema_metadata.h + * + * This file contains functions to query and modify tenant schema metadata, + * which is used to track the schemas used for schema-based sharding in + * Citus. + * + * ------------------------------------------------------------------------- + */ + +#ifndef TENANT_SCHEMA_METADATA_H +#define TENANT_SCHEMA_METADATA_H + +#include "postgres.h" + +/* accessors */ +extern uint32 SchemaIdGetTenantColocationId(Oid schemaId); +extern bool IsTenantSchema(Oid schemaId); +extern bool IsTenantSchemaColocationGroup(uint32 colocationId); + +/* + * Local only modifiers. + * + * These functions may not make much sense by themselves. They are mainly + * exported for tenant-schema management (schema_based_sharding.c) and + * metadata-sync layer (metadata_sync.c). + */ +extern void InsertTenantSchemaLocally(Oid schemaId, uint32 colocationId); +extern void DeleteTenantSchemaLocally(Oid schemaId); + +#endif /* TENANT_SCHEMA_METADATA_H */ diff --git a/src/test/regress/after_pg_upgrade_schedule b/src/test/regress/after_pg_upgrade_schedule index 3b49a76d0..b47763bdb 100644 --- a/src/test/regress/after_pg_upgrade_schedule +++ b/src/test/regress/after_pg_upgrade_schedule @@ -1,4 +1,4 @@ -test: upgrade_basic_after upgrade_ref2ref_after upgrade_type_after upgrade_distributed_function_after upgrade_rebalance_strategy_after upgrade_list_citus_objects upgrade_autoconverted_after upgrade_citus_stat_activity upgrade_citus_locks upgrade_single_shard_table_after +test: upgrade_basic_after upgrade_ref2ref_after upgrade_type_after upgrade_distributed_function_after upgrade_rebalance_strategy_after upgrade_list_citus_objects upgrade_autoconverted_after upgrade_citus_stat_activity upgrade_citus_locks upgrade_single_shard_table_after upgrade_schema_based_sharding_after # This test cannot be run with run_test.py currently due to its dependence on # the specific PG versions that we use to run upgrade tests. For now we leave diff --git a/src/test/regress/before_pg_upgrade_schedule b/src/test/regress/before_pg_upgrade_schedule index bb89de18a..05810d3d5 100644 --- a/src/test/regress/before_pg_upgrade_schedule +++ b/src/test/regress/before_pg_upgrade_schedule @@ -5,7 +5,7 @@ test: upgrade_basic_before test: upgrade_ref2ref_before test: upgrade_type_before test: upgrade_distributed_function_before upgrade_rebalance_strategy_before -test: upgrade_autoconverted_before upgrade_single_shard_table_before +test: upgrade_autoconverted_before upgrade_single_shard_table_before upgrade_schema_based_sharding_before test: upgrade_citus_stat_activity test: upgrade_citus_locks test: upgrade_distributed_triggers_before diff --git a/src/test/regress/citus_tests/run_test.py b/src/test/regress/citus_tests/run_test.py index 79bac0adf..44fe4770a 100755 --- a/src/test/regress/citus_tests/run_test.py +++ b/src/test/regress/citus_tests/run_test.py @@ -152,6 +152,7 @@ DEPS = { "isolation_extension_commands": TestDeps( None, ["isolation_setup", "isolation_add_remove_node"] ), + "schema_based_sharding": TestDeps("minimal_schedule"), } diff --git a/src/test/regress/expected/auto_undist_citus_local.out b/src/test/regress/expected/auto_undist_citus_local.out index afb725f3e..0eaec17e5 100644 --- a/src/test/regress/expected/auto_undist_citus_local.out +++ b/src/test/regress/expected/auto_undist_citus_local.out @@ -453,7 +453,9 @@ SELECT logicalrelid, partmethod, repmodel FROM pg_dist_partition WHERE logicalre DROP TABLE IF EXISTS citus_local_table_1, citus_local_table_2, citus_local_table_3; -- this GUC will add the next three tables to metadata automatically SET citus.use_citus_managed_tables TO ON; -CREATE TABLE citus_local_table_1(a INT UNIQUE); +-- try to create the table twice by using IF NOT EXISTS syntax +CREATE TABLE IF NOT EXISTS citus_local_table_1(a INT UNIQUE); +CREATE TABLE IF NOT EXISTS citus_local_table_1(a INT UNIQUE); CREATE TABLE citus_local_table_2(a INT UNIQUE); CREATE TABLE citus_local_table_3(a INT UNIQUE); RESET citus.use_citus_managed_tables; @@ -1340,3 +1342,4 @@ SELECT logicalrelid, partmethod, repmodel FROM pg_dist_partition WHERE logicalre (1 row) DROP SCHEMA drop_fkey_cascade CASCADE; +DROP USER another_user; diff --git a/src/test/regress/expected/create_single_shard_table.out b/src/test/regress/expected/create_single_shard_table.out index 8478dc293..41a81346b 100644 --- a/src/test/regress/expected/create_single_shard_table.out +++ b/src/test/regress/expected/create_single_shard_table.out @@ -116,11 +116,11 @@ SELECT create_distributed_table('nullkey_c1_t1', null, colocate_with=>'none'); (1 row) -SELECT colocationid AS nullkey_c1_t1_colocation_id FROM pg_dist_partition WHERE logicalrelid = 'create_single_shard_table.nullkey_c1_t1'::regclass \gset +SELECT colocationid AS nullkey_c1_t1_colocationid FROM pg_dist_partition WHERE logicalrelid = 'create_single_shard_table.nullkey_c1_t1'::regclass \gset BEGIN; DROP TABLE nullkey_c1_t1; -- make sure that we delete the colocation group after dropping the last table that belongs to it - SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = :'nullkey_c1_t1_colocation_id'; + SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = :'nullkey_c1_t1_colocationid'; ?column? --------------------------------------------------------------------- t diff --git a/src/test/regress/expected/isolation_schema_based_sharding.out b/src/test/regress/expected/isolation_schema_based_sharding.out new file mode 100644 index 000000000..48d00bfa0 --- /dev/null +++ b/src/test/regress/expected/isolation_schema_based_sharding.out @@ -0,0 +1,37 @@ +Parsed test spec with 2 sessions + +starting permutation: s1-begin s2-begin s1-tenant-1-create-table-1 s2-tenant-1-create-table-2 s1-commit s2-tenant-1-verify-colocation s2-commit +step s1-begin: BEGIN; +step s2-begin: BEGIN; +step s1-tenant-1-create-table-1: CREATE TABLE tenant_1.tbl_1 (a int); +step s2-tenant-1-create-table-2: CREATE TABLE tenant_1.tbl_2 (a int); +step s1-commit: COMMIT; +step s2-tenant-1-verify-colocation: SELECT COUNT(DISTINCT(colocationid))=1 FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant_1.%'; +?column? +--------------------------------------------------------------------- +t +(1 row) + +step s2-commit: COMMIT; + +starting permutation: s1-begin s2-begin s1-tenant-4-create-table-1 s2-tenant-4-create-table-2 s1-commit s2-tenant-4-verify-colocation s2-commit +step s1-begin: BEGIN; +step s2-begin: BEGIN; +step s1-tenant-4-create-table-1: CREATE TABLE tenant_4.tbl_1 (a int); +step s2-tenant-4-create-table-2: CREATE TABLE tenant_4.tbl_2 (a int); +step s1-commit: COMMIT; +step s2-tenant-4-verify-colocation: SELECT COUNT(DISTINCT(colocationid))=1 FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant_4.%'; +?column? +--------------------------------------------------------------------- +t +(1 row) + +step s2-commit: COMMIT; + +starting permutation: s1-begin s2-begin s1-tenant-2-create-table-1 s2-tenant-3-create-table-1 s1-commit s2-commit +step s1-begin: BEGIN; +step s2-begin: BEGIN; +step s1-tenant-2-create-table-1: CREATE TABLE tenant_2.tbl_1 (a int); +step s2-tenant-3-create-table-1: CREATE TABLE tenant_3.tbl_1 (a int); +step s1-commit: COMMIT; +step s2-commit: COMMIT; diff --git a/src/test/regress/expected/multi_extension.out b/src/test/regress/expected/multi_extension.out index c1c7a0401..ef94ebdd1 100644 --- a/src/test/regress/expected/multi_extension.out +++ b/src/test/regress/expected/multi_extension.out @@ -1357,9 +1357,13 @@ SELECT * FROM multi_extension.print_extension_changes(); -- Snapshot of state at 12.0-1 ALTER EXTENSION citus UPDATE TO '12.0-1'; SELECT * FROM multi_extension.print_extension_changes(); - previous_object | current_object + previous_object | current_object --------------------------------------------------------------------- -(0 rows) + | function citus_internal_add_tenant_schema(oid,integer) void + | function citus_internal_delete_tenant_schema(oid) void + | function citus_internal_unregister_tenant_schema_globally(oid,text) void + | table pg_dist_tenant_schema +(4 rows) DROP TABLE multi_extension.prev_objects, multi_extension.extension_diff; -- show running version @@ -1777,6 +1781,53 @@ SELECT citus_remove_node('localhost', :master_port); (1 row) +-- confirm that we can create a tenant schema / table on an empty node +SET citus.enable_schema_based_sharding TO ON; +CREATE SCHEMA tenant_schema; +CREATE TABLE tenant_schema.test(x int, y int); +SELECT colocationid = ( + SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_schema.test'::regclass +) +FROM pg_dist_tenant_schema +WHERE schemaid::regnamespace::text = 'tenant_schema'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- and make sure that we can't remove the coordinator due to "test" +SELECT citus_remove_node('localhost', :master_port); +ERROR: cannot remove or disable the node localhost:xxxxx because because it contains the only shard placement for shard xxxxx +DETAIL: One of the table(s) that prevents the operation complete successfully is tenant_schema.test +HINT: To proceed, either drop the tables or use undistribute_table() function to convert them to local tables +BEGIN; + SET LOCAL client_min_messages TO WARNING; + DROP SCHEMA tenant_schema CASCADE; +COMMIT; +-- and now we should be able to remove the coordinator +SELECT citus_remove_node('localhost', :master_port); + citus_remove_node +--------------------------------------------------------------------- + +(1 row) + +CREATE SCHEMA tenant_schema; +-- Make sure that we can sync metadata for empty tenant schemas +-- when adding the first node to the cluster. +SELECT 1 FROM citus_add_node('localhost', :worker_1_port); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +DROP SCHEMA tenant_schema; +SELECT citus_remove_node('localhost', :worker_1_port); + citus_remove_node +--------------------------------------------------------------------- + +(1 row) + +RESET citus.enable_schema_based_sharding; -- confirm that we can create a reference table on an empty node CREATE TABLE test (x int, y int); INSERT INTO test VALUES (1,2); @@ -1802,6 +1853,19 @@ SELECT citus_add_local_table_to_metadata('test'); (1 row) DROP TABLE test; +-- Verify that we don't consider the schemas created by extensions as tenant schemas. +-- Easiest way of verifying this is to drop and re-create columnar extension. +DROP EXTENSION citus_columnar; +SET citus.enable_schema_based_sharding TO ON; +CREATE EXTENSION citus_columnar; +SELECT COUNT(*)=0 FROM pg_dist_tenant_schema +WHERE schemaid IN ('columnar'::regnamespace, 'columnar_internal'::regnamespace); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +RESET citus.enable_schema_based_sharding; DROP EXTENSION citus; CREATE EXTENSION citus; DROP TABLE version_mismatch_table; diff --git a/src/test/regress/expected/multi_metadata_sync.out b/src/test/regress/expected/multi_metadata_sync.out index 8c75b4fd4..c64e5d5c7 100644 --- a/src/test/regress/expected/multi_metadata_sync.out +++ b/src/test/regress/expected/multi_metadata_sync.out @@ -76,6 +76,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; CREATE SCHEMA IF NOT EXISTS public AUTHORIZATION pg_database_owner DELETE FROM pg_catalog.pg_dist_colocation DELETE FROM pg_catalog.pg_dist_object + DELETE FROM pg_catalog.pg_dist_tenant_schema DELETE FROM pg_dist_node DELETE FROM pg_dist_partition DELETE FROM pg_dist_placement @@ -103,7 +104,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('database', ARRAY['regression']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('role', ARRAY['postgres']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('schema', ARRAY['public']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; -(32 rows) +(33 rows) -- Create a test table with constraints and SERIAL and default from user defined sequence CREATE SEQUENCE user_defined_seq; @@ -135,64 +136,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; CREATE TABLE public.mx_test_table (col_1 integer, col_2 text NOT NULL, col_3 bigint DEFAULT nextval('public.mx_test_table_col_3_seq'::regclass) NOT NULL, col_4 bigint DEFAULT nextval('public.user_defined_seq'::regclass)) USING heap DELETE FROM pg_catalog.pg_dist_colocation DELETE FROM pg_catalog.pg_dist_object - DELETE FROM pg_dist_node - DELETE FROM pg_dist_partition - DELETE FROM pg_dist_placement - DELETE FROM pg_dist_shard - DROP TABLE IF EXISTS public.mx_test_table CASCADE - GRANT CREATE ON SCHEMA public TO PUBLIC; - GRANT CREATE ON SCHEMA public TO pg_database_owner; - GRANT USAGE ON SCHEMA public TO PUBLIC; - GRANT USAGE ON SCHEMA public TO pg_database_owner; - INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (1, 1, 'localhost', 57637, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE),(2, 2, 'localhost', 57638, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE) - RESET ROLE - RESET ROLE - SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') - SELECT citus_internal_add_partition_metadata ('public.mx_test_table'::regclass, 'h', 'col_1', 2, 's') - SELECT pg_catalog.worker_drop_sequence_dependency('public.mx_test_table'); - SELECT pg_catalog.worker_drop_sequence_dependency(logicalrelid::regclass::text) FROM pg_dist_partition - SELECT pg_catalog.worker_record_sequence_dependency('public.mx_test_table_col_3_seq'::regclass,'public.mx_test_table'::regclass,'col_3') - SELECT worker_apply_sequence_command ('CREATE SEQUENCE IF NOT EXISTS public.mx_test_table_col_3_seq AS bigint INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1 NO CYCLE','bigint') - SELECT worker_apply_sequence_command ('CREATE SEQUENCE IF NOT EXISTS public.user_defined_seq AS bigint INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1 NO CYCLE','bigint') - SELECT worker_create_or_alter_role('postgres', 'CREATE ROLE postgres SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN REPLICATION BYPASSRLS CONNECTION LIMIT 0 PASSWORD ''md5c53670dddfc3bb4b5675c7872bc2249a'' VALID UNTIL ''2052-05-05 00:00:00-07''', 'ALTER ROLE postgres SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN REPLICATION BYPASSRLS CONNECTION LIMIT 0 PASSWORD ''md5c53670dddfc3bb4b5675c7872bc2249a'' VALID UNTIL ''2052-05-05 00:00:00-07''') - SELECT worker_create_truncate_trigger('public.mx_test_table') - SET ROLE pg_database_owner - SET ROLE pg_database_owner - SET citus.enable_ddl_propagation TO 'off' - SET citus.enable_ddl_propagation TO 'off' - SET citus.enable_ddl_propagation TO 'on' - SET citus.enable_ddl_propagation TO 'on' - UPDATE pg_dist_local_group SET groupid = 1 - UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 1 - WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (2, 8, 1, 'integer'::regtype, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) - WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('database', ARRAY['regression']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; - WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('role', ARRAY['postgres']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; - WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('schema', ARRAY['public']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; - WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('sequence', ARRAY['public', 'mx_test_table_col_3_seq']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; - WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('sequence', ARRAY['public', 'user_defined_seq']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; - WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('table', ARRAY['public', 'mx_test_table']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; - WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310000, 0, 1, 100000), (1310001, 0, 2, 100001), (1310002, 0, 1, 100002), (1310003, 0, 2, 100003), (1310004, 0, 1, 100004), (1310005, 0, 2, 100005), (1310006, 0, 1, 100006), (1310007, 0, 2, 100007)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; - WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.mx_test_table'::regclass, 1310000, 't'::"char", '-2147483648', '-1610612737'), ('public.mx_test_table'::regclass, 1310001, 't'::"char", '-1610612736', '-1073741825'), ('public.mx_test_table'::regclass, 1310002, 't'::"char", '-1073741824', '-536870913'), ('public.mx_test_table'::regclass, 1310003, 't'::"char", '-536870912', '-1'), ('public.mx_test_table'::regclass, 1310004, 't'::"char", '0', '536870911'), ('public.mx_test_table'::regclass, 1310005, 't'::"char", '536870912', '1073741823'), ('public.mx_test_table'::regclass, 1310006, 't'::"char", '1073741824', '1610612735'), ('public.mx_test_table'::regclass, 1310007, 't'::"char", '1610612736', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; -(50 rows) - --- Show that CREATE INDEX commands are included in the activate node snapshot -CREATE INDEX mx_index ON mx_test_table(col_2); -SELECT unnest(activate_node_snapshot()) order by 1; - unnest ---------------------------------------------------------------------- - ALTER DATABASE regression OWNER TO postgres; - ALTER SEQUENCE public.mx_test_table_col_3_seq OWNER TO postgres - ALTER SEQUENCE public.user_defined_seq OWNER TO postgres - ALTER TABLE public.mx_test_table ADD CONSTRAINT mx_test_table_col_1_key UNIQUE (col_1) - ALTER TABLE public.mx_test_table OWNER TO postgres - CALL pg_catalog.worker_drop_all_shell_tables(true) - CREATE INDEX mx_index ON public.mx_test_table USING btree (col_2) - CREATE SCHEMA IF NOT EXISTS public AUTHORIZATION pg_database_owner - CREATE TABLE public.mx_test_table (col_1 integer, col_2 text NOT NULL, col_3 bigint DEFAULT nextval('public.mx_test_table_col_3_seq'::regclass) NOT NULL, col_4 bigint DEFAULT nextval('public.user_defined_seq'::regclass)) USING heap - DELETE FROM pg_catalog.pg_dist_colocation - DELETE FROM pg_catalog.pg_dist_object + DELETE FROM pg_catalog.pg_dist_tenant_schema DELETE FROM pg_dist_node DELETE FROM pg_dist_partition DELETE FROM pg_dist_placement @@ -235,6 +179,65 @@ SELECT unnest(activate_node_snapshot()) order by 1; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.mx_test_table'::regclass, 1310000, 't'::"char", '-2147483648', '-1610612737'), ('public.mx_test_table'::regclass, 1310001, 't'::"char", '-1610612736', '-1073741825'), ('public.mx_test_table'::regclass, 1310002, 't'::"char", '-1073741824', '-536870913'), ('public.mx_test_table'::regclass, 1310003, 't'::"char", '-536870912', '-1'), ('public.mx_test_table'::regclass, 1310004, 't'::"char", '0', '536870911'), ('public.mx_test_table'::regclass, 1310005, 't'::"char", '536870912', '1073741823'), ('public.mx_test_table'::regclass, 1310006, 't'::"char", '1073741824', '1610612735'), ('public.mx_test_table'::regclass, 1310007, 't'::"char", '1610612736', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; (51 rows) +-- Show that CREATE INDEX commands are included in the activate node snapshot +CREATE INDEX mx_index ON mx_test_table(col_2); +SELECT unnest(activate_node_snapshot()) order by 1; + unnest +--------------------------------------------------------------------- + ALTER DATABASE regression OWNER TO postgres; + ALTER SEQUENCE public.mx_test_table_col_3_seq OWNER TO postgres + ALTER SEQUENCE public.user_defined_seq OWNER TO postgres + ALTER TABLE public.mx_test_table ADD CONSTRAINT mx_test_table_col_1_key UNIQUE (col_1) + ALTER TABLE public.mx_test_table OWNER TO postgres + CALL pg_catalog.worker_drop_all_shell_tables(true) + CREATE INDEX mx_index ON public.mx_test_table USING btree (col_2) + CREATE SCHEMA IF NOT EXISTS public AUTHORIZATION pg_database_owner + CREATE TABLE public.mx_test_table (col_1 integer, col_2 text NOT NULL, col_3 bigint DEFAULT nextval('public.mx_test_table_col_3_seq'::regclass) NOT NULL, col_4 bigint DEFAULT nextval('public.user_defined_seq'::regclass)) USING heap + DELETE FROM pg_catalog.pg_dist_colocation + DELETE FROM pg_catalog.pg_dist_object + DELETE FROM pg_catalog.pg_dist_tenant_schema + DELETE FROM pg_dist_node + DELETE FROM pg_dist_partition + DELETE FROM pg_dist_placement + DELETE FROM pg_dist_shard + DROP TABLE IF EXISTS public.mx_test_table CASCADE + GRANT CREATE ON SCHEMA public TO PUBLIC; + GRANT CREATE ON SCHEMA public TO pg_database_owner; + GRANT USAGE ON SCHEMA public TO PUBLIC; + GRANT USAGE ON SCHEMA public TO pg_database_owner; + INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (1, 1, 'localhost', 57637, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE),(2, 2, 'localhost', 57638, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE) + RESET ROLE + RESET ROLE + SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') + SELECT citus_internal_add_partition_metadata ('public.mx_test_table'::regclass, 'h', 'col_1', 2, 's') + SELECT pg_catalog.worker_drop_sequence_dependency('public.mx_test_table'); + SELECT pg_catalog.worker_drop_sequence_dependency(logicalrelid::regclass::text) FROM pg_dist_partition + SELECT pg_catalog.worker_record_sequence_dependency('public.mx_test_table_col_3_seq'::regclass,'public.mx_test_table'::regclass,'col_3') + SELECT worker_apply_sequence_command ('CREATE SEQUENCE IF NOT EXISTS public.mx_test_table_col_3_seq AS bigint INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1 NO CYCLE','bigint') + SELECT worker_apply_sequence_command ('CREATE SEQUENCE IF NOT EXISTS public.user_defined_seq AS bigint INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1 NO CYCLE','bigint') + SELECT worker_create_or_alter_role('postgres', 'CREATE ROLE postgres SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN REPLICATION BYPASSRLS CONNECTION LIMIT 0 PASSWORD ''md5c53670dddfc3bb4b5675c7872bc2249a'' VALID UNTIL ''2052-05-05 00:00:00-07''', 'ALTER ROLE postgres SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN REPLICATION BYPASSRLS CONNECTION LIMIT 0 PASSWORD ''md5c53670dddfc3bb4b5675c7872bc2249a'' VALID UNTIL ''2052-05-05 00:00:00-07''') + SELECT worker_create_truncate_trigger('public.mx_test_table') + SET ROLE pg_database_owner + SET ROLE pg_database_owner + SET citus.enable_ddl_propagation TO 'off' + SET citus.enable_ddl_propagation TO 'off' + SET citus.enable_ddl_propagation TO 'on' + SET citus.enable_ddl_propagation TO 'on' + UPDATE pg_dist_local_group SET groupid = 1 + UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 1 + UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 1 + UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 1 + WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (2, 8, 1, 'integer'::regtype, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) + WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('database', ARRAY['regression']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; + WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('role', ARRAY['postgres']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; + WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('schema', ARRAY['public']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; + WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('sequence', ARRAY['public', 'mx_test_table_col_3_seq']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; + WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('sequence', ARRAY['public', 'user_defined_seq']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; + WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('table', ARRAY['public', 'mx_test_table']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; + WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310000, 0, 1, 100000), (1310001, 0, 2, 100001), (1310002, 0, 1, 100002), (1310003, 0, 2, 100003), (1310004, 0, 1, 100004), (1310005, 0, 2, 100005), (1310006, 0, 1, 100006), (1310007, 0, 2, 100007)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; + WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.mx_test_table'::regclass, 1310000, 't'::"char", '-2147483648', '-1610612737'), ('public.mx_test_table'::regclass, 1310001, 't'::"char", '-1610612736', '-1073741825'), ('public.mx_test_table'::regclass, 1310002, 't'::"char", '-1073741824', '-536870913'), ('public.mx_test_table'::regclass, 1310003, 't'::"char", '-536870912', '-1'), ('public.mx_test_table'::regclass, 1310004, 't'::"char", '0', '536870911'), ('public.mx_test_table'::regclass, 1310005, 't'::"char", '536870912', '1073741823'), ('public.mx_test_table'::regclass, 1310006, 't'::"char", '1073741824', '1610612735'), ('public.mx_test_table'::regclass, 1310007, 't'::"char", '1610612736', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; +(52 rows) + -- Show that schema changes are included in the activate node snapshot CREATE SCHEMA mx_testing_schema; ALTER TABLE mx_test_table SET SCHEMA mx_testing_schema; @@ -253,6 +256,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; CREATE TABLE mx_testing_schema.mx_test_table (col_1 integer, col_2 text NOT NULL, col_3 bigint DEFAULT nextval('mx_testing_schema.mx_test_table_col_3_seq'::regclass) NOT NULL, col_4 bigint DEFAULT nextval('public.user_defined_seq'::regclass)) USING heap DELETE FROM pg_catalog.pg_dist_colocation DELETE FROM pg_catalog.pg_dist_object + DELETE FROM pg_catalog.pg_dist_tenant_schema DELETE FROM pg_dist_node DELETE FROM pg_dist_partition DELETE FROM pg_dist_placement @@ -294,7 +298,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('table', ARRAY['mx_testing_schema', 'mx_test_table']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310000, 0, 1, 100000), (1310001, 0, 2, 100001), (1310002, 0, 1, 100002), (1310003, 0, 2, 100003), (1310004, 0, 1, 100004), (1310005, 0, 2, 100005), (1310006, 0, 1, 100006), (1310007, 0, 2, 100007)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('mx_testing_schema.mx_test_table'::regclass, 1310000, 't'::"char", '-2147483648', '-1610612737'), ('mx_testing_schema.mx_test_table'::regclass, 1310001, 't'::"char", '-1610612736', '-1073741825'), ('mx_testing_schema.mx_test_table'::regclass, 1310002, 't'::"char", '-1073741824', '-536870913'), ('mx_testing_schema.mx_test_table'::regclass, 1310003, 't'::"char", '-536870912', '-1'), ('mx_testing_schema.mx_test_table'::regclass, 1310004, 't'::"char", '0', '536870911'), ('mx_testing_schema.mx_test_table'::regclass, 1310005, 't'::"char", '536870912', '1073741823'), ('mx_testing_schema.mx_test_table'::regclass, 1310006, 't'::"char", '1073741824', '1610612735'), ('mx_testing_schema.mx_test_table'::regclass, 1310007, 't'::"char", '1610612736', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; -(53 rows) +(54 rows) -- Show that append distributed tables are not included in the activate node snapshot CREATE TABLE non_mx_test_table (col_1 int, col_2 text); @@ -320,6 +324,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; CREATE TABLE mx_testing_schema.mx_test_table (col_1 integer, col_2 text NOT NULL, col_3 bigint DEFAULT nextval('mx_testing_schema.mx_test_table_col_3_seq'::regclass) NOT NULL, col_4 bigint DEFAULT nextval('public.user_defined_seq'::regclass)) USING heap DELETE FROM pg_catalog.pg_dist_colocation DELETE FROM pg_catalog.pg_dist_object + DELETE FROM pg_catalog.pg_dist_tenant_schema DELETE FROM pg_dist_node DELETE FROM pg_dist_partition DELETE FROM pg_dist_placement @@ -361,7 +366,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('table', ARRAY['mx_testing_schema', 'mx_test_table']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310000, 0, 1, 100000), (1310001, 0, 2, 100001), (1310002, 0, 1, 100002), (1310003, 0, 2, 100003), (1310004, 0, 1, 100004), (1310005, 0, 2, 100005), (1310006, 0, 1, 100006), (1310007, 0, 2, 100007)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('mx_testing_schema.mx_test_table'::regclass, 1310000, 't'::"char", '-2147483648', '-1610612737'), ('mx_testing_schema.mx_test_table'::regclass, 1310001, 't'::"char", '-1610612736', '-1073741825'), ('mx_testing_schema.mx_test_table'::regclass, 1310002, 't'::"char", '-1073741824', '-536870913'), ('mx_testing_schema.mx_test_table'::regclass, 1310003, 't'::"char", '-536870912', '-1'), ('mx_testing_schema.mx_test_table'::regclass, 1310004, 't'::"char", '0', '536870911'), ('mx_testing_schema.mx_test_table'::regclass, 1310005, 't'::"char", '536870912', '1073741823'), ('mx_testing_schema.mx_test_table'::regclass, 1310006, 't'::"char", '1073741824', '1610612735'), ('mx_testing_schema.mx_test_table'::regclass, 1310007, 't'::"char", '1610612736', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; -(53 rows) +(54 rows) -- Show that range distributed tables are not included in the activate node snapshot UPDATE pg_dist_partition SET partmethod='r' WHERE logicalrelid='non_mx_test_table'::regclass; @@ -380,6 +385,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; CREATE TABLE mx_testing_schema.mx_test_table (col_1 integer, col_2 text NOT NULL, col_3 bigint DEFAULT nextval('mx_testing_schema.mx_test_table_col_3_seq'::regclass) NOT NULL, col_4 bigint DEFAULT nextval('public.user_defined_seq'::regclass)) USING heap DELETE FROM pg_catalog.pg_dist_colocation DELETE FROM pg_catalog.pg_dist_object + DELETE FROM pg_catalog.pg_dist_tenant_schema DELETE FROM pg_dist_node DELETE FROM pg_dist_partition DELETE FROM pg_dist_placement @@ -421,7 +427,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('table', ARRAY['mx_testing_schema', 'mx_test_table']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310000, 0, 1, 100000), (1310001, 0, 2, 100001), (1310002, 0, 1, 100002), (1310003, 0, 2, 100003), (1310004, 0, 1, 100004), (1310005, 0, 2, 100005), (1310006, 0, 1, 100006), (1310007, 0, 2, 100007)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('mx_testing_schema.mx_test_table'::regclass, 1310000, 't'::"char", '-2147483648', '-1610612737'), ('mx_testing_schema.mx_test_table'::regclass, 1310001, 't'::"char", '-1610612736', '-1073741825'), ('mx_testing_schema.mx_test_table'::regclass, 1310002, 't'::"char", '-1073741824', '-536870913'), ('mx_testing_schema.mx_test_table'::regclass, 1310003, 't'::"char", '-536870912', '-1'), ('mx_testing_schema.mx_test_table'::regclass, 1310004, 't'::"char", '0', '536870911'), ('mx_testing_schema.mx_test_table'::regclass, 1310005, 't'::"char", '536870912', '1073741823'), ('mx_testing_schema.mx_test_table'::regclass, 1310006, 't'::"char", '1073741824', '1610612735'), ('mx_testing_schema.mx_test_table'::regclass, 1310007, 't'::"char", '1610612736', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; -(53 rows) +(54 rows) -- Test start_metadata_sync_to_node and citus_activate_node UDFs -- Ensure that hasmetadata=false for all nodes @@ -1924,6 +1930,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; CREATE TABLE public.test_table (id integer DEFAULT worker_nextval('public.mx_test_sequence_0'::regclass), id2 integer DEFAULT worker_nextval('public.mx_test_sequence_1'::regclass)) USING heap DELETE FROM pg_catalog.pg_dist_colocation DELETE FROM pg_catalog.pg_dist_object + DELETE FROM pg_catalog.pg_dist_tenant_schema DELETE FROM pg_dist_node DELETE FROM pg_dist_partition DELETE FROM pg_dist_placement @@ -2008,7 +2015,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.dist_table_1'::regclass, 1310074, 't'::"char", '-2147483648', '-1073741825'), ('public.dist_table_1'::regclass, 1310075, 't'::"char", '-1073741824', '-1'), ('public.dist_table_1'::regclass, 1310076, 't'::"char", '0', '1073741823'), ('public.dist_table_1'::regclass, 1310077, 't'::"char", '1073741824', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.mx_ref'::regclass, 1310073, 't'::"char", NULL, NULL)) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.test_table'::regclass, 1310083, 't'::"char", '-2147483648', '-1073741825'), ('public.test_table'::regclass, 1310084, 't'::"char", '-1073741824', '-1'), ('public.test_table'::regclass, 1310085, 't'::"char", '0', '1073741823'), ('public.test_table'::regclass, 1310086, 't'::"char", '1073741824', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; -(117 rows) +(118 rows) -- shouldn't work since test_table is MX ALTER TABLE test_table ADD COLUMN id3 bigserial; diff --git a/src/test/regress/expected/multi_metadata_sync_0.out b/src/test/regress/expected/multi_metadata_sync_0.out index 07e00ca93..20ee39761 100644 --- a/src/test/regress/expected/multi_metadata_sync_0.out +++ b/src/test/regress/expected/multi_metadata_sync_0.out @@ -76,6 +76,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; CREATE SCHEMA IF NOT EXISTS public AUTHORIZATION postgres DELETE FROM pg_catalog.pg_dist_colocation DELETE FROM pg_catalog.pg_dist_object + DELETE FROM pg_catalog.pg_dist_tenant_schema DELETE FROM pg_dist_node DELETE FROM pg_dist_partition DELETE FROM pg_dist_placement @@ -103,7 +104,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('database', ARRAY['regression']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('role', ARRAY['postgres']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('schema', ARRAY['public']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; -(32 rows) +(33 rows) -- Create a test table with constraints and SERIAL and default from user defined sequence CREATE SEQUENCE user_defined_seq; @@ -135,64 +136,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; CREATE TABLE public.mx_test_table (col_1 integer, col_2 text NOT NULL, col_3 bigint DEFAULT nextval('public.mx_test_table_col_3_seq'::regclass) NOT NULL, col_4 bigint DEFAULT nextval('public.user_defined_seq'::regclass)) USING heap DELETE FROM pg_catalog.pg_dist_colocation DELETE FROM pg_catalog.pg_dist_object - DELETE FROM pg_dist_node - DELETE FROM pg_dist_partition - DELETE FROM pg_dist_placement - DELETE FROM pg_dist_shard - DROP TABLE IF EXISTS public.mx_test_table CASCADE - GRANT CREATE ON SCHEMA public TO PUBLIC; - GRANT CREATE ON SCHEMA public TO postgres; - GRANT USAGE ON SCHEMA public TO PUBLIC; - GRANT USAGE ON SCHEMA public TO postgres; - INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (1, 1, 'localhost', 57637, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE),(2, 2, 'localhost', 57638, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE) - RESET ROLE - RESET ROLE - SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') - SELECT citus_internal_add_partition_metadata ('public.mx_test_table'::regclass, 'h', 'col_1', 2, 's') - SELECT pg_catalog.worker_drop_sequence_dependency('public.mx_test_table'); - SELECT pg_catalog.worker_drop_sequence_dependency(logicalrelid::regclass::text) FROM pg_dist_partition - SELECT pg_catalog.worker_record_sequence_dependency('public.mx_test_table_col_3_seq'::regclass,'public.mx_test_table'::regclass,'col_3') - SELECT worker_apply_sequence_command ('CREATE SEQUENCE IF NOT EXISTS public.mx_test_table_col_3_seq AS bigint INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1 NO CYCLE','bigint') - SELECT worker_apply_sequence_command ('CREATE SEQUENCE IF NOT EXISTS public.user_defined_seq AS bigint INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1 NO CYCLE','bigint') - SELECT worker_create_or_alter_role('postgres', 'CREATE ROLE postgres SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN REPLICATION BYPASSRLS CONNECTION LIMIT 0 PASSWORD ''md5c53670dddfc3bb4b5675c7872bc2249a'' VALID UNTIL ''2052-05-05 00:00:00-07''', 'ALTER ROLE postgres SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN REPLICATION BYPASSRLS CONNECTION LIMIT 0 PASSWORD ''md5c53670dddfc3bb4b5675c7872bc2249a'' VALID UNTIL ''2052-05-05 00:00:00-07''') - SELECT worker_create_truncate_trigger('public.mx_test_table') - SET ROLE postgres - SET ROLE postgres - SET citus.enable_ddl_propagation TO 'off' - SET citus.enable_ddl_propagation TO 'off' - SET citus.enable_ddl_propagation TO 'on' - SET citus.enable_ddl_propagation TO 'on' - UPDATE pg_dist_local_group SET groupid = 1 - UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 1 - WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (2, 8, 1, 'integer'::regtype, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) - WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('database', ARRAY['regression']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; - WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('role', ARRAY['postgres']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; - WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('schema', ARRAY['public']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; - WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('sequence', ARRAY['public', 'mx_test_table_col_3_seq']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; - WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('sequence', ARRAY['public', 'user_defined_seq']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; - WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('table', ARRAY['public', 'mx_test_table']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; - WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310000, 0, 1, 100000), (1310001, 0, 2, 100001), (1310002, 0, 1, 100002), (1310003, 0, 2, 100003), (1310004, 0, 1, 100004), (1310005, 0, 2, 100005), (1310006, 0, 1, 100006), (1310007, 0, 2, 100007)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; - WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.mx_test_table'::regclass, 1310000, 't'::"char", '-2147483648', '-1610612737'), ('public.mx_test_table'::regclass, 1310001, 't'::"char", '-1610612736', '-1073741825'), ('public.mx_test_table'::regclass, 1310002, 't'::"char", '-1073741824', '-536870913'), ('public.mx_test_table'::regclass, 1310003, 't'::"char", '-536870912', '-1'), ('public.mx_test_table'::regclass, 1310004, 't'::"char", '0', '536870911'), ('public.mx_test_table'::regclass, 1310005, 't'::"char", '536870912', '1073741823'), ('public.mx_test_table'::regclass, 1310006, 't'::"char", '1073741824', '1610612735'), ('public.mx_test_table'::regclass, 1310007, 't'::"char", '1610612736', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; -(50 rows) - --- Show that CREATE INDEX commands are included in the activate node snapshot -CREATE INDEX mx_index ON mx_test_table(col_2); -SELECT unnest(activate_node_snapshot()) order by 1; - unnest ---------------------------------------------------------------------- - ALTER DATABASE regression OWNER TO postgres; - ALTER SEQUENCE public.mx_test_table_col_3_seq OWNER TO postgres - ALTER SEQUENCE public.user_defined_seq OWNER TO postgres - ALTER TABLE public.mx_test_table ADD CONSTRAINT mx_test_table_col_1_key UNIQUE (col_1) - ALTER TABLE public.mx_test_table OWNER TO postgres - CALL pg_catalog.worker_drop_all_shell_tables(true) - CREATE INDEX mx_index ON public.mx_test_table USING btree (col_2) - CREATE SCHEMA IF NOT EXISTS public AUTHORIZATION postgres - CREATE TABLE public.mx_test_table (col_1 integer, col_2 text NOT NULL, col_3 bigint DEFAULT nextval('public.mx_test_table_col_3_seq'::regclass) NOT NULL, col_4 bigint DEFAULT nextval('public.user_defined_seq'::regclass)) USING heap - DELETE FROM pg_catalog.pg_dist_colocation - DELETE FROM pg_catalog.pg_dist_object + DELETE FROM pg_catalog.pg_dist_tenant_schema DELETE FROM pg_dist_node DELETE FROM pg_dist_partition DELETE FROM pg_dist_placement @@ -235,6 +179,65 @@ SELECT unnest(activate_node_snapshot()) order by 1; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.mx_test_table'::regclass, 1310000, 't'::"char", '-2147483648', '-1610612737'), ('public.mx_test_table'::regclass, 1310001, 't'::"char", '-1610612736', '-1073741825'), ('public.mx_test_table'::regclass, 1310002, 't'::"char", '-1073741824', '-536870913'), ('public.mx_test_table'::regclass, 1310003, 't'::"char", '-536870912', '-1'), ('public.mx_test_table'::regclass, 1310004, 't'::"char", '0', '536870911'), ('public.mx_test_table'::regclass, 1310005, 't'::"char", '536870912', '1073741823'), ('public.mx_test_table'::regclass, 1310006, 't'::"char", '1073741824', '1610612735'), ('public.mx_test_table'::regclass, 1310007, 't'::"char", '1610612736', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; (51 rows) +-- Show that CREATE INDEX commands are included in the activate node snapshot +CREATE INDEX mx_index ON mx_test_table(col_2); +SELECT unnest(activate_node_snapshot()) order by 1; + unnest +--------------------------------------------------------------------- + ALTER DATABASE regression OWNER TO postgres; + ALTER SEQUENCE public.mx_test_table_col_3_seq OWNER TO postgres + ALTER SEQUENCE public.user_defined_seq OWNER TO postgres + ALTER TABLE public.mx_test_table ADD CONSTRAINT mx_test_table_col_1_key UNIQUE (col_1) + ALTER TABLE public.mx_test_table OWNER TO postgres + CALL pg_catalog.worker_drop_all_shell_tables(true) + CREATE INDEX mx_index ON public.mx_test_table USING btree (col_2) + CREATE SCHEMA IF NOT EXISTS public AUTHORIZATION postgres + CREATE TABLE public.mx_test_table (col_1 integer, col_2 text NOT NULL, col_3 bigint DEFAULT nextval('public.mx_test_table_col_3_seq'::regclass) NOT NULL, col_4 bigint DEFAULT nextval('public.user_defined_seq'::regclass)) USING heap + DELETE FROM pg_catalog.pg_dist_colocation + DELETE FROM pg_catalog.pg_dist_object + DELETE FROM pg_catalog.pg_dist_tenant_schema + DELETE FROM pg_dist_node + DELETE FROM pg_dist_partition + DELETE FROM pg_dist_placement + DELETE FROM pg_dist_shard + DROP TABLE IF EXISTS public.mx_test_table CASCADE + GRANT CREATE ON SCHEMA public TO PUBLIC; + GRANT CREATE ON SCHEMA public TO postgres; + GRANT USAGE ON SCHEMA public TO PUBLIC; + GRANT USAGE ON SCHEMA public TO postgres; + INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (1, 1, 'localhost', 57637, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE),(2, 2, 'localhost', 57638, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE) + RESET ROLE + RESET ROLE + SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') + SELECT citus_internal_add_partition_metadata ('public.mx_test_table'::regclass, 'h', 'col_1', 2, 's') + SELECT pg_catalog.worker_drop_sequence_dependency('public.mx_test_table'); + SELECT pg_catalog.worker_drop_sequence_dependency(logicalrelid::regclass::text) FROM pg_dist_partition + SELECT pg_catalog.worker_record_sequence_dependency('public.mx_test_table_col_3_seq'::regclass,'public.mx_test_table'::regclass,'col_3') + SELECT worker_apply_sequence_command ('CREATE SEQUENCE IF NOT EXISTS public.mx_test_table_col_3_seq AS bigint INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1 NO CYCLE','bigint') + SELECT worker_apply_sequence_command ('CREATE SEQUENCE IF NOT EXISTS public.user_defined_seq AS bigint INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1 NO CYCLE','bigint') + SELECT worker_create_or_alter_role('postgres', 'CREATE ROLE postgres SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN REPLICATION BYPASSRLS CONNECTION LIMIT 0 PASSWORD ''md5c53670dddfc3bb4b5675c7872bc2249a'' VALID UNTIL ''2052-05-05 00:00:00-07''', 'ALTER ROLE postgres SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN REPLICATION BYPASSRLS CONNECTION LIMIT 0 PASSWORD ''md5c53670dddfc3bb4b5675c7872bc2249a'' VALID UNTIL ''2052-05-05 00:00:00-07''') + SELECT worker_create_truncate_trigger('public.mx_test_table') + SET ROLE postgres + SET ROLE postgres + SET citus.enable_ddl_propagation TO 'off' + SET citus.enable_ddl_propagation TO 'off' + SET citus.enable_ddl_propagation TO 'on' + SET citus.enable_ddl_propagation TO 'on' + UPDATE pg_dist_local_group SET groupid = 1 + UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 1 + UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 1 + UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 1 + WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (2, 8, 1, 'integer'::regtype, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) + WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('database', ARRAY['regression']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; + WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('role', ARRAY['postgres']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; + WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('schema', ARRAY['public']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; + WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('sequence', ARRAY['public', 'mx_test_table_col_3_seq']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; + WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('sequence', ARRAY['public', 'user_defined_seq']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; + WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('table', ARRAY['public', 'mx_test_table']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; + WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310000, 0, 1, 100000), (1310001, 0, 2, 100001), (1310002, 0, 1, 100002), (1310003, 0, 2, 100003), (1310004, 0, 1, 100004), (1310005, 0, 2, 100005), (1310006, 0, 1, 100006), (1310007, 0, 2, 100007)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; + WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.mx_test_table'::regclass, 1310000, 't'::"char", '-2147483648', '-1610612737'), ('public.mx_test_table'::regclass, 1310001, 't'::"char", '-1610612736', '-1073741825'), ('public.mx_test_table'::regclass, 1310002, 't'::"char", '-1073741824', '-536870913'), ('public.mx_test_table'::regclass, 1310003, 't'::"char", '-536870912', '-1'), ('public.mx_test_table'::regclass, 1310004, 't'::"char", '0', '536870911'), ('public.mx_test_table'::regclass, 1310005, 't'::"char", '536870912', '1073741823'), ('public.mx_test_table'::regclass, 1310006, 't'::"char", '1073741824', '1610612735'), ('public.mx_test_table'::regclass, 1310007, 't'::"char", '1610612736', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; +(52 rows) + -- Show that schema changes are included in the activate node snapshot CREATE SCHEMA mx_testing_schema; ALTER TABLE mx_test_table SET SCHEMA mx_testing_schema; @@ -253,6 +256,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; CREATE TABLE mx_testing_schema.mx_test_table (col_1 integer, col_2 text NOT NULL, col_3 bigint DEFAULT nextval('mx_testing_schema.mx_test_table_col_3_seq'::regclass) NOT NULL, col_4 bigint DEFAULT nextval('public.user_defined_seq'::regclass)) USING heap DELETE FROM pg_catalog.pg_dist_colocation DELETE FROM pg_catalog.pg_dist_object + DELETE FROM pg_catalog.pg_dist_tenant_schema DELETE FROM pg_dist_node DELETE FROM pg_dist_partition DELETE FROM pg_dist_placement @@ -294,7 +298,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('table', ARRAY['mx_testing_schema', 'mx_test_table']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310000, 0, 1, 100000), (1310001, 0, 2, 100001), (1310002, 0, 1, 100002), (1310003, 0, 2, 100003), (1310004, 0, 1, 100004), (1310005, 0, 2, 100005), (1310006, 0, 1, 100006), (1310007, 0, 2, 100007)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('mx_testing_schema.mx_test_table'::regclass, 1310000, 't'::"char", '-2147483648', '-1610612737'), ('mx_testing_schema.mx_test_table'::regclass, 1310001, 't'::"char", '-1610612736', '-1073741825'), ('mx_testing_schema.mx_test_table'::regclass, 1310002, 't'::"char", '-1073741824', '-536870913'), ('mx_testing_schema.mx_test_table'::regclass, 1310003, 't'::"char", '-536870912', '-1'), ('mx_testing_schema.mx_test_table'::regclass, 1310004, 't'::"char", '0', '536870911'), ('mx_testing_schema.mx_test_table'::regclass, 1310005, 't'::"char", '536870912', '1073741823'), ('mx_testing_schema.mx_test_table'::regclass, 1310006, 't'::"char", '1073741824', '1610612735'), ('mx_testing_schema.mx_test_table'::regclass, 1310007, 't'::"char", '1610612736', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; -(53 rows) +(54 rows) -- Show that append distributed tables are not included in the activate node snapshot CREATE TABLE non_mx_test_table (col_1 int, col_2 text); @@ -320,6 +324,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; CREATE TABLE mx_testing_schema.mx_test_table (col_1 integer, col_2 text NOT NULL, col_3 bigint DEFAULT nextval('mx_testing_schema.mx_test_table_col_3_seq'::regclass) NOT NULL, col_4 bigint DEFAULT nextval('public.user_defined_seq'::regclass)) USING heap DELETE FROM pg_catalog.pg_dist_colocation DELETE FROM pg_catalog.pg_dist_object + DELETE FROM pg_catalog.pg_dist_tenant_schema DELETE FROM pg_dist_node DELETE FROM pg_dist_partition DELETE FROM pg_dist_placement @@ -361,7 +366,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('table', ARRAY['mx_testing_schema', 'mx_test_table']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310000, 0, 1, 100000), (1310001, 0, 2, 100001), (1310002, 0, 1, 100002), (1310003, 0, 2, 100003), (1310004, 0, 1, 100004), (1310005, 0, 2, 100005), (1310006, 0, 1, 100006), (1310007, 0, 2, 100007)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('mx_testing_schema.mx_test_table'::regclass, 1310000, 't'::"char", '-2147483648', '-1610612737'), ('mx_testing_schema.mx_test_table'::regclass, 1310001, 't'::"char", '-1610612736', '-1073741825'), ('mx_testing_schema.mx_test_table'::regclass, 1310002, 't'::"char", '-1073741824', '-536870913'), ('mx_testing_schema.mx_test_table'::regclass, 1310003, 't'::"char", '-536870912', '-1'), ('mx_testing_schema.mx_test_table'::regclass, 1310004, 't'::"char", '0', '536870911'), ('mx_testing_schema.mx_test_table'::regclass, 1310005, 't'::"char", '536870912', '1073741823'), ('mx_testing_schema.mx_test_table'::regclass, 1310006, 't'::"char", '1073741824', '1610612735'), ('mx_testing_schema.mx_test_table'::regclass, 1310007, 't'::"char", '1610612736', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; -(53 rows) +(54 rows) -- Show that range distributed tables are not included in the activate node snapshot UPDATE pg_dist_partition SET partmethod='r' WHERE logicalrelid='non_mx_test_table'::regclass; @@ -380,6 +385,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; CREATE TABLE mx_testing_schema.mx_test_table (col_1 integer, col_2 text NOT NULL, col_3 bigint DEFAULT nextval('mx_testing_schema.mx_test_table_col_3_seq'::regclass) NOT NULL, col_4 bigint DEFAULT nextval('public.user_defined_seq'::regclass)) USING heap DELETE FROM pg_catalog.pg_dist_colocation DELETE FROM pg_catalog.pg_dist_object + DELETE FROM pg_catalog.pg_dist_tenant_schema DELETE FROM pg_dist_node DELETE FROM pg_dist_partition DELETE FROM pg_dist_placement @@ -421,7 +427,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('table', ARRAY['mx_testing_schema', 'mx_test_table']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310000, 0, 1, 100000), (1310001, 0, 2, 100001), (1310002, 0, 1, 100002), (1310003, 0, 2, 100003), (1310004, 0, 1, 100004), (1310005, 0, 2, 100005), (1310006, 0, 1, 100006), (1310007, 0, 2, 100007)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('mx_testing_schema.mx_test_table'::regclass, 1310000, 't'::"char", '-2147483648', '-1610612737'), ('mx_testing_schema.mx_test_table'::regclass, 1310001, 't'::"char", '-1610612736', '-1073741825'), ('mx_testing_schema.mx_test_table'::regclass, 1310002, 't'::"char", '-1073741824', '-536870913'), ('mx_testing_schema.mx_test_table'::regclass, 1310003, 't'::"char", '-536870912', '-1'), ('mx_testing_schema.mx_test_table'::regclass, 1310004, 't'::"char", '0', '536870911'), ('mx_testing_schema.mx_test_table'::regclass, 1310005, 't'::"char", '536870912', '1073741823'), ('mx_testing_schema.mx_test_table'::regclass, 1310006, 't'::"char", '1073741824', '1610612735'), ('mx_testing_schema.mx_test_table'::regclass, 1310007, 't'::"char", '1610612736', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; -(53 rows) +(54 rows) -- Test start_metadata_sync_to_node and citus_activate_node UDFs -- Ensure that hasmetadata=false for all nodes @@ -1924,6 +1930,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; CREATE TABLE public.test_table (id integer DEFAULT worker_nextval('public.mx_test_sequence_0'::regclass), id2 integer DEFAULT worker_nextval('public.mx_test_sequence_1'::regclass)) USING heap DELETE FROM pg_catalog.pg_dist_colocation DELETE FROM pg_catalog.pg_dist_object + DELETE FROM pg_catalog.pg_dist_tenant_schema DELETE FROM pg_dist_node DELETE FROM pg_dist_partition DELETE FROM pg_dist_placement @@ -2008,7 +2015,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.dist_table_1'::regclass, 1310074, 't'::"char", '-2147483648', '-1073741825'), ('public.dist_table_1'::regclass, 1310075, 't'::"char", '-1073741824', '-1'), ('public.dist_table_1'::regclass, 1310076, 't'::"char", '0', '1073741823'), ('public.dist_table_1'::regclass, 1310077, 't'::"char", '1073741824', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.mx_ref'::regclass, 1310073, 't'::"char", NULL, NULL)) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.test_table'::regclass, 1310083, 't'::"char", '-2147483648', '-1073741825'), ('public.test_table'::regclass, 1310084, 't'::"char", '-1073741824', '-1'), ('public.test_table'::regclass, 1310085, 't'::"char", '0', '1073741823'), ('public.test_table'::regclass, 1310086, 't'::"char", '1073741824', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; -(117 rows) +(118 rows) -- shouldn't work since test_table is MX ALTER TABLE test_table ADD COLUMN id3 bigserial; diff --git a/src/test/regress/expected/schema_based_sharding.out b/src/test/regress/expected/schema_based_sharding.out new file mode 100644 index 000000000..6beb5ddd7 --- /dev/null +++ b/src/test/regress/expected/schema_based_sharding.out @@ -0,0 +1,1368 @@ +CREATE SCHEMA regular_schema; +SET search_path TO regular_schema; +SET citus.next_shard_id TO 1920000; +SET citus.shard_count TO 32; +SET citus.shard_replication_factor TO 1; +SET client_min_messages TO WARNING; +SELECT 1 FROM citus_add_node('localhost', :master_port, groupid => 0); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +SET client_min_messages TO NOTICE; +-- Verify that the UDFs used to sync tenant schema metadata to workers +-- fail on NULL input. +SELECT citus_internal_add_tenant_schema(NULL, 1); +ERROR: schema_id cannot be NULL +SELECT citus_internal_add_tenant_schema(1, NULL); +ERROR: colocation_id cannot be NULL +SELECT citus_internal_delete_tenant_schema(NULL); +ERROR: schema_id cannot be NULL +SELECT citus_internal_unregister_tenant_schema_globally(1, NULL); +ERROR: schema_name cannot be NULL +SELECT citus_internal_unregister_tenant_schema_globally(NULL, 'text'); +ERROR: schema_id cannot be NULL +-- Verify that citus_internal_unregister_tenant_schema_globally can only +-- be called on schemas that are dropped already. +SELECT citus_internal_unregister_tenant_schema_globally('regular_schema'::regnamespace, 'regular_schema'); +ERROR: schema is expected to be already dropped because this function is only expected to be called from Citus drop hook +SELECT 1 FROM citus_remove_node('localhost', :worker_2_port); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +CREATE TABLE regular_schema.test_table(a int, b text); +SET citus.enable_schema_based_sharding TO ON; +-- show that regular_schema doesn't show up in pg_dist_tenant_schema +SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'regular_schema'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- empty tenant +CREATE SCHEMA "tenant\'_1"; +-- non-empty tenant +CREATE SCHEMA "tenant\'_2"; +CREATE TABLE "tenant\'_2".test_table(a int, b text); +-- empty tenant +CREATE SCHEMA "tenant\'_3"; +CREATE TABLE "tenant\'_3".test_table(a int, b text); +DROP TABLE "tenant\'_3".test_table; +-- add a node after creating tenant schemas +SELECT 1 FROM citus_add_node('localhost', :worker_2_port); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +ALTER SCHEMA "tenant\'_1" RENAME TO tenant_1; +ALTER SCHEMA "tenant\'_2" RENAME TO tenant_2; +ALTER SCHEMA "tenant\'_3" RENAME TO tenant_3; +-- verify that create_distributed_table() and others fail when called on tenant tables +SELECT create_distributed_table('tenant_2.test_table', 'a'); +ERROR: table "test_table" is already distributed +SELECT create_reference_table('tenant_2.test_table'); +ERROR: table "test_table" is already distributed +SELECT citus_add_local_table_to_metadata('tenant_2.test_table'); +ERROR: table "test_table" is already distributed +-- (on coordinator) verify that colocation id is set for empty tenants too +SELECT colocationid > 0 FROM pg_dist_tenant_schema +WHERE schemaid::regnamespace::text IN ('tenant_1', 'tenant_3'); + ?column? +--------------------------------------------------------------------- + t + t +(2 rows) + +-- (on workers) verify that colocation id is set for empty tenants too +SELECT result FROM run_command_on_workers($$ + SELECT array_agg(colocationid > 0) FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text IN ('tenant_1', 'tenant_3'); +$$); + result +--------------------------------------------------------------------- + {t,t} + {t,t} +(2 rows) + +-- Verify that tenant_2.test_table is recorded in pg_dist_partition as a +-- single-shard table. +SELECT COUNT(*)=1 FROM pg_dist_partition +WHERE logicalrelid = 'tenant_2.test_table'::regclass AND + partmethod = 'n' AND repmodel = 's' AND colocationid > 0; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- (on coordinator) verify that colocation id is properly set for non-empty tenant schema +SELECT colocationid = ( + SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_2.test_table'::regclass +) +FROM pg_dist_tenant_schema +WHERE schemaid::regnamespace::text = 'tenant_2'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- (on workers) verify that colocation id is properly set for non-empty tenant schema +SELECT result FROM run_command_on_workers($$ + SELECT colocationid = ( + SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_2.test_table'::regclass + ) + FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = 'tenant_2'; +$$); + result +--------------------------------------------------------------------- + t + t +(2 rows) + +-- create a tenant table for tenant_1 after add_node +CREATE TABLE tenant_1.test_table(a int, b text); +-- (on coordinator) verify that colocation id is properly set for now-non-empty tenant schema +SELECT colocationid = ( + SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_1.test_table'::regclass +) +FROM pg_dist_tenant_schema +WHERE schemaid::regnamespace::text = 'tenant_1'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- (on workers) verify that colocation id is properly set for now-non-empty tenant schema +SELECT result FROM run_command_on_workers($$ + SELECT colocationid = ( + SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_1.test_table'::regclass + ) + FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = 'tenant_1'; +$$); + result +--------------------------------------------------------------------- + t + t +(2 rows) + +-- verify that tenant_1 and tenant_2 have different colocation ids +SELECT COUNT(DISTINCT(colocationid))=2 FROM pg_dist_tenant_schema +WHERE schemaid::regnamespace::text IN ('tenant_1', 'tenant_2'); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- verify that we don't allow creating tenant tables via CREATE SCHEMA command +CREATE SCHEMA schema_using_schema_elements CREATE TABLE test_table(a int, b text); +ERROR: cannot create tenant table in CREATE SCHEMA statement +HINT: Use CREATE TABLE statement to create tenant tables. +CREATE SCHEMA tenant_4; +CREATE TABLE tenant_4.tbl_1(a int, b text); +CREATE TABLE tenant_4.tbl_2(a int, b text); +-- verify that we don't allow creating a foreign table in a tenant schema, with a nice error message +CREATE FOREIGN TABLE tenant_4.foreign_table ( + id bigint not null, + full_name text not null default '' +) SERVER fake_fdw_server OPTIONS (encoding 'utf-8', compression 'true', table_name 'foreign_table'); +ERROR: cannot create a tenant table from a foreign table +-- verify that we don't allow creating a foreign table in a tenant schema +CREATE TEMPORARY TABLE tenant_4.temp_table (a int, b text); +ERROR: cannot create temporary relation in non-temporary schema +CREATE TABLE tenant_4.partitioned_table(a int, b text, PRIMARY KEY (a)) PARTITION BY RANGE (a); +CREATE TABLE tenant_4.partitioned_table_child_1 PARTITION OF tenant_4.partitioned_table FOR VALUES FROM (1) TO (2); +CREATE TABLE tenant_4.another_partitioned_table(a int, b text, FOREIGN KEY (a) REFERENCES tenant_4.partitioned_table(a)) PARTITION BY RANGE (a); +CREATE TABLE tenant_4.another_partitioned_table_child PARTITION OF tenant_4.another_partitioned_table FOR VALUES FROM (1) TO (2); +-- verify that we allow creating partitioned tables in a tenant schema +SELECT COUNT(*)=1 FROM pg_dist_partition +WHERE logicalrelid = 'tenant_4.partitioned_table_child_1'::regclass AND + partmethod = 'n' AND repmodel = 's' AND colocationid = ( + SELECT colocationid FROM pg_dist_partition + WHERE logicalrelid = 'tenant_4.partitioned_table'::regclass); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SELECT EXISTS( + SELECT 1 + FROM pg_inherits + WHERE inhrelid = 'tenant_4.partitioned_table_child_1'::regclass AND + inhparent = 'tenant_4.partitioned_table'::regclass +) AS is_partition; + is_partition +--------------------------------------------------------------------- + t +(1 row) + +SELECT COUNT(*)=1 FROM pg_dist_partition +WHERE logicalrelid = 'tenant_4.another_partitioned_table_child'::regclass AND + partmethod = 'n' AND repmodel = 's' AND colocationid = ( + SELECT colocationid FROM pg_dist_partition + WHERE logicalrelid = 'tenant_4.another_partitioned_table'::regclass); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SELECT EXISTS( + SELECT 1 + FROM pg_inherits + WHERE inhrelid = 'tenant_4.another_partitioned_table_child'::regclass AND + inhparent = 'tenant_4.another_partitioned_table'::regclass +) AS is_partition; + is_partition +--------------------------------------------------------------------- + t +(1 row) + +-- verify the foreign key between parents +SELECT EXISTS( + SELECT 1 + FROM pg_constraint + WHERE conrelid = 'tenant_4.another_partitioned_table'::regclass AND + confrelid = 'tenant_4.partitioned_table'::regclass AND + contype = 'f' +) AS foreign_key_exists; + foreign_key_exists +--------------------------------------------------------------------- + t +(1 row) + +INSERT INTO tenant_4.another_partitioned_table VALUES (1, 'a'); +ERROR: insert or update on table "another_partitioned_table_child_1920008" violates foreign key constraint "another_partitioned_table_a_fkey_1920007" +DETAIL: Key (a)=(1) is not present in table "partitioned_table_1920005". +CONTEXT: while executing command on localhost:xxxxx +INSERT INTO tenant_4.partitioned_table VALUES (1, 'a'); +INSERT INTO tenant_4.another_partitioned_table VALUES (1, 'a'); +CREATE SCHEMA tenant_5; +CREATE TABLE tenant_5.tbl_1(a int, b text); +CREATE TABLE tenant_5.partitioned_table(a int, b text) PARTITION BY RANGE (a); +-- verify that we don't allow creating a partition table that is child of a partitioned table in a different tenant schema +CREATE TABLE tenant_4.partitioned_table_child_2 PARTITION OF tenant_5.partitioned_table FOR VALUES FROM (1) TO (2); +ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas +-- verify that we don't allow creating a local partition table that is child of a tenant partitioned table +CREATE TABLE regular_schema.local_child_table PARTITION OF tenant_5.partitioned_table FOR VALUES FROM (1) TO (2); +ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas +SET citus.use_citus_managed_tables TO ON; +CREATE TABLE regular_schema.local_child_table PARTITION OF tenant_5.partitioned_table FOR VALUES FROM (1) TO (2); +ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas +RESET citus.use_citus_managed_tables; +CREATE TABLE regular_schema.local_partitioned_table(a int, b text) PARTITION BY RANGE (a); +CREATE TABLE regular_schema.citus_local_partitioned_table(a int, b text) PARTITION BY RANGE (a); +SELECT citus_add_local_table_to_metadata('regular_schema.citus_local_partitioned_table'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE regular_schema.dist_partitioned_table(a int, b text) PARTITION BY RANGE (a); +SELECT create_distributed_table('regular_schema.dist_partitioned_table', 'a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- verify that we don't allow creating a partition table that is child of a non-tenant partitioned table +CREATE TABLE tenant_4.partitioned_table_child_2 PARTITION OF regular_schema.local_partitioned_table FOR VALUES FROM (1) TO (2); +ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas +CREATE TABLE tenant_4.partitioned_table_child_2 PARTITION OF regular_schema.citus_local_partitioned_table FOR VALUES FROM (1) TO (2); +ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas +CREATE TABLE tenant_4.partitioned_table_child_2 PARTITION OF regular_schema.dist_partitioned_table FOR VALUES FROM (1) TO (2); +ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas +CREATE TABLE tenant_4.parent_attach_test(a int, b text) PARTITION BY RANGE (a); +CREATE TABLE tenant_4.child_attach_test(a int, b text); +CREATE TABLE tenant_5.parent_attach_test(a int, b text) PARTITION BY RANGE (a); +CREATE TABLE tenant_5.child_attach_test(a int, b text); +CREATE TABLE regular_schema.parent_attach_test_local(a int, b text) PARTITION BY RANGE (a); +CREATE TABLE regular_schema.parent_attach_test_citus_local(a int, b text) PARTITION BY RANGE (a); +SELECT citus_add_local_table_to_metadata('regular_schema.parent_attach_test_citus_local'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE regular_schema.parent_attach_test_dist(a int, b text) PARTITION BY RANGE (a); +SELECT create_distributed_table('regular_schema.parent_attach_test_dist', 'a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE regular_schema.child_attach_test_local(a int, b text); +CREATE TABLE regular_schema.child_attach_test_citus_local(a int, b text); +SELECT citus_add_local_table_to_metadata('regular_schema.child_attach_test_citus_local'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE regular_schema.child_attach_test_dist(a int, b text); +SELECT create_distributed_table('regular_schema.child_attach_test_dist', 'a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- verify that we don't allow attaching a tenant table into a tenant partitioned table, if they are not in the same schema +ALTER TABLE tenant_4.parent_attach_test ATTACH PARTITION tenant_5.child_attach_test FOR VALUES FROM (1) TO (2); +ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas +-- verify that we don't allow attaching a non-tenant table into a tenant partitioned table +ALTER TABLE tenant_4.parent_attach_test ATTACH PARTITION regular_schema.child_attach_test_local FOR VALUES FROM (1) TO (2); +ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas +ALTER TABLE tenant_4.parent_attach_test ATTACH PARTITION regular_schema.child_attach_test_citus_local FOR VALUES FROM (1) TO (2); +ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas +ALTER TABLE tenant_4.parent_attach_test ATTACH PARTITION regular_schema.child_attach_test_dist FOR VALUES FROM (1) TO (2); +ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas +-- verify that we don't allow attaching a tenant table into a non-tenant partitioned table +ALTER TABLE regular_schema.parent_attach_test_local ATTACH PARTITION tenant_4.child_attach_test FOR VALUES FROM (1) TO (2); +ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas +ALTER TABLE regular_schema.parent_attach_test_citus_local ATTACH PARTITION tenant_4.child_attach_test FOR VALUES FROM (1) TO (2); +ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas +ALTER TABLE regular_schema.parent_attach_test_dist ATTACH PARTITION tenant_4.child_attach_test FOR VALUES FROM (1) TO (2); +ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas +ALTER TABLE tenant_4.parent_attach_test ATTACH PARTITION tenant_4.child_attach_test FOR VALUES FROM (1) TO (2); +-- verify that we don't allow multi-level partitioning on tenant tables +CREATE TABLE tenant_4.multi_level_test(a int, b text) PARTITION BY RANGE (a); +ALTER TABLE tenant_4.parent_attach_test ATTACH PARTITION tenant_4.multi_level_test FOR VALUES FROM (1) TO (2); +ERROR: Citus doesn't support multi-level partitioned tables +DETAIL: Relation "multi_level_test" is partitioned table itself and it is also partition of relation "parent_attach_test". +-- verify that we allow attaching a tenant table into a tenant partitioned table, if they are in the same schema +SELECT COUNT(*)=1 FROM pg_dist_partition +WHERE logicalrelid = 'tenant_4.parent_attach_test'::regclass AND + partmethod = 'n' AND repmodel = 's' AND colocationid = ( + SELECT colocationid FROM pg_dist_partition + WHERE logicalrelid = 'tenant_4.child_attach_test'::regclass); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SELECT EXISTS( + SELECT 1 + FROM pg_inherits + WHERE inhrelid = 'tenant_4.child_attach_test'::regclass AND + inhparent = 'tenant_4.parent_attach_test'::regclass +) AS is_partition; + is_partition +--------------------------------------------------------------------- + t +(1 row) + +-- verify that we don't allow creating tenant tables by using CREATE TABLE AS / SELECT INTO commands +CREATE TABLE tenant_4.tbl_3 AS SELECT 1 AS a, 'text' as b; +ERROR: cannot create a tenant table using CREATE TABLE AS or SELECT INTO statements +CREATE TABLE IF NOT EXISTS tenant_4.tbl_3 AS SELECT 1 as a, 'text' as b; +ERROR: cannot create a tenant table using CREATE TABLE AS or SELECT INTO statements +SELECT 1 as a, 'text' as b INTO tenant_4.tbl_3; +ERROR: cannot create a tenant table using CREATE TABLE AS or SELECT INTO statements +CREATE TYPE employee_type AS (name text, salary numeric); +-- verify that we don't allow creating tenant tables by using CREATE TABLE OF commands +CREATE TABLE tenant_4.employees OF employee_type ( + PRIMARY KEY (name), + salary WITH OPTIONS DEFAULT 1000 +); +ERROR: cannot create a tenant table by using CREATE TABLE OF syntax +-- verify that we act accordingly when if not exists is used +CREATE TABLE IF NOT EXISTS tenant_4.tbl_3(a int, b text); +CREATE TABLE IF NOT EXISTS tenant_4.tbl_3(a int, b text); +NOTICE: relation "tbl_3" already exists, skipping +CREATE TABLE regular_schema.local(a int, b text); +CREATE TABLE regular_schema.citus_local(a int, b text); +SELECT citus_add_local_table_to_metadata('regular_schema.citus_local'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE regular_schema.dist(a int, b text); +SELECT create_distributed_table('regular_schema.dist', 'a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- verify that we can create a table LIKE another table +CREATE TABLE tenant_5.test_table_like_1(LIKE tenant_5.tbl_1); -- using a table from the same schema +CREATE TABLE tenant_5.test_table_like_2(LIKE tenant_4.tbl_1); -- using a table from another schema +CREATE TABLE tenant_5.test_table_like_3(LIKE regular_schema.local); -- using a local table +CREATE TABLE tenant_5.test_table_like_4(LIKE regular_schema.citus_local); -- using a citus local table +CREATE TABLE tenant_5.test_table_like_5(LIKE regular_schema.dist); -- using a distributed table +-- verify that all of them are converted to tenant tables +SELECT COUNT(*) = 5 +FROM pg_dist_partition +WHERE logicalrelid::text LIKE 'tenant_5.test_table_like_%' AND + partmethod = 'n' AND repmodel = 's' AND colocationid = ( + SELECT colocationid FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = 'tenant_5' + ); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +CREATE TABLE regular_schema.local_table_using_like(LIKE tenant_5.tbl_1); +-- verify that regular_schema.local_table_using_like is not a tenant table +SELECT COUNT(*) = 0 FROM pg_dist_partition +WHERE logicalrelid = 'regular_schema.local_table_using_like'::regclass; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- verify that INHERITS syntax is not supported when creating a tenant table +CREATE TABLE tenant_5.test_table_inherits_1(x int) INHERITS (tenant_5.tbl_1); -- using a table from the same schema +ERROR: tenant tables cannot inherit or be inherited +CREATE TABLE tenant_5.test_table_inherits_2(x int) INHERITS (tenant_4.tbl_1); -- using a table from another schema +ERROR: tenant tables cannot inherit or be inherited +CREATE TABLE tenant_5.test_table_inherits_3(x int) INHERITS (regular_schema.local); -- using a local table +ERROR: tenant tables cannot inherit or be inherited +CREATE TABLE tenant_5.test_table_inherits_4(x int) INHERITS (regular_schema.citus_local); -- using a citus local table +ERROR: tenant tables cannot inherit or be inherited +CREATE TABLE tenant_5.test_table_inherits_5(x int) INHERITS (regular_schema.dist); -- using a distributed table +ERROR: tenant tables cannot inherit or be inherited +-- verify that INHERITS syntax is not supported when creating a local table based on a tenant table +CREATE TABLE regular_schema.local_table_using_inherits(x int) INHERITS (tenant_5.tbl_1); +ERROR: tenant tables cannot inherit or be inherited +CREATE TABLE tenant_5.tbl_2(a int, b text); +CREATE SCHEMA "CiTuS.TeeN_108"; +ALTER SCHEMA "CiTuS.TeeN_108" RENAME TO citus_teen_proper; +SELECT schemaid AS citus_teen_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'citus_teen_proper' \gset +SELECT colocationid AS citus_teen_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'citus_teen_proper' \gset +SELECT result FROM run_command_on_workers($$ + SELECT schemaid INTO citus_teen_schemaid FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = 'citus_teen_proper' +$$); + result +--------------------------------------------------------------------- + SELECT 1 + SELECT 1 +(2 rows) + +-- (on coordinator) verify that colocation id is set for the tenant with a weird name too +SELECT :citus_teen_colocationid > 0; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- (on workers) verify that the same colocation id is used on workers too +SELECT format( + 'SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=1 FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = ''citus_teen_proper'' AND + colocationid = %s; + $$);', +:citus_teen_colocationid) AS verify_workers_query \gset +:verify_workers_query + result +--------------------------------------------------------------------- + t + t +(2 rows) + +ALTER SCHEMA citus_teen_proper RENAME TO "CiTuS.TeeN_108"; +SET citus.enable_schema_based_sharding TO OFF; +-- Show that the tables created in tenant schemas are considered to be +-- tenant tables even if the GUC was set to off when creating the table. +CREATE TABLE tenant_5.tbl_3(a int, b text); +SELECT COUNT(*)=1 FROM pg_dist_partition WHERE logicalrelid = 'tenant_5.tbl_3'::regclass; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SET citus.enable_schema_based_sharding TO ON; +-- Verify that tables that belong to tenant_4 and tenant_5 are stored on +-- different worker nodes due to order we followed when creating first tenant +-- tables in each of them. +SELECT COUNT(DISTINCT(nodename, nodeport))=2 FROM citus_shards +WHERE table_name IN ('tenant_4.tbl_1'::regclass, 'tenant_5.tbl_1'::regclass); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- show that all the tables in tenant_4 are colocated with each other. +SELECT COUNT(DISTINCT(colocationid))=1 FROM pg_dist_partition +WHERE logicalrelid::regclass::text LIKE 'tenant_4.%'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- verify the same for tenant_5 too +SELECT COUNT(DISTINCT(colocationid))=1 FROM pg_dist_partition +WHERE logicalrelid::regclass::text LIKE 'tenant_5.%'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SELECT schemaid AS tenant_4_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_4' \gset +SELECT colocationid AS tenant_4_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_4' \gset +SELECT result FROM run_command_on_workers($$ + SELECT schemaid INTO tenant_4_schemaid FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = 'tenant_4' +$$); + result +--------------------------------------------------------------------- + SELECT 1 + SELECT 1 +(2 rows) + +SET client_min_messages TO WARNING; +-- Rename it to a name that contains a single quote to verify that we properly +-- escape its name when sending the command to delete the pg_dist_tenant_schema +-- entry on workers. +ALTER SCHEMA tenant_4 RENAME TO "tenant\'_4"; +DROP SCHEMA "tenant\'_4", "CiTuS.TeeN_108" CASCADE; +SET client_min_messages TO NOTICE; +-- (on coordinator) Verify that dropping a tenant schema deletes the associated +-- pg_dist_tenant_schema entry and pg_dist_colocation too. +SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid = :tenant_4_schemaid; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = :tenant_4_colocationid; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid = :citus_teen_schemaid; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = :citus_teen_colocationid; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- (on workers) Verify that dropping a tenant schema deletes the associated +-- pg_dist_tenant_schema entry and pg_dist_colocation too. +SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=0 FROM pg_dist_tenant_schema + WHERE schemaid = (SELECT schemaid FROM tenant_4_schemaid) +$$); + result +--------------------------------------------------------------------- + t + t +(2 rows) + +SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=0 FROM pg_dist_tenant_schema + WHERE schemaid = (SELECT schemaid FROM citus_teen_schemaid) +$$); + result +--------------------------------------------------------------------- + t + t +(2 rows) + +SELECT format( + 'SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = %s; + $$);', +:tenant_4_colocationid) AS verify_workers_query \gset +:verify_workers_query + result +--------------------------------------------------------------------- + t + t +(2 rows) + +SELECT result FROM run_command_on_workers($$ + DROP TABLE tenant_4_schemaid +$$); + result +--------------------------------------------------------------------- + DROP TABLE + DROP TABLE +(2 rows) + +SELECT format( + 'SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = %s; + $$);', +:citus_teen_colocationid) AS verify_workers_query \gset +:verify_workers_query + result +--------------------------------------------------------------------- + t + t +(2 rows) + +SELECT result FROM run_command_on_workers($$ + DROP TABLE citus_teen_schemaid +$$); + result +--------------------------------------------------------------------- + DROP TABLE + DROP TABLE +(2 rows) + +-- show that we don't allow colocating a Citus table with a tenant table +CREATE TABLE regular_schema.null_shard_key_1(a int, b text); +SELECT create_distributed_table('regular_schema.null_shard_key_1', null, colocate_with => 'tenant_5.tbl_2'); +ERROR: cannot colocate tables tbl_2 and null_shard_key_1 +DETAIL: Cannot colocate tables with tenant tables by using colocate_with option. +HINT: Consider using "CREATE TABLE" statement to create this table as a tenant table in the same schema to automatically colocate it with tenant_5.tbl_2 +SELECT create_distributed_table('regular_schema.null_shard_key_1', 'a', colocate_with => 'tenant_5.tbl_2'); +ERROR: cannot colocate tables tbl_2 and null_shard_key_1 +DETAIL: Distribution column types don't match for tbl_2 and null_shard_key_1. +CREATE TABLE regular_schema.null_shard_key_table_2(a int, b text); +SELECT create_distributed_table('regular_schema.null_shard_key_table_2', null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- Show that we don't chose to colocate regular single-shard tables with +-- tenant tables by default. +SELECT * FROM pg_dist_tenant_schema WHERE colocationid = ( + SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'regular_schema.null_shard_key_table_2'::regclass +); + schemaid | colocationid +--------------------------------------------------------------------- +(0 rows) + +-- save the colocation id used for tenant_5 +SELECT colocationid AS tenant_5_old_colocationid FROM pg_dist_tenant_schema +WHERE schemaid::regnamespace::text = 'tenant_5' \gset +-- drop all the tables that belong to tenant_5 and create a new one +DROP TABLE tenant_5.tbl_1, tenant_5.tbl_2, tenant_5.tbl_3; +CREATE TABLE tenant_5.tbl_4(a int, b text); +-- (on coordinator) verify that tenant_5 is still associated with the same colocation id +SELECT colocationid = :tenant_5_old_colocationid FROM pg_dist_tenant_schema +WHERE schemaid::regnamespace::text = 'tenant_5'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- (on workers) verify that tenant_5 is still associated with the same colocation id +SELECT format( + 'SELECT result FROM run_command_on_workers($$ + SELECT colocationid = %s FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = ''tenant_5''; + $$);', +:tenant_5_old_colocationid) AS verify_workers_query \gset +:verify_workers_query + result +--------------------------------------------------------------------- + t + t +(2 rows) + +SELECT schemaid AS tenant_1_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_1' \gset +SELECT colocationid AS tenant_1_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_1' \gset +SELECT schemaid AS tenant_2_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_2' \gset +SELECT colocationid AS tenant_2_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_2' \gset +SELECT result FROM run_command_on_workers($$ + SELECT schemaid INTO tenant_1_schemaid FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = 'tenant_1' +$$); + result +--------------------------------------------------------------------- + SELECT 1 + SELECT 1 +(2 rows) + +SELECT result FROM run_command_on_workers($$ + SELECT schemaid INTO tenant_2_schemaid FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = 'tenant_2' +$$); + result +--------------------------------------------------------------------- + SELECT 1 + SELECT 1 +(2 rows) + +SET client_min_messages TO WARNING; +SET citus.enable_schema_based_sharding TO OFF; +DROP SCHEMA tenant_1 CASCADE; +CREATE ROLE test_non_super_user; +ALTER ROLE test_non_super_user NOSUPERUSER; +ALTER SCHEMA tenant_2 OWNER TO test_non_super_user; +-- XXX: ALTER SCHEMA .. OWNER TO .. is not propagated to workers, +-- see https://github.com/citusdata/citus/issues/4812. +SELECT result FROM run_command_on_workers($$ALTER SCHEMA tenant_2 OWNER TO test_non_super_user$$); + result +--------------------------------------------------------------------- + ALTER SCHEMA + ALTER SCHEMA +(2 rows) + +DROP OWNED BY test_non_super_user CASCADE; +DROP ROLE test_non_super_user; +SET client_min_messages TO NOTICE; +-- (on coordinator) Verify that dropping a tenant schema always deletes +-- the associated pg_dist_tenant_schema entry even if the the schema was +-- dropped while the GUC was set to off. +SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid IN (:tenant_1_schemaid, :tenant_2_schemaid); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid IN (:tenant_1_colocationid, :tenant_2_colocationid); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- (on workers) Verify that dropping a tenant schema always deletes +-- the associated pg_dist_tenant_schema entry even if the the schema was +-- dropped while the GUC was set to off. +SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=0 FROM pg_dist_tenant_schema + WHERE schemaid IN (SELECT schemaid FROM tenant_1_schemaid UNION SELECT schemaid FROM tenant_2_schemaid) +$$); + result +--------------------------------------------------------------------- + t + t +(2 rows) + +SELECT format( + 'SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid IN (%s, %s); + $$);', +:tenant_1_colocationid, :tenant_2_colocationid) AS verify_workers_query \gset +:verify_workers_query + result +--------------------------------------------------------------------- + t + t +(2 rows) + +SELECT result FROM run_command_on_workers($$ + DROP TABLE tenant_1_schemaid +$$); + result +--------------------------------------------------------------------- + DROP TABLE + DROP TABLE +(2 rows) + +SELECT result FROM run_command_on_workers($$ + DROP TABLE tenant_2_schemaid +$$); + result +--------------------------------------------------------------------- + DROP TABLE + DROP TABLE +(2 rows) + +SET citus.enable_schema_based_sharding TO ON; +SET client_min_messages TO NOTICE; +-- show that all schemaid values are unique and non-null in pg_dist_tenant_schema +SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid IS NULL; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SELECT (SELECT COUNT(*) FROM pg_dist_tenant_schema) = + (SELECT COUNT(DISTINCT(schemaid)) FROM pg_dist_tenant_schema); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- show that all colocationid values are unique and non-null in pg_dist_tenant_schema +SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE colocationid IS NULL; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SELECT (SELECT COUNT(*) FROM pg_dist_tenant_schema) = + (SELECT COUNT(DISTINCT(colocationid)) FROM pg_dist_tenant_schema); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +CREATE TABLE public.cannot_be_a_tenant_table(a int, b text); +-- show that we don't consider public schema as a tenant schema +SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'public'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +DROP TABLE public.cannot_be_a_tenant_table; +BEGIN; + ALTER SCHEMA public RENAME TO public_renamed; + CREATE SCHEMA public; + -- Show that we don't consider public schema as a tenant schema, + -- even if it's recreated. + SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'public'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +ROLLBACK; +CREATE TEMPORARY TABLE temp_table(a int, b text); +-- show that we don't consider temporary schemas as tenant schemas +SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = '%pg_temp%'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +DROP TABLE temp_table; +-- test creating a tenant schema and a tenant table for it in the same transaction +BEGIN; + CREATE SCHEMA tenant_7; + CREATE TABLE tenant_7.tbl_1(a int, b text); + CREATE TABLE tenant_7.tbl_2(a int, b text); + SELECT colocationid = ( + SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_7.tbl_1'::regclass + ) + FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = 'tenant_7'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + + -- make sure that both tables created in tenant_7 are colocated + SELECT COUNT(DISTINCT(colocationid)) = 1 FROM pg_dist_partition + WHERE logicalrelid IN ('tenant_7.tbl_1'::regclass, 'tenant_7.tbl_2'::regclass); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +COMMIT; +-- Test creating a tenant schema and a tenant table for it in the same transaction +-- but this time rollback the transaction. +BEGIN; + CREATE SCHEMA tenant_8; + CREATE TABLE tenant_8.tbl_1(a int, b text); + CREATE TABLE tenant_8.tbl_2(a int, b text); +ROLLBACK; +SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_8'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SELECT COUNT(*)=0 FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant_8.%'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- Verify that citus.enable_schema_based_sharding and citus.use_citus_managed_tables +-- GUC don't interfere with each other when creating a table in tenant schema. +-- +-- In utility hook, we check whether the CREATE TABLE command is issued on a tenant +-- schema before checking whether citus.use_citus_managed_tables is set to ON to +-- avoid converting the table into a Citus managed table unnecessarily. +-- +-- If the CREATE TABLE command is issued on a tenant schema, we skip the check +-- for citus.use_citus_managed_tables. +SET citus.use_citus_managed_tables TO ON; +CREATE TABLE tenant_7.tbl_3(a int, b text, PRIMARY KEY(a)); +RESET citus.use_citus_managed_tables; +-- Verify that we don't unnecessarily convert a table into a Citus managed +-- table when creating it with a pre-defined foreign key to a reference table. +CREATE TABLE reference_table(a int PRIMARY KEY); +SELECT create_reference_table('reference_table'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +-- Notice that tenant_7.tbl_4 have foreign keys both to tenant_7.tbl_3 and +-- to reference_table. +CREATE TABLE tenant_7.tbl_4(a int REFERENCES reference_table, FOREIGN KEY(a) REFERENCES tenant_7.tbl_3(a) ON DELETE CASCADE); +INSERT INTO tenant_7.tbl_3 VALUES (1, 'a'), (2, 'b'), (3, 'c'); +INSERT INTO reference_table VALUES (1), (2), (3); +INSERT INTO tenant_7.tbl_4 VALUES (1), (2), (3); +DELETE FROM tenant_7.tbl_3 WHERE a < 3; +SELECT * FROM tenant_7.tbl_4 ORDER BY a; + a +--------------------------------------------------------------------- + 3 +(1 row) + +SELECT COUNT(*)=2 FROM pg_dist_partition +WHERE logicalrelid IN ('tenant_7.tbl_3'::regclass, 'tenant_7.tbl_4'::regclass) AND + partmethod = 'n' AND repmodel = 's' AND + colocationid = (SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_7.tbl_1'::regclass); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +CREATE TABLE local_table(a int PRIMARY KEY); +-- fails because tenant tables cannot have foreign keys to local tables +CREATE TABLE tenant_7.tbl_5(a int REFERENCES local_table(a)); +ERROR: referenced table "local_table" must be a distributed table or a reference table +DETAIL: To enforce foreign keys, the referencing and referenced rows need to be stored on the same node. +HINT: You could use SELECT create_reference_table('local_table') to replicate the referenced table to all nodes or consider dropping the foreign key +-- Fails because tenant tables cannot have foreign keys to tenant tables +-- that belong to different tenant schemas. +CREATE TABLE tenant_5.tbl_5(a int, b text, FOREIGN KEY(a) REFERENCES tenant_7.tbl_3(a)); +ERROR: cannot create foreign key constraint since relations are not colocated or not referencing a reference table +DETAIL: A distributed table can only have foreign keys if it is referencing another colocated hash distributed table or a reference table +CREATE SCHEMA tenant_9; +SELECT schemaid AS tenant_9_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_9' \gset +SELECT colocationid AS tenant_9_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_9' \gset +SELECT result FROM run_command_on_workers($$ + SELECT schemaid INTO tenant_9_schemaid FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = 'tenant_9' +$$); + result +--------------------------------------------------------------------- + SELECT 1 + SELECT 1 +(2 rows) + +DROP SCHEMA tenant_9; +-- (on coordinator) Make sure that dropping an empty tenant schema +-- doesn't leave any dangling entries in pg_dist_tenant_schema and +-- pg_dist_colocation. +SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid = :tenant_9_schemaid; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = :tenant_9_colocationid; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- (on workers) Make sure that dropping an empty tenant schema +-- doesn't leave any dangling entries in pg_dist_tenant_schema and +-- pg_dist_colocation. +SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=0 FROM pg_dist_tenant_schema + WHERE schemaid = (SELECT schemaid FROM tenant_9_schemaid) +$$); + result +--------------------------------------------------------------------- + t + t +(2 rows) + +SELECT format( + 'SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = %s; + $$);', +:tenant_9_colocationid) AS verify_workers_query \gset +:verify_workers_query + result +--------------------------------------------------------------------- + t + t +(2 rows) + +SELECT result FROM run_command_on_workers($$ + DROP TABLE tenant_9_schemaid +$$); + result +--------------------------------------------------------------------- + DROP TABLE + DROP TABLE +(2 rows) + +CREATE TABLE tenant_3.search_path_test(a int); +INSERT INTO tenant_3.search_path_test VALUES (1), (10); +CREATE TABLE tenant_5.search_path_test(a int); +INSERT INTO tenant_5.search_path_test VALUES (2); +CREATE TABLE tenant_7.search_path_test(a int); +INSERT INTO tenant_7.search_path_test VALUES (3); +CREATE FUNCTION increment_one() +RETURNS void +LANGUAGE plpgsql +AS $$ +BEGIN + UPDATE search_path_test SET a = a + 1; +END; +$$; +CREATE FUNCTION decrement_one() +RETURNS void +LANGUAGE plpgsql +AS $$ +BEGIN + UPDATE search_path_test SET a = a - 1; +END; +$$; +SET search_path TO tenant_5; +PREPARE list_tuples AS SELECT * FROM search_path_test ORDER BY a; +SELECT * FROM search_path_test ORDER BY a; + a +--------------------------------------------------------------------- + 2 +(1 row) + +SET search_path TO tenant_3; +DELETE FROM search_path_test WHERE a = 1; +SELECT * FROM search_path_test ORDER BY a; + a +--------------------------------------------------------------------- + 10 +(1 row) + +SELECT regular_schema.increment_one(); + increment_one +--------------------------------------------------------------------- + +(1 row) + +EXECUTE list_tuples; + a +--------------------------------------------------------------------- + 11 +(1 row) + +SET search_path TO tenant_7; +DROP TABLE search_path_test; +SELECT * FROM pg_dist_partition WHERE logicalrelid::text = 'search_path_test'; + logicalrelid | partmethod | partkey | colocationid | repmodel | autoconverted +--------------------------------------------------------------------- +(0 rows) + +SET search_path TO tenant_5; +SELECT regular_schema.decrement_one(); + decrement_one +--------------------------------------------------------------------- + +(1 row) + +EXECUTE list_tuples; + a +--------------------------------------------------------------------- + 1 +(1 row) + +SET search_path TO regular_schema; +CREATE USER test_other_super_user WITH superuser; +\c - test_other_super_user +SET citus.enable_schema_based_sharding TO ON; +CREATE SCHEMA tenant_9; +\c - postgres +SET search_path TO regular_schema; +SET citus.next_shard_id TO 1930000; +SET citus.shard_count TO 32; +SET citus.shard_replication_factor TO 1; +SET client_min_messages TO NOTICE; +SET citus.enable_schema_based_sharding TO ON; +SELECT schemaid AS tenant_9_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_9' \gset +SELECT colocationid AS tenant_9_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_9' \gset +SELECT result FROM run_command_on_workers($$ + SELECT schemaid INTO tenant_9_schemaid FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = 'tenant_9' +$$); + result +--------------------------------------------------------------------- + SELECT 1 + SELECT 1 +(2 rows) + +DROP OWNED BY test_other_super_user; +-- (on coordinator) Make sure that dropping an empty tenant schema +-- (via DROP OWNED BY) doesn't leave any dangling entries in +-- pg_dist_tenant_schema and pg_dist_colocation. +SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid = :tenant_9_schemaid; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = :tenant_9_colocationid; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- (on workers) Make sure that dropping an empty tenant schema +-- (via DROP OWNED BY) doesn't leave any dangling entries in +-- pg_dist_tenant_schema and pg_dist_colocation. +SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=0 FROM pg_dist_tenant_schema + WHERE schemaid = (SELECT schemaid FROM tenant_9_schemaid) +$$); + result +--------------------------------------------------------------------- + t + t +(2 rows) + +SELECT format( + 'SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = %s; + $$);', +:tenant_9_colocationid) AS verify_workers_query \gset +:verify_workers_query + result +--------------------------------------------------------------------- + t + t +(2 rows) + +SELECT result FROM run_command_on_workers($$ + DROP TABLE tenant_9_schemaid +$$); + result +--------------------------------------------------------------------- + DROP TABLE + DROP TABLE +(2 rows) + +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; +SET citus.next_shard_id TO 1940000; +SET citus.shard_count TO 32; +SET citus.shard_replication_factor TO 1; +SET client_min_messages TO NOTICE; +SET citus.enable_schema_based_sharding TO ON; +-- test create / drop tenant schema / table +CREATE SCHEMA tenant_10; +CREATE TABLE tenant_10.tbl_1(a int, b text); +CREATE TABLE tenant_10.tbl_2(a int, b text); +DROP TABLE tenant_10.tbl_2; +CREATE SCHEMA tenant_11; +SELECT schemaid AS tenant_10_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_10' \gset +SELECT colocationid AS tenant_10_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_10' \gset +SELECT schemaid AS tenant_11_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_11' \gset +SELECT colocationid AS tenant_11_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_11' \gset +SELECT result FROM run_command_on_workers($$ + SELECT schemaid INTO tenant_10_schemaid FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = 'tenant_10' +$$); + result +--------------------------------------------------------------------- + SELECT 1 + SELECT 1 +(2 rows) + +SELECT result FROM run_command_on_workers($$ + SELECT schemaid INTO tenant_11_schemaid FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = 'tenant_11' +$$); + result +--------------------------------------------------------------------- + SELECT 1 + SELECT 1 +(2 rows) + +-- (on coordinator) Verify metadata for tenant schemas that are created via non-super-user. +SELECT COUNT(DISTINCT(schemaid))=2 FROM pg_dist_tenant_schema WHERE schemaid IN (:tenant_10_schemaid, :tenant_11_schemaid); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SELECT COUNT(DISTINCT(colocationid))=2 FROM pg_dist_colocation WHERE colocationid IN (:tenant_10_colocationid, :tenant_11_colocationid); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- (on workers) Verify metadata for tenant schemas that are created via non-super-user. +SELECT result FROM run_command_on_workers($$ + SELECT COUNT(DISTINCT(schemaid))=2 FROM pg_dist_tenant_schema + WHERE schemaid IN (SELECT schemaid FROM tenant_10_schemaid UNION SELECT schemaid FROM tenant_11_schemaid) +$$); + result +--------------------------------------------------------------------- + t + t +(2 rows) + +SELECT format( + 'SELECT result FROM run_command_on_workers($$ + SELECT COUNT(DISTINCT(colocationid))=2 FROM pg_dist_colocation WHERE colocationid IN (%s, %s); + $$);', +:tenant_10_colocationid, :tenant_11_colocationid) AS verify_workers_query \gset +:verify_workers_query + result +--------------------------------------------------------------------- + t + t +(2 rows) + +SET client_min_messages TO WARNING; +DROP SCHEMA tenant_10, tenant_11 CASCADE; +SET client_min_messages TO NOTICE; +-- (on coordinator) Verify that dropping a tenant schema via non-super-user +-- deletes the associated pg_dist_tenant_schema entry. +SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid IN (:tenant_10_schemaid, :tenant_11_schemaid); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid IN (:tenant_10_colocationid, :tenant_11_colocationid); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- (on workers) Verify that dropping a tenant schema via non-super-user +-- deletes the associated pg_dist_tenant_schema entry. +SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=0 FROM pg_dist_tenant_schema + WHERE schemaid IN (SELECT schemaid FROM tenant_10_schemaid UNION SELECT schemaid FROM tenant_11_schemaid) +$$); + result +--------------------------------------------------------------------- + t + t +(2 rows) + +SELECT format( + 'SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid IN (%s, %s); + $$);', +:tenant_10_colocationid, :tenant_11_colocationid) AS verify_workers_query \gset +:verify_workers_query + result +--------------------------------------------------------------------- + t + t +(2 rows) + +SELECT result FROM run_command_on_workers($$ + DROP TABLE tenant_10_schemaid +$$); + result +--------------------------------------------------------------------- + DROP TABLE + DROP TABLE +(2 rows) + +SELECT result FROM run_command_on_workers($$ + DROP TABLE tenant_11_schemaid +$$); + result +--------------------------------------------------------------------- + DROP TABLE + DROP TABLE +(2 rows) + +\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 +-- test creating a tenant table from workers +CREATE TABLE tenant_3.tbl_1(a int, b text); +ERROR: cannot create a tenant table from a worker node +HINT: Connect to the coordinator node and try again. +-- test creating a tenant schema from workers +SET citus.enable_schema_based_sharding TO ON; +CREATE SCHEMA worker_tenant_schema; +ERROR: operation is not allowed on this node +HINT: Connect to the coordinator and run it again. +SET citus.enable_schema_based_sharding TO OFF; +-- Enable the GUC on workers to make sure that the CREATE SCHEMA/ TABLE +-- commands that we send to workers don't recursively try creating a +-- tenant schema / table. +ALTER SYSTEM SET citus.enable_schema_based_sharding TO ON; +SELECT pg_reload_conf(); + pg_reload_conf +--------------------------------------------------------------------- + t +(1 row) + +\c - - - :worker_2_port +ALTER SYSTEM SET citus.enable_schema_based_sharding TO ON; +SELECT pg_reload_conf(); + pg_reload_conf +--------------------------------------------------------------------- + t +(1 row) + +-- Verify that citus_internal_unregister_tenant_schema_globally is a no-op +-- on workers. +SELECT citus_internal_unregister_tenant_schema_globally('tenant_3'::regnamespace, 'tenant_3'); + citus_internal_unregister_tenant_schema_globally +--------------------------------------------------------------------- + +(1 row) + +\c - - - :master_port +SET search_path TO regular_schema; +SET citus.next_shard_id TO 1950000; +SET citus.shard_count TO 32; +SET citus.shard_replication_factor TO 1; +SET client_min_messages TO NOTICE; +CREATE TABLE tenant_3.tbl_1(a int, b text); +SET citus.enable_schema_based_sharding TO ON; +CREATE SCHEMA tenant_6; +CREATE TABLE tenant_6.tbl_1(a int, b text); +-- verify pg_dist_partition entries for tenant_3.tbl_1 and tenant_6.tbl_1 +SELECT COUNT(*)=2 FROM pg_dist_partition +WHERE logicalrelid IN ('tenant_3.tbl_1'::regclass, 'tenant_6.tbl_1'::regclass) AND + partmethod = 'n' AND repmodel = 's' AND colocationid > 0; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +\c - - - :worker_1_port +ALTER SYSTEM RESET citus.enable_schema_based_sharding; +SELECT pg_reload_conf(); + pg_reload_conf +--------------------------------------------------------------------- + t +(1 row) + +\c - - - :worker_2_port +ALTER SYSTEM RESET citus.enable_schema_based_sharding; +SELECT pg_reload_conf(); + pg_reload_conf +--------------------------------------------------------------------- + t +(1 row) + +\c - - - :master_port +SET client_min_messages TO WARNING; +DROP SCHEMA regular_schema, tenant_3, tenant_5, tenant_7, tenant_6 CASCADE; +SELECT citus_remove_node('localhost', :master_port); + citus_remove_node +--------------------------------------------------------------------- + +(1 row) + diff --git a/src/test/regress/expected/single_node.out b/src/test/regress/expected/single_node.out index 5efad578e..785cea2f8 100644 --- a/src/test/regress/expected/single_node.out +++ b/src/test/regress/expected/single_node.out @@ -156,8 +156,30 @@ WHERE shardid = ( SELECT shardid AS round_robin_test_c1_shard_id FROM pg_dist_shard WHERE logicalrelid = 'single_node.single_node_nullkey_c1'::regclass \gset SELECT create_distributed_table('single_node_nullkey_c1_' || :round_robin_test_c1_shard_id , null, colocate_with=>'none', distribution_type=>null); ERROR: relation "single_node_nullkey_c1_90630532" is a shard relation +-- create a tenant schema on single node setup +SET citus.enable_schema_based_sharding TO ON; +CREATE SCHEMA tenant_1; +CREATE TABLE tenant_1.tbl_1 (a int); +-- verify that we recorded tenant_1 in pg_dist_tenant_schema +SELECT COUNT(*)=1 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_1'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- verify that tenant_1.tbl_1 is recorded in pg_dist_partition, as a single-shard table +SELECT COUNT(*)=1 FROM pg_dist_partition +WHERE logicalrelid = 'tenant_1.tbl_1'::regclass AND + partmethod = 'n' AND repmodel = 's' AND colocationid IS NOT NULL; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +RESET citus.enable_schema_based_sharding; SET client_min_messages TO WARNING; DROP TABLE failover_to_local, single_node_nullkey_c1, single_node_nullkey_c2; +DROP SCHEMA tenant_1 CASCADE; RESET client_min_messages; -- so that we don't have to update rest of the test output SET citus.next_shard_id TO 90630500; diff --git a/src/test/regress/expected/single_node_0.out b/src/test/regress/expected/single_node_0.out index 446db9aed..8ca701e40 100644 --- a/src/test/regress/expected/single_node_0.out +++ b/src/test/regress/expected/single_node_0.out @@ -156,8 +156,30 @@ WHERE shardid = ( SELECT shardid AS round_robin_test_c1_shard_id FROM pg_dist_shard WHERE logicalrelid = 'single_node.single_node_nullkey_c1'::regclass \gset SELECT create_distributed_table('single_node_nullkey_c1_' || :round_robin_test_c1_shard_id , null, colocate_with=>'none', distribution_type=>null); ERROR: relation "single_node_nullkey_c1_90630532" is a shard relation +-- create a tenant schema on single node setup +SET citus.enable_schema_based_sharding TO ON; +CREATE SCHEMA tenant_1; +CREATE TABLE tenant_1.tbl_1 (a int); +-- verify that we recorded tenant_1 in pg_dist_tenant_schema +SELECT COUNT(*)=1 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_1'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- verify that tenant_1.tbl_1 is recorded in pg_dist_partition, as a single-shard table +SELECT COUNT(*)=1 FROM pg_dist_partition +WHERE logicalrelid = 'tenant_1.tbl_1'::regclass AND + partmethod = 'n' AND repmodel = 's' AND colocationid IS NOT NULL; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +RESET citus.enable_schema_based_sharding; SET client_min_messages TO WARNING; DROP TABLE failover_to_local, single_node_nullkey_c1, single_node_nullkey_c2; +DROP SCHEMA tenant_1 CASCADE; RESET client_min_messages; -- so that we don't have to update rest of the test output SET citus.next_shard_id TO 90630500; diff --git a/src/test/regress/expected/upgrade_list_citus_objects.out b/src/test/regress/expected/upgrade_list_citus_objects.out index 29db2faea..1d44bf5a3 100644 --- a/src/test/regress/expected/upgrade_list_citus_objects.out +++ b/src/test/regress/expected/upgrade_list_citus_objects.out @@ -69,16 +69,19 @@ ORDER BY 1; function citus_internal_add_placement_metadata(bigint,bigint,integer,bigint) function citus_internal_add_placement_metadata(bigint,integer,bigint,integer,bigint) function citus_internal_add_shard_metadata(regclass,bigint,"char",text,text) + function citus_internal_add_tenant_schema(oid,integer) function citus_internal_adjust_local_clock_to_remote(cluster_clock) function citus_internal_delete_colocation_metadata(integer) function citus_internal_delete_partition_metadata(regclass) function citus_internal_delete_shard_metadata(bigint) + function citus_internal_delete_tenant_schema(oid) function citus_internal_global_blocked_processes() function citus_internal_is_replication_origin_tracking_active() function citus_internal_local_blocked_processes() function citus_internal_mark_node_not_synced(integer,integer) function citus_internal_start_replication_origin_tracking() function citus_internal_stop_replication_origin_tracking() + function citus_internal_unregister_tenant_schema_globally(oid,text) function citus_internal_update_placement_metadata(bigint,integer,integer) function citus_internal_update_relation_colocation(oid,integer) function citus_is_clock_after(cluster_clock,cluster_clock) @@ -306,6 +309,7 @@ ORDER BY 1; table pg_dist_poolinfo table pg_dist_rebalance_strategy table pg_dist_shard + table pg_dist_tenant_schema table pg_dist_transaction type citus.distribution_type type citus.shard_transfer_mode @@ -330,5 +334,5 @@ ORDER BY 1; view citus_stat_tenants_local view pg_dist_shard_placement view time_partitions -(322 rows) +(326 rows) diff --git a/src/test/regress/expected/upgrade_schema_based_sharding_after.out b/src/test/regress/expected/upgrade_schema_based_sharding_after.out new file mode 100644 index 000000000..4ecb7cf12 --- /dev/null +++ b/src/test/regress/expected/upgrade_schema_based_sharding_after.out @@ -0,0 +1,98 @@ +ALTER SCHEMA "tenant\'_1" RENAME TO tenant_1; +ALTER SCHEMA "tenant\'_2" RENAME TO tenant_2; +-- verify that colocation id is set even for empty tenant +SELECT colocationid > 0 FROM pg_dist_tenant_schema +WHERE schemaid::regnamespace::text = 'tenant_1'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- verify the same on workers +SELECT result FROM run_command_on_workers($$ + SELECT colocationid > 0 FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = 'tenant_1'; +$$); + result +--------------------------------------------------------------------- + t + t +(2 rows) + +-- verify that colocation id is set for non-empty tenant +SELECT colocationid = ( + SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_2.test_table'::regclass +) +FROM pg_dist_tenant_schema +WHERE schemaid::regnamespace::text = 'tenant_2'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- verify the same on workers +SELECT result FROM run_command_on_workers($$ + SELECT colocationid = ( + SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_2.test_table'::regclass + ) + FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = 'tenant_2'; +$$); + result +--------------------------------------------------------------------- + t + t +(2 rows) + +CREATE TABLE tenant_1.tbl_1(a int, b text); +CREATE TABLE tenant_2.tbl_1(a int, b text); +-- Show that we can create further tenant tables in the tenant schemas +-- after pg upgrade. +SELECT COUNT(*)=2 FROM pg_dist_partition +WHERE logicalrelid IN ('tenant_1.tbl_1'::regclass, 'tenant_2.tbl_1'::regclass) AND + partmethod = 'n' AND repmodel = 's' AND colocationid > 0; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SELECT colocationid = ( + SELECT colocationid FROM pg_dist_partition + WHERE logicalrelid = 'tenant_1.tbl_1'::regclass AND + partmethod = 'n' AND repmodel = 's' +) +FROM pg_dist_tenant_schema +WHERE schemaid::regnamespace::text = 'tenant_1'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SELECT colocationid = ( + SELECT colocationid FROM pg_dist_partition + WHERE logicalrelid = 'tenant_2.tbl_1'::regclass AND + partmethod = 'n' AND repmodel = 's' +) +FROM pg_dist_tenant_schema +WHERE schemaid::regnamespace::text = 'tenant_2'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- rollback the changes made on following schemas to make this test idempotent +DROP TABLE tenant_1.tbl_1, tenant_2.tbl_1; +ALTER SCHEMA tenant_1 RENAME TO "tenant\'_1"; +ALTER SCHEMA tenant_2 RENAME TO "tenant\'_2"; +SET citus.enable_schema_based_sharding TO ON; +CREATE SCHEMA tenant_3; +-- Show that we can create furher tenant schemas after pg upgrade. +SELECT COUNT(*)=1 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_3'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- drop the schema created in this test to this test idempotent +DROP SCHEMA tenant_3 CASCADE; +RESET citus.enable_schema_based_sharding; diff --git a/src/test/regress/expected/upgrade_schema_based_sharding_before.out b/src/test/regress/expected/upgrade_schema_based_sharding_before.out new file mode 100644 index 000000000..e9f0c5e46 --- /dev/null +++ b/src/test/regress/expected/upgrade_schema_based_sharding_before.out @@ -0,0 +1,10 @@ +SET citus.enable_schema_based_sharding TO ON; +-- Create tenant tables with schema names that need escaping +-- to verify that citus_prepare_pg_upgrade() correctly saves +-- them into public schema. +-- empty tenant +CREATE SCHEMA "tenant\'_1"; +-- non-empty tenant +CREATE SCHEMA "tenant\'_2"; +CREATE TABLE "tenant\'_2".test_table(a int, b text); +RESET citus.enable_schema_based_sharding; diff --git a/src/test/regress/isolation_schedule b/src/test/regress/isolation_schedule index ca893ba8d..fed454ac3 100644 --- a/src/test/regress/isolation_schedule +++ b/src/test/regress/isolation_schedule @@ -76,6 +76,7 @@ test: isolation_fix_partition_shard_index_names test: isolation_global_pid test: isolation_citus_locks test: isolation_reference_table +test: isolation_schema_based_sharding # Rebalancer test: isolation_blocking_move_single_shard_commands diff --git a/src/test/regress/multi_1_schedule b/src/test/regress/multi_1_schedule index 0936a9625..e5f24910c 100644 --- a/src/test/regress/multi_1_schedule +++ b/src/test/regress/multi_1_schedule @@ -33,6 +33,7 @@ test: ref_citus_local_fkeys test: alter_database_owner test: distributed_triggers test: create_single_shard_table +test: schema_based_sharding test: multi_test_catalog_views test: multi_table_ddl diff --git a/src/test/regress/spec/isolation_schema_based_sharding.spec b/src/test/regress/spec/isolation_schema_based_sharding.spec new file mode 100644 index 000000000..2f6180538 --- /dev/null +++ b/src/test/regress/spec/isolation_schema_based_sharding.spec @@ -0,0 +1,42 @@ +setup +{ + SET citus.enable_schema_based_sharding TO ON; + CREATE SCHEMA tenant_1; + CREATE SCHEMA tenant_2; + CREATE SCHEMA tenant_3; + + CREATE SCHEMA tenant_4; + CREATE TABLE tenant_4.first_table (a int); +} + +teardown +{ + DROP SCHEMA tenant_1, tenant_2, tenant_3, tenant_4 CASCADE; +} + +session "s1" + +step "s1-begin" { BEGIN; } +step "s1-tenant-1-create-table-1" { CREATE TABLE tenant_1.tbl_1 (a int); } +step "s1-tenant-4-create-table-1" { CREATE TABLE tenant_4.tbl_1 (a int); } +step "s1-tenant-2-create-table-1" { CREATE TABLE tenant_2.tbl_1 (a int); } +step "s1-commit" { COMMIT; } + +session "s2" + +step "s2-begin" { BEGIN; } +step "s2-tenant-1-create-table-2" { CREATE TABLE tenant_1.tbl_2 (a int); } +step "s2-tenant-4-create-table-2" { CREATE TABLE tenant_4.tbl_2 (a int); } +step "s2-tenant-1-verify-colocation" { SELECT COUNT(DISTINCT(colocationid))=1 FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant_1.%'; } +step "s2-tenant-4-verify-colocation" { SELECT COUNT(DISTINCT(colocationid))=1 FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant_4.%'; } +step "s2-tenant-3-create-table-1" { CREATE TABLE tenant_3.tbl_1 (a int); } +step "s2-commit" { COMMIT; } + +// two sessions competing with each other to create the first table in the same schema +permutation "s1-begin" "s2-begin" "s1-tenant-1-create-table-1" "s2-tenant-1-create-table-2" "s1-commit" "s2-tenant-1-verify-colocation" "s2-commit" + +// two sessions creating further tenant tables in the same schema +permutation "s1-begin" "s2-begin" "s1-tenant-4-create-table-1" "s2-tenant-4-create-table-2" "s1-commit" "s2-tenant-4-verify-colocation" "s2-commit" + +// two sessions creating tenant tables in different schemas +permutation "s1-begin" "s2-begin" "s1-tenant-2-create-table-1" "s2-tenant-3-create-table-1" "s1-commit" "s2-commit" diff --git a/src/test/regress/sql/auto_undist_citus_local.sql b/src/test/regress/sql/auto_undist_citus_local.sql index 7c7814a46..895e2f902 100644 --- a/src/test/regress/sql/auto_undist_citus_local.sql +++ b/src/test/regress/sql/auto_undist_citus_local.sql @@ -214,7 +214,11 @@ DROP TABLE IF EXISTS citus_local_table_1, citus_local_table_2, citus_local_table -- this GUC will add the next three tables to metadata automatically SET citus.use_citus_managed_tables TO ON; -CREATE TABLE citus_local_table_1(a INT UNIQUE); + +-- try to create the table twice by using IF NOT EXISTS syntax +CREATE TABLE IF NOT EXISTS citus_local_table_1(a INT UNIQUE); +CREATE TABLE IF NOT EXISTS citus_local_table_1(a INT UNIQUE); + CREATE TABLE citus_local_table_2(a INT UNIQUE); CREATE TABLE citus_local_table_3(a INT UNIQUE); RESET citus.use_citus_managed_tables; @@ -738,3 +742,4 @@ CALL drop_constraint_via_proc_exception(); SELECT logicalrelid, partmethod, repmodel FROM pg_dist_partition WHERE logicalrelid IN ('reference_table_1'::regclass, 'citus_local_table_1'::regclass) ORDER BY logicalrelid; DROP SCHEMA drop_fkey_cascade CASCADE; +DROP USER another_user; diff --git a/src/test/regress/sql/create_single_shard_table.sql b/src/test/regress/sql/create_single_shard_table.sql index c7a2d49c2..55f390921 100644 --- a/src/test/regress/sql/create_single_shard_table.sql +++ b/src/test/regress/sql/create_single_shard_table.sql @@ -66,12 +66,12 @@ CREATE TABLE nullkey_c1_t2(a int, b int); CREATE TABLE nullkey_c1_t3(a int, b int); SELECT create_distributed_table('nullkey_c1_t1', null, colocate_with=>'none'); -SELECT colocationid AS nullkey_c1_t1_colocation_id FROM pg_dist_partition WHERE logicalrelid = 'create_single_shard_table.nullkey_c1_t1'::regclass \gset +SELECT colocationid AS nullkey_c1_t1_colocationid FROM pg_dist_partition WHERE logicalrelid = 'create_single_shard_table.nullkey_c1_t1'::regclass \gset BEGIN; DROP TABLE nullkey_c1_t1; -- make sure that we delete the colocation group after dropping the last table that belongs to it - SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = :'nullkey_c1_t1_colocation_id'; + SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = :'nullkey_c1_t1_colocationid'; ROLLBACK; SELECT create_distributed_table('nullkey_c1_t2', null, colocate_with=>'nullkey_c1_t1'); diff --git a/src/test/regress/sql/multi_extension.sql b/src/test/regress/sql/multi_extension.sql index 40b1e963b..c71216ecd 100644 --- a/src/test/regress/sql/multi_extension.sql +++ b/src/test/regress/sql/multi_extension.sql @@ -928,6 +928,41 @@ DROP TABLE test; -- and now we should be able to remove the coordinator SELECT citus_remove_node('localhost', :master_port); +-- confirm that we can create a tenant schema / table on an empty node + +SET citus.enable_schema_based_sharding TO ON; + +CREATE SCHEMA tenant_schema; +CREATE TABLE tenant_schema.test(x int, y int); + +SELECT colocationid = ( + SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_schema.test'::regclass +) +FROM pg_dist_tenant_schema +WHERE schemaid::regnamespace::text = 'tenant_schema'; + +-- and make sure that we can't remove the coordinator due to "test" +SELECT citus_remove_node('localhost', :master_port); + +BEGIN; + SET LOCAL client_min_messages TO WARNING; + DROP SCHEMA tenant_schema CASCADE; +COMMIT; + +-- and now we should be able to remove the coordinator +SELECT citus_remove_node('localhost', :master_port); + +CREATE SCHEMA tenant_schema; + +-- Make sure that we can sync metadata for empty tenant schemas +-- when adding the first node to the cluster. +SELECT 1 FROM citus_add_node('localhost', :worker_1_port); + +DROP SCHEMA tenant_schema; +SELECT citus_remove_node('localhost', :worker_1_port); + +RESET citus.enable_schema_based_sharding; + -- confirm that we can create a reference table on an empty node CREATE TABLE test (x int, y int); INSERT INTO test VALUES (1,2); @@ -940,6 +975,19 @@ CREATE TABLE test (x int, y int); INSERT INTO test VALUES (1,2); SELECT citus_add_local_table_to_metadata('test'); DROP TABLE test; + +-- Verify that we don't consider the schemas created by extensions as tenant schemas. +-- Easiest way of verifying this is to drop and re-create columnar extension. +DROP EXTENSION citus_columnar; + +SET citus.enable_schema_based_sharding TO ON; + +CREATE EXTENSION citus_columnar; +SELECT COUNT(*)=0 FROM pg_dist_tenant_schema +WHERE schemaid IN ('columnar'::regnamespace, 'columnar_internal'::regnamespace); + +RESET citus.enable_schema_based_sharding; + DROP EXTENSION citus; CREATE EXTENSION citus; diff --git a/src/test/regress/sql/schema_based_sharding.sql b/src/test/regress/sql/schema_based_sharding.sql new file mode 100644 index 000000000..12dec9f0b --- /dev/null +++ b/src/test/regress/sql/schema_based_sharding.sql @@ -0,0 +1,941 @@ +CREATE SCHEMA regular_schema; +SET search_path TO regular_schema; + +SET citus.next_shard_id TO 1920000; +SET citus.shard_count TO 32; +SET citus.shard_replication_factor TO 1; + +SET client_min_messages TO WARNING; +SELECT 1 FROM citus_add_node('localhost', :master_port, groupid => 0); + +SET client_min_messages TO NOTICE; + +-- Verify that the UDFs used to sync tenant schema metadata to workers +-- fail on NULL input. +SELECT citus_internal_add_tenant_schema(NULL, 1); +SELECT citus_internal_add_tenant_schema(1, NULL); +SELECT citus_internal_delete_tenant_schema(NULL); +SELECT citus_internal_unregister_tenant_schema_globally(1, NULL); +SELECT citus_internal_unregister_tenant_schema_globally(NULL, 'text'); + +-- Verify that citus_internal_unregister_tenant_schema_globally can only +-- be called on schemas that are dropped already. +SELECT citus_internal_unregister_tenant_schema_globally('regular_schema'::regnamespace, 'regular_schema'); + +SELECT 1 FROM citus_remove_node('localhost', :worker_2_port); + +CREATE TABLE regular_schema.test_table(a int, b text); + +SET citus.enable_schema_based_sharding TO ON; + +-- show that regular_schema doesn't show up in pg_dist_tenant_schema +SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'regular_schema'; + +-- empty tenant +CREATE SCHEMA "tenant\'_1"; + +-- non-empty tenant +CREATE SCHEMA "tenant\'_2"; +CREATE TABLE "tenant\'_2".test_table(a int, b text); + +-- empty tenant +CREATE SCHEMA "tenant\'_3"; +CREATE TABLE "tenant\'_3".test_table(a int, b text); +DROP TABLE "tenant\'_3".test_table; + +-- add a node after creating tenant schemas +SELECT 1 FROM citus_add_node('localhost', :worker_2_port); + +ALTER SCHEMA "tenant\'_1" RENAME TO tenant_1; +ALTER SCHEMA "tenant\'_2" RENAME TO tenant_2; +ALTER SCHEMA "tenant\'_3" RENAME TO tenant_3; + +-- verify that create_distributed_table() and others fail when called on tenant tables +SELECT create_distributed_table('tenant_2.test_table', 'a'); +SELECT create_reference_table('tenant_2.test_table'); +SELECT citus_add_local_table_to_metadata('tenant_2.test_table'); + +-- (on coordinator) verify that colocation id is set for empty tenants too +SELECT colocationid > 0 FROM pg_dist_tenant_schema +WHERE schemaid::regnamespace::text IN ('tenant_1', 'tenant_3'); + +-- (on workers) verify that colocation id is set for empty tenants too +SELECT result FROM run_command_on_workers($$ + SELECT array_agg(colocationid > 0) FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text IN ('tenant_1', 'tenant_3'); +$$); + +-- Verify that tenant_2.test_table is recorded in pg_dist_partition as a +-- single-shard table. +SELECT COUNT(*)=1 FROM pg_dist_partition +WHERE logicalrelid = 'tenant_2.test_table'::regclass AND + partmethod = 'n' AND repmodel = 's' AND colocationid > 0; + +-- (on coordinator) verify that colocation id is properly set for non-empty tenant schema +SELECT colocationid = ( + SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_2.test_table'::regclass +) +FROM pg_dist_tenant_schema +WHERE schemaid::regnamespace::text = 'tenant_2'; + +-- (on workers) verify that colocation id is properly set for non-empty tenant schema +SELECT result FROM run_command_on_workers($$ + SELECT colocationid = ( + SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_2.test_table'::regclass + ) + FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = 'tenant_2'; +$$); + +-- create a tenant table for tenant_1 after add_node +CREATE TABLE tenant_1.test_table(a int, b text); + +-- (on coordinator) verify that colocation id is properly set for now-non-empty tenant schema +SELECT colocationid = ( + SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_1.test_table'::regclass +) +FROM pg_dist_tenant_schema +WHERE schemaid::regnamespace::text = 'tenant_1'; + +-- (on workers) verify that colocation id is properly set for now-non-empty tenant schema +SELECT result FROM run_command_on_workers($$ + SELECT colocationid = ( + SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_1.test_table'::regclass + ) + FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = 'tenant_1'; +$$); + +-- verify that tenant_1 and tenant_2 have different colocation ids +SELECT COUNT(DISTINCT(colocationid))=2 FROM pg_dist_tenant_schema +WHERE schemaid::regnamespace::text IN ('tenant_1', 'tenant_2'); + +-- verify that we don't allow creating tenant tables via CREATE SCHEMA command +CREATE SCHEMA schema_using_schema_elements CREATE TABLE test_table(a int, b text); + +CREATE SCHEMA tenant_4; +CREATE TABLE tenant_4.tbl_1(a int, b text); +CREATE TABLE tenant_4.tbl_2(a int, b text); + +-- verify that we don't allow creating a foreign table in a tenant schema, with a nice error message +CREATE FOREIGN TABLE tenant_4.foreign_table ( + id bigint not null, + full_name text not null default '' +) SERVER fake_fdw_server OPTIONS (encoding 'utf-8', compression 'true', table_name 'foreign_table'); + +-- verify that we don't allow creating a foreign table in a tenant schema +CREATE TEMPORARY TABLE tenant_4.temp_table (a int, b text); + +CREATE TABLE tenant_4.partitioned_table(a int, b text, PRIMARY KEY (a)) PARTITION BY RANGE (a); +CREATE TABLE tenant_4.partitioned_table_child_1 PARTITION OF tenant_4.partitioned_table FOR VALUES FROM (1) TO (2); + +CREATE TABLE tenant_4.another_partitioned_table(a int, b text, FOREIGN KEY (a) REFERENCES tenant_4.partitioned_table(a)) PARTITION BY RANGE (a); +CREATE TABLE tenant_4.another_partitioned_table_child PARTITION OF tenant_4.another_partitioned_table FOR VALUES FROM (1) TO (2); + +-- verify that we allow creating partitioned tables in a tenant schema +SELECT COUNT(*)=1 FROM pg_dist_partition +WHERE logicalrelid = 'tenant_4.partitioned_table_child_1'::regclass AND + partmethod = 'n' AND repmodel = 's' AND colocationid = ( + SELECT colocationid FROM pg_dist_partition + WHERE logicalrelid = 'tenant_4.partitioned_table'::regclass); + +SELECT EXISTS( + SELECT 1 + FROM pg_inherits + WHERE inhrelid = 'tenant_4.partitioned_table_child_1'::regclass AND + inhparent = 'tenant_4.partitioned_table'::regclass +) AS is_partition; + +SELECT COUNT(*)=1 FROM pg_dist_partition +WHERE logicalrelid = 'tenant_4.another_partitioned_table_child'::regclass AND + partmethod = 'n' AND repmodel = 's' AND colocationid = ( + SELECT colocationid FROM pg_dist_partition + WHERE logicalrelid = 'tenant_4.another_partitioned_table'::regclass); + +SELECT EXISTS( + SELECT 1 + FROM pg_inherits + WHERE inhrelid = 'tenant_4.another_partitioned_table_child'::regclass AND + inhparent = 'tenant_4.another_partitioned_table'::regclass +) AS is_partition; + +-- verify the foreign key between parents +SELECT EXISTS( + SELECT 1 + FROM pg_constraint + WHERE conrelid = 'tenant_4.another_partitioned_table'::regclass AND + confrelid = 'tenant_4.partitioned_table'::regclass AND + contype = 'f' +) AS foreign_key_exists; + +INSERT INTO tenant_4.another_partitioned_table VALUES (1, 'a'); + +INSERT INTO tenant_4.partitioned_table VALUES (1, 'a'); +INSERT INTO tenant_4.another_partitioned_table VALUES (1, 'a'); + +CREATE SCHEMA tenant_5; +CREATE TABLE tenant_5.tbl_1(a int, b text); + +CREATE TABLE tenant_5.partitioned_table(a int, b text) PARTITION BY RANGE (a); + +-- verify that we don't allow creating a partition table that is child of a partitioned table in a different tenant schema +CREATE TABLE tenant_4.partitioned_table_child_2 PARTITION OF tenant_5.partitioned_table FOR VALUES FROM (1) TO (2); + +-- verify that we don't allow creating a local partition table that is child of a tenant partitioned table +CREATE TABLE regular_schema.local_child_table PARTITION OF tenant_5.partitioned_table FOR VALUES FROM (1) TO (2); + +SET citus.use_citus_managed_tables TO ON; +CREATE TABLE regular_schema.local_child_table PARTITION OF tenant_5.partitioned_table FOR VALUES FROM (1) TO (2); +RESET citus.use_citus_managed_tables; + +CREATE TABLE regular_schema.local_partitioned_table(a int, b text) PARTITION BY RANGE (a); + +CREATE TABLE regular_schema.citus_local_partitioned_table(a int, b text) PARTITION BY RANGE (a); +SELECT citus_add_local_table_to_metadata('regular_schema.citus_local_partitioned_table'); + +CREATE TABLE regular_schema.dist_partitioned_table(a int, b text) PARTITION BY RANGE (a); +SELECT create_distributed_table('regular_schema.dist_partitioned_table', 'a'); + +-- verify that we don't allow creating a partition table that is child of a non-tenant partitioned table +CREATE TABLE tenant_4.partitioned_table_child_2 PARTITION OF regular_schema.local_partitioned_table FOR VALUES FROM (1) TO (2); +CREATE TABLE tenant_4.partitioned_table_child_2 PARTITION OF regular_schema.citus_local_partitioned_table FOR VALUES FROM (1) TO (2); +CREATE TABLE tenant_4.partitioned_table_child_2 PARTITION OF regular_schema.dist_partitioned_table FOR VALUES FROM (1) TO (2); + +CREATE TABLE tenant_4.parent_attach_test(a int, b text) PARTITION BY RANGE (a); +CREATE TABLE tenant_4.child_attach_test(a int, b text); + +CREATE TABLE tenant_5.parent_attach_test(a int, b text) PARTITION BY RANGE (a); +CREATE TABLE tenant_5.child_attach_test(a int, b text); + +CREATE TABLE regular_schema.parent_attach_test_local(a int, b text) PARTITION BY RANGE (a); + +CREATE TABLE regular_schema.parent_attach_test_citus_local(a int, b text) PARTITION BY RANGE (a); +SELECT citus_add_local_table_to_metadata('regular_schema.parent_attach_test_citus_local'); + +CREATE TABLE regular_schema.parent_attach_test_dist(a int, b text) PARTITION BY RANGE (a); +SELECT create_distributed_table('regular_schema.parent_attach_test_dist', 'a'); + +CREATE TABLE regular_schema.child_attach_test_local(a int, b text); + +CREATE TABLE regular_schema.child_attach_test_citus_local(a int, b text); +SELECT citus_add_local_table_to_metadata('regular_schema.child_attach_test_citus_local'); + +CREATE TABLE regular_schema.child_attach_test_dist(a int, b text); +SELECT create_distributed_table('regular_schema.child_attach_test_dist', 'a'); + +-- verify that we don't allow attaching a tenant table into a tenant partitioned table, if they are not in the same schema +ALTER TABLE tenant_4.parent_attach_test ATTACH PARTITION tenant_5.child_attach_test FOR VALUES FROM (1) TO (2); + +-- verify that we don't allow attaching a non-tenant table into a tenant partitioned table +ALTER TABLE tenant_4.parent_attach_test ATTACH PARTITION regular_schema.child_attach_test_local FOR VALUES FROM (1) TO (2); +ALTER TABLE tenant_4.parent_attach_test ATTACH PARTITION regular_schema.child_attach_test_citus_local FOR VALUES FROM (1) TO (2); +ALTER TABLE tenant_4.parent_attach_test ATTACH PARTITION regular_schema.child_attach_test_dist FOR VALUES FROM (1) TO (2); + +-- verify that we don't allow attaching a tenant table into a non-tenant partitioned table +ALTER TABLE regular_schema.parent_attach_test_local ATTACH PARTITION tenant_4.child_attach_test FOR VALUES FROM (1) TO (2); +ALTER TABLE regular_schema.parent_attach_test_citus_local ATTACH PARTITION tenant_4.child_attach_test FOR VALUES FROM (1) TO (2); +ALTER TABLE regular_schema.parent_attach_test_dist ATTACH PARTITION tenant_4.child_attach_test FOR VALUES FROM (1) TO (2); + +ALTER TABLE tenant_4.parent_attach_test ATTACH PARTITION tenant_4.child_attach_test FOR VALUES FROM (1) TO (2); + +-- verify that we don't allow multi-level partitioning on tenant tables +CREATE TABLE tenant_4.multi_level_test(a int, b text) PARTITION BY RANGE (a); +ALTER TABLE tenant_4.parent_attach_test ATTACH PARTITION tenant_4.multi_level_test FOR VALUES FROM (1) TO (2); + +-- verify that we allow attaching a tenant table into a tenant partitioned table, if they are in the same schema +SELECT COUNT(*)=1 FROM pg_dist_partition +WHERE logicalrelid = 'tenant_4.parent_attach_test'::regclass AND + partmethod = 'n' AND repmodel = 's' AND colocationid = ( + SELECT colocationid FROM pg_dist_partition + WHERE logicalrelid = 'tenant_4.child_attach_test'::regclass); + +SELECT EXISTS( + SELECT 1 + FROM pg_inherits + WHERE inhrelid = 'tenant_4.child_attach_test'::regclass AND + inhparent = 'tenant_4.parent_attach_test'::regclass +) AS is_partition; + +-- verify that we don't allow creating tenant tables by using CREATE TABLE AS / SELECT INTO commands +CREATE TABLE tenant_4.tbl_3 AS SELECT 1 AS a, 'text' as b; +CREATE TABLE IF NOT EXISTS tenant_4.tbl_3 AS SELECT 1 as a, 'text' as b; +SELECT 1 as a, 'text' as b INTO tenant_4.tbl_3; + +CREATE TYPE employee_type AS (name text, salary numeric); + +-- verify that we don't allow creating tenant tables by using CREATE TABLE OF commands +CREATE TABLE tenant_4.employees OF employee_type ( + PRIMARY KEY (name), + salary WITH OPTIONS DEFAULT 1000 +); + +-- verify that we act accordingly when if not exists is used +CREATE TABLE IF NOT EXISTS tenant_4.tbl_3(a int, b text); +CREATE TABLE IF NOT EXISTS tenant_4.tbl_3(a int, b text); + +CREATE TABLE regular_schema.local(a int, b text); + +CREATE TABLE regular_schema.citus_local(a int, b text); +SELECT citus_add_local_table_to_metadata('regular_schema.citus_local'); + +CREATE TABLE regular_schema.dist(a int, b text); +SELECT create_distributed_table('regular_schema.dist', 'a'); + +-- verify that we can create a table LIKE another table +CREATE TABLE tenant_5.test_table_like_1(LIKE tenant_5.tbl_1); -- using a table from the same schema +CREATE TABLE tenant_5.test_table_like_2(LIKE tenant_4.tbl_1); -- using a table from another schema +CREATE TABLE tenant_5.test_table_like_3(LIKE regular_schema.local); -- using a local table +CREATE TABLE tenant_5.test_table_like_4(LIKE regular_schema.citus_local); -- using a citus local table +CREATE TABLE tenant_5.test_table_like_5(LIKE regular_schema.dist); -- using a distributed table + +-- verify that all of them are converted to tenant tables +SELECT COUNT(*) = 5 +FROM pg_dist_partition +WHERE logicalrelid::text LIKE 'tenant_5.test_table_like_%' AND + partmethod = 'n' AND repmodel = 's' AND colocationid = ( + SELECT colocationid FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = 'tenant_5' + ); + +CREATE TABLE regular_schema.local_table_using_like(LIKE tenant_5.tbl_1); + +-- verify that regular_schema.local_table_using_like is not a tenant table +SELECT COUNT(*) = 0 FROM pg_dist_partition +WHERE logicalrelid = 'regular_schema.local_table_using_like'::regclass; + +-- verify that INHERITS syntax is not supported when creating a tenant table +CREATE TABLE tenant_5.test_table_inherits_1(x int) INHERITS (tenant_5.tbl_1); -- using a table from the same schema +CREATE TABLE tenant_5.test_table_inherits_2(x int) INHERITS (tenant_4.tbl_1); -- using a table from another schema +CREATE TABLE tenant_5.test_table_inherits_3(x int) INHERITS (regular_schema.local); -- using a local table +CREATE TABLE tenant_5.test_table_inherits_4(x int) INHERITS (regular_schema.citus_local); -- using a citus local table +CREATE TABLE tenant_5.test_table_inherits_5(x int) INHERITS (regular_schema.dist); -- using a distributed table + +-- verify that INHERITS syntax is not supported when creating a local table based on a tenant table +CREATE TABLE regular_schema.local_table_using_inherits(x int) INHERITS (tenant_5.tbl_1); + +CREATE TABLE tenant_5.tbl_2(a int, b text); + +CREATE SCHEMA "CiTuS.TeeN_108"; +ALTER SCHEMA "CiTuS.TeeN_108" RENAME TO citus_teen_proper; + +SELECT schemaid AS citus_teen_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'citus_teen_proper' \gset +SELECT colocationid AS citus_teen_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'citus_teen_proper' \gset + +SELECT result FROM run_command_on_workers($$ + SELECT schemaid INTO citus_teen_schemaid FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = 'citus_teen_proper' +$$); + +-- (on coordinator) verify that colocation id is set for the tenant with a weird name too +SELECT :citus_teen_colocationid > 0; + +-- (on workers) verify that the same colocation id is used on workers too +SELECT format( + 'SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=1 FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = ''citus_teen_proper'' AND + colocationid = %s; + $$);', +:citus_teen_colocationid) AS verify_workers_query \gset + +:verify_workers_query + +ALTER SCHEMA citus_teen_proper RENAME TO "CiTuS.TeeN_108"; + +SET citus.enable_schema_based_sharding TO OFF; + +-- Show that the tables created in tenant schemas are considered to be +-- tenant tables even if the GUC was set to off when creating the table. +CREATE TABLE tenant_5.tbl_3(a int, b text); +SELECT COUNT(*)=1 FROM pg_dist_partition WHERE logicalrelid = 'tenant_5.tbl_3'::regclass; + +SET citus.enable_schema_based_sharding TO ON; + +-- Verify that tables that belong to tenant_4 and tenant_5 are stored on +-- different worker nodes due to order we followed when creating first tenant +-- tables in each of them. +SELECT COUNT(DISTINCT(nodename, nodeport))=2 FROM citus_shards +WHERE table_name IN ('tenant_4.tbl_1'::regclass, 'tenant_5.tbl_1'::regclass); + +-- show that all the tables in tenant_4 are colocated with each other. +SELECT COUNT(DISTINCT(colocationid))=1 FROM pg_dist_partition +WHERE logicalrelid::regclass::text LIKE 'tenant_4.%'; + +-- verify the same for tenant_5 too +SELECT COUNT(DISTINCT(colocationid))=1 FROM pg_dist_partition +WHERE logicalrelid::regclass::text LIKE 'tenant_5.%'; + +SELECT schemaid AS tenant_4_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_4' \gset +SELECT colocationid AS tenant_4_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_4' \gset + +SELECT result FROM run_command_on_workers($$ + SELECT schemaid INTO tenant_4_schemaid FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = 'tenant_4' +$$); + +SET client_min_messages TO WARNING; + +-- Rename it to a name that contains a single quote to verify that we properly +-- escape its name when sending the command to delete the pg_dist_tenant_schema +-- entry on workers. +ALTER SCHEMA tenant_4 RENAME TO "tenant\'_4"; + +DROP SCHEMA "tenant\'_4", "CiTuS.TeeN_108" CASCADE; + +SET client_min_messages TO NOTICE; + +-- (on coordinator) Verify that dropping a tenant schema deletes the associated +-- pg_dist_tenant_schema entry and pg_dist_colocation too. +SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid = :tenant_4_schemaid; +SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = :tenant_4_colocationid; + +SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid = :citus_teen_schemaid; +SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = :citus_teen_colocationid; + +-- (on workers) Verify that dropping a tenant schema deletes the associated +-- pg_dist_tenant_schema entry and pg_dist_colocation too. +SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=0 FROM pg_dist_tenant_schema + WHERE schemaid = (SELECT schemaid FROM tenant_4_schemaid) +$$); + +SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=0 FROM pg_dist_tenant_schema + WHERE schemaid = (SELECT schemaid FROM citus_teen_schemaid) +$$); + +SELECT format( + 'SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = %s; + $$);', +:tenant_4_colocationid) AS verify_workers_query \gset + +:verify_workers_query + +SELECT result FROM run_command_on_workers($$ + DROP TABLE tenant_4_schemaid +$$); + +SELECT format( + 'SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = %s; + $$);', +:citus_teen_colocationid) AS verify_workers_query \gset + +:verify_workers_query + +SELECT result FROM run_command_on_workers($$ + DROP TABLE citus_teen_schemaid +$$); + +-- show that we don't allow colocating a Citus table with a tenant table +CREATE TABLE regular_schema.null_shard_key_1(a int, b text); +SELECT create_distributed_table('regular_schema.null_shard_key_1', null, colocate_with => 'tenant_5.tbl_2'); +SELECT create_distributed_table('regular_schema.null_shard_key_1', 'a', colocate_with => 'tenant_5.tbl_2'); + +CREATE TABLE regular_schema.null_shard_key_table_2(a int, b text); +SELECT create_distributed_table('regular_schema.null_shard_key_table_2', null); + +-- Show that we don't chose to colocate regular single-shard tables with +-- tenant tables by default. +SELECT * FROM pg_dist_tenant_schema WHERE colocationid = ( + SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'regular_schema.null_shard_key_table_2'::regclass +); + +-- save the colocation id used for tenant_5 +SELECT colocationid AS tenant_5_old_colocationid FROM pg_dist_tenant_schema +WHERE schemaid::regnamespace::text = 'tenant_5' \gset + +-- drop all the tables that belong to tenant_5 and create a new one +DROP TABLE tenant_5.tbl_1, tenant_5.tbl_2, tenant_5.tbl_3; +CREATE TABLE tenant_5.tbl_4(a int, b text); + +-- (on coordinator) verify that tenant_5 is still associated with the same colocation id +SELECT colocationid = :tenant_5_old_colocationid FROM pg_dist_tenant_schema +WHERE schemaid::regnamespace::text = 'tenant_5'; + +-- (on workers) verify that tenant_5 is still associated with the same colocation id +SELECT format( + 'SELECT result FROM run_command_on_workers($$ + SELECT colocationid = %s FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = ''tenant_5''; + $$);', +:tenant_5_old_colocationid) AS verify_workers_query \gset + +:verify_workers_query + +SELECT schemaid AS tenant_1_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_1' \gset +SELECT colocationid AS tenant_1_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_1' \gset + +SELECT schemaid AS tenant_2_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_2' \gset +SELECT colocationid AS tenant_2_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_2' \gset + +SELECT result FROM run_command_on_workers($$ + SELECT schemaid INTO tenant_1_schemaid FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = 'tenant_1' +$$); + +SELECT result FROM run_command_on_workers($$ + SELECT schemaid INTO tenant_2_schemaid FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = 'tenant_2' +$$); + +SET client_min_messages TO WARNING; +SET citus.enable_schema_based_sharding TO OFF; + +DROP SCHEMA tenant_1 CASCADE; + +CREATE ROLE test_non_super_user; +ALTER ROLE test_non_super_user NOSUPERUSER; + +ALTER SCHEMA tenant_2 OWNER TO test_non_super_user; +-- XXX: ALTER SCHEMA .. OWNER TO .. is not propagated to workers, +-- see https://github.com/citusdata/citus/issues/4812. +SELECT result FROM run_command_on_workers($$ALTER SCHEMA tenant_2 OWNER TO test_non_super_user$$); + +DROP OWNED BY test_non_super_user CASCADE; + +DROP ROLE test_non_super_user; + +SET client_min_messages TO NOTICE; + +-- (on coordinator) Verify that dropping a tenant schema always deletes +-- the associated pg_dist_tenant_schema entry even if the the schema was +-- dropped while the GUC was set to off. +SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid IN (:tenant_1_schemaid, :tenant_2_schemaid); +SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid IN (:tenant_1_colocationid, :tenant_2_colocationid); + +-- (on workers) Verify that dropping a tenant schema always deletes +-- the associated pg_dist_tenant_schema entry even if the the schema was +-- dropped while the GUC was set to off. +SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=0 FROM pg_dist_tenant_schema + WHERE schemaid IN (SELECT schemaid FROM tenant_1_schemaid UNION SELECT schemaid FROM tenant_2_schemaid) +$$); + +SELECT format( + 'SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid IN (%s, %s); + $$);', +:tenant_1_colocationid, :tenant_2_colocationid) AS verify_workers_query \gset + +:verify_workers_query + +SELECT result FROM run_command_on_workers($$ + DROP TABLE tenant_1_schemaid +$$); + +SELECT result FROM run_command_on_workers($$ + DROP TABLE tenant_2_schemaid +$$); + +SET citus.enable_schema_based_sharding TO ON; +SET client_min_messages TO NOTICE; + +-- show that all schemaid values are unique and non-null in pg_dist_tenant_schema +SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid IS NULL; +SELECT (SELECT COUNT(*) FROM pg_dist_tenant_schema) = + (SELECT COUNT(DISTINCT(schemaid)) FROM pg_dist_tenant_schema); + +-- show that all colocationid values are unique and non-null in pg_dist_tenant_schema +SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE colocationid IS NULL; +SELECT (SELECT COUNT(*) FROM pg_dist_tenant_schema) = + (SELECT COUNT(DISTINCT(colocationid)) FROM pg_dist_tenant_schema); + +CREATE TABLE public.cannot_be_a_tenant_table(a int, b text); + +-- show that we don't consider public schema as a tenant schema +SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'public'; + +DROP TABLE public.cannot_be_a_tenant_table; + +BEGIN; + ALTER SCHEMA public RENAME TO public_renamed; + CREATE SCHEMA public; + + -- Show that we don't consider public schema as a tenant schema, + -- even if it's recreated. + SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'public'; +ROLLBACK; + +CREATE TEMPORARY TABLE temp_table(a int, b text); + +-- show that we don't consider temporary schemas as tenant schemas +SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = '%pg_temp%'; + +DROP TABLE temp_table; + +-- test creating a tenant schema and a tenant table for it in the same transaction +BEGIN; + CREATE SCHEMA tenant_7; + CREATE TABLE tenant_7.tbl_1(a int, b text); + CREATE TABLE tenant_7.tbl_2(a int, b text); + + SELECT colocationid = ( + SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_7.tbl_1'::regclass + ) + FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = 'tenant_7'; + + -- make sure that both tables created in tenant_7 are colocated + SELECT COUNT(DISTINCT(colocationid)) = 1 FROM pg_dist_partition + WHERE logicalrelid IN ('tenant_7.tbl_1'::regclass, 'tenant_7.tbl_2'::regclass); +COMMIT; + +-- Test creating a tenant schema and a tenant table for it in the same transaction +-- but this time rollback the transaction. +BEGIN; + CREATE SCHEMA tenant_8; + CREATE TABLE tenant_8.tbl_1(a int, b text); + CREATE TABLE tenant_8.tbl_2(a int, b text); +ROLLBACK; + +SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_8'; +SELECT COUNT(*)=0 FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant_8.%'; + +-- Verify that citus.enable_schema_based_sharding and citus.use_citus_managed_tables +-- GUC don't interfere with each other when creating a table in tenant schema. +-- +-- In utility hook, we check whether the CREATE TABLE command is issued on a tenant +-- schema before checking whether citus.use_citus_managed_tables is set to ON to +-- avoid converting the table into a Citus managed table unnecessarily. +-- +-- If the CREATE TABLE command is issued on a tenant schema, we skip the check +-- for citus.use_citus_managed_tables. +SET citus.use_citus_managed_tables TO ON; +CREATE TABLE tenant_7.tbl_3(a int, b text, PRIMARY KEY(a)); +RESET citus.use_citus_managed_tables; + +-- Verify that we don't unnecessarily convert a table into a Citus managed +-- table when creating it with a pre-defined foreign key to a reference table. +CREATE TABLE reference_table(a int PRIMARY KEY); +SELECT create_reference_table('reference_table'); + +-- Notice that tenant_7.tbl_4 have foreign keys both to tenant_7.tbl_3 and +-- to reference_table. +CREATE TABLE tenant_7.tbl_4(a int REFERENCES reference_table, FOREIGN KEY(a) REFERENCES tenant_7.tbl_3(a) ON DELETE CASCADE); + +INSERT INTO tenant_7.tbl_3 VALUES (1, 'a'), (2, 'b'), (3, 'c'); +INSERT INTO reference_table VALUES (1), (2), (3); +INSERT INTO tenant_7.tbl_4 VALUES (1), (2), (3); + +DELETE FROM tenant_7.tbl_3 WHERE a < 3; +SELECT * FROM tenant_7.tbl_4 ORDER BY a; + +SELECT COUNT(*)=2 FROM pg_dist_partition +WHERE logicalrelid IN ('tenant_7.tbl_3'::regclass, 'tenant_7.tbl_4'::regclass) AND + partmethod = 'n' AND repmodel = 's' AND + colocationid = (SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_7.tbl_1'::regclass); + +CREATE TABLE local_table(a int PRIMARY KEY); + +-- fails because tenant tables cannot have foreign keys to local tables +CREATE TABLE tenant_7.tbl_5(a int REFERENCES local_table(a)); + +-- Fails because tenant tables cannot have foreign keys to tenant tables +-- that belong to different tenant schemas. +CREATE TABLE tenant_5.tbl_5(a int, b text, FOREIGN KEY(a) REFERENCES tenant_7.tbl_3(a)); + +CREATE SCHEMA tenant_9; + +SELECT schemaid AS tenant_9_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_9' \gset +SELECT colocationid AS tenant_9_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_9' \gset + +SELECT result FROM run_command_on_workers($$ + SELECT schemaid INTO tenant_9_schemaid FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = 'tenant_9' +$$); + +DROP SCHEMA tenant_9; + +-- (on coordinator) Make sure that dropping an empty tenant schema +-- doesn't leave any dangling entries in pg_dist_tenant_schema and +-- pg_dist_colocation. +SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid = :tenant_9_schemaid; +SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = :tenant_9_colocationid; + +-- (on workers) Make sure that dropping an empty tenant schema +-- doesn't leave any dangling entries in pg_dist_tenant_schema and +-- pg_dist_colocation. +SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=0 FROM pg_dist_tenant_schema + WHERE schemaid = (SELECT schemaid FROM tenant_9_schemaid) +$$); + +SELECT format( + 'SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = %s; + $$);', +:tenant_9_colocationid) AS verify_workers_query \gset + +:verify_workers_query + +SELECT result FROM run_command_on_workers($$ + DROP TABLE tenant_9_schemaid +$$); + +CREATE TABLE tenant_3.search_path_test(a int); +INSERT INTO tenant_3.search_path_test VALUES (1), (10); + +CREATE TABLE tenant_5.search_path_test(a int); +INSERT INTO tenant_5.search_path_test VALUES (2); + +CREATE TABLE tenant_7.search_path_test(a int); +INSERT INTO tenant_7.search_path_test VALUES (3); + +CREATE FUNCTION increment_one() +RETURNS void +LANGUAGE plpgsql +AS $$ +BEGIN + UPDATE search_path_test SET a = a + 1; +END; +$$; + +CREATE FUNCTION decrement_one() +RETURNS void +LANGUAGE plpgsql +AS $$ +BEGIN + UPDATE search_path_test SET a = a - 1; +END; +$$; + +SET search_path TO tenant_5; + +PREPARE list_tuples AS SELECT * FROM search_path_test ORDER BY a; + +SELECT * FROM search_path_test ORDER BY a; + +SET search_path TO tenant_3; +DELETE FROM search_path_test WHERE a = 1; +SELECT * FROM search_path_test ORDER BY a; +SELECT regular_schema.increment_one(); +EXECUTE list_tuples; + +SET search_path TO tenant_7; +DROP TABLE search_path_test; +SELECT * FROM pg_dist_partition WHERE logicalrelid::text = 'search_path_test'; + +SET search_path TO tenant_5; +SELECT regular_schema.decrement_one(); +EXECUTE list_tuples; + +SET search_path TO regular_schema; + +CREATE USER test_other_super_user WITH superuser; + +\c - test_other_super_user + +SET citus.enable_schema_based_sharding TO ON; +CREATE SCHEMA tenant_9; + +\c - postgres + +SET search_path TO regular_schema; +SET citus.next_shard_id TO 1930000; +SET citus.shard_count TO 32; +SET citus.shard_replication_factor TO 1; +SET client_min_messages TO NOTICE; +SET citus.enable_schema_based_sharding TO ON; + +SELECT schemaid AS tenant_9_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_9' \gset +SELECT colocationid AS tenant_9_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_9' \gset + +SELECT result FROM run_command_on_workers($$ + SELECT schemaid INTO tenant_9_schemaid FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = 'tenant_9' +$$); + +DROP OWNED BY test_other_super_user; + +-- (on coordinator) Make sure that dropping an empty tenant schema +-- (via DROP OWNED BY) doesn't leave any dangling entries in +-- pg_dist_tenant_schema and pg_dist_colocation. +SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid = :tenant_9_schemaid; +SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = :tenant_9_colocationid; + +-- (on workers) Make sure that dropping an empty tenant schema +-- (via DROP OWNED BY) doesn't leave any dangling entries in +-- pg_dist_tenant_schema and pg_dist_colocation. +SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=0 FROM pg_dist_tenant_schema + WHERE schemaid = (SELECT schemaid FROM tenant_9_schemaid) +$$); + +SELECT format( + 'SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = %s; + $$);', +:tenant_9_colocationid) AS verify_workers_query \gset + +:verify_workers_query + +SELECT result FROM run_command_on_workers($$ + DROP TABLE tenant_9_schemaid +$$); + +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$$); + +GRANT CREATE ON SCHEMA public TO test_non_super_user ; + +\c - test_non_super_user + +SET search_path TO regular_schema; +SET citus.next_shard_id TO 1940000; +SET citus.shard_count TO 32; +SET citus.shard_replication_factor TO 1; +SET client_min_messages TO NOTICE; +SET citus.enable_schema_based_sharding TO ON; + +-- test create / drop tenant schema / table + +CREATE SCHEMA tenant_10; +CREATE TABLE tenant_10.tbl_1(a int, b text); +CREATE TABLE tenant_10.tbl_2(a int, b text); + +DROP TABLE tenant_10.tbl_2; + +CREATE SCHEMA tenant_11; + +SELECT schemaid AS tenant_10_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_10' \gset +SELECT colocationid AS tenant_10_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_10' \gset + +SELECT schemaid AS tenant_11_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_11' \gset +SELECT colocationid AS tenant_11_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_11' \gset + +SELECT result FROM run_command_on_workers($$ + SELECT schemaid INTO tenant_10_schemaid FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = 'tenant_10' +$$); + +SELECT result FROM run_command_on_workers($$ + SELECT schemaid INTO tenant_11_schemaid FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = 'tenant_11' +$$); + +-- (on coordinator) Verify metadata for tenant schemas that are created via non-super-user. +SELECT COUNT(DISTINCT(schemaid))=2 FROM pg_dist_tenant_schema WHERE schemaid IN (:tenant_10_schemaid, :tenant_11_schemaid); +SELECT COUNT(DISTINCT(colocationid))=2 FROM pg_dist_colocation WHERE colocationid IN (:tenant_10_colocationid, :tenant_11_colocationid); + +-- (on workers) Verify metadata for tenant schemas that are created via non-super-user. +SELECT result FROM run_command_on_workers($$ + SELECT COUNT(DISTINCT(schemaid))=2 FROM pg_dist_tenant_schema + WHERE schemaid IN (SELECT schemaid FROM tenant_10_schemaid UNION SELECT schemaid FROM tenant_11_schemaid) +$$); + +SELECT format( + 'SELECT result FROM run_command_on_workers($$ + SELECT COUNT(DISTINCT(colocationid))=2 FROM pg_dist_colocation WHERE colocationid IN (%s, %s); + $$);', +:tenant_10_colocationid, :tenant_11_colocationid) AS verify_workers_query \gset + +:verify_workers_query + +SET client_min_messages TO WARNING; +DROP SCHEMA tenant_10, tenant_11 CASCADE; +SET client_min_messages TO NOTICE; + +-- (on coordinator) Verify that dropping a tenant schema via non-super-user +-- deletes the associated pg_dist_tenant_schema entry. +SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid IN (:tenant_10_schemaid, :tenant_11_schemaid); +SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid IN (:tenant_10_colocationid, :tenant_11_colocationid); + +-- (on workers) Verify that dropping a tenant schema via non-super-user +-- deletes the associated pg_dist_tenant_schema entry. +SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=0 FROM pg_dist_tenant_schema + WHERE schemaid IN (SELECT schemaid FROM tenant_10_schemaid UNION SELECT schemaid FROM tenant_11_schemaid) +$$); + +SELECT format( + 'SELECT result FROM run_command_on_workers($$ + SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid IN (%s, %s); + $$);', +:tenant_10_colocationid, :tenant_11_colocationid) AS verify_workers_query \gset + +:verify_workers_query + +SELECT result FROM run_command_on_workers($$ + DROP TABLE tenant_10_schemaid +$$); + +SELECT result FROM run_command_on_workers($$ + DROP TABLE tenant_11_schemaid +$$); + +\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; + +DROP ROLE test_non_super_user; + +\c - - - :worker_1_port + +-- test creating a tenant table from workers +CREATE TABLE tenant_3.tbl_1(a int, b text); + +-- test creating a tenant schema from workers +SET citus.enable_schema_based_sharding TO ON; +CREATE SCHEMA worker_tenant_schema; +SET citus.enable_schema_based_sharding TO OFF; + +-- Enable the GUC on workers to make sure that the CREATE SCHEMA/ TABLE +-- commands that we send to workers don't recursively try creating a +-- tenant schema / table. +ALTER SYSTEM SET citus.enable_schema_based_sharding TO ON; +SELECT pg_reload_conf(); + +\c - - - :worker_2_port + +ALTER SYSTEM SET citus.enable_schema_based_sharding TO ON; +SELECT pg_reload_conf(); + +-- Verify that citus_internal_unregister_tenant_schema_globally is a no-op +-- on workers. +SELECT citus_internal_unregister_tenant_schema_globally('tenant_3'::regnamespace, 'tenant_3'); + +\c - - - :master_port + +SET search_path TO regular_schema; +SET citus.next_shard_id TO 1950000; +SET citus.shard_count TO 32; +SET citus.shard_replication_factor TO 1; +SET client_min_messages TO NOTICE; + +CREATE TABLE tenant_3.tbl_1(a int, b text); + +SET citus.enable_schema_based_sharding TO ON; +CREATE SCHEMA tenant_6; +CREATE TABLE tenant_6.tbl_1(a int, b text); + +-- verify pg_dist_partition entries for tenant_3.tbl_1 and tenant_6.tbl_1 +SELECT COUNT(*)=2 FROM pg_dist_partition +WHERE logicalrelid IN ('tenant_3.tbl_1'::regclass, 'tenant_6.tbl_1'::regclass) AND + partmethod = 'n' AND repmodel = 's' AND colocationid > 0; + +\c - - - :worker_1_port + +ALTER SYSTEM RESET citus.enable_schema_based_sharding; +SELECT pg_reload_conf(); + +\c - - - :worker_2_port + +ALTER SYSTEM RESET citus.enable_schema_based_sharding; +SELECT pg_reload_conf(); + +\c - - - :master_port + +SET client_min_messages TO WARNING; +DROP SCHEMA regular_schema, tenant_3, tenant_5, tenant_7, tenant_6 CASCADE; + +SELECT citus_remove_node('localhost', :master_port); diff --git a/src/test/regress/sql/single_node.sql b/src/test/regress/sql/single_node.sql index bdfb3e260..f74699b20 100644 --- a/src/test/regress/sql/single_node.sql +++ b/src/test/regress/sql/single_node.sql @@ -98,8 +98,25 @@ WHERE shardid = ( SELECT shardid AS round_robin_test_c1_shard_id FROM pg_dist_shard WHERE logicalrelid = 'single_node.single_node_nullkey_c1'::regclass \gset SELECT create_distributed_table('single_node_nullkey_c1_' || :round_robin_test_c1_shard_id , null, colocate_with=>'none', distribution_type=>null); +-- create a tenant schema on single node setup +SET citus.enable_schema_based_sharding TO ON; + +CREATE SCHEMA tenant_1; +CREATE TABLE tenant_1.tbl_1 (a int); + +-- verify that we recorded tenant_1 in pg_dist_tenant_schema +SELECT COUNT(*)=1 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_1'; + +-- verify that tenant_1.tbl_1 is recorded in pg_dist_partition, as a single-shard table +SELECT COUNT(*)=1 FROM pg_dist_partition +WHERE logicalrelid = 'tenant_1.tbl_1'::regclass AND + partmethod = 'n' AND repmodel = 's' AND colocationid IS NOT NULL; + +RESET citus.enable_schema_based_sharding; + SET client_min_messages TO WARNING; DROP TABLE failover_to_local, single_node_nullkey_c1, single_node_nullkey_c2; +DROP SCHEMA tenant_1 CASCADE; RESET client_min_messages; -- so that we don't have to update rest of the test output diff --git a/src/test/regress/sql/upgrade_schema_based_sharding_after.sql b/src/test/regress/sql/upgrade_schema_based_sharding_after.sql new file mode 100644 index 000000000..300d94574 --- /dev/null +++ b/src/test/regress/sql/upgrade_schema_based_sharding_after.sql @@ -0,0 +1,70 @@ +ALTER SCHEMA "tenant\'_1" RENAME TO tenant_1; +ALTER SCHEMA "tenant\'_2" RENAME TO tenant_2; + +-- verify that colocation id is set even for empty tenant +SELECT colocationid > 0 FROM pg_dist_tenant_schema +WHERE schemaid::regnamespace::text = 'tenant_1'; + +-- verify the same on workers +SELECT result FROM run_command_on_workers($$ + SELECT colocationid > 0 FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = 'tenant_1'; +$$); + +-- verify that colocation id is set for non-empty tenant +SELECT colocationid = ( + SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_2.test_table'::regclass +) +FROM pg_dist_tenant_schema +WHERE schemaid::regnamespace::text = 'tenant_2'; + +-- verify the same on workers +SELECT result FROM run_command_on_workers($$ + SELECT colocationid = ( + SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_2.test_table'::regclass + ) + FROM pg_dist_tenant_schema + WHERE schemaid::regnamespace::text = 'tenant_2'; +$$); + +CREATE TABLE tenant_1.tbl_1(a int, b text); +CREATE TABLE tenant_2.tbl_1(a int, b text); + +-- Show that we can create further tenant tables in the tenant schemas +-- after pg upgrade. +SELECT COUNT(*)=2 FROM pg_dist_partition +WHERE logicalrelid IN ('tenant_1.tbl_1'::regclass, 'tenant_2.tbl_1'::regclass) AND + partmethod = 'n' AND repmodel = 's' AND colocationid > 0; + +SELECT colocationid = ( + SELECT colocationid FROM pg_dist_partition + WHERE logicalrelid = 'tenant_1.tbl_1'::regclass AND + partmethod = 'n' AND repmodel = 's' +) +FROM pg_dist_tenant_schema +WHERE schemaid::regnamespace::text = 'tenant_1'; + +SELECT colocationid = ( + SELECT colocationid FROM pg_dist_partition + WHERE logicalrelid = 'tenant_2.tbl_1'::regclass AND + partmethod = 'n' AND repmodel = 's' +) +FROM pg_dist_tenant_schema +WHERE schemaid::regnamespace::text = 'tenant_2'; + +-- rollback the changes made on following schemas to make this test idempotent +DROP TABLE tenant_1.tbl_1, tenant_2.tbl_1; +ALTER SCHEMA tenant_1 RENAME TO "tenant\'_1"; +ALTER SCHEMA tenant_2 RENAME TO "tenant\'_2"; + +SET citus.enable_schema_based_sharding TO ON; + +CREATE SCHEMA tenant_3; + +-- Show that we can create furher tenant schemas after pg upgrade. +SELECT COUNT(*)=1 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_3'; + +-- drop the schema created in this test to this test idempotent +DROP SCHEMA tenant_3 CASCADE; + +RESET citus.enable_schema_based_sharding; diff --git a/src/test/regress/sql/upgrade_schema_based_sharding_before.sql b/src/test/regress/sql/upgrade_schema_based_sharding_before.sql new file mode 100644 index 000000000..0f3141fe8 --- /dev/null +++ b/src/test/regress/sql/upgrade_schema_based_sharding_before.sql @@ -0,0 +1,14 @@ +SET citus.enable_schema_based_sharding TO ON; + +-- Create tenant tables with schema names that need escaping +-- to verify that citus_prepare_pg_upgrade() correctly saves +-- them into public schema. + +-- empty tenant +CREATE SCHEMA "tenant\'_1"; + +-- non-empty tenant +CREATE SCHEMA "tenant\'_2"; +CREATE TABLE "tenant\'_2".test_table(a int, b text); + +RESET citus.enable_schema_based_sharding; From 1ca80813f623708770c288dbb01cdb8619d2a6e3 Mon Sep 17 00:00:00 2001 From: Ahmet Gedemenli Date: Fri, 26 May 2023 17:30:05 +0300 Subject: [PATCH 051/118] Citus UDFs support for single shard tables (#6916) Verify Citus UDFs work well with single shard tables SUPPORTED * citus_table_size * citus_total_relation_size * citus_relation_size * citus_shard_sizes * truncate_local_data_after_distributing_table * create_distributed_function // test function colocated with a single shard table * undistribute_table * alter_table_set_access_method UNSUPPORTED - error out for single shard tables * master_create_empty_shard * create_distributed_table_concurrently * create_distributed_table * create_reference_table * citus_add_local_table_to_metadata * citus_split_shard_by_split_points * alter_distributed_table --- .../distributed/commands/alter_table.c | 25 +- src/backend/distributed/commands/function.c | 45 +++- .../planner/function_call_delegation.c | 44 +++- src/include/distributed/metadata_cache.h | 2 + src/test/regress/citus_tests/run_test.py | 1 + .../expected/alter_distributed_table.out | 10 + .../expected/distributed_functions.out | 20 ++ .../regress/expected/function_propagation.out | 24 +- .../regress/expected/multi_metadata_sync.out | 102 +++++--- .../expected/multi_metadata_sync_0.out | 102 +++++--- .../multi_mx_function_call_delegation.out | 24 +- .../multi_mx_function_call_delegation_0.out | 24 +- .../expected/single_shard_table_udfs.out | 240 ++++++++++++++++++ .../regress/expected/view_propagation.out | 5 +- src/test/regress/multi_1_schedule | 1 + .../regress/sql/alter_distributed_table.sql | 5 + .../regress/sql/distributed_functions.sql | 6 + src/test/regress/sql/function_propagation.sql | 9 + src/test/regress/sql/multi_metadata_sync.sql | 17 ++ .../sql/multi_mx_function_call_delegation.sql | 8 + .../regress/sql/single_shard_table_udfs.sql | 105 ++++++++ src/test/regress/sql/view_propagation.sql | 3 + 22 files changed, 748 insertions(+), 74 deletions(-) create mode 100644 src/test/regress/expected/single_shard_table_udfs.out create mode 100644 src/test/regress/sql/single_shard_table_udfs.sql diff --git a/src/backend/distributed/commands/alter_table.c b/src/backend/distributed/commands/alter_table.c index 9e03eb82f..a0359d335 100644 --- a/src/backend/distributed/commands/alter_table.c +++ b/src/backend/distributed/commands/alter_table.c @@ -477,8 +477,11 @@ AlterTableSetAccessMethod(TableConversionParameters *params) EnsureTableNotReferencing(params->relationId, ALTER_TABLE_SET_ACCESS_METHOD); EnsureTableNotReferenced(params->relationId, ALTER_TABLE_SET_ACCESS_METHOD); EnsureTableNotForeign(params->relationId); - if (IsCitusTableType(params->relationId, DISTRIBUTED_TABLE)) + + if (!IsCitusTableType(params->relationId, SINGLE_SHARD_DISTRIBUTED) && + IsCitusTableType(params->relationId, DISTRIBUTED_TABLE)) { + /* we do not support non-hash distributed tables, except single shard tables */ EnsureHashDistributedTable(params->relationId); } @@ -1365,7 +1368,19 @@ CreateCitusTableLike(TableConversionState *con) { if (IsCitusTableType(con->relationId, DISTRIBUTED_TABLE)) { - CreateDistributedTableLike(con); + if (IsCitusTableType(con->relationId, SINGLE_SHARD_DISTRIBUTED)) + { + ColocationParam colocationParam = { + .colocationParamType = COLOCATE_WITH_TABLE_LIKE_OPT, + .colocateWithTableName = quote_qualified_identifier(con->schemaName, + con->relationName) + }; + CreateSingleShardTable(con->newRelationId, colocationParam); + } + else + { + CreateDistributedTableLike(con); + } } else if (IsCitusTableType(con->relationId, REFERENCE_TABLE)) { @@ -1856,6 +1871,12 @@ CheckAlterDistributedTableConversionParameters(TableConversionState *con) "it is not a distributed table", con->colocateWith))); } + else if (IsCitusTableType(colocateWithTableOid, SINGLE_SHARD_DISTRIBUTED)) + { + ereport(ERROR, (errmsg("cannot colocate with %s because " + "it is a single shard distributed table", + con->colocateWith))); + } } /* shard_count:=0 is not allowed */ diff --git a/src/backend/distributed/commands/function.c b/src/backend/distributed/commands/function.c index c5a0652ae..c992bc4fb 100644 --- a/src/backend/distributed/commands/function.c +++ b/src/backend/distributed/commands/function.c @@ -105,6 +105,9 @@ static void DistributeFunctionColocatedWithDistributedTable(RegProcedure funcOid char *colocateWithTableName, const ObjectAddress * functionAddress); +static void DistributeFunctionColocatedWithSingleShardTable(const + ObjectAddress *functionAddress, + text *colocateWithText); static void DistributeFunctionColocatedWithReferenceTable(const ObjectAddress *functionAddress); static List * FilterDistributedFunctions(GrantStmt *grantStmt); @@ -133,6 +136,7 @@ create_distributed_function(PG_FUNCTION_ARGS) Oid distributionArgumentOid = InvalidOid; bool colocatedWithReferenceTable = false; + bool colocatedWithSingleShardTable = false; char *distributionArgumentName = NULL; char *colocateWithTableName = NULL; @@ -187,6 +191,8 @@ create_distributed_function(PG_FUNCTION_ARGS) Oid colocationRelationId = ResolveRelationId(colocateWithText, false); colocatedWithReferenceTable = IsCitusTableType(colocationRelationId, REFERENCE_TABLE); + colocatedWithSingleShardTable = IsCitusTableType(colocationRelationId, + SINGLE_SHARD_DISTRIBUTED); } } @@ -276,11 +282,16 @@ create_distributed_function(PG_FUNCTION_ARGS) forceDelegationAddress, functionAddress); } - else if (!colocatedWithReferenceTable) + else if (!colocatedWithReferenceTable && !colocatedWithSingleShardTable) { DistributeFunctionColocatedWithDistributedTable(funcOid, colocateWithTableName, functionAddress); } + else if (colocatedWithSingleShardTable) + { + DistributeFunctionColocatedWithSingleShardTable(functionAddress, + colocateWithText); + } else if (colocatedWithReferenceTable) { /* @@ -435,6 +446,25 @@ DistributeFunctionColocatedWithDistributedTable(RegProcedure funcOid, } +/* + * DistributeFunctionColocatedWithSingleShardTable updates pg_dist_object records for + * a function/procedure that is colocated with a single shard table. + */ +static void +DistributeFunctionColocatedWithSingleShardTable(const ObjectAddress *functionAddress, + text *colocateWithText) +{ + /* get the single shard table's colocation id */ + int colocationId = TableColocationId(ResolveRelationId(colocateWithText, false)); + + /* set distribution argument to NULL */ + int *distributionArgumentIndex = NULL; + UpdateFunctionDistributionInfo(functionAddress, distributionArgumentIndex, + &colocationId, + NULL); +} + + /* * DistributeFunctionColocatedWithReferenceTable updates pg_dist_object records for * a function/procedure that is colocated with a reference table. @@ -641,6 +671,19 @@ EnsureFunctionCanBeColocatedWithTable(Oid functionOid, Oid distributionColumnTyp CitusTableCacheEntry *sourceTableEntry = GetCitusTableCacheEntry(sourceRelationId); char sourceReplicationModel = sourceTableEntry->replicationModel; + if (IsCitusTableTypeCacheEntry(sourceTableEntry, SINGLE_SHARD_DISTRIBUTED) && + distributionColumnType != InvalidOid) + { + char *functionName = get_func_name(functionOid); + char *sourceRelationName = get_rel_name(sourceRelationId); + + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot colocate function \"%s\" and table \"%s\" because " + "distribution arguments are not supported when " + "colocating with single shard distributed tables.", + functionName, sourceRelationName))); + } + if (!IsCitusTableTypeCacheEntry(sourceTableEntry, HASH_DISTRIBUTED) && !IsCitusTableTypeCacheEntry(sourceTableEntry, REFERENCE_TABLE)) { diff --git a/src/backend/distributed/planner/function_call_delegation.c b/src/backend/distributed/planner/function_call_delegation.c index 0cdde52a6..2f8da29c0 100644 --- a/src/backend/distributed/planner/function_call_delegation.c +++ b/src/backend/distributed/planner/function_call_delegation.c @@ -116,7 +116,6 @@ contain_param_walker(Node *node, void *context) PlannedStmt * TryToDelegateFunctionCall(DistributedPlanningContext *planContext) { - bool colocatedWithReferenceTable = false; ShardPlacement *placement = NULL; struct ParamWalkerContext walkerParamContext = { 0 }; bool inTransactionBlock = false; @@ -392,13 +391,6 @@ TryToDelegateFunctionCall(DistributedPlanningContext *planContext) return NULL; } - CitusTableCacheEntry *distTable = GetCitusTableCacheEntry(colocatedRelationId); - Var *partitionColumn = distTable->partitionColumn; - if (partitionColumn == NULL) - { - colocatedWithReferenceTable = true; - } - /* * This can be called in queries like SELECT ... WHERE EXISTS(SELECT func()), or other * forms of CTEs or subqueries. We don't push-down in those cases. @@ -410,14 +402,20 @@ TryToDelegateFunctionCall(DistributedPlanningContext *planContext) return NULL; } - if (colocatedWithReferenceTable) + CitusTableCacheEntry *distTable = GetCitusTableCacheEntry(colocatedRelationId); + if (IsCitusTableType(colocatedRelationId, REFERENCE_TABLE)) { placement = ShardPlacementForFunctionColocatedWithReferenceTable(distTable); } + else if (IsCitusTableType(colocatedRelationId, SINGLE_SHARD_DISTRIBUTED)) + { + placement = ShardPlacementForFunctionColocatedWithSingleShardTable(distTable); + } else { placement = ShardPlacementForFunctionColocatedWithDistTable(procedure, funcExpr->args, + distTable-> partitionColumn, distTable, planContext->plan); @@ -570,6 +568,34 @@ ShardPlacementForFunctionColocatedWithDistTable(DistObjectCacheEntry *procedure, } +/* + * ShardPlacementForFunctionColocatedWithSingleShardTable decides on a placement + * for delegating a function call that reads from a single shard table. + */ +ShardPlacement * +ShardPlacementForFunctionColocatedWithSingleShardTable(CitusTableCacheEntry *cacheEntry) +{ + const ShardInterval *shardInterval = cacheEntry->sortedShardIntervalArray[0]; + + if (shardInterval == NULL) + { + ereport(DEBUG1, (errmsg("cannot push down call, failed to find shard interval"))); + return NULL; + } + + List *placementList = ActiveShardPlacementList(shardInterval->shardId); + if (list_length(placementList) != 1) + { + /* punt on this for now */ + ereport(DEBUG1, (errmsg( + "cannot push down function call for replicated distributed tables"))); + return NULL; + } + + return (ShardPlacement *) linitial(placementList); +} + + /* * ShardPlacementForFunctionColocatedWithReferenceTable decides on a placement for delegating * a function call that reads from a reference table. diff --git a/src/include/distributed/metadata_cache.h b/src/include/distributed/metadata_cache.h index 27a6eb632..dd98da764 100644 --- a/src/include/distributed/metadata_cache.h +++ b/src/include/distributed/metadata_cache.h @@ -196,6 +196,8 @@ extern bool HasOverlappingShardInterval(ShardInterval **shardIntervalArray, Oid shardIntervalCollation, FmgrInfo *shardIntervalSortCompareFunction); +extern ShardPlacement * ShardPlacementForFunctionColocatedWithSingleShardTable( + CitusTableCacheEntry *cacheEntry); extern ShardPlacement * ShardPlacementForFunctionColocatedWithReferenceTable( CitusTableCacheEntry *cacheEntry); extern ShardPlacement * ShardPlacementForFunctionColocatedWithDistTable( diff --git a/src/test/regress/citus_tests/run_test.py b/src/test/regress/citus_tests/run_test.py index 44fe4770a..04bf606f8 100755 --- a/src/test/regress/citus_tests/run_test.py +++ b/src/test/regress/citus_tests/run_test.py @@ -143,6 +143,7 @@ DEPS = { ], worker_count=6, ), + "function_propagation": TestDeps("minimal_schedule"), "multi_mx_modifying_xacts": TestDeps(None, ["multi_mx_create_table"]), "multi_mx_router_planner": TestDeps(None, ["multi_mx_create_table"]), "multi_mx_copy_data": TestDeps(None, ["multi_mx_create_table"]), diff --git a/src/test/regress/expected/alter_distributed_table.out b/src/test/regress/expected/alter_distributed_table.out index 1a99f1964..b8b86cd11 100644 --- a/src/test/regress/expected/alter_distributed_table.out +++ b/src/test/regress/expected/alter_distributed_table.out @@ -1020,6 +1020,16 @@ SELECT create_reference_table('reference_table'); SELECT alter_distributed_table('dist_table', colocate_with:='reference_table'); ERROR: cannot colocate with reference_table because it is not a distributed table +-- test colocating with single shard table +CREATE TABLE single_shard_table (a INT); +SELECT create_distributed_table('single_shard_table', null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT alter_distributed_table('dist_table', colocate_with:='single_shard_table'); +ERROR: cannot colocate with single_shard_table because it is a single shard distributed table -- test append table CREATE TABLE append_table (a INT); SELECT create_distributed_table('append_table', 'a', 'append'); diff --git a/src/test/regress/expected/distributed_functions.out b/src/test/regress/expected/distributed_functions.out index 516ff97e6..bc72af14c 100644 --- a/src/test/regress/expected/distributed_functions.out +++ b/src/test/regress/expected/distributed_functions.out @@ -704,6 +704,25 @@ WHERE pg_dist_partition.logicalrelid = 'replicated_table_func_test_4'::regclass t (1 row) +-- a function cannot be colocated with a single shard distributed table when a distribution column is provided +SELECT create_distributed_table('replicated_table_func_test_3', null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_function('eq_with_param_names(macaddr, macaddr)', 'val1', colocate_with:='replicated_table_func_test_3'); +ERROR: cannot colocate function "eq_with_param_names" and table "replicated_table_func_test_3" because distribution arguments are not supported when colocating with single shard distributed tables. +SELECT undistribute_table('replicated_table_func_test_3'); +NOTICE: creating a new table for function_tests.replicated_table_func_test_3 +NOTICE: moving the data of function_tests.replicated_table_func_test_3 +NOTICE: dropping the old function_tests.replicated_table_func_test_3 +NOTICE: renaming the new table to function_tests.replicated_table_func_test_3 + undistribute_table +--------------------------------------------------------------------- + +(1 row) + -- a function cannot be colocated with a reference table when a distribution column is provided SELECT create_reference_table('replicated_table_func_test_3'); create_reference_table @@ -1116,6 +1135,7 @@ TRUNCATE pg_dist_node; \c - - - :master_port SET client_min_messages TO ERROR; DROP USER functionuser; +DROP ROLE r1; SELECT 1 FROM run_command_on_workers($$DROP USER functionuser$$); ?column? --------------------------------------------------------------------- diff --git a/src/test/regress/expected/function_propagation.out b/src/test/regress/expected/function_propagation.out index 2787e7daf..8a588a3cf 100644 --- a/src/test/regress/expected/function_propagation.out +++ b/src/test/regress/expected/function_propagation.out @@ -864,8 +864,8 @@ BEGIN; (0 rows) CREATE TABLE citus_local_table_to_test_func(l1 int DEFAULT func_in_transaction_for_local_table()); + SET LOCAL client_min_messages TO WARNING; SELECT 1 FROM master_add_node('localhost', :master_port, groupid => 0); -NOTICE: localhost:xxxxx is the coordinator and already contains metadata, skipping syncing the metadata ?column? --------------------------------------------------------------------- 1 @@ -1097,6 +1097,14 @@ SELECT create_reference_table('tbl_to_colocate_ref'); (1 row) +-- test colocating function with single shard table +CREATE TABLE single_shard_tbl (a int); +SELECT create_distributed_table('single_shard_tbl', null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + CREATE FUNCTION func_to_colocate (a int) returns int as $$select 1;$$ language sql; -- see the empty pg_dist_object entries SELECT distribution_argument_index, colocationid, force_delegation FROM pg_catalog.pg_dist_object WHERE objid = 'func_to_colocate'::regproc; @@ -1119,6 +1127,20 @@ SELECT distribution_argument_index, colocationid, force_delegation FROM pg_catal | 10002 | (1 row) +-- colocate the function with single shard table table +SELECT create_distributed_function('func_to_colocate(int)', colocate_with:='single_shard_tbl'); + create_distributed_function +--------------------------------------------------------------------- + +(1 row) + +-- see the pg_dist_object entry +SELECT distribution_argument_index, colocationid, force_delegation FROM pg_catalog.pg_dist_object WHERE objid = 'func_to_colocate'::regproc; + distribution_argument_index | colocationid | force_delegation +--------------------------------------------------------------------- + | 10003 | +(1 row) + -- convert to non-delegated SELECT create_distributed_function('func_to_colocate(int)'); create_distributed_function diff --git a/src/test/regress/expected/multi_metadata_sync.out b/src/test/regress/expected/multi_metadata_sync.out index c64e5d5c7..626584de6 100644 --- a/src/test/regress/expected/multi_metadata_sync.out +++ b/src/test/regress/expected/multi_metadata_sync.out @@ -122,6 +122,15 @@ reset citus.shard_replication_factor; -- Set the replication model of the test table to streaming replication so that it is -- considered as an MX table UPDATE pg_dist_partition SET repmodel='s' WHERE logicalrelid='mx_test_table'::regclass; +-- add a single shard table and verify the creation commands are included in the activate node snapshot +CREATE TABLE single_shard_tbl(a int); +SELECT create_distributed_table('single_shard_tbl', null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO single_shard_tbl VALUES (1); -- Show that the created MX table is and its sequences are included in the activate node snapshot SELECT unnest(activate_node_snapshot()) order by 1; unnest @@ -131,9 +140,11 @@ SELECT unnest(activate_node_snapshot()) order by 1; ALTER SEQUENCE public.user_defined_seq OWNER TO postgres ALTER TABLE public.mx_test_table ADD CONSTRAINT mx_test_table_col_1_key UNIQUE (col_1) ALTER TABLE public.mx_test_table OWNER TO postgres + ALTER TABLE public.single_shard_tbl OWNER TO postgres CALL pg_catalog.worker_drop_all_shell_tables(true) CREATE SCHEMA IF NOT EXISTS public AUTHORIZATION pg_database_owner CREATE TABLE public.mx_test_table (col_1 integer, col_2 text NOT NULL, col_3 bigint DEFAULT nextval('public.mx_test_table_col_3_seq'::regclass) NOT NULL, col_4 bigint DEFAULT nextval('public.user_defined_seq'::regclass)) USING heap + CREATE TABLE public.single_shard_tbl (a integer) USING heap DELETE FROM pg_catalog.pg_dist_colocation DELETE FROM pg_catalog.pg_dist_object DELETE FROM pg_catalog.pg_dist_tenant_schema @@ -142,6 +153,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; DELETE FROM pg_dist_placement DELETE FROM pg_dist_shard DROP TABLE IF EXISTS public.mx_test_table CASCADE + DROP TABLE IF EXISTS public.single_shard_tbl CASCADE GRANT CREATE ON SCHEMA public TO PUBLIC; GRANT CREATE ON SCHEMA public TO pg_database_owner; GRANT USAGE ON SCHEMA public TO PUBLIC; @@ -151,13 +163,16 @@ SELECT unnest(activate_node_snapshot()) order by 1; RESET ROLE SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') SELECT citus_internal_add_partition_metadata ('public.mx_test_table'::regclass, 'h', 'col_1', 2, 's') + SELECT citus_internal_add_partition_metadata ('public.single_shard_tbl'::regclass, 'n', NULL, 3, 's') SELECT pg_catalog.worker_drop_sequence_dependency('public.mx_test_table'); + SELECT pg_catalog.worker_drop_sequence_dependency('public.single_shard_tbl'); SELECT pg_catalog.worker_drop_sequence_dependency(logicalrelid::regclass::text) FROM pg_dist_partition SELECT pg_catalog.worker_record_sequence_dependency('public.mx_test_table_col_3_seq'::regclass,'public.mx_test_table'::regclass,'col_3') SELECT worker_apply_sequence_command ('CREATE SEQUENCE IF NOT EXISTS public.mx_test_table_col_3_seq AS bigint INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1 NO CYCLE','bigint') SELECT worker_apply_sequence_command ('CREATE SEQUENCE IF NOT EXISTS public.user_defined_seq AS bigint INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1 NO CYCLE','bigint') SELECT worker_create_or_alter_role('postgres', 'CREATE ROLE postgres SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN REPLICATION BYPASSRLS CONNECTION LIMIT 0 PASSWORD ''md5c53670dddfc3bb4b5675c7872bc2249a'' VALID UNTIL ''2052-05-05 00:00:00-07''', 'ALTER ROLE postgres SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN REPLICATION BYPASSRLS CONNECTION LIMIT 0 PASSWORD ''md5c53670dddfc3bb4b5675c7872bc2249a'' VALID UNTIL ''2052-05-05 00:00:00-07''') SELECT worker_create_truncate_trigger('public.mx_test_table') + SELECT worker_create_truncate_trigger('public.single_shard_tbl') SET ROLE pg_database_owner SET ROLE pg_database_owner SET citus.enable_ddl_propagation TO 'off' @@ -169,16 +184,22 @@ SELECT unnest(activate_node_snapshot()) order by 1; UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 1 UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 1 WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (2, 8, 1, 'integer'::regtype, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) + WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (3, 1, 2, 0, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('database', ARRAY['regression']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('role', ARRAY['postgres']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('schema', ARRAY['public']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('sequence', ARRAY['public', 'mx_test_table_col_3_seq']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('sequence', ARRAY['public', 'user_defined_seq']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('table', ARRAY['public', 'mx_test_table']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; + WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('table', ARRAY['public', 'single_shard_tbl']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310000, 0, 1, 100000), (1310001, 0, 2, 100001), (1310002, 0, 1, 100002), (1310003, 0, 2, 100003), (1310004, 0, 1, 100004), (1310005, 0, 2, 100005), (1310006, 0, 1, 100006), (1310007, 0, 2, 100007)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; + WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310008, 0, 2, 100008)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.mx_test_table'::regclass, 1310000, 't'::"char", '-2147483648', '-1610612737'), ('public.mx_test_table'::regclass, 1310001, 't'::"char", '-1610612736', '-1073741825'), ('public.mx_test_table'::regclass, 1310002, 't'::"char", '-1073741824', '-536870913'), ('public.mx_test_table'::regclass, 1310003, 't'::"char", '-536870912', '-1'), ('public.mx_test_table'::regclass, 1310004, 't'::"char", '0', '536870911'), ('public.mx_test_table'::regclass, 1310005, 't'::"char", '536870912', '1073741823'), ('public.mx_test_table'::regclass, 1310006, 't'::"char", '1073741824', '1610612735'), ('public.mx_test_table'::regclass, 1310007, 't'::"char", '1610612736', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; -(51 rows) + WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.single_shard_tbl'::regclass, 1310008, 't'::"char", NULL, NULL)) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; +(61 rows) +-- Drop single shard table +DROP TABLE single_shard_tbl; -- Show that CREATE INDEX commands are included in the activate node snapshot CREATE INDEX mx_index ON mx_test_table(col_2); SELECT unnest(activate_node_snapshot()) order by 1; @@ -739,6 +760,14 @@ SELECT create_distributed_table('mx_query_test', 'a'); (1 row) +CREATE TABLE single_shard_tbl(a int); +SELECT create_distributed_table('single_shard_tbl', null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO single_shard_tbl VALUES (1); SELECT repmodel FROM pg_dist_partition WHERE logicalrelid='mx_query_test'::regclass; repmodel --------------------------------------------------------------------- @@ -763,6 +792,13 @@ SELECT * FROM mx_query_test ORDER BY a; INSERT INTO mx_query_test VALUES (6, 'six', 36); UPDATE mx_query_test SET c = 25 WHERE a = 5; +SELECT * FROM single_shard_tbl ORDER BY a; + a +--------------------------------------------------------------------- + 1 +(1 row) + +INSERT INTO single_shard_tbl VALUES (2); \c - - - :master_port SELECT * FROM mx_query_test ORDER BY a; a | b | c @@ -775,8 +811,16 @@ SELECT * FROM mx_query_test ORDER BY a; 6 | six | 36 (6 rows) +SELECT * FROM single_shard_tbl ORDER BY a; + a +--------------------------------------------------------------------- + 1 + 2 +(2 rows) + \c - - - :master_port DROP TABLE mx_query_test; +DROP TABLE single_shard_tbl; -- Check that stop_metadata_sync_to_node function sets hasmetadata of the node to false \c - - - :master_port SELECT start_metadata_sync_to_node('localhost', :worker_1_port); @@ -902,16 +946,16 @@ ORDER BY logicalrelid::text, shardid; logicalrelid | shardid | nodename | nodeport --------------------------------------------------------------------- - mx_test_schema_1.mx_table_1 | 1310020 | localhost | 57637 - mx_test_schema_1.mx_table_1 | 1310021 | localhost | 57638 mx_test_schema_1.mx_table_1 | 1310022 | localhost | 57637 mx_test_schema_1.mx_table_1 | 1310023 | localhost | 57638 mx_test_schema_1.mx_table_1 | 1310024 | localhost | 57637 - mx_test_schema_2.mx_table_2 | 1310025 | localhost | 57637 - mx_test_schema_2.mx_table_2 | 1310026 | localhost | 57638 + mx_test_schema_1.mx_table_1 | 1310025 | localhost | 57638 + mx_test_schema_1.mx_table_1 | 1310026 | localhost | 57637 mx_test_schema_2.mx_table_2 | 1310027 | localhost | 57637 mx_test_schema_2.mx_table_2 | 1310028 | localhost | 57638 mx_test_schema_2.mx_table_2 | 1310029 | localhost | 57637 + mx_test_schema_2.mx_table_2 | 1310030 | localhost | 57638 + mx_test_schema_2.mx_table_2 | 1310031 | localhost | 57637 (10 rows) -- Check that metadata of MX tables exist on the metadata worker @@ -953,16 +997,16 @@ ORDER BY logicalrelid::text, shardid; logicalrelid | shardid | nodename | nodeport --------------------------------------------------------------------- - mx_test_schema_1.mx_table_1 | 1310020 | localhost | 57637 - mx_test_schema_1.mx_table_1 | 1310021 | localhost | 57638 mx_test_schema_1.mx_table_1 | 1310022 | localhost | 57637 mx_test_schema_1.mx_table_1 | 1310023 | localhost | 57638 mx_test_schema_1.mx_table_1 | 1310024 | localhost | 57637 - mx_test_schema_2.mx_table_2 | 1310025 | localhost | 57637 - mx_test_schema_2.mx_table_2 | 1310026 | localhost | 57638 + mx_test_schema_1.mx_table_1 | 1310025 | localhost | 57638 + mx_test_schema_1.mx_table_1 | 1310026 | localhost | 57637 mx_test_schema_2.mx_table_2 | 1310027 | localhost | 57637 mx_test_schema_2.mx_table_2 | 1310028 | localhost | 57638 mx_test_schema_2.mx_table_2 | 1310029 | localhost | 57637 + mx_test_schema_2.mx_table_2 | 1310030 | localhost | 57638 + mx_test_schema_2.mx_table_2 | 1310031 | localhost | 57637 (10 rows) -- Check that metadata of MX tables don't exist on the non-metadata worker @@ -1576,8 +1620,8 @@ ORDER BY nodeport; logicalrelid | partmethod | repmodel | shardid | placementid | nodename | nodeport --------------------------------------------------------------------- - mx_ref | n | t | 1310072 | 100072 | localhost | 57637 - mx_ref | n | t | 1310072 | 100073 | localhost | 57638 + mx_ref | n | t | 1310074 | 100074 | localhost | 57637 + mx_ref | n | t | 1310074 | 100075 | localhost | 57638 (2 rows) SELECT shardid AS ref_table_shardid FROM pg_dist_shard WHERE logicalrelid='mx_ref'::regclass \gset @@ -1653,8 +1697,8 @@ DELETE FROM pg_dist_placement WHERE groupid = :old_worker_2_group; SELECT master_remove_node('localhost', :worker_2_port); WARNING: could not find any shard placements for shardId 1310001 -WARNING: could not find any shard placements for shardId 1310021 -WARNING: could not find any shard placements for shardId 1310026 +WARNING: could not find any shard placements for shardId 1310023 +WARNING: could not find any shard placements for shardId 1310028 master_remove_node --------------------------------------------------------------------- @@ -1672,7 +1716,7 @@ FROM pg_dist_shard NATURAL JOIN pg_dist_shard_placement WHERE logicalrelid='mx_ref'::regclass; shardid | nodename | nodeport --------------------------------------------------------------------- - 1310073 | localhost | 57637 + 1310075 | localhost | 57637 (1 row) \c - - - :worker_1_port @@ -1681,7 +1725,7 @@ FROM pg_dist_shard NATURAL JOIN pg_dist_shard_placement WHERE logicalrelid='mx_ref'::regclass; shardid | nodename | nodeport --------------------------------------------------------------------- - 1310073 | localhost | 57637 + 1310075 | localhost | 57637 (1 row) \c - - - :master_port @@ -1699,7 +1743,7 @@ WHERE logicalrelid='mx_ref'::regclass ORDER BY shardid, nodeport; shardid | nodename | nodeport --------------------------------------------------------------------- - 1310073 | localhost | 57637 + 1310075 | localhost | 57637 (1 row) \c - - - :worker_1_port @@ -1709,7 +1753,7 @@ WHERE logicalrelid='mx_ref'::regclass ORDER BY shardid, nodeport; shardid | nodename | nodeport --------------------------------------------------------------------- - 1310073 | localhost | 57637 + 1310075 | localhost | 57637 (1 row) -- Get the metadata back into a consistent state @@ -1949,8 +1993,8 @@ SELECT unnest(activate_node_snapshot()) order by 1; RESET ROLE RESET ROLE SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') - SELECT citus_internal_add_partition_metadata ('mx_test_schema_1.mx_table_1'::regclass, 'h', 'col1', 5, 's') - SELECT citus_internal_add_partition_metadata ('mx_test_schema_2.mx_table_2'::regclass, 'h', 'col1', 5, 's') + SELECT citus_internal_add_partition_metadata ('mx_test_schema_1.mx_table_1'::regclass, 'h', 'col1', 7, 's') + SELECT citus_internal_add_partition_metadata ('mx_test_schema_2.mx_table_2'::regclass, 'h', 'col1', 7, 's') SELECT citus_internal_add_partition_metadata ('mx_testing_schema.mx_test_table'::regclass, 'h', 'col_1', 2, 's') SELECT citus_internal_add_partition_metadata ('public.dist_table_1'::regclass, 'h', 'a', 10010, 's') SELECT citus_internal_add_partition_metadata ('public.mx_ref'::regclass, 'n', NULL, 10009, 't') @@ -2004,17 +2048,17 @@ SELECT unnest(activate_node_snapshot()) order by 1; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('table', ARRAY['public', 'mx_ref']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('table', ARRAY['public', 'test_table']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310000, 0, 1, 100000), (1310001, 0, 5, 100001), (1310002, 0, 1, 100002), (1310003, 0, 5, 100003), (1310004, 0, 1, 100004), (1310005, 0, 5, 100005), (1310006, 0, 1, 100006), (1310007, 0, 5, 100007)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; - WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310020, 0, 1, 100020), (1310021, 0, 5, 100021), (1310022, 0, 1, 100022), (1310023, 0, 5, 100023), (1310024, 0, 1, 100024)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; - WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310025, 0, 1, 100025), (1310026, 0, 5, 100026), (1310027, 0, 1, 100027), (1310028, 0, 5, 100028), (1310029, 0, 1, 100029)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; - WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310073, 0, 1, 100074), (1310073, 0, 5, 100075)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; - WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310074, 0, 1, 100076), (1310075, 0, 5, 100077), (1310076, 0, 1, 100078), (1310077, 0, 5, 100079)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; - WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310083, 0, 1, 100086), (1310084, 0, 5, 100087), (1310085, 0, 1, 100088), (1310086, 0, 5, 100089)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; - WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('mx_test_schema_1.mx_table_1'::regclass, 1310020, 't'::"char", '-2147483648', '-1288490190'), ('mx_test_schema_1.mx_table_1'::regclass, 1310021, 't'::"char", '-1288490189', '-429496731'), ('mx_test_schema_1.mx_table_1'::regclass, 1310022, 't'::"char", '-429496730', '429496728'), ('mx_test_schema_1.mx_table_1'::regclass, 1310023, 't'::"char", '429496729', '1288490187'), ('mx_test_schema_1.mx_table_1'::regclass, 1310024, 't'::"char", '1288490188', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; - WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('mx_test_schema_2.mx_table_2'::regclass, 1310025, 't'::"char", '-2147483648', '-1288490190'), ('mx_test_schema_2.mx_table_2'::regclass, 1310026, 't'::"char", '-1288490189', '-429496731'), ('mx_test_schema_2.mx_table_2'::regclass, 1310027, 't'::"char", '-429496730', '429496728'), ('mx_test_schema_2.mx_table_2'::regclass, 1310028, 't'::"char", '429496729', '1288490187'), ('mx_test_schema_2.mx_table_2'::regclass, 1310029, 't'::"char", '1288490188', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; + WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310022, 0, 1, 100022), (1310023, 0, 5, 100023), (1310024, 0, 1, 100024), (1310025, 0, 5, 100025), (1310026, 0, 1, 100026)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; + WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310027, 0, 1, 100027), (1310028, 0, 5, 100028), (1310029, 0, 1, 100029), (1310030, 0, 5, 100030), (1310031, 0, 1, 100031)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; + WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310075, 0, 1, 100076), (1310075, 0, 5, 100077)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; + WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310076, 0, 1, 100078), (1310077, 0, 5, 100079), (1310078, 0, 1, 100080), (1310079, 0, 5, 100081)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; + WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310085, 0, 1, 100088), (1310086, 0, 5, 100089), (1310087, 0, 1, 100090), (1310088, 0, 5, 100091)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; + WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('mx_test_schema_1.mx_table_1'::regclass, 1310022, 't'::"char", '-2147483648', '-1288490190'), ('mx_test_schema_1.mx_table_1'::regclass, 1310023, 't'::"char", '-1288490189', '-429496731'), ('mx_test_schema_1.mx_table_1'::regclass, 1310024, 't'::"char", '-429496730', '429496728'), ('mx_test_schema_1.mx_table_1'::regclass, 1310025, 't'::"char", '429496729', '1288490187'), ('mx_test_schema_1.mx_table_1'::regclass, 1310026, 't'::"char", '1288490188', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; + WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('mx_test_schema_2.mx_table_2'::regclass, 1310027, 't'::"char", '-2147483648', '-1288490190'), ('mx_test_schema_2.mx_table_2'::regclass, 1310028, 't'::"char", '-1288490189', '-429496731'), ('mx_test_schema_2.mx_table_2'::regclass, 1310029, 't'::"char", '-429496730', '429496728'), ('mx_test_schema_2.mx_table_2'::regclass, 1310030, 't'::"char", '429496729', '1288490187'), ('mx_test_schema_2.mx_table_2'::regclass, 1310031, 't'::"char", '1288490188', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('mx_testing_schema.mx_test_table'::regclass, 1310000, 't'::"char", '-2147483648', '-1610612737'), ('mx_testing_schema.mx_test_table'::regclass, 1310001, 't'::"char", '-1610612736', '-1073741825'), ('mx_testing_schema.mx_test_table'::regclass, 1310002, 't'::"char", '-1073741824', '-536870913'), ('mx_testing_schema.mx_test_table'::regclass, 1310003, 't'::"char", '-536870912', '-1'), ('mx_testing_schema.mx_test_table'::regclass, 1310004, 't'::"char", '0', '536870911'), ('mx_testing_schema.mx_test_table'::regclass, 1310005, 't'::"char", '536870912', '1073741823'), ('mx_testing_schema.mx_test_table'::regclass, 1310006, 't'::"char", '1073741824', '1610612735'), ('mx_testing_schema.mx_test_table'::regclass, 1310007, 't'::"char", '1610612736', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; - WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.dist_table_1'::regclass, 1310074, 't'::"char", '-2147483648', '-1073741825'), ('public.dist_table_1'::regclass, 1310075, 't'::"char", '-1073741824', '-1'), ('public.dist_table_1'::regclass, 1310076, 't'::"char", '0', '1073741823'), ('public.dist_table_1'::regclass, 1310077, 't'::"char", '1073741824', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; - WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.mx_ref'::regclass, 1310073, 't'::"char", NULL, NULL)) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; - WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.test_table'::regclass, 1310083, 't'::"char", '-2147483648', '-1073741825'), ('public.test_table'::regclass, 1310084, 't'::"char", '-1073741824', '-1'), ('public.test_table'::regclass, 1310085, 't'::"char", '0', '1073741823'), ('public.test_table'::regclass, 1310086, 't'::"char", '1073741824', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; + WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.dist_table_1'::regclass, 1310076, 't'::"char", '-2147483648', '-1073741825'), ('public.dist_table_1'::regclass, 1310077, 't'::"char", '-1073741824', '-1'), ('public.dist_table_1'::regclass, 1310078, 't'::"char", '0', '1073741823'), ('public.dist_table_1'::regclass, 1310079, 't'::"char", '1073741824', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; + WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.mx_ref'::regclass, 1310075, 't'::"char", NULL, NULL)) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; + WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.test_table'::regclass, 1310085, 't'::"char", '-2147483648', '-1073741825'), ('public.test_table'::regclass, 1310086, 't'::"char", '-1073741824', '-1'), ('public.test_table'::regclass, 1310087, 't'::"char", '0', '1073741823'), ('public.test_table'::regclass, 1310088, 't'::"char", '1073741824', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; (118 rows) -- shouldn't work since test_table is MX diff --git a/src/test/regress/expected/multi_metadata_sync_0.out b/src/test/regress/expected/multi_metadata_sync_0.out index 20ee39761..341a0f319 100644 --- a/src/test/regress/expected/multi_metadata_sync_0.out +++ b/src/test/regress/expected/multi_metadata_sync_0.out @@ -122,6 +122,15 @@ reset citus.shard_replication_factor; -- Set the replication model of the test table to streaming replication so that it is -- considered as an MX table UPDATE pg_dist_partition SET repmodel='s' WHERE logicalrelid='mx_test_table'::regclass; +-- add a single shard table and verify the creation commands are included in the activate node snapshot +CREATE TABLE single_shard_tbl(a int); +SELECT create_distributed_table('single_shard_tbl', null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO single_shard_tbl VALUES (1); -- Show that the created MX table is and its sequences are included in the activate node snapshot SELECT unnest(activate_node_snapshot()) order by 1; unnest @@ -131,9 +140,11 @@ SELECT unnest(activate_node_snapshot()) order by 1; ALTER SEQUENCE public.user_defined_seq OWNER TO postgres ALTER TABLE public.mx_test_table ADD CONSTRAINT mx_test_table_col_1_key UNIQUE (col_1) ALTER TABLE public.mx_test_table OWNER TO postgres + ALTER TABLE public.single_shard_tbl OWNER TO postgres CALL pg_catalog.worker_drop_all_shell_tables(true) CREATE SCHEMA IF NOT EXISTS public AUTHORIZATION postgres CREATE TABLE public.mx_test_table (col_1 integer, col_2 text NOT NULL, col_3 bigint DEFAULT nextval('public.mx_test_table_col_3_seq'::regclass) NOT NULL, col_4 bigint DEFAULT nextval('public.user_defined_seq'::regclass)) USING heap + CREATE TABLE public.single_shard_tbl (a integer) USING heap DELETE FROM pg_catalog.pg_dist_colocation DELETE FROM pg_catalog.pg_dist_object DELETE FROM pg_catalog.pg_dist_tenant_schema @@ -142,6 +153,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; DELETE FROM pg_dist_placement DELETE FROM pg_dist_shard DROP TABLE IF EXISTS public.mx_test_table CASCADE + DROP TABLE IF EXISTS public.single_shard_tbl CASCADE GRANT CREATE ON SCHEMA public TO PUBLIC; GRANT CREATE ON SCHEMA public TO postgres; GRANT USAGE ON SCHEMA public TO PUBLIC; @@ -151,13 +163,16 @@ SELECT unnest(activate_node_snapshot()) order by 1; RESET ROLE SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') SELECT citus_internal_add_partition_metadata ('public.mx_test_table'::regclass, 'h', 'col_1', 2, 's') + SELECT citus_internal_add_partition_metadata ('public.single_shard_tbl'::regclass, 'n', NULL, 3, 's') SELECT pg_catalog.worker_drop_sequence_dependency('public.mx_test_table'); + SELECT pg_catalog.worker_drop_sequence_dependency('public.single_shard_tbl'); SELECT pg_catalog.worker_drop_sequence_dependency(logicalrelid::regclass::text) FROM pg_dist_partition SELECT pg_catalog.worker_record_sequence_dependency('public.mx_test_table_col_3_seq'::regclass,'public.mx_test_table'::regclass,'col_3') SELECT worker_apply_sequence_command ('CREATE SEQUENCE IF NOT EXISTS public.mx_test_table_col_3_seq AS bigint INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1 NO CYCLE','bigint') SELECT worker_apply_sequence_command ('CREATE SEQUENCE IF NOT EXISTS public.user_defined_seq AS bigint INCREMENT BY 1 MINVALUE 1 MAXVALUE 9223372036854775807 START WITH 1 CACHE 1 NO CYCLE','bigint') SELECT worker_create_or_alter_role('postgres', 'CREATE ROLE postgres SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN REPLICATION BYPASSRLS CONNECTION LIMIT 0 PASSWORD ''md5c53670dddfc3bb4b5675c7872bc2249a'' VALID UNTIL ''2052-05-05 00:00:00-07''', 'ALTER ROLE postgres SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN REPLICATION BYPASSRLS CONNECTION LIMIT 0 PASSWORD ''md5c53670dddfc3bb4b5675c7872bc2249a'' VALID UNTIL ''2052-05-05 00:00:00-07''') SELECT worker_create_truncate_trigger('public.mx_test_table') + SELECT worker_create_truncate_trigger('public.single_shard_tbl') SET ROLE postgres SET ROLE postgres SET citus.enable_ddl_propagation TO 'off' @@ -169,16 +184,22 @@ SELECT unnest(activate_node_snapshot()) order by 1; UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 1 UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 1 WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (2, 8, 1, 'integer'::regtype, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) + WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (3, 1, 2, 0, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('database', ARRAY['regression']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('role', ARRAY['postgres']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('schema', ARRAY['public']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('sequence', ARRAY['public', 'mx_test_table_col_3_seq']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('sequence', ARRAY['public', 'user_defined_seq']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('table', ARRAY['public', 'mx_test_table']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; + WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('table', ARRAY['public', 'single_shard_tbl']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310000, 0, 1, 100000), (1310001, 0, 2, 100001), (1310002, 0, 1, 100002), (1310003, 0, 2, 100003), (1310004, 0, 1, 100004), (1310005, 0, 2, 100005), (1310006, 0, 1, 100006), (1310007, 0, 2, 100007)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; + WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310008, 0, 2, 100008)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.mx_test_table'::regclass, 1310000, 't'::"char", '-2147483648', '-1610612737'), ('public.mx_test_table'::regclass, 1310001, 't'::"char", '-1610612736', '-1073741825'), ('public.mx_test_table'::regclass, 1310002, 't'::"char", '-1073741824', '-536870913'), ('public.mx_test_table'::regclass, 1310003, 't'::"char", '-536870912', '-1'), ('public.mx_test_table'::regclass, 1310004, 't'::"char", '0', '536870911'), ('public.mx_test_table'::regclass, 1310005, 't'::"char", '536870912', '1073741823'), ('public.mx_test_table'::regclass, 1310006, 't'::"char", '1073741824', '1610612735'), ('public.mx_test_table'::regclass, 1310007, 't'::"char", '1610612736', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; -(51 rows) + WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.single_shard_tbl'::regclass, 1310008, 't'::"char", NULL, NULL)) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; +(61 rows) +-- Drop single shard table +DROP TABLE single_shard_tbl; -- Show that CREATE INDEX commands are included in the activate node snapshot CREATE INDEX mx_index ON mx_test_table(col_2); SELECT unnest(activate_node_snapshot()) order by 1; @@ -739,6 +760,14 @@ SELECT create_distributed_table('mx_query_test', 'a'); (1 row) +CREATE TABLE single_shard_tbl(a int); +SELECT create_distributed_table('single_shard_tbl', null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO single_shard_tbl VALUES (1); SELECT repmodel FROM pg_dist_partition WHERE logicalrelid='mx_query_test'::regclass; repmodel --------------------------------------------------------------------- @@ -763,6 +792,13 @@ SELECT * FROM mx_query_test ORDER BY a; INSERT INTO mx_query_test VALUES (6, 'six', 36); UPDATE mx_query_test SET c = 25 WHERE a = 5; +SELECT * FROM single_shard_tbl ORDER BY a; + a +--------------------------------------------------------------------- + 1 +(1 row) + +INSERT INTO single_shard_tbl VALUES (2); \c - - - :master_port SELECT * FROM mx_query_test ORDER BY a; a | b | c @@ -775,8 +811,16 @@ SELECT * FROM mx_query_test ORDER BY a; 6 | six | 36 (6 rows) +SELECT * FROM single_shard_tbl ORDER BY a; + a +--------------------------------------------------------------------- + 1 + 2 +(2 rows) + \c - - - :master_port DROP TABLE mx_query_test; +DROP TABLE single_shard_tbl; -- Check that stop_metadata_sync_to_node function sets hasmetadata of the node to false \c - - - :master_port SELECT start_metadata_sync_to_node('localhost', :worker_1_port); @@ -902,16 +946,16 @@ ORDER BY logicalrelid::text, shardid; logicalrelid | shardid | nodename | nodeport --------------------------------------------------------------------- - mx_test_schema_1.mx_table_1 | 1310020 | localhost | 57637 - mx_test_schema_1.mx_table_1 | 1310021 | localhost | 57638 mx_test_schema_1.mx_table_1 | 1310022 | localhost | 57637 mx_test_schema_1.mx_table_1 | 1310023 | localhost | 57638 mx_test_schema_1.mx_table_1 | 1310024 | localhost | 57637 - mx_test_schema_2.mx_table_2 | 1310025 | localhost | 57637 - mx_test_schema_2.mx_table_2 | 1310026 | localhost | 57638 + mx_test_schema_1.mx_table_1 | 1310025 | localhost | 57638 + mx_test_schema_1.mx_table_1 | 1310026 | localhost | 57637 mx_test_schema_2.mx_table_2 | 1310027 | localhost | 57637 mx_test_schema_2.mx_table_2 | 1310028 | localhost | 57638 mx_test_schema_2.mx_table_2 | 1310029 | localhost | 57637 + mx_test_schema_2.mx_table_2 | 1310030 | localhost | 57638 + mx_test_schema_2.mx_table_2 | 1310031 | localhost | 57637 (10 rows) -- Check that metadata of MX tables exist on the metadata worker @@ -953,16 +997,16 @@ ORDER BY logicalrelid::text, shardid; logicalrelid | shardid | nodename | nodeport --------------------------------------------------------------------- - mx_test_schema_1.mx_table_1 | 1310020 | localhost | 57637 - mx_test_schema_1.mx_table_1 | 1310021 | localhost | 57638 mx_test_schema_1.mx_table_1 | 1310022 | localhost | 57637 mx_test_schema_1.mx_table_1 | 1310023 | localhost | 57638 mx_test_schema_1.mx_table_1 | 1310024 | localhost | 57637 - mx_test_schema_2.mx_table_2 | 1310025 | localhost | 57637 - mx_test_schema_2.mx_table_2 | 1310026 | localhost | 57638 + mx_test_schema_1.mx_table_1 | 1310025 | localhost | 57638 + mx_test_schema_1.mx_table_1 | 1310026 | localhost | 57637 mx_test_schema_2.mx_table_2 | 1310027 | localhost | 57637 mx_test_schema_2.mx_table_2 | 1310028 | localhost | 57638 mx_test_schema_2.mx_table_2 | 1310029 | localhost | 57637 + mx_test_schema_2.mx_table_2 | 1310030 | localhost | 57638 + mx_test_schema_2.mx_table_2 | 1310031 | localhost | 57637 (10 rows) -- Check that metadata of MX tables don't exist on the non-metadata worker @@ -1576,8 +1620,8 @@ ORDER BY nodeport; logicalrelid | partmethod | repmodel | shardid | placementid | nodename | nodeport --------------------------------------------------------------------- - mx_ref | n | t | 1310072 | 100072 | localhost | 57637 - mx_ref | n | t | 1310072 | 100073 | localhost | 57638 + mx_ref | n | t | 1310074 | 100074 | localhost | 57637 + mx_ref | n | t | 1310074 | 100075 | localhost | 57638 (2 rows) SELECT shardid AS ref_table_shardid FROM pg_dist_shard WHERE logicalrelid='mx_ref'::regclass \gset @@ -1653,8 +1697,8 @@ DELETE FROM pg_dist_placement WHERE groupid = :old_worker_2_group; SELECT master_remove_node('localhost', :worker_2_port); WARNING: could not find any shard placements for shardId 1310001 -WARNING: could not find any shard placements for shardId 1310021 -WARNING: could not find any shard placements for shardId 1310026 +WARNING: could not find any shard placements for shardId 1310023 +WARNING: could not find any shard placements for shardId 1310028 master_remove_node --------------------------------------------------------------------- @@ -1672,7 +1716,7 @@ FROM pg_dist_shard NATURAL JOIN pg_dist_shard_placement WHERE logicalrelid='mx_ref'::regclass; shardid | nodename | nodeport --------------------------------------------------------------------- - 1310073 | localhost | 57637 + 1310075 | localhost | 57637 (1 row) \c - - - :worker_1_port @@ -1681,7 +1725,7 @@ FROM pg_dist_shard NATURAL JOIN pg_dist_shard_placement WHERE logicalrelid='mx_ref'::regclass; shardid | nodename | nodeport --------------------------------------------------------------------- - 1310073 | localhost | 57637 + 1310075 | localhost | 57637 (1 row) \c - - - :master_port @@ -1699,7 +1743,7 @@ WHERE logicalrelid='mx_ref'::regclass ORDER BY shardid, nodeport; shardid | nodename | nodeport --------------------------------------------------------------------- - 1310073 | localhost | 57637 + 1310075 | localhost | 57637 (1 row) \c - - - :worker_1_port @@ -1709,7 +1753,7 @@ WHERE logicalrelid='mx_ref'::regclass ORDER BY shardid, nodeport; shardid | nodename | nodeport --------------------------------------------------------------------- - 1310073 | localhost | 57637 + 1310075 | localhost | 57637 (1 row) -- Get the metadata back into a consistent state @@ -1949,8 +1993,8 @@ SELECT unnest(activate_node_snapshot()) order by 1; RESET ROLE RESET ROLE SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') - SELECT citus_internal_add_partition_metadata ('mx_test_schema_1.mx_table_1'::regclass, 'h', 'col1', 5, 's') - SELECT citus_internal_add_partition_metadata ('mx_test_schema_2.mx_table_2'::regclass, 'h', 'col1', 5, 's') + SELECT citus_internal_add_partition_metadata ('mx_test_schema_1.mx_table_1'::regclass, 'h', 'col1', 7, 's') + SELECT citus_internal_add_partition_metadata ('mx_test_schema_2.mx_table_2'::regclass, 'h', 'col1', 7, 's') SELECT citus_internal_add_partition_metadata ('mx_testing_schema.mx_test_table'::regclass, 'h', 'col_1', 2, 's') SELECT citus_internal_add_partition_metadata ('public.dist_table_1'::regclass, 'h', 'a', 10010, 's') SELECT citus_internal_add_partition_metadata ('public.mx_ref'::regclass, 'n', NULL, 10009, 't') @@ -2004,17 +2048,17 @@ SELECT unnest(activate_node_snapshot()) order by 1; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('table', ARRAY['public', 'mx_ref']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('table', ARRAY['public', 'test_table']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310000, 0, 1, 100000), (1310001, 0, 5, 100001), (1310002, 0, 1, 100002), (1310003, 0, 5, 100003), (1310004, 0, 1, 100004), (1310005, 0, 5, 100005), (1310006, 0, 1, 100006), (1310007, 0, 5, 100007)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; - WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310020, 0, 1, 100020), (1310021, 0, 5, 100021), (1310022, 0, 1, 100022), (1310023, 0, 5, 100023), (1310024, 0, 1, 100024)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; - WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310025, 0, 1, 100025), (1310026, 0, 5, 100026), (1310027, 0, 1, 100027), (1310028, 0, 5, 100028), (1310029, 0, 1, 100029)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; - WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310073, 0, 1, 100074), (1310073, 0, 5, 100075)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; - WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310074, 0, 1, 100076), (1310075, 0, 5, 100077), (1310076, 0, 1, 100078), (1310077, 0, 5, 100079)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; - WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310083, 0, 1, 100086), (1310084, 0, 5, 100087), (1310085, 0, 1, 100088), (1310086, 0, 5, 100089)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; - WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('mx_test_schema_1.mx_table_1'::regclass, 1310020, 't'::"char", '-2147483648', '-1288490190'), ('mx_test_schema_1.mx_table_1'::regclass, 1310021, 't'::"char", '-1288490189', '-429496731'), ('mx_test_schema_1.mx_table_1'::regclass, 1310022, 't'::"char", '-429496730', '429496728'), ('mx_test_schema_1.mx_table_1'::regclass, 1310023, 't'::"char", '429496729', '1288490187'), ('mx_test_schema_1.mx_table_1'::regclass, 1310024, 't'::"char", '1288490188', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; - WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('mx_test_schema_2.mx_table_2'::regclass, 1310025, 't'::"char", '-2147483648', '-1288490190'), ('mx_test_schema_2.mx_table_2'::regclass, 1310026, 't'::"char", '-1288490189', '-429496731'), ('mx_test_schema_2.mx_table_2'::regclass, 1310027, 't'::"char", '-429496730', '429496728'), ('mx_test_schema_2.mx_table_2'::regclass, 1310028, 't'::"char", '429496729', '1288490187'), ('mx_test_schema_2.mx_table_2'::regclass, 1310029, 't'::"char", '1288490188', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; + WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310022, 0, 1, 100022), (1310023, 0, 5, 100023), (1310024, 0, 1, 100024), (1310025, 0, 5, 100025), (1310026, 0, 1, 100026)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; + WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310027, 0, 1, 100027), (1310028, 0, 5, 100028), (1310029, 0, 1, 100029), (1310030, 0, 5, 100030), (1310031, 0, 1, 100031)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; + WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310075, 0, 1, 100076), (1310075, 0, 5, 100077)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; + WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310076, 0, 1, 100078), (1310077, 0, 5, 100079), (1310078, 0, 1, 100080), (1310079, 0, 5, 100081)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; + WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310085, 0, 1, 100088), (1310086, 0, 5, 100089), (1310087, 0, 1, 100090), (1310088, 0, 5, 100091)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; + WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('mx_test_schema_1.mx_table_1'::regclass, 1310022, 't'::"char", '-2147483648', '-1288490190'), ('mx_test_schema_1.mx_table_1'::regclass, 1310023, 't'::"char", '-1288490189', '-429496731'), ('mx_test_schema_1.mx_table_1'::regclass, 1310024, 't'::"char", '-429496730', '429496728'), ('mx_test_schema_1.mx_table_1'::regclass, 1310025, 't'::"char", '429496729', '1288490187'), ('mx_test_schema_1.mx_table_1'::regclass, 1310026, 't'::"char", '1288490188', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; + WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('mx_test_schema_2.mx_table_2'::regclass, 1310027, 't'::"char", '-2147483648', '-1288490190'), ('mx_test_schema_2.mx_table_2'::regclass, 1310028, 't'::"char", '-1288490189', '-429496731'), ('mx_test_schema_2.mx_table_2'::regclass, 1310029, 't'::"char", '-429496730', '429496728'), ('mx_test_schema_2.mx_table_2'::regclass, 1310030, 't'::"char", '429496729', '1288490187'), ('mx_test_schema_2.mx_table_2'::regclass, 1310031, 't'::"char", '1288490188', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('mx_testing_schema.mx_test_table'::regclass, 1310000, 't'::"char", '-2147483648', '-1610612737'), ('mx_testing_schema.mx_test_table'::regclass, 1310001, 't'::"char", '-1610612736', '-1073741825'), ('mx_testing_schema.mx_test_table'::regclass, 1310002, 't'::"char", '-1073741824', '-536870913'), ('mx_testing_schema.mx_test_table'::regclass, 1310003, 't'::"char", '-536870912', '-1'), ('mx_testing_schema.mx_test_table'::regclass, 1310004, 't'::"char", '0', '536870911'), ('mx_testing_schema.mx_test_table'::regclass, 1310005, 't'::"char", '536870912', '1073741823'), ('mx_testing_schema.mx_test_table'::regclass, 1310006, 't'::"char", '1073741824', '1610612735'), ('mx_testing_schema.mx_test_table'::regclass, 1310007, 't'::"char", '1610612736', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; - WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.dist_table_1'::regclass, 1310074, 't'::"char", '-2147483648', '-1073741825'), ('public.dist_table_1'::regclass, 1310075, 't'::"char", '-1073741824', '-1'), ('public.dist_table_1'::regclass, 1310076, 't'::"char", '0', '1073741823'), ('public.dist_table_1'::regclass, 1310077, 't'::"char", '1073741824', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; - WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.mx_ref'::regclass, 1310073, 't'::"char", NULL, NULL)) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; - WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.test_table'::regclass, 1310083, 't'::"char", '-2147483648', '-1073741825'), ('public.test_table'::regclass, 1310084, 't'::"char", '-1073741824', '-1'), ('public.test_table'::regclass, 1310085, 't'::"char", '0', '1073741823'), ('public.test_table'::regclass, 1310086, 't'::"char", '1073741824', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; + WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.dist_table_1'::regclass, 1310076, 't'::"char", '-2147483648', '-1073741825'), ('public.dist_table_1'::regclass, 1310077, 't'::"char", '-1073741824', '-1'), ('public.dist_table_1'::regclass, 1310078, 't'::"char", '0', '1073741823'), ('public.dist_table_1'::regclass, 1310079, 't'::"char", '1073741824', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; + WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.mx_ref'::regclass, 1310075, 't'::"char", NULL, NULL)) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; + WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('public.test_table'::regclass, 1310085, 't'::"char", '-2147483648', '-1073741825'), ('public.test_table'::regclass, 1310086, 't'::"char", '-1073741824', '-1'), ('public.test_table'::regclass, 1310087, 't'::"char", '0', '1073741823'), ('public.test_table'::regclass, 1310088, 't'::"char", '1073741824', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; (118 rows) -- shouldn't work since test_table is MX diff --git a/src/test/regress/expected/multi_mx_function_call_delegation.out b/src/test/regress/expected/multi_mx_function_call_delegation.out index be758a1d1..9f6aecfa2 100644 --- a/src/test/regress/expected/multi_mx_function_call_delegation.out +++ b/src/test/regress/expected/multi_mx_function_call_delegation.out @@ -40,6 +40,14 @@ select create_distributed_table('mx_call_dist_table_bigint', 'id'); (1 row) insert into mx_call_dist_table_bigint values (1,1),(1,2),(2,2),(3,3),(3,4); +create table mx_call_dist_table_single_shard(id int, val int); +select create_distributed_table('mx_call_dist_table_single_shard', null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +insert into mx_call_dist_table_single_shard values (2,7),(1,8),(2,8),(1,8),(2,8); create table mx_call_dist_table_ref(id int, val int); select create_reference_table('mx_call_dist_table_ref'); create_reference_table @@ -348,6 +356,20 @@ DEBUG: pushing down the function call 28 (1 row) +-- We support colocating with single shard tables +select colocate_proc_with_table('mx_call_func', 'mx_call_dist_table_single_shard'::regclass, 1); + colocate_proc_with_table +--------------------------------------------------------------------- + +(1 row) + +select mx_call_func(2, 0); +DEBUG: pushing down the function call + mx_call_func +--------------------------------------------------------------------- + 28 +(1 row) + -- We don't currently support colocating with replicated tables select colocate_proc_with_table('mx_call_func', 'mx_call_dist_table_replica'::regclass, 1); colocate_proc_with_table @@ -803,4 +825,4 @@ SET search_path TO multi_mx_function_call_delegation, public; RESET client_min_messages; \set VERBOSITY terse DROP SCHEMA multi_mx_function_call_delegation CASCADE; -NOTICE: drop cascades to 16 other objects +NOTICE: drop cascades to 17 other objects diff --git a/src/test/regress/expected/multi_mx_function_call_delegation_0.out b/src/test/regress/expected/multi_mx_function_call_delegation_0.out index ddf867067..b4c57920d 100644 --- a/src/test/regress/expected/multi_mx_function_call_delegation_0.out +++ b/src/test/regress/expected/multi_mx_function_call_delegation_0.out @@ -40,6 +40,14 @@ select create_distributed_table('mx_call_dist_table_bigint', 'id'); (1 row) insert into mx_call_dist_table_bigint values (1,1),(1,2),(2,2),(3,3),(3,4); +create table mx_call_dist_table_single_shard(id int, val int); +select create_distributed_table('mx_call_dist_table_single_shard', null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +insert into mx_call_dist_table_single_shard values (2,7),(1,8),(2,8),(1,8),(2,8); create table mx_call_dist_table_ref(id int, val int); select create_reference_table('mx_call_dist_table_ref'); create_reference_table @@ -348,6 +356,20 @@ DEBUG: pushing down the function call 28 (1 row) +-- We support colocating with single shard tables +select colocate_proc_with_table('mx_call_func', 'mx_call_dist_table_single_shard'::regclass, 1); + colocate_proc_with_table +--------------------------------------------------------------------- + +(1 row) + +select mx_call_func(2, 0); +DEBUG: pushing down the function call + mx_call_func +--------------------------------------------------------------------- + 28 +(1 row) + -- We don't currently support colocating with replicated tables select colocate_proc_with_table('mx_call_func', 'mx_call_dist_table_replica'::regclass, 1); colocate_proc_with_table @@ -803,4 +825,4 @@ SET search_path TO multi_mx_function_call_delegation, public; RESET client_min_messages; \set VERBOSITY terse DROP SCHEMA multi_mx_function_call_delegation CASCADE; -NOTICE: drop cascades to 16 other objects +NOTICE: drop cascades to 17 other objects diff --git a/src/test/regress/expected/single_shard_table_udfs.out b/src/test/regress/expected/single_shard_table_udfs.out new file mode 100644 index 000000000..34392f342 --- /dev/null +++ b/src/test/regress/expected/single_shard_table_udfs.out @@ -0,0 +1,240 @@ +CREATE SCHEMA null_dist_key_udfs; +SET search_path TO null_dist_key_udfs; +SET citus.next_shard_id TO 1820000; +SET citus.shard_count TO 32; +SET citus.shard_replication_factor TO 1; +ALTER SEQUENCE pg_catalog.pg_dist_colocationid_seq RESTART 198000; +SET client_min_messages TO ERROR; +SELECT 1 FROM citus_add_node('localhost', :master_port, groupid=>0); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +RESET client_min_messages; +-- test some other udf's with single shard tables +CREATE TABLE null_dist_key_table(a int); +SELECT create_distributed_table('null_dist_key_table', null, colocate_with=>'none', distribution_type=>null); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT truncate_local_data_after_distributing_table('null_dist_key_table'); + truncate_local_data_after_distributing_table +--------------------------------------------------------------------- + +(1 row) + +-- should work -- +-- insert some data & create an index for table size udf's +INSERT INTO null_dist_key_table VALUES (1), (2), (3); +CREATE INDEX null_dist_key_idx ON null_dist_key_table(a); +SELECT citus_table_size('null_dist_key_table'); + citus_table_size +--------------------------------------------------------------------- + 8192 +(1 row) + +SELECT citus_total_relation_size('null_dist_key_table'); + citus_total_relation_size +--------------------------------------------------------------------- + 24576 +(1 row) + +SELECT citus_relation_size('null_dist_key_table'); + citus_relation_size +--------------------------------------------------------------------- + 8192 +(1 row) + +SELECT * FROM pg_catalog.citus_shard_sizes() WHERE table_name LIKE '%null_dist_key_table%'; + table_name | size +--------------------------------------------------------------------- + null_dist_key_udfs.null_dist_key_table_1820000 | 24576 +(1 row) + +BEGIN; + SELECT lock_relation_if_exists('null_dist_key_table', 'ACCESS SHARE'); + lock_relation_if_exists +--------------------------------------------------------------------- + t +(1 row) + + SELECT count(*) FROM pg_locks where relation='null_dist_key_table'::regclass; + count +--------------------------------------------------------------------- + 1 +(1 row) + +COMMIT; +SELECT partmethod, repmodel FROM pg_dist_partition WHERE logicalrelid = 'null_dist_key_table'::regclass; + partmethod | repmodel +--------------------------------------------------------------------- + n | s +(1 row) + +SELECT master_get_table_ddl_events('null_dist_key_table'); + master_get_table_ddl_events +--------------------------------------------------------------------- + CREATE TABLE null_dist_key_udfs.null_dist_key_table (a integer) USING heap + ALTER TABLE null_dist_key_udfs.null_dist_key_table OWNER TO postgres + CREATE INDEX null_dist_key_idx ON null_dist_key_udfs.null_dist_key_table USING btree (a) +(3 rows) + +SELECT column_to_column_name(logicalrelid, partkey) +FROM pg_dist_partition WHERE logicalrelid = 'null_dist_key_table'::regclass; + column_to_column_name +--------------------------------------------------------------------- + +(1 row) + +SELECT column_name_to_column('null_dist_key_table', 'a'); + column_name_to_column +--------------------------------------------------------------------- + {VAR :varno 1 :varattno 1 :vartype 23 :vartypmod -1 :varcollid 0 :varlevelsup 0 :varnosyn 1 :varattnosyn 1 :location -1} +(1 row) + +SELECT master_update_shard_statistics(shardid) +FROM (SELECT shardid FROM pg_dist_shard WHERE logicalrelid='null_dist_key_table'::regclass) as shardid; + master_update_shard_statistics +--------------------------------------------------------------------- + 8192 +(1 row) + +SELECT truncate_local_data_after_distributing_table('null_dist_key_table'); + truncate_local_data_after_distributing_table +--------------------------------------------------------------------- + +(1 row) + +-- should return a single element array that only includes its own shard id +SELECT shardid=unnest(get_colocated_shard_array(shardid)) +FROM (SELECT shardid FROM pg_dist_shard WHERE logicalrelid='null_dist_key_table'::regclass) as shardid; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +BEGIN; + SELECT master_remove_partition_metadata('null_dist_key_table'::regclass::oid, 'null_dist_key_udfs', 'null_dist_key_table'); + master_remove_partition_metadata +--------------------------------------------------------------------- + +(1 row) + + -- should print 0 + select count(*) from pg_dist_partition where logicalrelid='null_dist_key_table'::regclass; + count +--------------------------------------------------------------------- + 0 +(1 row) + +ROLLBACK; +-- should fail -- +SELECT update_distributed_table_colocation('null_dist_key_table', colocate_with => 'none'); +ERROR: relation null_dist_key_table should be a hash distributed table +SELECT master_create_empty_shard('null_dist_key_table'); +ERROR: relation "null_dist_key_table" is a single shard table +DETAIL: We currently don't support creating shards on single shard tables +-- return true +SELECT citus_table_is_visible('null_dist_key_table'::regclass::oid); + citus_table_is_visible +--------------------------------------------------------------------- + t +(1 row) + +-- return false +SELECT relation_is_a_known_shard('null_dist_key_table'); + relation_is_a_known_shard +--------------------------------------------------------------------- + f +(1 row) + +-- return | false | true | +SELECT citus_table_is_visible(tableName::regclass::oid), relation_is_a_known_shard(tableName::regclass) +FROM (SELECT tableName FROM pg_catalog.pg_tables WHERE tablename LIKE 'null_dist_key_table%') as tableName; + citus_table_is_visible | relation_is_a_known_shard +--------------------------------------------------------------------- + t | f +(1 row) + +-- should fail, maybe support in the future +SELECT create_reference_table('null_dist_key_table'); +ERROR: table "null_dist_key_table" is already distributed +SELECT create_distributed_table('null_dist_key_table', 'a'); +ERROR: table "null_dist_key_table" is already distributed +SELECT create_distributed_table_concurrently('null_dist_key_table', 'a'); +ERROR: table "null_dist_key_table" is already distributed +SELECT citus_add_local_table_to_metadata('null_dist_key_table'); +ERROR: table "null_dist_key_table" is already distributed +-- test altering distribution column, fails for single shard tables +SELECT alter_distributed_table('null_dist_key_table', distribution_column := 'a'); +ERROR: relation null_dist_key_table should be a hash distributed table +-- test altering shard count, fails for single shard tables +SELECT alter_distributed_table('null_dist_key_table', shard_count := 6); +ERROR: relation null_dist_key_table should be a hash distributed table +-- test shard splitting udf, fails for single shard tables +SELECT nodeid AS worker_1_node FROM pg_dist_node WHERE nodeport=:worker_1_port \gset +SELECT nodeid AS worker_2_node FROM pg_dist_node WHERE nodeport=:worker_2_port \gset +SELECT citus_split_shard_by_split_points( + 1820000, + ARRAY['-1073741826'], + ARRAY[:worker_1_node, :worker_2_node], + 'block_writes'); +ERROR: Cannot split shard as operation is only supported for hash distributed tables. +SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE '%null_dist_key_table%'; + colocationid +--------------------------------------------------------------------- + 198000 +(1 row) + +-- test alter_table_set_access_method and verify it doesn't change the colocation id +SELECT alter_table_set_access_method('null_dist_key_table', 'columnar'); +NOTICE: creating a new table for null_dist_key_udfs.null_dist_key_table +NOTICE: moving the data of null_dist_key_udfs.null_dist_key_table +NOTICE: dropping the old null_dist_key_udfs.null_dist_key_table +NOTICE: renaming the new table to null_dist_key_udfs.null_dist_key_table + alter_table_set_access_method +--------------------------------------------------------------------- + +(1 row) + +SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE '%null_dist_key_table%'; + colocationid +--------------------------------------------------------------------- + 198000 +(1 row) + +-- undistribute +SELECT undistribute_table('null_dist_key_table'); +NOTICE: creating a new table for null_dist_key_udfs.null_dist_key_table +NOTICE: moving the data of null_dist_key_udfs.null_dist_key_table +NOTICE: dropping the old null_dist_key_udfs.null_dist_key_table +NOTICE: renaming the new table to null_dist_key_udfs.null_dist_key_table + undistribute_table +--------------------------------------------------------------------- + +(1 row) + +-- verify that the metadata is gone +SELECT COUNT(*) = 0 FROM pg_dist_partition WHERE logicalrelid::text LIKE '%null_dist_key_table%'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SELECT COUNT(*) = 0 FROM pg_dist_placement WHERE shardid IN (SELECT shardid FROM pg_dist_shard WHERE logicalrelid::text LIKE '%null_dist_key_table%'); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SELECT COUNT(*) = 0 FROM pg_dist_shard WHERE logicalrelid::text LIKE '%null_dist_key_table%'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +DROP SCHEMA null_dist_key_udfs CASCADE; +NOTICE: drop cascades to table null_dist_key_table diff --git a/src/test/regress/expected/view_propagation.out b/src/test/regress/expected/view_propagation.out index 6ff1ab5a3..d3d5bdb7b 100644 --- a/src/test/regress/expected/view_propagation.out +++ b/src/test/regress/expected/view_propagation.out @@ -1,6 +1,7 @@ -- Tests to check propagation of all view commands CREATE SCHEMA view_prop_schema; SET search_path to view_prop_schema; +SET citus.next_shard_id TO 1420195; -- Check creating views depending on different types of tables -- and from multiple schemas -- Check the most basic one @@ -411,7 +412,7 @@ DROP TABLE view_table_2 CASCADE; NOTICE: drop cascades to 2 other objects DETAIL: drop cascades to view prop_view_2 drop cascades to constraint f_key_for_local_table on table view_table_3 -NOTICE: drop cascades to constraint f_key_for_local_table_1410200 on table view_prop_schema.view_table_3_1410200 +NOTICE: drop cascades to constraint f_key_for_local_table_1420200 on table view_prop_schema.view_table_3_1420200 CONTEXT: SQL statement "SELECT citus_drop_all_shards(v_obj.objid, v_obj.schema_name, v_obj.object_name, drop_shards_metadata_only := false)" PL/pgSQL function citus_drop_trigger() line XX at PERFORM NOTICE: removing table view_prop_schema.view_table_3 from metadata as it is not connected to any reference tables via foreign keys @@ -935,5 +936,7 @@ DETAIL: "view vv3" circularly depends itself, resolve circular dependency first RESET citus.enable_unsupported_feature_messages; RESET citus.enforce_object_restrictions_for_local_objects; SET client_min_messages TO ERROR; +DROP TABLE public.parent_1, public.employees CASCADE; DROP SCHEMA view_prop_schema_inner CASCADE; DROP SCHEMA view_prop_schema, axx CASCADE; +DROP ROLE view_creation_user, alter_view_user, grant_view_user; diff --git a/src/test/regress/multi_1_schedule b/src/test/regress/multi_1_schedule index e5f24910c..d358321b3 100644 --- a/src/test/regress/multi_1_schedule +++ b/src/test/regress/multi_1_schedule @@ -33,6 +33,7 @@ test: ref_citus_local_fkeys test: alter_database_owner test: distributed_triggers test: create_single_shard_table +test: single_shard_table_udfs test: schema_based_sharding test: multi_test_catalog_views diff --git a/src/test/regress/sql/alter_distributed_table.sql b/src/test/regress/sql/alter_distributed_table.sql index 4b86fa5ef..348aba7b1 100644 --- a/src/test/regress/sql/alter_distributed_table.sql +++ b/src/test/regress/sql/alter_distributed_table.sql @@ -386,6 +386,11 @@ CREATE TABLE reference_table (a INT); SELECT create_reference_table('reference_table'); SELECT alter_distributed_table('dist_table', colocate_with:='reference_table'); +-- test colocating with single shard table +CREATE TABLE single_shard_table (a INT); +SELECT create_distributed_table('single_shard_table', null); +SELECT alter_distributed_table('dist_table', colocate_with:='single_shard_table'); + -- test append table CREATE TABLE append_table (a INT); SELECT create_distributed_table('append_table', 'a', 'append'); diff --git a/src/test/regress/sql/distributed_functions.sql b/src/test/regress/sql/distributed_functions.sql index b155cf986..18198a217 100644 --- a/src/test/regress/sql/distributed_functions.sql +++ b/src/test/regress/sql/distributed_functions.sql @@ -419,6 +419,11 @@ FROM pg_dist_partition, pg_catalog.pg_dist_object as objects WHERE pg_dist_partition.logicalrelid = 'replicated_table_func_test_4'::regclass AND objects.objid = 'eq_with_param_names(macaddr, macaddr)'::regprocedure; +-- a function cannot be colocated with a single shard distributed table when a distribution column is provided +SELECT create_distributed_table('replicated_table_func_test_3', null); +SELECT create_distributed_function('eq_with_param_names(macaddr, macaddr)', 'val1', colocate_with:='replicated_table_func_test_3'); +SELECT undistribute_table('replicated_table_func_test_3'); + -- a function cannot be colocated with a reference table when a distribution column is provided SELECT create_reference_table('replicated_table_func_test_3'); SELECT create_distributed_function('eq_with_param_names(macaddr, macaddr)', 'val1', colocate_with:='replicated_table_func_test_3'); @@ -704,6 +709,7 @@ TRUNCATE pg_dist_node; SET client_min_messages TO ERROR; DROP USER functionuser; +DROP ROLE r1; SELECT 1 FROM run_command_on_workers($$DROP USER functionuser$$); -- sync metadata again diff --git a/src/test/regress/sql/function_propagation.sql b/src/test/regress/sql/function_propagation.sql index eca10beb5..cd718d17a 100644 --- a/src/test/regress/sql/function_propagation.sql +++ b/src/test/regress/sql/function_propagation.sql @@ -565,6 +565,7 @@ BEGIN; SELECT pg_identify_object_as_address(classid, objid, objsubid) from pg_catalog.pg_dist_object where objid = 'function_propagation_schema.func_in_transaction_for_local_table'::regproc::oid; CREATE TABLE citus_local_table_to_test_func(l1 int DEFAULT func_in_transaction_for_local_table()); + SET LOCAL client_min_messages TO WARNING; SELECT 1 FROM master_add_node('localhost', :master_port, groupid => 0); SELECT citus_add_local_table_to_metadata('citus_local_table_to_test_func'); @@ -717,6 +718,9 @@ SELECT create_distributed_table('tbl_to_colocate', 'a'); -- first test colocating function with a ref table CREATE TABLE tbl_to_colocate_ref (a int); SELECT create_reference_table('tbl_to_colocate_ref'); +-- test colocating function with single shard table +CREATE TABLE single_shard_tbl (a int); +SELECT create_distributed_table('single_shard_tbl', null); CREATE FUNCTION func_to_colocate (a int) returns int as $$select 1;$$ language sql; -- see the empty pg_dist_object entries @@ -727,6 +731,11 @@ SELECT create_distributed_function('func_to_colocate(int)', colocate_with:='tbl_ -- see the pg_dist_object entry SELECT distribution_argument_index, colocationid, force_delegation FROM pg_catalog.pg_dist_object WHERE objid = 'func_to_colocate'::regproc; +-- colocate the function with single shard table table +SELECT create_distributed_function('func_to_colocate(int)', colocate_with:='single_shard_tbl'); +-- see the pg_dist_object entry +SELECT distribution_argument_index, colocationid, force_delegation FROM pg_catalog.pg_dist_object WHERE objid = 'func_to_colocate'::regproc; + -- convert to non-delegated SELECT create_distributed_function('func_to_colocate(int)'); -- show that the pg_dist_object fields are gone diff --git a/src/test/regress/sql/multi_metadata_sync.sql b/src/test/regress/sql/multi_metadata_sync.sql index 0b9d46fe2..b03843edc 100644 --- a/src/test/regress/sql/multi_metadata_sync.sql +++ b/src/test/regress/sql/multi_metadata_sync.sql @@ -62,9 +62,17 @@ reset citus.shard_replication_factor; -- considered as an MX table UPDATE pg_dist_partition SET repmodel='s' WHERE logicalrelid='mx_test_table'::regclass; +-- add a single shard table and verify the creation commands are included in the activate node snapshot +CREATE TABLE single_shard_tbl(a int); +SELECT create_distributed_table('single_shard_tbl', null); +INSERT INTO single_shard_tbl VALUES (1); + -- Show that the created MX table is and its sequences are included in the activate node snapshot SELECT unnest(activate_node_snapshot()) order by 1; +-- Drop single shard table +DROP TABLE single_shard_tbl; + -- Show that CREATE INDEX commands are included in the activate node snapshot CREATE INDEX mx_index ON mx_test_table(col_2); SELECT unnest(activate_node_snapshot()) order by 1; @@ -187,6 +195,10 @@ SELECT 1 FROM citus_activate_node('localhost', :worker_1_port); CREATE TABLE mx_query_test (a int, b text, c int); SELECT create_distributed_table('mx_query_test', 'a'); +CREATE TABLE single_shard_tbl(a int); +SELECT create_distributed_table('single_shard_tbl', null); +INSERT INTO single_shard_tbl VALUES (1); + SELECT repmodel FROM pg_dist_partition WHERE logicalrelid='mx_query_test'::regclass; INSERT INTO mx_query_test VALUES (1, 'one', 1); @@ -200,11 +212,16 @@ SELECT * FROM mx_query_test ORDER BY a; INSERT INTO mx_query_test VALUES (6, 'six', 36); UPDATE mx_query_test SET c = 25 WHERE a = 5; +SELECT * FROM single_shard_tbl ORDER BY a; +INSERT INTO single_shard_tbl VALUES (2); + \c - - - :master_port SELECT * FROM mx_query_test ORDER BY a; +SELECT * FROM single_shard_tbl ORDER BY a; \c - - - :master_port DROP TABLE mx_query_test; +DROP TABLE single_shard_tbl; -- Check that stop_metadata_sync_to_node function sets hasmetadata of the node to false \c - - - :master_port diff --git a/src/test/regress/sql/multi_mx_function_call_delegation.sql b/src/test/regress/sql/multi_mx_function_call_delegation.sql index e4eeaebc2..84b96648b 100644 --- a/src/test/regress/sql/multi_mx_function_call_delegation.sql +++ b/src/test/regress/sql/multi_mx_function_call_delegation.sql @@ -28,6 +28,10 @@ create table mx_call_dist_table_bigint(id bigint, val bigint); select create_distributed_table('mx_call_dist_table_bigint', 'id'); insert into mx_call_dist_table_bigint values (1,1),(1,2),(2,2),(3,3),(3,4); +create table mx_call_dist_table_single_shard(id int, val int); +select create_distributed_table('mx_call_dist_table_single_shard', null); +insert into mx_call_dist_table_single_shard values (2,7),(1,8),(2,8),(1,8),(2,8); + create table mx_call_dist_table_ref(id int, val int); select create_reference_table('mx_call_dist_table_ref'); insert into mx_call_dist_table_ref values (2,7),(1,8),(2,8),(1,8),(2,8); @@ -157,6 +161,10 @@ select mx_call_func(2, 0); select colocate_proc_with_table('mx_call_func', 'mx_call_dist_table_ref'::regclass, 1); select mx_call_func(2, 0); +-- We support colocating with single shard tables +select colocate_proc_with_table('mx_call_func', 'mx_call_dist_table_single_shard'::regclass, 1); +select mx_call_func(2, 0); + -- We don't currently support colocating with replicated tables select colocate_proc_with_table('mx_call_func', 'mx_call_dist_table_replica'::regclass, 1); select mx_call_func(2, 0); diff --git a/src/test/regress/sql/single_shard_table_udfs.sql b/src/test/regress/sql/single_shard_table_udfs.sql new file mode 100644 index 000000000..65264e962 --- /dev/null +++ b/src/test/regress/sql/single_shard_table_udfs.sql @@ -0,0 +1,105 @@ +CREATE SCHEMA null_dist_key_udfs; +SET search_path TO null_dist_key_udfs; + +SET citus.next_shard_id TO 1820000; +SET citus.shard_count TO 32; +SET citus.shard_replication_factor TO 1; +ALTER SEQUENCE pg_catalog.pg_dist_colocationid_seq RESTART 198000; +SET client_min_messages TO ERROR; +SELECT 1 FROM citus_add_node('localhost', :master_port, groupid=>0); +RESET client_min_messages; +-- test some other udf's with single shard tables +CREATE TABLE null_dist_key_table(a int); +SELECT create_distributed_table('null_dist_key_table', null, colocate_with=>'none', distribution_type=>null); + +SELECT truncate_local_data_after_distributing_table('null_dist_key_table'); + +-- should work -- +-- insert some data & create an index for table size udf's +INSERT INTO null_dist_key_table VALUES (1), (2), (3); +CREATE INDEX null_dist_key_idx ON null_dist_key_table(a); + +SELECT citus_table_size('null_dist_key_table'); +SELECT citus_total_relation_size('null_dist_key_table'); +SELECT citus_relation_size('null_dist_key_table'); +SELECT * FROM pg_catalog.citus_shard_sizes() WHERE table_name LIKE '%null_dist_key_table%'; + +BEGIN; + SELECT lock_relation_if_exists('null_dist_key_table', 'ACCESS SHARE'); + SELECT count(*) FROM pg_locks where relation='null_dist_key_table'::regclass; +COMMIT; + +SELECT partmethod, repmodel FROM pg_dist_partition WHERE logicalrelid = 'null_dist_key_table'::regclass; +SELECT master_get_table_ddl_events('null_dist_key_table'); + +SELECT column_to_column_name(logicalrelid, partkey) +FROM pg_dist_partition WHERE logicalrelid = 'null_dist_key_table'::regclass; + +SELECT column_name_to_column('null_dist_key_table', 'a'); + +SELECT master_update_shard_statistics(shardid) +FROM (SELECT shardid FROM pg_dist_shard WHERE logicalrelid='null_dist_key_table'::regclass) as shardid; + +SELECT truncate_local_data_after_distributing_table('null_dist_key_table'); + +-- should return a single element array that only includes its own shard id +SELECT shardid=unnest(get_colocated_shard_array(shardid)) +FROM (SELECT shardid FROM pg_dist_shard WHERE logicalrelid='null_dist_key_table'::regclass) as shardid; + +BEGIN; + SELECT master_remove_partition_metadata('null_dist_key_table'::regclass::oid, 'null_dist_key_udfs', 'null_dist_key_table'); + + -- should print 0 + select count(*) from pg_dist_partition where logicalrelid='null_dist_key_table'::regclass; +ROLLBACK; + +-- should fail -- + +SELECT update_distributed_table_colocation('null_dist_key_table', colocate_with => 'none'); + +SELECT master_create_empty_shard('null_dist_key_table'); + +-- return true +SELECT citus_table_is_visible('null_dist_key_table'::regclass::oid); + +-- return false +SELECT relation_is_a_known_shard('null_dist_key_table'); + +-- return | false | true | +SELECT citus_table_is_visible(tableName::regclass::oid), relation_is_a_known_shard(tableName::regclass) +FROM (SELECT tableName FROM pg_catalog.pg_tables WHERE tablename LIKE 'null_dist_key_table%') as tableName; + +-- should fail, maybe support in the future +SELECT create_reference_table('null_dist_key_table'); +SELECT create_distributed_table('null_dist_key_table', 'a'); +SELECT create_distributed_table_concurrently('null_dist_key_table', 'a'); +SELECT citus_add_local_table_to_metadata('null_dist_key_table'); + +-- test altering distribution column, fails for single shard tables +SELECT alter_distributed_table('null_dist_key_table', distribution_column := 'a'); + +-- test altering shard count, fails for single shard tables +SELECT alter_distributed_table('null_dist_key_table', shard_count := 6); + +-- test shard splitting udf, fails for single shard tables +SELECT nodeid AS worker_1_node FROM pg_dist_node WHERE nodeport=:worker_1_port \gset +SELECT nodeid AS worker_2_node FROM pg_dist_node WHERE nodeport=:worker_2_port \gset +SELECT citus_split_shard_by_split_points( + 1820000, + ARRAY['-1073741826'], + ARRAY[:worker_1_node, :worker_2_node], + 'block_writes'); + +SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE '%null_dist_key_table%'; +-- test alter_table_set_access_method and verify it doesn't change the colocation id +SELECT alter_table_set_access_method('null_dist_key_table', 'columnar'); +SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE '%null_dist_key_table%'; + +-- undistribute +SELECT undistribute_table('null_dist_key_table'); +-- verify that the metadata is gone +SELECT COUNT(*) = 0 FROM pg_dist_partition WHERE logicalrelid::text LIKE '%null_dist_key_table%'; +SELECT COUNT(*) = 0 FROM pg_dist_placement WHERE shardid IN (SELECT shardid FROM pg_dist_shard WHERE logicalrelid::text LIKE '%null_dist_key_table%'); +SELECT COUNT(*) = 0 FROM pg_dist_shard WHERE logicalrelid::text LIKE '%null_dist_key_table%'; + +DROP SCHEMA null_dist_key_udfs CASCADE; diff --git a/src/test/regress/sql/view_propagation.sql b/src/test/regress/sql/view_propagation.sql index 97e314375..44bbbf7b0 100644 --- a/src/test/regress/sql/view_propagation.sql +++ b/src/test/regress/sql/view_propagation.sql @@ -1,6 +1,7 @@ -- Tests to check propagation of all view commands CREATE SCHEMA view_prop_schema; SET search_path to view_prop_schema; +SET citus.next_shard_id TO 1420195; -- Check creating views depending on different types of tables -- and from multiple schemas @@ -542,5 +543,7 @@ CREATE OR REPLACE VIEW vv3 as SELECT * FROM vv4; RESET citus.enable_unsupported_feature_messages; RESET citus.enforce_object_restrictions_for_local_objects; SET client_min_messages TO ERROR; +DROP TABLE public.parent_1, public.employees CASCADE; DROP SCHEMA view_prop_schema_inner CASCADE; DROP SCHEMA view_prop_schema, axx CASCADE; +DROP ROLE view_creation_user, alter_view_user, grant_view_user; From 321fcfcdb5cefc02feb0f13e760211511c7d91c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20Ozan=20Akg=C3=BCl?= Date: Mon, 29 May 2023 11:47:50 +0300 Subject: [PATCH 052/118] Add Support for Single Shard Tables in update_distributed_table_colocation (#6924) Adds Support for Single Shard Tables in `update_distributed_table_colocation`. This PR changes checks that make sure tables should be hash distributed table to hash or single shard distributed tables. --- .../distributed/metadata/metadata_sync.c | 6 +- .../distributed/metadata/metadata_utility.c | 18 +++ .../distributed/utils/colocation_utils.c | 6 +- src/include/distributed/metadata_utility.h | 1 + src/test/regress/bin/normalize.sed | 1 + .../regress/expected/citus_local_tables.out | 2 +- .../expected/metadata_sync_helpers.out | 2 +- .../expected/multi_colocation_utils.out | 16 +-- .../expected/multi_reference_table.out | 4 +- .../expected/single_shard_table_udfs.out | 126 +++++++++++++++++- src/test/regress/multi_1_schedule | 1 + .../regress/sql/single_shard_table_udfs.sql | 70 +++++++++- 12 files changed, 228 insertions(+), 25 deletions(-) diff --git a/src/backend/distributed/metadata/metadata_sync.c b/src/backend/distributed/metadata/metadata_sync.c index 558644a82..2dfc59eeb 100644 --- a/src/backend/distributed/metadata/metadata_sync.c +++ b/src/backend/distributed/metadata/metadata_sync.c @@ -3708,12 +3708,14 @@ citus_internal_update_relation_colocation(PG_FUNCTION_ARGS) "entry in pg_dist_partition.", get_rel_name(relationId)))); } - else if (partitionMethod != DISTRIBUTE_BY_HASH) + else if (!IsCitusTableType(relationId, HASH_DISTRIBUTED) && + !IsCitusTableType(relationId, SINGLE_SHARD_DISTRIBUTED)) { /* connection from the coordinator operating on a shard */ ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("Updating colocation ids are only allowed for hash " - "distributed tables: %c", partitionMethod))); + "and single shard distributed tables: %c", + partitionMethod))); } int count = 1; diff --git a/src/backend/distributed/metadata/metadata_utility.c b/src/backend/distributed/metadata/metadata_utility.c index 9fd4290ba..00aceaf04 100644 --- a/src/backend/distributed/metadata/metadata_utility.c +++ b/src/backend/distributed/metadata/metadata_utility.c @@ -2288,6 +2288,24 @@ EnsureHashDistributedTable(Oid relationId) } +/* + * EnsureHashOrSingleShardDistributedTable error out if the given relation is not a + * hash or single shard distributed table with the given message. + */ +void +EnsureHashOrSingleShardDistributedTable(Oid relationId) +{ + if (!IsCitusTableType(relationId, HASH_DISTRIBUTED) && + !IsCitusTableType(relationId, SINGLE_SHARD_DISTRIBUTED)) + { + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("relation %s should be a " + "hash or single shard distributed table", + get_rel_name(relationId)))); + } +} + + /* * EnsureSuperUser check that the current user is a superuser and errors out if not. */ diff --git a/src/backend/distributed/utils/colocation_utils.c b/src/backend/distributed/utils/colocation_utils.c index 015eb13df..8f8dade6b 100644 --- a/src/backend/distributed/utils/colocation_utils.c +++ b/src/backend/distributed/utils/colocation_utils.c @@ -120,7 +120,7 @@ update_distributed_table_colocation(PG_FUNCTION_ARGS) char *colocateWithTableName = text_to_cstring(colocateWithTableNameText); if (IsColocateWithNone(colocateWithTableName)) { - EnsureHashDistributedTable(targetRelationId); + EnsureHashOrSingleShardDistributedTable(targetRelationId); BreakColocation(targetRelationId); } else @@ -264,8 +264,8 @@ MarkTablesColocated(Oid sourceRelationId, Oid targetRelationId) "other tables"))); } - EnsureHashDistributedTable(sourceRelationId); - EnsureHashDistributedTable(targetRelationId); + EnsureHashOrSingleShardDistributedTable(sourceRelationId); + EnsureHashOrSingleShardDistributedTable(targetRelationId); CheckReplicationModel(sourceRelationId, targetRelationId); CheckDistributionColumnType(sourceRelationId, targetRelationId); diff --git a/src/include/distributed/metadata_utility.h b/src/include/distributed/metadata_utility.h index ae9350770..70fa32324 100644 --- a/src/include/distributed/metadata_utility.h +++ b/src/include/distributed/metadata_utility.h @@ -381,6 +381,7 @@ extern char * TableOwner(Oid relationId); extern void EnsureTablePermissions(Oid relationId, AclMode mode); extern void EnsureTableOwner(Oid relationId); extern void EnsureHashDistributedTable(Oid relationId); +extern void EnsureHashOrSingleShardDistributedTable(Oid relationId); extern void EnsureFunctionOwner(Oid functionId); extern void EnsureSuperUser(void); extern void ErrorIfTableIsACatalogTable(Relation relation); diff --git a/src/test/regress/bin/normalize.sed b/src/test/regress/bin/normalize.sed index 56e40ac51..2fe05ce21 100644 --- a/src/test/regress/bin/normalize.sed +++ b/src/test/regress/bin/normalize.sed @@ -11,6 +11,7 @@ s/localhost:[0-9]+/localhost:xxxxx/g s/ port=[0-9]+ / port=xxxxx /g s/placement [0-9]+/placement xxxxx/g s/shard [0-9]+/shard xxxxx/g +s/Shard [0-9]+/Shard xxxxx/g s/assigned task [0-9]+ to node/assigned task to node/ s/node group [12] (but|does)/node group \1/ diff --git a/src/test/regress/expected/citus_local_tables.out b/src/test/regress/expected/citus_local_tables.out index 0ece7ba91..cfa6410ba 100644 --- a/src/test/regress/expected/citus_local_tables.out +++ b/src/test/regress/expected/citus_local_tables.out @@ -693,7 +693,7 @@ BEGIN; ROLLBACK; -- should fail -- SELECT update_distributed_table_colocation('citus_local_table_4', colocate_with => 'none'); -ERROR: relation citus_local_table_4 should be a hash distributed table +ERROR: relation citus_local_table_4 should be a hash or single shard distributed table SELECT master_create_empty_shard('citus_local_table_4'); ERROR: relation "citus_local_table_4" is a local table -- return true diff --git a/src/test/regress/expected/metadata_sync_helpers.out b/src/test/regress/expected/metadata_sync_helpers.out index f745b0fe2..62268b32f 100644 --- a/src/test/regress/expected/metadata_sync_helpers.out +++ b/src/test/regress/expected/metadata_sync_helpers.out @@ -1317,7 +1317,7 @@ BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED; UPDATE pg_dist_partition SET partmethod = 'a' WHERE logicalrelid = 'test_2'::regclass; SELECT citus_internal_update_relation_colocation('test_2'::regclass, 251); -ERROR: Updating colocation ids are only allowed for hash distributed tables: a +ERROR: Updating colocation ids are only allowed for hash and single shard distributed tables: a ROLLBACK; -- colocated hash distributed table should have the same dist key columns CREATE TABLE test_5(int_col int, text_col text); diff --git a/src/test/regress/expected/multi_colocation_utils.out b/src/test/regress/expected/multi_colocation_utils.out index 219327dc1..7415983a2 100644 --- a/src/test/regress/expected/multi_colocation_utils.out +++ b/src/test/regress/expected/multi_colocation_utils.out @@ -868,9 +868,9 @@ ERROR: cannot colocate tables table1_groupd and table1_groupb DETAIL: Shard counts don't match for table1_groupd and table1_groupb. SELECT update_distributed_table_colocation('table1_groupB', colocate_with => 'table1_groupE'); ERROR: cannot colocate tables table1_groupe and table1_groupb -DETAIL: Shard 1300050 of table1_groupe and shard xxxxx of table1_groupb have different number of shard placements. +DETAIL: Shard xxxxx of table1_groupe and shard xxxxx of table1_groupb have different number of shard placements. SELECT update_distributed_table_colocation('table1_groupB', colocate_with => 'table1_groupF'); -ERROR: relation table1_groupf should be a hash distributed table +ERROR: relation table1_groupf should be a hash or single shard distributed table SELECT update_distributed_table_colocation('table1_groupB', colocate_with => 'table1_groupD'); ERROR: cannot colocate tables table1_groupd and table1_groupb DETAIL: Shard counts don't match for table1_groupd and table1_groupb. @@ -1369,9 +1369,9 @@ SELECT tables_colocated('d2', 'none'); -- make sure reference and local tables return an error. SELECT update_distributed_table_colocation('ref', colocate_with => 'none'); -ERROR: relation ref should be a hash distributed table +ERROR: relation ref should be a hash or single shard distributed table SELECT update_distributed_table_colocation('local_table', colocate_with => 'none'); -ERROR: relation local_table should be a hash distributed table +ERROR: relation local_table should be a hash or single shard distributed table -- make sure that different types cannot be colocated SELECT update_distributed_table_colocation('different_d1', colocate_with => 'd1'); ERROR: cannot colocate tables d1 and different_d1 @@ -1381,13 +1381,13 @@ ERROR: cannot colocate tables different_d1 and d1 DETAIL: Distribution column types don't match for different_d1 and d1. -- make sure that append distributed tables cannot be colocated SELECT update_distributed_table_colocation('append_table', colocate_with => 'd1'); -ERROR: relation append_table should be a hash distributed table +ERROR: relation append_table should be a hash or single shard distributed table SELECT update_distributed_table_colocation('d1', colocate_with => 'append_table'); -ERROR: relation append_table should be a hash distributed table +ERROR: relation append_table should be a hash or single shard distributed table SELECT update_distributed_table_colocation('range_table', colocate_with => 'd1'); -ERROR: relation range_table should be a hash distributed table +ERROR: relation range_table should be a hash or single shard distributed table SELECT update_distributed_table_colocation('d1', colocate_with => 'range_table'); -ERROR: relation range_table should be a hash distributed table +ERROR: relation range_table should be a hash or single shard distributed table -- drop tables to clean test space DROP TABLE table1_groupb; DROP TABLE table2_groupb; diff --git a/src/test/regress/expected/multi_reference_table.out b/src/test/regress/expected/multi_reference_table.out index fd7c9bb50..75a9c3b64 100644 --- a/src/test/regress/expected/multi_reference_table.out +++ b/src/test/regress/expected/multi_reference_table.out @@ -1203,9 +1203,9 @@ RESET client_min_messages; -- some tests for mark_tables_colocated -- should error out SELECT update_distributed_table_colocation('colocated_table_test_2', colocate_with => 'reference_table_test'); -ERROR: relation reference_table_test should be a hash distributed table +ERROR: relation reference_table_test should be a hash or single shard distributed table SELECT update_distributed_table_colocation('reference_table_test', colocate_with => 'reference_table_test_fifth'); -ERROR: relation reference_table_test_fifth should be a hash distributed table +ERROR: relation reference_table_test_fifth should be a hash or single shard distributed table -- ensure that reference tables on -- different queries works as expected CREATE SCHEMA reference_schema; diff --git a/src/test/regress/expected/single_shard_table_udfs.out b/src/test/regress/expected/single_shard_table_udfs.out index 34392f342..c1a63de89 100644 --- a/src/test/regress/expected/single_shard_table_udfs.out +++ b/src/test/regress/expected/single_shard_table_udfs.out @@ -131,9 +131,6 @@ BEGIN; (1 row) ROLLBACK; --- should fail -- -SELECT update_distributed_table_colocation('null_dist_key_table', colocate_with => 'none'); -ERROR: relation null_dist_key_table should be a hash distributed table SELECT master_create_empty_shard('null_dist_key_table'); ERROR: relation "null_dist_key_table" is a single shard table DETAIL: We currently don't support creating shards on single shard tables @@ -236,5 +233,126 @@ SELECT COUNT(*) = 0 FROM pg_dist_shard WHERE logicalrelid::text LIKE '%null_dist t (1 row) +-- test update_distributed_table_colocation +CREATE TABLE update_col_1 (a INT); +CREATE TABLE update_col_2 (a INT); +CREATE TABLE update_col_3 (a INT); +-- create colocated single shard distributed tables, so the shards will be +-- in the same worker node +SELECT create_distributed_table ('update_col_1', null, colocate_with:='none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table ('update_col_2', null, colocate_with:='update_col_1'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- now create a third single shard distributed table that is not colocated, +-- with the new colocation id the new table will be in the other worker node +SELECT create_distributed_table ('update_col_3', null, colocate_with:='none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- make sure nodes are correct +SELECT c1.nodeport = c2.nodeport AS same_node +FROM citus_shards c1, citus_shards c2, pg_dist_node p1, pg_dist_node p2 +WHERE c1.table_name::text = 'update_col_1' AND c2.table_name::text = 'update_col_2' AND + p1.nodeport = c1.nodeport AND p2.nodeport = c2.nodeport AND + p1.noderole = 'primary' AND p2.noderole = 'primary'; + same_node +--------------------------------------------------------------------- + t +(1 row) + +SELECT c1.nodeport = c2.nodeport AS same_node +FROM citus_shards c1, citus_shards c2, pg_dist_node p1, pg_dist_node p2 +WHERE c1.table_name::text = 'update_col_1' AND c2.table_name::text = 'update_col_3' AND + p1.nodeport = c1.nodeport AND p2.nodeport = c2.nodeport AND + p1.noderole = 'primary' AND p2.noderole = 'primary'; + same_node +--------------------------------------------------------------------- + f +(1 row) + +-- and the update_col_1 and update_col_2 are colocated +SELECT c1.colocation_id = c2.colocation_id AS colocated +FROM public.citus_tables c1, public.citus_tables c2 +WHERE c1.table_name::text = 'update_col_1' AND c2.table_name::text = 'update_col_2'; + colocated +--------------------------------------------------------------------- + t +(1 row) + +-- break the colocation +SELECT update_distributed_table_colocation('update_col_2', colocate_with:='none'); + update_distributed_table_colocation +--------------------------------------------------------------------- + +(1 row) + +SELECT c1.colocation_id = c2.colocation_id AS colocated +FROM public.citus_tables c1, public.citus_tables c2 +WHERE c1.table_name::text = 'update_col_1' AND c2.table_name::text = 'update_col_2'; + colocated +--------------------------------------------------------------------- + f +(1 row) + +-- re-colocate, the shards were already in the same node +SELECT update_distributed_table_colocation('update_col_2', colocate_with:='update_col_1'); + update_distributed_table_colocation +--------------------------------------------------------------------- + +(1 row) + +SELECT c1.colocation_id = c2.colocation_id AS colocated +FROM public.citus_tables c1, public.citus_tables c2 +WHERE c1.table_name::text = 'update_col_1' AND c2.table_name::text = 'update_col_2'; + colocated +--------------------------------------------------------------------- + t +(1 row) + +-- update_col_1 and update_col_3 are not colocated, because they are not in the some node +SELECT c1.colocation_id = c2.colocation_id AS colocated +FROM public.citus_tables c1, public.citus_tables c2 +WHERE c1.table_name::text = 'update_col_1' AND c2.table_name::text = 'update_col_3'; + colocated +--------------------------------------------------------------------- + f +(1 row) + +-- they should not be able to be colocated since the shards are in different nodes +SELECT update_distributed_table_colocation('update_col_3', colocate_with:='update_col_1'); +ERROR: cannot colocate tables update_col_1 and update_col_3 +DETAIL: Shard xxxxx of update_col_1 and shard xxxxx of update_col_3 are not colocated. +SELECT c1.colocation_id = c2.colocation_id AS colocated +FROM public.citus_tables c1, public.citus_tables c2 +WHERE c1.table_name::text = 'update_col_1' AND c2.table_name::text = 'update_col_3'; + colocated +--------------------------------------------------------------------- + f +(1 row) + +-- hash distributed and single shard distributed tables cannot be colocated +CREATE TABLE update_col_4 (a INT); +SELECT create_distributed_table ('update_col_4', 'a', colocate_with:='none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT update_distributed_table_colocation('update_col_1', colocate_with:='update_col_4'); +ERROR: cannot colocate tables update_col_4 and update_col_1 +DETAIL: Distribution column types don't match for update_col_4 and update_col_1. +SELECT update_distributed_table_colocation('update_col_4', colocate_with:='update_col_1'); +ERROR: cannot colocate tables update_col_1 and update_col_4 +DETAIL: Distribution column types don't match for update_col_1 and update_col_4. +SET client_min_messages TO WARNING; DROP SCHEMA null_dist_key_udfs CASCADE; -NOTICE: drop cascades to table null_dist_key_table diff --git a/src/test/regress/multi_1_schedule b/src/test/regress/multi_1_schedule index d358321b3..5a8a6b635 100644 --- a/src/test/regress/multi_1_schedule +++ b/src/test/regress/multi_1_schedule @@ -33,6 +33,7 @@ test: ref_citus_local_fkeys test: alter_database_owner test: distributed_triggers test: create_single_shard_table +# don't parallelize single_shard_table_udfs to make sure colocation ids are sequential test: single_shard_table_udfs test: schema_based_sharding diff --git a/src/test/regress/sql/single_shard_table_udfs.sql b/src/test/regress/sql/single_shard_table_udfs.sql index 65264e962..a865e8619 100644 --- a/src/test/regress/sql/single_shard_table_udfs.sql +++ b/src/test/regress/sql/single_shard_table_udfs.sql @@ -53,10 +53,6 @@ BEGIN; select count(*) from pg_dist_partition where logicalrelid='null_dist_key_table'::regclass; ROLLBACK; --- should fail -- - -SELECT update_distributed_table_colocation('null_dist_key_table', colocate_with => 'none'); - SELECT master_create_empty_shard('null_dist_key_table'); -- return true @@ -102,4 +98,70 @@ SELECT COUNT(*) = 0 FROM pg_dist_partition WHERE logicalrelid::text LIKE '%null_ SELECT COUNT(*) = 0 FROM pg_dist_placement WHERE shardid IN (SELECT shardid FROM pg_dist_shard WHERE logicalrelid::text LIKE '%null_dist_key_table%'); SELECT COUNT(*) = 0 FROM pg_dist_shard WHERE logicalrelid::text LIKE '%null_dist_key_table%'; +-- test update_distributed_table_colocation +CREATE TABLE update_col_1 (a INT); +CREATE TABLE update_col_2 (a INT); +CREATE TABLE update_col_3 (a INT); + +-- create colocated single shard distributed tables, so the shards will be +-- in the same worker node +SELECT create_distributed_table ('update_col_1', null, colocate_with:='none'); +SELECT create_distributed_table ('update_col_2', null, colocate_with:='update_col_1'); + +-- now create a third single shard distributed table that is not colocated, +-- with the new colocation id the new table will be in the other worker node +SELECT create_distributed_table ('update_col_3', null, colocate_with:='none'); + +-- make sure nodes are correct +SELECT c1.nodeport = c2.nodeport AS same_node +FROM citus_shards c1, citus_shards c2, pg_dist_node p1, pg_dist_node p2 +WHERE c1.table_name::text = 'update_col_1' AND c2.table_name::text = 'update_col_2' AND + p1.nodeport = c1.nodeport AND p2.nodeport = c2.nodeport AND + p1.noderole = 'primary' AND p2.noderole = 'primary'; + +SELECT c1.nodeport = c2.nodeport AS same_node +FROM citus_shards c1, citus_shards c2, pg_dist_node p1, pg_dist_node p2 +WHERE c1.table_name::text = 'update_col_1' AND c2.table_name::text = 'update_col_3' AND + p1.nodeport = c1.nodeport AND p2.nodeport = c2.nodeport AND + p1.noderole = 'primary' AND p2.noderole = 'primary'; + +-- and the update_col_1 and update_col_2 are colocated +SELECT c1.colocation_id = c2.colocation_id AS colocated +FROM public.citus_tables c1, public.citus_tables c2 +WHERE c1.table_name::text = 'update_col_1' AND c2.table_name::text = 'update_col_2'; + +-- break the colocation +SELECT update_distributed_table_colocation('update_col_2', colocate_with:='none'); + +SELECT c1.colocation_id = c2.colocation_id AS colocated +FROM public.citus_tables c1, public.citus_tables c2 +WHERE c1.table_name::text = 'update_col_1' AND c2.table_name::text = 'update_col_2'; + +-- re-colocate, the shards were already in the same node +SELECT update_distributed_table_colocation('update_col_2', colocate_with:='update_col_1'); + +SELECT c1.colocation_id = c2.colocation_id AS colocated +FROM public.citus_tables c1, public.citus_tables c2 +WHERE c1.table_name::text = 'update_col_1' AND c2.table_name::text = 'update_col_2'; + +-- update_col_1 and update_col_3 are not colocated, because they are not in the some node +SELECT c1.colocation_id = c2.colocation_id AS colocated +FROM public.citus_tables c1, public.citus_tables c2 +WHERE c1.table_name::text = 'update_col_1' AND c2.table_name::text = 'update_col_3'; + +-- they should not be able to be colocated since the shards are in different nodes +SELECT update_distributed_table_colocation('update_col_3', colocate_with:='update_col_1'); + +SELECT c1.colocation_id = c2.colocation_id AS colocated +FROM public.citus_tables c1, public.citus_tables c2 +WHERE c1.table_name::text = 'update_col_1' AND c2.table_name::text = 'update_col_3'; + +-- hash distributed and single shard distributed tables cannot be colocated +CREATE TABLE update_col_4 (a INT); +SELECT create_distributed_table ('update_col_4', 'a', colocate_with:='none'); + +SELECT update_distributed_table_colocation('update_col_1', colocate_with:='update_col_4'); +SELECT update_distributed_table_colocation('update_col_4', colocate_with:='update_col_1'); + +SET client_min_messages TO WARNING; DROP SCHEMA null_dist_key_udfs CASCADE; From 9d9b3817c1096b71c975dcc6a772aa77123e2992 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20Ozan=20Akg=C3=BCl?= Date: Mon, 29 May 2023 13:53:00 +0300 Subject: [PATCH 053/118] Single Shard Table Columnar UDFs Tests (#6937) Adds columnar UDF tests for single shard tables. --- .../expected/single_shard_table_udfs.out | 76 +++++++++++++++++++ .../regress/sql/single_shard_table_udfs.sql | 33 ++++++++ 2 files changed, 109 insertions(+) diff --git a/src/test/regress/expected/single_shard_table_udfs.out b/src/test/regress/expected/single_shard_table_udfs.out index c1a63de89..612110ea6 100644 --- a/src/test/regress/expected/single_shard_table_udfs.out +++ b/src/test/regress/expected/single_shard_table_udfs.out @@ -354,5 +354,81 @@ DETAIL: Distribution column types don't match for update_col_4 and update_col_1 SELECT update_distributed_table_colocation('update_col_4', colocate_with:='update_col_1'); ERROR: cannot colocate tables update_col_1 and update_col_4 DETAIL: Distribution column types don't match for update_col_1 and update_col_4. +-- test columnar UDFs +CREATE TABLE columnar_tbl (a INT) USING COLUMNAR; +SELECT create_distributed_table('columnar_tbl', NULL, colocate_with:='none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT * FROM columnar.options WHERE relation = 'columnar_tbl'::regclass; + relation | chunk_group_row_limit | stripe_row_limit | compression | compression_level +--------------------------------------------------------------------- + columnar_tbl | 10000 | 150000 | zstd | 3 +(1 row) + +SELECT alter_columnar_table_set('columnar_tbl', compression_level => 2); + alter_columnar_table_set +--------------------------------------------------------------------- + +(1 row) + +SELECT * FROM columnar.options WHERE relation = 'columnar_tbl'::regclass; + relation | chunk_group_row_limit | stripe_row_limit | compression | compression_level +--------------------------------------------------------------------- + columnar_tbl | 10000 | 150000 | zstd | 2 +(1 row) + +SELECT alter_columnar_table_reset('columnar_tbl', compression_level => true); + alter_columnar_table_reset +--------------------------------------------------------------------- + +(1 row) + +SELECT * FROM columnar.options WHERE relation = 'columnar_tbl'::regclass; + relation | chunk_group_row_limit | stripe_row_limit | compression | compression_level +--------------------------------------------------------------------- + columnar_tbl | 10000 | 150000 | zstd | 3 +(1 row) + +SELECT columnar_internal.upgrade_columnar_storage(c.oid) +FROM pg_class c, pg_am a +WHERE c.relam = a.oid AND amname = 'columnar' AND relname = 'columnar_tbl'; + upgrade_columnar_storage +--------------------------------------------------------------------- + +(1 row) + +SELECT columnar_internal.downgrade_columnar_storage(c.oid) +FROM pg_class c, pg_am a +WHERE c.relam = a.oid AND amname = 'columnar' AND relname = 'columnar_tbl'; + downgrade_columnar_storage +--------------------------------------------------------------------- + +(1 row) + +CREATE OR REPLACE FUNCTION columnar_storage_info( + rel regclass, + version_major OUT int4, + version_minor OUT int4, + storage_id OUT int8, + reserved_stripe_id OUT int8, + reserved_row_number OUT int8, + reserved_offset OUT int8) + STRICT + LANGUAGE c AS 'citus', $$columnar_storage_info$$; +SELECT version_major, version_minor, reserved_stripe_id, reserved_row_number, reserved_offset FROM columnar_storage_info('columnar_tbl'); + version_major | version_minor | reserved_stripe_id | reserved_row_number | reserved_offset +--------------------------------------------------------------------- + 2 | 0 | 1 | 1 | 16336 +(1 row) + +SELECT columnar.get_storage_id(oid) = storage_id FROM pg_class, columnar_storage_info('columnar_tbl') WHERE relname = 'columnar_tbl'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + SET client_min_messages TO WARNING; DROP SCHEMA null_dist_key_udfs CASCADE; diff --git a/src/test/regress/sql/single_shard_table_udfs.sql b/src/test/regress/sql/single_shard_table_udfs.sql index a865e8619..440bd2c70 100644 --- a/src/test/regress/sql/single_shard_table_udfs.sql +++ b/src/test/regress/sql/single_shard_table_udfs.sql @@ -163,5 +163,38 @@ SELECT create_distributed_table ('update_col_4', 'a', colocate_with:='none'); SELECT update_distributed_table_colocation('update_col_1', colocate_with:='update_col_4'); SELECT update_distributed_table_colocation('update_col_4', colocate_with:='update_col_1'); +-- test columnar UDFs +CREATE TABLE columnar_tbl (a INT) USING COLUMNAR; +SELECT create_distributed_table('columnar_tbl', NULL, colocate_with:='none'); + +SELECT * FROM columnar.options WHERE relation = 'columnar_tbl'::regclass; +SELECT alter_columnar_table_set('columnar_tbl', compression_level => 2); +SELECT * FROM columnar.options WHERE relation = 'columnar_tbl'::regclass; +SELECT alter_columnar_table_reset('columnar_tbl', compression_level => true); +SELECT * FROM columnar.options WHERE relation = 'columnar_tbl'::regclass; + +SELECT columnar_internal.upgrade_columnar_storage(c.oid) +FROM pg_class c, pg_am a +WHERE c.relam = a.oid AND amname = 'columnar' AND relname = 'columnar_tbl'; + +SELECT columnar_internal.downgrade_columnar_storage(c.oid) +FROM pg_class c, pg_am a +WHERE c.relam = a.oid AND amname = 'columnar' AND relname = 'columnar_tbl'; + +CREATE OR REPLACE FUNCTION columnar_storage_info( + rel regclass, + version_major OUT int4, + version_minor OUT int4, + storage_id OUT int8, + reserved_stripe_id OUT int8, + reserved_row_number OUT int8, + reserved_offset OUT int8) + STRICT + LANGUAGE c AS 'citus', $$columnar_storage_info$$; + +SELECT version_major, version_minor, reserved_stripe_id, reserved_row_number, reserved_offset FROM columnar_storage_info('columnar_tbl'); + +SELECT columnar.get_storage_id(oid) = storage_id FROM pg_class, columnar_storage_info('columnar_tbl') WHERE relname = 'columnar_tbl'; + SET client_min_messages TO WARNING; DROP SCHEMA null_dist_key_udfs CASCADE; From 5b54700b93ec34b614b10ca47a53b9837f14b1e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20Ozan=20Akg=C3=BCl?= Date: Mon, 29 May 2023 14:18:56 +0300 Subject: [PATCH 054/118] Single Shard Table Tests for Time Partitions (#6941) This PR adds tests for time partitions UDFs and view with single shard tables. --- .../expected/single_shard_table_udfs.out | 51 +++++++++++++++++++ .../regress/sql/single_shard_table_udfs.sql | 20 ++++++++ 2 files changed, 71 insertions(+) diff --git a/src/test/regress/expected/single_shard_table_udfs.out b/src/test/regress/expected/single_shard_table_udfs.out index 612110ea6..fb5be779d 100644 --- a/src/test/regress/expected/single_shard_table_udfs.out +++ b/src/test/regress/expected/single_shard_table_udfs.out @@ -430,5 +430,56 @@ SELECT columnar.get_storage_id(oid) = storage_id FROM pg_class, columnar_storage t (1 row) +-- test time series functions +CREATE TABLE part_tbl (a DATE) PARTITION BY RANGE (a); +CREATE TABLE part_tbl_1 PARTITION OF part_tbl FOR VALUES FROM ('2000-01-01') TO ('2010-01-01'); +CREATE TABLE part_tbl_2 PARTITION OF part_tbl FOR VALUES FROM ('2020-01-01') TO ('2030-01-01'); +SELECT create_distributed_table('part_tbl', NULL, colocate_with:='none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT * FROM time_partitions WHERE parent_table::text = 'part_tbl'; + parent_table | partition_column | partition | from_value | to_value | access_method +--------------------------------------------------------------------- + part_tbl | a | part_tbl_1 | 01-01-2000 | 01-01-2010 | heap + part_tbl | a | part_tbl_2 | 01-01-2020 | 01-01-2030 | heap +(2 rows) + +SELECT time_partition_range('part_tbl_2'); + time_partition_range +--------------------------------------------------------------------- + (01-01-2020,01-01-2030) +(1 row) + +SELECT get_missing_time_partition_ranges('part_tbl', INTERVAL '10 years', '2050-01-01', '2000-01-01'); + get_missing_time_partition_ranges +--------------------------------------------------------------------- + (part_tbl_p2010,01-01-2010,01-01-2020) + (part_tbl_p2030,01-01-2030,01-01-2040) + (part_tbl_p2040,01-01-2040,01-01-2050) +(3 rows) + +SELECT create_time_partitions('part_tbl', INTERVAL '10 years', '2050-01-01', '2000-01-01'); + create_time_partitions +--------------------------------------------------------------------- + t +(1 row) + +CALL drop_old_time_partitions('part_tbl', '2030-01-01'); +NOTICE: dropping part_tbl_1 with start time 01-01-2000 and end time 01-01-2010 +CONTEXT: PL/pgSQL function drop_old_time_partitions(regclass,timestamp with time zone) line XX at RAISE +NOTICE: dropping part_tbl_p2010 with start time 01-01-2010 and end time 01-01-2020 +CONTEXT: PL/pgSQL function drop_old_time_partitions(regclass,timestamp with time zone) line XX at RAISE +NOTICE: dropping part_tbl_2 with start time 01-01-2020 and end time 01-01-2030 +CONTEXT: PL/pgSQL function drop_old_time_partitions(regclass,timestamp with time zone) line XX at RAISE +SELECT * FROM time_partitions WHERE parent_table::text = 'part_tbl'; + parent_table | partition_column | partition | from_value | to_value | access_method +--------------------------------------------------------------------- + part_tbl | a | part_tbl_p2030 | 01-01-2030 | 01-01-2040 | heap + part_tbl | a | part_tbl_p2040 | 01-01-2040 | 01-01-2050 | heap +(2 rows) + SET client_min_messages TO WARNING; DROP SCHEMA null_dist_key_udfs CASCADE; diff --git a/src/test/regress/sql/single_shard_table_udfs.sql b/src/test/regress/sql/single_shard_table_udfs.sql index 440bd2c70..7e47c99b5 100644 --- a/src/test/regress/sql/single_shard_table_udfs.sql +++ b/src/test/regress/sql/single_shard_table_udfs.sql @@ -196,5 +196,25 @@ SELECT version_major, version_minor, reserved_stripe_id, reserved_row_number, re SELECT columnar.get_storage_id(oid) = storage_id FROM pg_class, columnar_storage_info('columnar_tbl') WHERE relname = 'columnar_tbl'; + +-- test time series functions +CREATE TABLE part_tbl (a DATE) PARTITION BY RANGE (a); +CREATE TABLE part_tbl_1 PARTITION OF part_tbl FOR VALUES FROM ('2000-01-01') TO ('2010-01-01'); +CREATE TABLE part_tbl_2 PARTITION OF part_tbl FOR VALUES FROM ('2020-01-01') TO ('2030-01-01'); + +SELECT create_distributed_table('part_tbl', NULL, colocate_with:='none'); + +SELECT * FROM time_partitions WHERE parent_table::text = 'part_tbl'; + +SELECT time_partition_range('part_tbl_2'); + +SELECT get_missing_time_partition_ranges('part_tbl', INTERVAL '10 years', '2050-01-01', '2000-01-01'); + +SELECT create_time_partitions('part_tbl', INTERVAL '10 years', '2050-01-01', '2000-01-01'); + +CALL drop_old_time_partitions('part_tbl', '2030-01-01'); + +SELECT * FROM time_partitions WHERE parent_table::text = 'part_tbl'; + SET client_min_messages TO WARNING; DROP SCHEMA null_dist_key_udfs CASCADE; From d99a5e2f62f3de0e520062c835025eeeb487447e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20Ozan=20Akg=C3=BCl?= Date: Tue, 30 May 2023 12:23:41 +0300 Subject: [PATCH 055/118] Single Shard Table Tests for Shard Lock UDFs (#6944) This PR adds single shard table tests for shard lock UDFs, `shard_lock_metadata`, `shard_lock_resources` --- src/backend/distributed/utils/resource_lock.c | 3 +- .../expected/single_shard_table_udfs.out | 76 +++++++++++++++++++ .../regress/sql/single_shard_table_udfs.sql | 41 ++++++++++ 3 files changed, 119 insertions(+), 1 deletion(-) diff --git a/src/backend/distributed/utils/resource_lock.c b/src/backend/distributed/utils/resource_lock.c index 7b8edf758..c76830c1d 100644 --- a/src/backend/distributed/utils/resource_lock.c +++ b/src/backend/distributed/utils/resource_lock.c @@ -491,7 +491,8 @@ SetLocktagForShardDistributionMetadata(int64 shardId, LOCKTAG *tag) uint32 colocationId = citusTable->colocationId; if (colocationId == INVALID_COLOCATION_ID || - !IsCitusTableTypeCacheEntry(citusTable, HASH_DISTRIBUTED)) + (!IsCitusTableTypeCacheEntry(citusTable, HASH_DISTRIBUTED) && + !IsCitusTableTypeCacheEntry(citusTable, SINGLE_SHARD_DISTRIBUTED))) { SET_LOCKTAG_SHARD_METADATA_RESOURCE(*tag, MyDatabaseId, shardId); } diff --git a/src/test/regress/expected/single_shard_table_udfs.out b/src/test/regress/expected/single_shard_table_udfs.out index fb5be779d..1ae4d19cd 100644 --- a/src/test/regress/expected/single_shard_table_udfs.out +++ b/src/test/regress/expected/single_shard_table_udfs.out @@ -481,5 +481,81 @@ SELECT * FROM time_partitions WHERE parent_table::text = 'part_tbl'; part_tbl | a | part_tbl_p2040 | 01-01-2040 | 01-01-2050 | heap (2 rows) +-- test locking shards +CREATE TABLE lock_tbl_1 (a INT); +SELECT create_distributed_table('lock_tbl_1', NULL, colocate_with:='none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE lock_tbl_2 (a INT); +SELECT create_distributed_table('lock_tbl_2', NULL, colocate_with:='none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +BEGIN; +SELECT lock_shard_metadata(3, array_agg(distinct(shardid))) +FROM citus_shards WHERE table_name::text = 'lock_tbl_1'; + lock_shard_metadata +--------------------------------------------------------------------- + +(1 row) + +SELECT lock_shard_metadata(5, array_agg(distinct(shardid))) +FROM citus_shards WHERE table_name::text LIKE 'lock\_tbl\__'; + lock_shard_metadata +--------------------------------------------------------------------- + +(1 row) + +SELECT table_name, classid, mode, granted +FROM pg_locks, public.citus_tables +WHERE + locktype = 'advisory' AND + table_name::text LIKE 'lock\_tbl\__' AND + objid = colocation_id + ORDER BY 1, 3; + table_name | classid | mode | granted +--------------------------------------------------------------------- + lock_tbl_1 | 0 | RowExclusiveLock | t + lock_tbl_1 | 0 | ShareLock | t + lock_tbl_2 | 0 | ShareLock | t +(3 rows) + +END; +BEGIN; +SELECT lock_shard_resources(3, array_agg(distinct(shardid))) +FROM citus_shards WHERE table_name::text = 'lock_tbl_1'; + lock_shard_resources +--------------------------------------------------------------------- + +(1 row) + +SELECT lock_shard_resources(5, array_agg(distinct(shardid))) +FROM citus_shards WHERE table_name::text LIKE 'lock\_tbl\__'; + lock_shard_resources +--------------------------------------------------------------------- + +(1 row) + +SELECT locktype, table_name, mode, granted +FROM pg_locks, citus_shards, pg_dist_node +WHERE + objid = shardid AND + table_name::text LIKE 'lock\_tbl\__' AND + citus_shards.nodeport = pg_dist_node.nodeport AND + noderole = 'primary' + ORDER BY 2, 3; + locktype | table_name | mode | granted +--------------------------------------------------------------------- + advisory | lock_tbl_1 | RowExclusiveLock | t + advisory | lock_tbl_1 | ShareLock | t + advisory | lock_tbl_2 | ShareLock | t +(3 rows) + +END; SET client_min_messages TO WARNING; DROP SCHEMA null_dist_key_udfs CASCADE; diff --git a/src/test/regress/sql/single_shard_table_udfs.sql b/src/test/regress/sql/single_shard_table_udfs.sql index 7e47c99b5..7aeacdf0c 100644 --- a/src/test/regress/sql/single_shard_table_udfs.sql +++ b/src/test/regress/sql/single_shard_table_udfs.sql @@ -216,5 +216,46 @@ CALL drop_old_time_partitions('part_tbl', '2030-01-01'); SELECT * FROM time_partitions WHERE parent_table::text = 'part_tbl'; +-- test locking shards +CREATE TABLE lock_tbl_1 (a INT); +SELECT create_distributed_table('lock_tbl_1', NULL, colocate_with:='none'); + +CREATE TABLE lock_tbl_2 (a INT); +SELECT create_distributed_table('lock_tbl_2', NULL, colocate_with:='none'); + +BEGIN; +SELECT lock_shard_metadata(3, array_agg(distinct(shardid))) +FROM citus_shards WHERE table_name::text = 'lock_tbl_1'; + +SELECT lock_shard_metadata(5, array_agg(distinct(shardid))) +FROM citus_shards WHERE table_name::text LIKE 'lock\_tbl\__'; + +SELECT table_name, classid, mode, granted +FROM pg_locks, public.citus_tables +WHERE + locktype = 'advisory' AND + table_name::text LIKE 'lock\_tbl\__' AND + objid = colocation_id + ORDER BY 1, 3; +END; + + +BEGIN; +SELECT lock_shard_resources(3, array_agg(distinct(shardid))) +FROM citus_shards WHERE table_name::text = 'lock_tbl_1'; + +SELECT lock_shard_resources(5, array_agg(distinct(shardid))) +FROM citus_shards WHERE table_name::text LIKE 'lock\_tbl\__'; + +SELECT locktype, table_name, mode, granted +FROM pg_locks, citus_shards, pg_dist_node +WHERE + objid = shardid AND + table_name::text LIKE 'lock\_tbl\__' AND + citus_shards.nodeport = pg_dist_node.nodeport AND + noderole = 'primary' + ORDER BY 2, 3; +END; + SET client_min_messages TO WARNING; DROP SCHEMA null_dist_key_udfs CASCADE; From f9dbe7784b734d11eb7daf1faa34b227f8c32a58 Mon Sep 17 00:00:00 2001 From: Teja Mupparti Date: Fri, 26 May 2023 11:03:45 -0700 Subject: [PATCH 056/118] This commit adds a safety-net to the issue seen in #6785. The fix for the underlying issue will be in the PR#6943 --- .../planner/multi_router_planner.c | 37 ++++++++++++++----- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/src/backend/distributed/planner/multi_router_planner.c b/src/backend/distributed/planner/multi_router_planner.c index 0c6ec9dca..47769f5bb 100644 --- a/src/backend/distributed/planner/multi_router_planner.c +++ b/src/backend/distributed/planner/multi_router_planner.c @@ -1906,17 +1906,36 @@ RouterJob(Query *originalQuery, PlannerRestrictionContext *plannerRestrictionCon { RangeTblEntry *updateOrDeleteOrMergeRTE = ExtractResultRelationRTE(originalQuery); - /* - * If all of the shards are pruned, we replace the relation RTE into - * subquery RTE that returns no results. However, this is not useful - * for UPDATE and DELETE queries. Therefore, if we detect a UPDATE or - * DELETE RTE with subquery type, we just set task list to empty and return - * the job. - */ if (updateOrDeleteOrMergeRTE->rtekind == RTE_SUBQUERY) { - job->taskList = NIL; - return job; + /* + * Not generating tasks for MERGE target relation might + * result in incorrect behavior as source rows with NOT + * MATCHED clause might qualify for insertion. + */ + if (IsMergeQuery(originalQuery)) + { + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Merge command is currently " + "unsupported with filters that " + "prunes down to zero shards"), + errhint("Avoid `WHERE false` clause or " + "any equivalent filters that " + "could prune down to zero shards"))); + } + else + { + /* + * If all of the shards are pruned, we replace the + * relation RTE into subquery RTE that returns no + * results. However, this is not useful for UPDATE + * and DELETE queries. Therefore, if we detect a + * UPDATE or DELETE RTE with subquery type, we just + * set task list to empty and return the job. + */ + job->taskList = NIL; + return job; + } } } From ee42af7ad2231eaf415a7db4b6d7b888ced7654d Mon Sep 17 00:00:00 2001 From: ahmet gedemenli Date: Tue, 30 May 2023 12:35:28 +0300 Subject: [PATCH 057/118] Add test for rebalancer with single shard tables --- .../expected/single_shard_table_udfs.out | 237 ++++++++++++++++++ .../regress/sql/single_shard_table_udfs.sql | 75 ++++++ 2 files changed, 312 insertions(+) diff --git a/src/test/regress/expected/single_shard_table_udfs.out b/src/test/regress/expected/single_shard_table_udfs.out index 1ae4d19cd..15919a137 100644 --- a/src/test/regress/expected/single_shard_table_udfs.out +++ b/src/test/regress/expected/single_shard_table_udfs.out @@ -233,6 +233,243 @@ SELECT COUNT(*) = 0 FROM pg_dist_shard WHERE logicalrelid::text LIKE '%null_dist t (1 row) +-- create 7 single shard tables, 3 of them are colocated, for testing shard moves / rebalance on them +CREATE TABLE single_shard_table_col1_1 (a INT PRIMARY KEY); +CREATE TABLE single_shard_table_col1_2 (a TEXT PRIMARY KEY); +CREATE TABLE single_shard_table_col1_3 (a TIMESTAMP PRIMARY KEY); +CREATE TABLE single_shard_table_col2_1 (a INT PRIMARY KEY); +CREATE TABLE single_shard_table_col3_1 (a INT PRIMARY KEY); +CREATE TABLE single_shard_table_col4_1 (a INT PRIMARY KEY); +CREATE TABLE single_shard_table_col5_1 (a INT PRIMARY KEY); +SELECT create_distributed_table('single_shard_table_col1_1', null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('single_shard_table_col1_2', null, colocate_with=>'single_shard_table_col1_1'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('single_shard_table_col1_3', null, colocate_with=>'single_shard_table_col1_2'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('single_shard_table_col2_1', null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('single_shard_table_col3_1', null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('single_shard_table_col4_1', null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('single_shard_table_col5_1', null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- initial status +SELECT shardid, nodeport FROM pg_dist_shard_placement WHERE shardid > 1820000 ORDER BY shardid; + shardid | nodeport +--------------------------------------------------------------------- + 1820002 | 57638 + 1820003 | 57638 + 1820004 | 57638 + 1820005 | 57637 + 1820006 | 57638 + 1820007 | 57637 + 1820008 | 57638 +(7 rows) + +-- errors out because streaming replicated +SELECT citus_copy_shard_placement(1820005, 'localhost', :worker_1_port, 'localhost', :worker_2_port); +ERROR: Table 'single_shard_table_col2_1' is streaming replicated. Shards of streaming replicated tables cannot be copied +SELECT master_copy_shard_placement(1820005, 'localhost', :worker_1_port, 'localhost', :worker_2_port); +WARNING: do_repair argument is deprecated +ERROR: Table 'single_shard_table_col2_1' is streaming replicated. Shards of streaming replicated tables cannot be copied +SELECT citus_copy_shard_placement(1820005, :worker_1_node, :worker_2_node); +ERROR: Table 'single_shard_table_col2_1' is streaming replicated. Shards of streaming replicated tables cannot be copied +-- no changes because it's already balanced +SELECT rebalance_table_shards(); + rebalance_table_shards +--------------------------------------------------------------------- + +(1 row) + +-- same placements +SELECT shardid, nodeport FROM pg_dist_shard_placement WHERE shardid > 1820000 ORDER BY shardid; + shardid | nodeport +--------------------------------------------------------------------- + 1820002 | 57638 + 1820003 | 57638 + 1820004 | 57638 + 1820005 | 57637 + 1820006 | 57638 + 1820007 | 57637 + 1820008 | 57638 +(7 rows) + +-- manually move 2 shard from 2 colocation groups to make the cluster unbalanced +SELECT citus_move_shard_placement(1820005, 'localhost', :worker_1_port, 'localhost', :worker_2_port); + citus_move_shard_placement +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_move_shard_placement(1820007, :worker_1_node, :worker_2_node); + citus_move_shard_placement +--------------------------------------------------------------------- + +(1 row) + +-- all placements are located on worker 2 +SELECT shardid, nodeport FROM pg_dist_shard_placement WHERE shardid > 1820000 ORDER BY shardid; + shardid | nodeport +--------------------------------------------------------------------- + 1820002 | 57638 + 1820003 | 57638 + 1820004 | 57638 + 1820005 | 57638 + 1820006 | 57638 + 1820007 | 57638 + 1820008 | 57638 +(7 rows) + +-- move some of them to worker 1 to balance the cluster +SELECT rebalance_table_shards(); +NOTICE: Moving shard xxxxx from localhost:xxxxx to localhost:xxxxx ... +NOTICE: Moving shard xxxxx from localhost:xxxxx to localhost:xxxxx ... + rebalance_table_shards +--------------------------------------------------------------------- + +(1 row) + +-- the final status, balanced +SELECT shardid, nodeport FROM pg_dist_shard_placement WHERE shardid > 1820000 ORDER BY shardid; + shardid | nodeport +--------------------------------------------------------------------- + 1820002 | 57637 + 1820003 | 57637 + 1820004 | 57637 + 1820005 | 57637 + 1820006 | 57638 + 1820007 | 57638 + 1820008 | 57638 +(7 rows) + +-- verify we didn't break any colocations +SELECT logicalrelid, colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE '%single_shard_table_col%' ORDER BY colocationid; + logicalrelid | colocationid +--------------------------------------------------------------------- + single_shard_table_col1_1 | 198001 + single_shard_table_col1_2 | 198001 + single_shard_table_col1_3 | 198001 + single_shard_table_col2_1 | 198002 + single_shard_table_col3_1 | 198003 + single_shard_table_col4_1 | 198004 + single_shard_table_col5_1 | 198005 +(7 rows) + +-- again, manually move 2 shard from 2 colocation groups to make the cluster unbalanced +-- consider using citus_drain_node when the issue is fixed: https://github.com/citusdata/citus/issues/6948 +SELECT citus_move_shard_placement(1820005, 'localhost', :worker_1_port, 'localhost', :worker_2_port); + citus_move_shard_placement +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_move_shard_placement(1820003, :worker_1_node, :worker_2_node); + citus_move_shard_placement +--------------------------------------------------------------------- + +(1 row) + +-- see the plan for moving 4 shards, 3 of them are in the same colocation group +SELECT * FROM get_rebalance_table_shards_plan(); + table_name | shardid | shard_size | sourcename | sourceport | targetname | targetport +--------------------------------------------------------------------- + single_shard_table_col1_1 | 1820002 | 0 | localhost | 57638 | localhost | 57637 + single_shard_table_col1_2 | 1820003 | 0 | localhost | 57638 | localhost | 57637 + single_shard_table_col1_3 | 1820004 | 0 | localhost | 57638 | localhost | 57637 + single_shard_table_col2_1 | 1820005 | 0 | localhost | 57638 | localhost | 57637 +(4 rows) + +-- move some of them to worker 1 to balance the cluster +SELECT 1 FROM citus_rebalance_start(); +NOTICE: Scheduled 2 moves as job xxx +DETAIL: Rebalance scheduled as background job +HINT: To monitor progress, run: SELECT * FROM citus_rebalance_status(); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +-- stop it +SELECT * FROM citus_rebalance_stop(); + citus_rebalance_stop +--------------------------------------------------------------------- + +(1 row) + +-- show rebalance status, see the cancelled job for two moves +SELECT state, details FROM citus_rebalance_status(); + state | details +--------------------------------------------------------------------- + cancelled | {"tasks": [], "task_state_counts": {"cancelled": 2}} +(1 row) + +-- start again +SELECT 1 FROM citus_rebalance_start(); +NOTICE: Scheduled 2 moves as job xxx +DETAIL: Rebalance scheduled as background job +HINT: To monitor progress, run: SELECT * FROM citus_rebalance_status(); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +-- show rebalance status, scheduled a job for two moves +SELECT state, details FROM citus_rebalance_status(); + state | details +--------------------------------------------------------------------- + scheduled | {"tasks": [], "task_state_counts": {"runnable": 2}} +(1 row) + +-- wait for rebalance to be completed +SELECT * FROM citus_rebalance_wait(); + citus_rebalance_wait +--------------------------------------------------------------------- + +(1 row) + +-- the final status, balanced +SELECT shardid, nodeport FROM pg_dist_shard_placement WHERE shardid > 1820000 ORDER BY shardid; + shardid | nodeport +--------------------------------------------------------------------- + 1820002 | 57637 + 1820003 | 57637 + 1820004 | 57637 + 1820005 | 57637 + 1820006 | 57638 + 1820007 | 57638 + 1820008 | 57638 +(7 rows) + -- test update_distributed_table_colocation CREATE TABLE update_col_1 (a INT); CREATE TABLE update_col_2 (a INT); diff --git a/src/test/regress/sql/single_shard_table_udfs.sql b/src/test/regress/sql/single_shard_table_udfs.sql index 7aeacdf0c..a06658701 100644 --- a/src/test/regress/sql/single_shard_table_udfs.sql +++ b/src/test/regress/sql/single_shard_table_udfs.sql @@ -98,6 +98,81 @@ SELECT COUNT(*) = 0 FROM pg_dist_partition WHERE logicalrelid::text LIKE '%null_ SELECT COUNT(*) = 0 FROM pg_dist_placement WHERE shardid IN (SELECT shardid FROM pg_dist_shard WHERE logicalrelid::text LIKE '%null_dist_key_table%'); SELECT COUNT(*) = 0 FROM pg_dist_shard WHERE logicalrelid::text LIKE '%null_dist_key_table%'; +-- create 7 single shard tables, 3 of them are colocated, for testing shard moves / rebalance on them +CREATE TABLE single_shard_table_col1_1 (a INT PRIMARY KEY); +CREATE TABLE single_shard_table_col1_2 (a TEXT PRIMARY KEY); +CREATE TABLE single_shard_table_col1_3 (a TIMESTAMP PRIMARY KEY); +CREATE TABLE single_shard_table_col2_1 (a INT PRIMARY KEY); +CREATE TABLE single_shard_table_col3_1 (a INT PRIMARY KEY); +CREATE TABLE single_shard_table_col4_1 (a INT PRIMARY KEY); +CREATE TABLE single_shard_table_col5_1 (a INT PRIMARY KEY); +SELECT create_distributed_table('single_shard_table_col1_1', null, colocate_with=>'none'); +SELECT create_distributed_table('single_shard_table_col1_2', null, colocate_with=>'single_shard_table_col1_1'); +SELECT create_distributed_table('single_shard_table_col1_3', null, colocate_with=>'single_shard_table_col1_2'); +SELECT create_distributed_table('single_shard_table_col2_1', null, colocate_with=>'none'); +SELECT create_distributed_table('single_shard_table_col3_1', null, colocate_with=>'none'); +SELECT create_distributed_table('single_shard_table_col4_1', null, colocate_with=>'none'); +SELECT create_distributed_table('single_shard_table_col5_1', null, colocate_with=>'none'); + +-- initial status +SELECT shardid, nodeport FROM pg_dist_shard_placement WHERE shardid > 1820000 ORDER BY shardid; + +-- errors out because streaming replicated +SELECT citus_copy_shard_placement(1820005, 'localhost', :worker_1_port, 'localhost', :worker_2_port); +SELECT master_copy_shard_placement(1820005, 'localhost', :worker_1_port, 'localhost', :worker_2_port); +SELECT citus_copy_shard_placement(1820005, :worker_1_node, :worker_2_node); + +-- no changes because it's already balanced +SELECT rebalance_table_shards(); + +-- same placements +SELECT shardid, nodeport FROM pg_dist_shard_placement WHERE shardid > 1820000 ORDER BY shardid; + +-- manually move 2 shard from 2 colocation groups to make the cluster unbalanced +SELECT citus_move_shard_placement(1820005, 'localhost', :worker_1_port, 'localhost', :worker_2_port); +SELECT citus_move_shard_placement(1820007, :worker_1_node, :worker_2_node); + +-- all placements are located on worker 2 +SELECT shardid, nodeport FROM pg_dist_shard_placement WHERE shardid > 1820000 ORDER BY shardid; + +-- move some of them to worker 1 to balance the cluster +SELECT rebalance_table_shards(); + +-- the final status, balanced +SELECT shardid, nodeport FROM pg_dist_shard_placement WHERE shardid > 1820000 ORDER BY shardid; + +-- verify we didn't break any colocations +SELECT logicalrelid, colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE '%single_shard_table_col%' ORDER BY colocationid; + +-- again, manually move 2 shard from 2 colocation groups to make the cluster unbalanced +-- consider using citus_drain_node when the issue is fixed: https://github.com/citusdata/citus/issues/6948 +SELECT citus_move_shard_placement(1820005, 'localhost', :worker_1_port, 'localhost', :worker_2_port); +SELECT citus_move_shard_placement(1820003, :worker_1_node, :worker_2_node); + +-- see the plan for moving 4 shards, 3 of them are in the same colocation group +SELECT * FROM get_rebalance_table_shards_plan(); + +-- move some of them to worker 1 to balance the cluster +SELECT 1 FROM citus_rebalance_start(); + +-- stop it +SELECT * FROM citus_rebalance_stop(); + +-- show rebalance status, see the cancelled job for two moves +SELECT state, details FROM citus_rebalance_status(); + +-- start again +SELECT 1 FROM citus_rebalance_start(); + +-- show rebalance status, scheduled a job for two moves +SELECT state, details FROM citus_rebalance_status(); + +-- wait for rebalance to be completed +SELECT * FROM citus_rebalance_wait(); + +-- the final status, balanced +SELECT shardid, nodeport FROM pg_dist_shard_placement WHERE shardid > 1820000 ORDER BY shardid; + -- test update_distributed_table_colocation CREATE TABLE update_col_1 (a INT); CREATE TABLE update_col_2 (a INT); From 8ace5a7af58ba44b0e8f9dfc0b9a83ee18c6cfcf Mon Sep 17 00:00:00 2001 From: ahmet gedemenli Date: Wed, 31 May 2023 13:52:18 +0300 Subject: [PATCH 058/118] Use citus_drain_node with single shard tables --- .../expected/single_shard_table_udfs.out | 44 +++++++++++-------- .../regress/sql/single_shard_table_udfs.sql | 18 +++++--- 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/src/test/regress/expected/single_shard_table_udfs.out b/src/test/regress/expected/single_shard_table_udfs.out index 15919a137..2c60a8320 100644 --- a/src/test/regress/expected/single_shard_table_udfs.out +++ b/src/test/regress/expected/single_shard_table_udfs.out @@ -385,31 +385,39 @@ SELECT logicalrelid, colocationid FROM pg_dist_partition WHERE logicalrelid::tex single_shard_table_col5_1 | 198005 (7 rows) --- again, manually move 2 shard from 2 colocation groups to make the cluster unbalanced --- consider using citus_drain_node when the issue is fixed: https://github.com/citusdata/citus/issues/6948 -SELECT citus_move_shard_placement(1820005, 'localhost', :worker_1_port, 'localhost', :worker_2_port); - citus_move_shard_placement +-- drop preexisting tables +-- we can remove the drop commands once the issue is fixed: https://github.com/citusdata/citus/issues/6948 +SET client_min_messages TO ERROR; +DROP TABLE IF EXISTS public.lineitem, public.orders, public.customer_append, public.part_append, public.supplier_single_shard, + public.events, public.users, public.lineitem_hash_part, public.lineitem_subquery, public.orders_hash_part, + public.orders_subquery, public.unlogged_table CASCADE; +DROP SCHEMA IF EXISTS with_basics, subquery_and_ctes CASCADE; +DROP TABLE IF EXISTS public.users_table, public.events_table, public.agg_results, public.agg_results_second, public.agg_results_third, public.agg_results_fourth, public.agg_results_window CASCADE; +-- drain node +SELECT citus_drain_node('localhost', :worker_2_port, 'block_writes'); + citus_drain_node --------------------------------------------------------------------- (1 row) -SELECT citus_move_shard_placement(1820003, :worker_1_node, :worker_2_node); - citus_move_shard_placement +SELECT citus_set_node_property('localhost', :worker_2_port, 'shouldhaveshards', true); + citus_set_node_property --------------------------------------------------------------------- (1 row) +RESET client_min_messages; -- see the plan for moving 4 shards, 3 of them are in the same colocation group SELECT * FROM get_rebalance_table_shards_plan(); table_name | shardid | shard_size | sourcename | sourceport | targetname | targetport --------------------------------------------------------------------- - single_shard_table_col1_1 | 1820002 | 0 | localhost | 57638 | localhost | 57637 - single_shard_table_col1_2 | 1820003 | 0 | localhost | 57638 | localhost | 57637 - single_shard_table_col1_3 | 1820004 | 0 | localhost | 57638 | localhost | 57637 - single_shard_table_col2_1 | 1820005 | 0 | localhost | 57638 | localhost | 57637 + single_shard_table_col1_1 | 1820002 | 0 | localhost | 57637 | localhost | 57638 + single_shard_table_col1_2 | 1820003 | 0 | localhost | 57637 | localhost | 57638 + single_shard_table_col1_3 | 1820004 | 0 | localhost | 57637 | localhost | 57638 + single_shard_table_col2_1 | 1820005 | 0 | localhost | 57637 | localhost | 57638 (4 rows) --- move some of them to worker 1 to balance the cluster +-- move some of them to worker 2 to balance the cluster SELECT 1 FROM citus_rebalance_start(); NOTICE: Scheduled 2 moves as job xxx DETAIL: Rebalance scheduled as background job @@ -461,13 +469,13 @@ SELECT * FROM citus_rebalance_wait(); SELECT shardid, nodeport FROM pg_dist_shard_placement WHERE shardid > 1820000 ORDER BY shardid; shardid | nodeport --------------------------------------------------------------------- - 1820002 | 57637 - 1820003 | 57637 - 1820004 | 57637 - 1820005 | 57637 - 1820006 | 57638 - 1820007 | 57638 - 1820008 | 57638 + 1820002 | 57638 + 1820003 | 57638 + 1820004 | 57638 + 1820005 | 57638 + 1820006 | 57637 + 1820007 | 57637 + 1820008 | 57637 (7 rows) -- test update_distributed_table_colocation diff --git a/src/test/regress/sql/single_shard_table_udfs.sql b/src/test/regress/sql/single_shard_table_udfs.sql index a06658701..36476fae7 100644 --- a/src/test/regress/sql/single_shard_table_udfs.sql +++ b/src/test/regress/sql/single_shard_table_udfs.sql @@ -144,15 +144,23 @@ SELECT shardid, nodeport FROM pg_dist_shard_placement WHERE shardid > 1820000 OR -- verify we didn't break any colocations SELECT logicalrelid, colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE '%single_shard_table_col%' ORDER BY colocationid; --- again, manually move 2 shard from 2 colocation groups to make the cluster unbalanced --- consider using citus_drain_node when the issue is fixed: https://github.com/citusdata/citus/issues/6948 -SELECT citus_move_shard_placement(1820005, 'localhost', :worker_1_port, 'localhost', :worker_2_port); -SELECT citus_move_shard_placement(1820003, :worker_1_node, :worker_2_node); +-- drop preexisting tables +-- we can remove the drop commands once the issue is fixed: https://github.com/citusdata/citus/issues/6948 +SET client_min_messages TO ERROR; +DROP TABLE IF EXISTS public.lineitem, public.orders, public.customer_append, public.part_append, public.supplier_single_shard, + public.events, public.users, public.lineitem_hash_part, public.lineitem_subquery, public.orders_hash_part, + public.orders_subquery, public.unlogged_table CASCADE; +DROP SCHEMA IF EXISTS with_basics, subquery_and_ctes CASCADE; +DROP TABLE IF EXISTS public.users_table, public.events_table, public.agg_results, public.agg_results_second, public.agg_results_third, public.agg_results_fourth, public.agg_results_window CASCADE; +-- drain node +SELECT citus_drain_node('localhost', :worker_2_port, 'block_writes'); +SELECT citus_set_node_property('localhost', :worker_2_port, 'shouldhaveshards', true); +RESET client_min_messages; -- see the plan for moving 4 shards, 3 of them are in the same colocation group SELECT * FROM get_rebalance_table_shards_plan(); --- move some of them to worker 1 to balance the cluster +-- move some of them to worker 2 to balance the cluster SELECT 1 FROM citus_rebalance_start(); -- stop it From 9961d39d97f02e5a51cb1d4f89c0be33920065ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20Ozan=20Akg=C3=BCl?= Date: Thu, 1 Jun 2023 12:56:06 +0300 Subject: [PATCH 059/118] Adds Single Shard Table Tests for Foreign Key UDFs (#6959) This PR adds tests for: - get_referencing_relation_id_list - get_referenced_relation_id_list - get_foreign_key_connected_relations --- .../expected/single_shard_table_udfs.out | 68 +++++++++++++++++++ .../regress/sql/single_shard_table_udfs.sql | 40 +++++++++++ 2 files changed, 108 insertions(+) diff --git a/src/test/regress/expected/single_shard_table_udfs.out b/src/test/regress/expected/single_shard_table_udfs.out index 2c60a8320..f56e1baf2 100644 --- a/src/test/regress/expected/single_shard_table_udfs.out +++ b/src/test/regress/expected/single_shard_table_udfs.out @@ -12,6 +12,18 @@ SELECT 1 FROM citus_add_node('localhost', :master_port, groupid=>0); (1 row) RESET client_min_messages; +CREATE FUNCTION get_referencing_relation_id_list(Oid) +RETURNS SETOF Oid +LANGUAGE C STABLE STRICT +AS 'citus', $$get_referencing_relation_id_list$$; +CREATE FUNCTION get_referenced_relation_id_list(Oid) +RETURNS SETOF Oid +LANGUAGE C STABLE STRICT +AS 'citus', $$get_referenced_relation_id_list$$; +CREATE OR REPLACE FUNCTION get_foreign_key_connected_relations(IN table_name regclass) +RETURNS SETOF RECORD +LANGUAGE C STRICT +AS 'citus', $$get_foreign_key_connected_relations$$; -- test some other udf's with single shard tables CREATE TABLE null_dist_key_table(a int); SELECT create_distributed_table('null_dist_key_table', null, colocate_with=>'none', distribution_type=>null); @@ -802,5 +814,61 @@ WHERE (3 rows) END; +-- test foreign key UDFs +CREATE TABLE fkey_s1 (a INT UNIQUE); +CREATE TABLE fkey_r (a INT UNIQUE); +CREATE TABLE fkey_s2 (x INT, y INT); +CREATE TABLE fkey_s3 (x INT, y INT); +SELECT create_distributed_table('fkey_s1', NULL, colocate_with:='none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_reference_table('fkey_r'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('fkey_s2', NULL, colocate_with:='fkey_s1'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('fkey_s3', NULL, colocate_with:='fkey_s1'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +ALTER TABLE fkey_s2 ADD CONSTRAINT f1 FOREIGN KEY (x) REFERENCES fkey_s1 (a); +ALTER TABLE fkey_s2 ADD CONSTRAINT f2 FOREIGN KEY (y) REFERENCES fkey_r (a); +ALTER TABLE fkey_s3 ADD CONSTRAINT f3 FOREIGN KEY (x) REFERENCES fkey_s1 (a); +ALTER TABLE fkey_s3 ADD CONSTRAINT f4 FOREIGN KEY (y) REFERENCES fkey_r (a); +SELECT get_referencing_relation_id_list::regclass::text FROM get_referencing_relation_id_list('fkey_s1'::regclass) ORDER BY 1; + get_referencing_relation_id_list +--------------------------------------------------------------------- + fkey_s2 + fkey_s3 +(2 rows) + +SELECT get_referenced_relation_id_list::regclass::text FROM get_referenced_relation_id_list('fkey_s2'::regclass) ORDER BY 1; + get_referenced_relation_id_list +--------------------------------------------------------------------- + fkey_r + fkey_s1 +(2 rows) + +SELECT oid::regclass::text FROM get_foreign_key_connected_relations('fkey_s1'::regclass) AS f(oid oid) ORDER BY 1; + oid +--------------------------------------------------------------------- + fkey_r + fkey_s1 + fkey_s2 + fkey_s3 +(4 rows) + SET client_min_messages TO WARNING; DROP SCHEMA null_dist_key_udfs CASCADE; diff --git a/src/test/regress/sql/single_shard_table_udfs.sql b/src/test/regress/sql/single_shard_table_udfs.sql index 36476fae7..a392252c2 100644 --- a/src/test/regress/sql/single_shard_table_udfs.sql +++ b/src/test/regress/sql/single_shard_table_udfs.sql @@ -8,6 +8,22 @@ ALTER SEQUENCE pg_catalog.pg_dist_colocationid_seq RESTART 198000; SET client_min_messages TO ERROR; SELECT 1 FROM citus_add_node('localhost', :master_port, groupid=>0); RESET client_min_messages; + +CREATE FUNCTION get_referencing_relation_id_list(Oid) +RETURNS SETOF Oid +LANGUAGE C STABLE STRICT +AS 'citus', $$get_referencing_relation_id_list$$; + +CREATE FUNCTION get_referenced_relation_id_list(Oid) +RETURNS SETOF Oid +LANGUAGE C STABLE STRICT +AS 'citus', $$get_referenced_relation_id_list$$; + +CREATE OR REPLACE FUNCTION get_foreign_key_connected_relations(IN table_name regclass) +RETURNS SETOF RECORD +LANGUAGE C STRICT +AS 'citus', $$get_foreign_key_connected_relations$$; + -- test some other udf's with single shard tables CREATE TABLE null_dist_key_table(a int); SELECT create_distributed_table('null_dist_key_table', null, colocate_with=>'none', distribution_type=>null); @@ -340,5 +356,29 @@ WHERE ORDER BY 2, 3; END; +-- test foreign key UDFs +CREATE TABLE fkey_s1 (a INT UNIQUE); +CREATE TABLE fkey_r (a INT UNIQUE); + +CREATE TABLE fkey_s2 (x INT, y INT); +CREATE TABLE fkey_s3 (x INT, y INT); + +SELECT create_distributed_table('fkey_s1', NULL, colocate_with:='none'); +SELECT create_reference_table('fkey_r'); + +SELECT create_distributed_table('fkey_s2', NULL, colocate_with:='fkey_s1'); +SELECT create_distributed_table('fkey_s3', NULL, colocate_with:='fkey_s1'); + +ALTER TABLE fkey_s2 ADD CONSTRAINT f1 FOREIGN KEY (x) REFERENCES fkey_s1 (a); +ALTER TABLE fkey_s2 ADD CONSTRAINT f2 FOREIGN KEY (y) REFERENCES fkey_r (a); + +ALTER TABLE fkey_s3 ADD CONSTRAINT f3 FOREIGN KEY (x) REFERENCES fkey_s1 (a); +ALTER TABLE fkey_s3 ADD CONSTRAINT f4 FOREIGN KEY (y) REFERENCES fkey_r (a); + +SELECT get_referencing_relation_id_list::regclass::text FROM get_referencing_relation_id_list('fkey_s1'::regclass) ORDER BY 1; +SELECT get_referenced_relation_id_list::regclass::text FROM get_referenced_relation_id_list('fkey_s2'::regclass) ORDER BY 1; + +SELECT oid::regclass::text FROM get_foreign_key_connected_relations('fkey_s1'::regclass) AS f(oid oid) ORDER BY 1; + SET client_min_messages TO WARNING; DROP SCHEMA null_dist_key_udfs CASCADE; From ff2062e8c36ede4c6e85179cdf7e37f4a1ce92db Mon Sep 17 00:00:00 2001 From: Teja Mupparti Date: Tue, 30 May 2023 16:06:10 -0700 Subject: [PATCH 060/118] Rename insert-select redistribute code base to generic purpose --- .../distributed/executor/citus_custom_scan.c | 2 +- .../executor/insert_select_executor.c | 238 ++---------------- .../executor/multi_server_executor.c | 2 +- .../executor/repartition_executor.c | 216 ++++++++++++++++ .../planner/insert_select_planner.c | 10 +- .../planner/intermediate_result_pruning.c | 7 +- .../distributed/planner/multi_explain.c | 5 +- .../distributed/utils/citus_copyfuncs.c | 6 +- .../distributed/utils/citus_outfuncs.c | 4 +- .../distributed/multi_physical_planner.h | 42 ++-- .../distributed/repartition_executor.h | 28 +++ 11 files changed, 311 insertions(+), 249 deletions(-) create mode 100644 src/backend/distributed/executor/repartition_executor.c create mode 100644 src/include/distributed/repartition_executor.h diff --git a/src/backend/distributed/executor/citus_custom_scan.c b/src/backend/distributed/executor/citus_custom_scan.c index 28486f23d..be04f38f4 100644 --- a/src/backend/distributed/executor/citus_custom_scan.c +++ b/src/backend/distributed/executor/citus_custom_scan.c @@ -182,7 +182,7 @@ CitusBeginScan(CustomScanState *node, EState *estate, int eflags) node->ss.ps.qual = ExecInitQual(node->ss.ps.plan->qual, (PlanState *) node); DistributedPlan *distributedPlan = scanState->distributedPlan; - if (distributedPlan->insertSelectQuery != NULL) + if (distributedPlan->modifyQueryViaCoordinatorOrRepartition != NULL) { /* * INSERT..SELECT via coordinator or re-partitioning are special because diff --git a/src/backend/distributed/executor/insert_select_executor.c b/src/backend/distributed/executor/insert_select_executor.c index a69ae0f22..71e66567a 100644 --- a/src/backend/distributed/executor/insert_select_executor.c +++ b/src/backend/distributed/executor/insert_select_executor.c @@ -30,6 +30,7 @@ #include "distributed/distributed_planner.h" #include "distributed/recursive_planning.h" #include "distributed/relation_access_tracking.h" +#include "distributed/repartition_executor.h" #include "distributed/resource_lock.h" #include "distributed/shardinterval_utils.h" #include "distributed/subplan_execution.h" @@ -55,8 +56,6 @@ bool EnableRepartitionedInsertSelect = true; -static List * TwoPhaseInsertSelectTaskList(Oid targetRelationId, Query *insertSelectQuery, - char *resultIdPrefix); static void ExecutePlanIntoRelation(Oid targetRelationId, List *insertTargetList, PlannedStmt *selectPlan, EState *executorState); static HTAB * ExecutePlanIntoColocatedIntermediateResults(Oid targetRelationId, @@ -67,11 +66,6 @@ static HTAB * ExecutePlanIntoColocatedIntermediateResults(Oid targetRelationId, static List * BuildColumnNameListFromTargetList(Oid targetRelationId, List *insertTargetList); static int PartitionColumnIndexFromColumnList(Oid relationId, List *columnNameList); -static List * RedistributedInsertSelectTaskList(Query *insertSelectQuery, - CitusTableCacheEntry *targetRelation, - List **redistributedResults, - bool useBinaryFormat); -static int PartitionColumnIndex(List *insertTargetList, Var *partitionColumn); static void WrapTaskListForProjection(List *taskList, List *projectedTargetEntries); @@ -89,7 +83,8 @@ NonPushableInsertSelectExecScan(CustomScanState *node) { EState *executorState = ScanStateGetExecutorState(scanState); DistributedPlan *distributedPlan = scanState->distributedPlan; - Query *insertSelectQuery = copyObject(distributedPlan->insertSelectQuery); + Query *insertSelectQuery = + copyObject(distributedPlan->modifyQueryViaCoordinatorOrRepartition); List *insertTargetList = insertSelectQuery->targetList; RangeTblEntry *selectRte = ExtractSelectRangeTableEntry(insertSelectQuery); RangeTblEntry *insertRte = ExtractResultRelationRTE(insertSelectQuery); @@ -99,7 +94,8 @@ NonPushableInsertSelectExecScan(CustomScanState *node) HTAB *shardStateHash = NULL; Query *selectQuery = selectRte->subquery; - PlannedStmt *selectPlan = copyObject(distributedPlan->selectPlanForInsertSelect); + PlannedStmt *selectPlan = + copyObject(distributedPlan->selectPlanForModifyViaCoordinatorOrRepartition); /* * If we are dealing with partitioned table, we also need to lock its @@ -111,7 +107,7 @@ NonPushableInsertSelectExecScan(CustomScanState *node) LockPartitionRelations(targetRelationId, RowExclusiveLock); } - if (distributedPlan->insertSelectMethod == INSERT_SELECT_REPARTITION) + if (distributedPlan->modifyWithSelectMethod == MODIFY_WITH_SELECT_REPARTITION) { ereport(DEBUG1, (errmsg("performing repartitioned INSERT ... SELECT"))); @@ -142,9 +138,10 @@ NonPushableInsertSelectExecScan(CustomScanState *node) CitusTableCacheEntry *targetRelation = GetCitusTableCacheEntry(targetRelationId); - int partitionColumnIndex = - PartitionColumnIndex(insertTargetList, targetRelation->partitionColumn); - if (partitionColumnIndex == -1) + int distributionColumnIndex = + DistributionColumnIndex(insertTargetList, + targetRelation->partitionColumn); + if (distributionColumnIndex == -1) { char *relationName = get_rel_name(targetRelationId); Oid schemaOid = get_rel_namespace(targetRelationId); @@ -158,13 +155,13 @@ NonPushableInsertSelectExecScan(CustomScanState *node) } TargetEntry *selectPartitionTE = list_nth(selectQuery->targetList, - partitionColumnIndex); + distributionColumnIndex); const char *partitionColumnName = selectPartitionTE->resname ? selectPartitionTE->resname : "(none)"; ereport(DEBUG2, (errmsg( "partitioning SELECT query by column index %d with name %s", - partitionColumnIndex, quote_literal_cstr( + distributionColumnIndex, quote_literal_cstr( partitionColumnName)))); /* @@ -182,7 +179,7 @@ NonPushableInsertSelectExecScan(CustomScanState *node) List **redistributedResults = RedistributeTaskListResults(distResultPrefix, distSelectTaskList, - partitionColumnIndex, + distributionColumnIndex, targetRelation, binaryFormat); @@ -192,10 +189,10 @@ NonPushableInsertSelectExecScan(CustomScanState *node) * target shard. Create and execute a list of tasks of form * INSERT INTO ... SELECT * FROM read_intermediate_results(...); */ - List *taskList = RedistributedInsertSelectTaskList(insertSelectQuery, - targetRelation, - redistributedResults, - binaryFormat); + List *taskList = GenerateTaskListWithRedistributedResults(insertSelectQuery, + targetRelation, + redistributedResults, + binaryFormat); scanState->tuplestorestate = tuplestore_begin_heap(randomAccess, interTransactions, work_mem); @@ -235,9 +232,10 @@ NonPushableInsertSelectExecScan(CustomScanState *node) intermediateResultIdPrefix); /* generate tasks for the INSERT..SELECT phase */ - List *taskList = TwoPhaseInsertSelectTaskList(targetRelationId, - insertSelectQuery, - intermediateResultIdPrefix); + List *taskList = + GenerateTaskListWithColocatedIntermediateResults( + targetRelationId, insertSelectQuery, + intermediateResultIdPrefix); /* * We cannot actually execute INSERT...SELECT tasks that read from @@ -298,94 +296,6 @@ NonPushableInsertSelectExecScan(CustomScanState *node) } -/* - * TwoPhaseInsertSelectTaskList generates a list of tasks for a query that - * inserts into a target relation and selects from a set of co-located - * intermediate results. - */ -static List * -TwoPhaseInsertSelectTaskList(Oid targetRelationId, Query *insertSelectQuery, - char *resultIdPrefix) -{ - List *taskList = NIL; - - /* - * Make a copy of the INSERT ... SELECT. We'll repeatedly replace the - * subquery of insertResultQuery for different intermediate results and - * then deparse it. - */ - Query *insertResultQuery = copyObject(insertSelectQuery); - RangeTblEntry *insertRte = ExtractResultRelationRTE(insertResultQuery); - RangeTblEntry *selectRte = ExtractSelectRangeTableEntry(insertResultQuery); - - CitusTableCacheEntry *targetCacheEntry = GetCitusTableCacheEntry(targetRelationId); - int shardCount = targetCacheEntry->shardIntervalArrayLength; - uint32 taskIdIndex = 1; - uint64 jobId = INVALID_JOB_ID; - - for (int shardOffset = 0; shardOffset < shardCount; shardOffset++) - { - ShardInterval *targetShardInterval = - targetCacheEntry->sortedShardIntervalArray[shardOffset]; - uint64 shardId = targetShardInterval->shardId; - List *columnAliasList = NIL; - StringInfo queryString = makeStringInfo(); - StringInfo resultId = makeStringInfo(); - - /* during COPY, the shard ID is appended to the result name */ - appendStringInfo(resultId, "%s_" UINT64_FORMAT, resultIdPrefix, shardId); - - /* generate the query on the intermediate result */ - Query *resultSelectQuery = BuildSubPlanResultQuery(insertSelectQuery->targetList, - columnAliasList, - resultId->data); - - /* put the intermediate result query in the INSERT..SELECT */ - selectRte->subquery = resultSelectQuery; - - /* setting an alias simplifies deparsing of RETURNING */ - if (insertRte->alias == NULL) - { - Alias *alias = makeAlias(CITUS_TABLE_ALIAS, NIL); - insertRte->alias = alias; - } - - /* - * Generate a query string for the query that inserts into a shard and reads - * from an intermediate result. - * - * Since CTEs have already been converted to intermediate results, they need - * to removed from the query. Otherwise, worker queries include both - * intermediate results and CTEs in the query. - */ - insertResultQuery->cteList = NIL; - deparse_shard_query(insertResultQuery, targetRelationId, shardId, queryString); - ereport(DEBUG2, (errmsg("distributed statement: %s", queryString->data))); - - LockShardDistributionMetadata(shardId, ShareLock); - List *insertShardPlacementList = ActiveShardPlacementList(shardId); - - RelationShard *relationShard = CitusMakeNode(RelationShard); - relationShard->relationId = targetShardInterval->relationId; - relationShard->shardId = targetShardInterval->shardId; - - Task *modifyTask = CreateBasicTask(jobId, taskIdIndex, MODIFY_TASK, - queryString->data); - modifyTask->dependentTaskList = NIL; - modifyTask->anchorShardId = shardId; - modifyTask->taskPlacementList = insertShardPlacementList; - modifyTask->relationShardList = list_make1(relationShard); - modifyTask->replicationModel = targetCacheEntry->replicationModel; - - taskList = lappend(taskList, modifyTask); - - taskIdIndex++; - } - - return taskList; -} - - /* * ExecutePlanIntoColocatedIntermediateResults executes the given PlannedStmt * and inserts tuples into a set of intermediate results that are colocated with @@ -529,111 +439,11 @@ IsSupportedRedistributionTarget(Oid targetRelationId) /* - * RedistributedInsertSelectTaskList returns a task list to insert given - * redistributedResults into the given target relation. - * redistributedResults[shardIndex] is list of cstrings each of which is - * a result name which should be inserted into - * targetRelation->sortedShardIntervalArray[shardIndex]. - */ -static List * -RedistributedInsertSelectTaskList(Query *insertSelectQuery, - CitusTableCacheEntry *targetRelation, - List **redistributedResults, - bool useBinaryFormat) -{ - List *taskList = NIL; - - /* - * Make a copy of the INSERT ... SELECT. We'll repeatedly replace the - * subquery of insertResultQuery for different intermediate results and - * then deparse it. - */ - Query *insertResultQuery = copyObject(insertSelectQuery); - RangeTblEntry *insertRte = ExtractResultRelationRTE(insertResultQuery); - RangeTblEntry *selectRte = ExtractSelectRangeTableEntry(insertResultQuery); - List *selectTargetList = selectRte->subquery->targetList; - Oid targetRelationId = targetRelation->relationId; - - int shardCount = targetRelation->shardIntervalArrayLength; - int shardOffset = 0; - uint32 taskIdIndex = 1; - uint64 jobId = INVALID_JOB_ID; - - for (shardOffset = 0; shardOffset < shardCount; shardOffset++) - { - ShardInterval *targetShardInterval = - targetRelation->sortedShardIntervalArray[shardOffset]; - List *resultIdList = redistributedResults[targetShardInterval->shardIndex]; - uint64 shardId = targetShardInterval->shardId; - StringInfo queryString = makeStringInfo(); - - /* skip empty tasks */ - if (resultIdList == NIL) - { - continue; - } - - /* sort result ids for consistent test output */ - List *sortedResultIds = SortList(resultIdList, pg_qsort_strcmp); - - /* generate the query on the intermediate result */ - Query *fragmentSetQuery = BuildReadIntermediateResultsArrayQuery(selectTargetList, - NIL, - sortedResultIds, - useBinaryFormat); - - /* put the intermediate result query in the INSERT..SELECT */ - selectRte->subquery = fragmentSetQuery; - - /* setting an alias simplifies deparsing of RETURNING */ - if (insertRte->alias == NULL) - { - Alias *alias = makeAlias(CITUS_TABLE_ALIAS, NIL); - insertRte->alias = alias; - } - - /* - * Generate a query string for the query that inserts into a shard and reads - * from an intermediate result. - * - * Since CTEs have already been converted to intermediate results, they need - * to removed from the query. Otherwise, worker queries include both - * intermediate results and CTEs in the query. - */ - insertResultQuery->cteList = NIL; - deparse_shard_query(insertResultQuery, targetRelationId, shardId, queryString); - ereport(DEBUG2, (errmsg("distributed statement: %s", queryString->data))); - - LockShardDistributionMetadata(shardId, ShareLock); - List *insertShardPlacementList = ActiveShardPlacementList(shardId); - - RelationShard *relationShard = CitusMakeNode(RelationShard); - relationShard->relationId = targetShardInterval->relationId; - relationShard->shardId = targetShardInterval->shardId; - - Task *modifyTask = CreateBasicTask(jobId, taskIdIndex, MODIFY_TASK, - queryString->data); - modifyTask->dependentTaskList = NIL; - modifyTask->anchorShardId = shardId; - modifyTask->taskPlacementList = insertShardPlacementList; - modifyTask->relationShardList = list_make1(relationShard); - modifyTask->replicationModel = targetRelation->replicationModel; - - taskList = lappend(taskList, modifyTask); - - taskIdIndex++; - } - - return taskList; -} - - -/* - * PartitionColumnIndex finds the index of given partition column in the + * DistributionColumnIndex finds the index of given distribution column in the * given target list. */ -static int -PartitionColumnIndex(List *insertTargetList, Var *partitionColumn) +int +DistributionColumnIndex(List *insertTargetList, Var *partitionColumn) { TargetEntry *insertTargetEntry = NULL; int targetEntryIndex = 0; diff --git a/src/backend/distributed/executor/multi_server_executor.c b/src/backend/distributed/executor/multi_server_executor.c index caf6797da..d92b39bfb 100644 --- a/src/backend/distributed/executor/multi_server_executor.c +++ b/src/backend/distributed/executor/multi_server_executor.c @@ -47,7 +47,7 @@ JobExecutorType(DistributedPlan *distributedPlan) { Job *job = distributedPlan->workerJob; - if (distributedPlan->insertSelectQuery != NULL) + if (distributedPlan->modifyQueryViaCoordinatorOrRepartition != NULL) { /* * We go through diff --git a/src/backend/distributed/executor/repartition_executor.c b/src/backend/distributed/executor/repartition_executor.c new file mode 100644 index 000000000..2d70a1356 --- /dev/null +++ b/src/backend/distributed/executor/repartition_executor.c @@ -0,0 +1,216 @@ +/*------------------------------------------------------------------- + * + * repartition_executor.c + * + * Definitions for public functions and types related to repartition + * of select query results. + * + * Copyright (c) Citus Data, Inc. + *------------------------------------------------------------------- + */ + +#include "postgres.h" +#include "miscadmin.h" + +#include "nodes/makefuncs.h" +#include "nodes/parsenodes.h" + +#include "distributed/intermediate_results.h" +#include "distributed/listutils.h" +#include "distributed/multi_physical_planner.h" +#include "distributed/multi_router_planner.h" +#include "distributed/recursive_planning.h" +#include "distributed/repartition_executor.h" +#include "distributed/resource_lock.h" + + +/* + * GenerateTaskListWithColocatedIntermediateResults generates a list of tasks + * for a query that inserts into a target relation and selects from a set of + * co-located intermediate results. + */ +List * +GenerateTaskListWithColocatedIntermediateResults(Oid targetRelationId, + Query * + modifyQueryViaCoordinatorOrRepartition, + char *resultIdPrefix) +{ + List *taskList = NIL; + + /* + * Make a copy of the ... SELECT. We'll repeatedly replace + * the subquery of modifyResultQuery for different intermediate results and + * then deparse it. + */ + Query *modifyWithResultQuery = copyObject(modifyQueryViaCoordinatorOrRepartition); + RangeTblEntry *insertRte = ExtractResultRelationRTE(modifyWithResultQuery); + RangeTblEntry *selectRte = ExtractSelectRangeTableEntry(modifyWithResultQuery); + + CitusTableCacheEntry *targetCacheEntry = GetCitusTableCacheEntry(targetRelationId); + int shardCount = targetCacheEntry->shardIntervalArrayLength; + uint32 taskIdIndex = 1; + uint64 jobId = INVALID_JOB_ID; + + for (int shardOffset = 0; shardOffset < shardCount; shardOffset++) + { + ShardInterval *targetShardInterval = + targetCacheEntry->sortedShardIntervalArray[shardOffset]; + uint64 shardId = targetShardInterval->shardId; + List *columnAliasList = NIL; + StringInfo queryString = makeStringInfo(); + StringInfo resultId = makeStringInfo(); + + /* during COPY, the shard ID is appended to the result name */ + appendStringInfo(resultId, "%s_" UINT64_FORMAT, resultIdPrefix, shardId); + + /* generate the query on the intermediate result */ + Query *resultSelectQuery = BuildSubPlanResultQuery( + modifyQueryViaCoordinatorOrRepartition->targetList, + columnAliasList, + resultId->data); + + /* put the intermediate result query in the INSERT..SELECT */ + selectRte->subquery = resultSelectQuery; + + /* setting an alias simplifies deparsing of RETURNING */ + if (insertRte->alias == NULL) + { + Alias *alias = makeAlias(CITUS_TABLE_ALIAS, NIL); + insertRte->alias = alias; + } + + /* + * Generate a query string for the query that inserts into a shard and reads + * from an intermediate result. + * + * Since CTEs have already been converted to intermediate results, they need + * to removed from the query. Otherwise, worker queries include both + * intermediate results and CTEs in the query. + */ + modifyWithResultQuery->cteList = NIL; + deparse_shard_query(modifyWithResultQuery, targetRelationId, shardId, + queryString); + ereport(DEBUG2, (errmsg("distributed statement: %s", queryString->data))); + + LockShardDistributionMetadata(shardId, ShareLock); + List *insertShardPlacementList = ActiveShardPlacementList(shardId); + + RelationShard *relationShard = CitusMakeNode(RelationShard); + relationShard->relationId = targetShardInterval->relationId; + relationShard->shardId = targetShardInterval->shardId; + + Task *modifyTask = CreateBasicTask(jobId, taskIdIndex, MODIFY_TASK, + queryString->data); + modifyTask->dependentTaskList = NIL; + modifyTask->anchorShardId = shardId; + modifyTask->taskPlacementList = insertShardPlacementList; + modifyTask->relationShardList = list_make1(relationShard); + modifyTask->replicationModel = targetCacheEntry->replicationModel; + + taskList = lappend(taskList, modifyTask); + + taskIdIndex++; + } + + return taskList; +} + + +/* + * GenerateTaskListWithRedistributedResults returns a task list to insert given + * redistributedResults into the given target relation. + * redistributedResults[shardIndex] is list of cstrings each of which is + * a result name which should be inserted into + * targetRelation->sortedShardIntervalArray[shardIndex]. + */ +List * +GenerateTaskListWithRedistributedResults(Query *modifyQueryViaCoordinatorOrRepartition, + CitusTableCacheEntry *targetRelation, + List **redistributedResults, bool + useBinaryFormat) +{ + List *taskList = NIL; + + /* + * Make a copy of the ... SELECT. We'll repeatedly replace + * the subquery of modifyResultQuery for different intermediate results and + * then deparse it. + */ + Query *modifyResultQuery = copyObject(modifyQueryViaCoordinatorOrRepartition); + RangeTblEntry *insertRte = ExtractResultRelationRTE(modifyResultQuery); + RangeTblEntry *selectRte = ExtractSelectRangeTableEntry(modifyResultQuery); + List *selectTargetList = selectRte->subquery->targetList; + Oid targetRelationId = targetRelation->relationId; + + int shardCount = targetRelation->shardIntervalArrayLength; + int shardOffset = 0; + uint32 taskIdIndex = 1; + uint64 jobId = INVALID_JOB_ID; + + for (shardOffset = 0; shardOffset < shardCount; shardOffset++) + { + ShardInterval *targetShardInterval = + targetRelation->sortedShardIntervalArray[shardOffset]; + List *resultIdList = redistributedResults[targetShardInterval->shardIndex]; + uint64 shardId = targetShardInterval->shardId; + StringInfo queryString = makeStringInfo(); + + /* skip empty tasks */ + if (resultIdList == NIL) + { + continue; + } + + /* sort result ids for consistent test output */ + List *sortedResultIds = SortList(resultIdList, pg_qsort_strcmp); + + /* generate the query on the intermediate result */ + Query *fragmentSetQuery = BuildReadIntermediateResultsArrayQuery(selectTargetList, + NIL, + sortedResultIds, + useBinaryFormat); + + /* put the intermediate result query in the INSERT..SELECT */ + selectRte->subquery = fragmentSetQuery; + + /* setting an alias simplifies deparsing of RETURNING */ + if (insertRte->alias == NULL) + { + Alias *alias = makeAlias(CITUS_TABLE_ALIAS, NIL); + insertRte->alias = alias; + } + + /* + * Generate a query string for the query that inserts into a shard and reads + * from an intermediate result. + * + * Since CTEs have already been converted to intermediate results, they need + * to removed from the query. Otherwise, worker queries include both + * intermediate results and CTEs in the query. + */ + modifyResultQuery->cteList = NIL; + deparse_shard_query(modifyResultQuery, targetRelationId, shardId, queryString); + ereport(DEBUG2, (errmsg("distributed statement: %s", queryString->data))); + + LockShardDistributionMetadata(shardId, ShareLock); + List *insertShardPlacementList = ActiveShardPlacementList(shardId); + + RelationShard *relationShard = CitusMakeNode(RelationShard); + relationShard->relationId = targetShardInterval->relationId; + relationShard->shardId = targetShardInterval->shardId; + + Task *modifyTask = CreateBasicTask(jobId, taskIdIndex, MODIFY_TASK, + queryString->data); + modifyTask->dependentTaskList = NIL; + modifyTask->anchorShardId = shardId; + modifyTask->taskPlacementList = insertShardPlacementList; + modifyTask->relationShardList = list_make1(relationShard); + modifyTask->replicationModel = targetRelation->replicationModel; + + taskList = lappend(taskList, modifyTask); + + taskIdIndex++; + } + + return taskList; +} diff --git a/src/backend/distributed/planner/insert_select_planner.c b/src/backend/distributed/planner/insert_select_planner.c index 06e446783..8a58b0f13 100644 --- a/src/backend/distributed/planner/insert_select_planner.c +++ b/src/backend/distributed/planner/insert_select_planner.c @@ -1417,11 +1417,11 @@ CreateNonPushableInsertSelectPlan(uint64 planId, Query *parse, ParamListInfo bou Assert(!repartitioned || !GetRTEListPropertiesForQuery(selectQueryCopy)->hasSingleShardDistTable); - distributedPlan->insertSelectQuery = insertSelectQuery; - distributedPlan->selectPlanForInsertSelect = selectPlan; - distributedPlan->insertSelectMethod = repartitioned ? - INSERT_SELECT_REPARTITION : - INSERT_SELECT_VIA_COORDINATOR; + distributedPlan->modifyQueryViaCoordinatorOrRepartition = insertSelectQuery; + distributedPlan->selectPlanForModifyViaCoordinatorOrRepartition = selectPlan; + distributedPlan->modifyWithSelectMethod = repartitioned ? + MODIFY_WITH_SELECT_REPARTITION : + MODIFY_WITH_SELECT_VIA_COORDINATOR; distributedPlan->expectResults = insertSelectQuery->returningList != NIL; distributedPlan->intermediateResultIdPrefix = InsertSelectResultIdPrefix(planId); distributedPlan->targetRelationId = targetRelationId; diff --git a/src/backend/distributed/planner/intermediate_result_pruning.c b/src/backend/distributed/planner/intermediate_result_pruning.c index 76aba8321..cefbfb833 100644 --- a/src/backend/distributed/planner/intermediate_result_pruning.c +++ b/src/backend/distributed/planner/intermediate_result_pruning.c @@ -69,7 +69,7 @@ FindSubPlanUsages(DistributedPlan *plan) SUBPLAN_ACCESS_REMOTE); } - if (plan->insertSelectQuery != NULL) + if (plan->modifyQueryViaCoordinatorOrRepartition != NULL) { /* INSERT..SELECT plans currently do not have a workerJob */ Assert(plan->workerJob == NULL); @@ -79,8 +79,9 @@ FindSubPlanUsages(DistributedPlan *plan) * perform pruning. We therefore require all subplans used in the * INSERT..SELECT to be available all nodes. */ - remoteSubPlans = FindSubPlansUsedInNode((Node *) plan->insertSelectQuery, - SUBPLAN_ACCESS_ANYWHERE); + remoteSubPlans = + FindSubPlansUsedInNode((Node *) plan->modifyQueryViaCoordinatorOrRepartition, + SUBPLAN_ACCESS_ANYWHERE); } /* merge the used subplans */ diff --git a/src/backend/distributed/planner/multi_explain.c b/src/backend/distributed/planner/multi_explain.c index c23509df1..248117904 100644 --- a/src/backend/distributed/planner/multi_explain.c +++ b/src/backend/distributed/planner/multi_explain.c @@ -234,7 +234,7 @@ NonPushableInsertSelectExplainScan(CustomScanState *node, List *ancestors, { CitusScanState *scanState = (CitusScanState *) node; DistributedPlan *distributedPlan = scanState->distributedPlan; - Query *insertSelectQuery = distributedPlan->insertSelectQuery; + Query *insertSelectQuery = distributedPlan->modifyQueryViaCoordinatorOrRepartition; RangeTblEntry *selectRte = ExtractSelectRangeTableEntry(insertSelectQuery); /* @@ -244,7 +244,8 @@ NonPushableInsertSelectExplainScan(CustomScanState *node, List *ancestors, */ Query *queryCopy = copyObject(selectRte->subquery); - bool repartition = distributedPlan->insertSelectMethod == INSERT_SELECT_REPARTITION; + bool repartition = distributedPlan->modifyWithSelectMethod == + MODIFY_WITH_SELECT_REPARTITION; if (es->analyze) diff --git a/src/backend/distributed/utils/citus_copyfuncs.c b/src/backend/distributed/utils/citus_copyfuncs.c index d4e95e16c..7e1379ef3 100644 --- a/src/backend/distributed/utils/citus_copyfuncs.c +++ b/src/backend/distributed/utils/citus_copyfuncs.c @@ -127,9 +127,9 @@ CopyNodeDistributedPlan(COPYFUNC_ARGS) COPY_SCALAR_FIELD(queryId); COPY_NODE_FIELD(relationIdList); COPY_SCALAR_FIELD(targetRelationId); - COPY_NODE_FIELD(insertSelectQuery); - COPY_NODE_FIELD(selectPlanForInsertSelect); - COPY_SCALAR_FIELD(insertSelectMethod); + COPY_NODE_FIELD(modifyQueryViaCoordinatorOrRepartition); + COPY_NODE_FIELD(selectPlanForModifyViaCoordinatorOrRepartition); + COPY_SCALAR_FIELD(modifyWithSelectMethod); COPY_STRING_FIELD(intermediateResultIdPrefix); COPY_NODE_FIELD(subPlanList); diff --git a/src/backend/distributed/utils/citus_outfuncs.c b/src/backend/distributed/utils/citus_outfuncs.c index b02626233..b4062751a 100644 --- a/src/backend/distributed/utils/citus_outfuncs.c +++ b/src/backend/distributed/utils/citus_outfuncs.c @@ -192,7 +192,9 @@ OutDistributedPlan(OUTFUNC_ARGS) WRITE_UINT64_FIELD(queryId); WRITE_NODE_FIELD(relationIdList); WRITE_OID_FIELD(targetRelationId); - WRITE_NODE_FIELD(insertSelectQuery); + WRITE_NODE_FIELD(modifyQueryViaCoordinatorOrRepartition); + WRITE_NODE_FIELD(selectPlanForModifyViaCoordinatorOrRepartition); + WRITE_ENUM_FIELD(modifyWithSelectMethod, ModifyWithSelectMethod); WRITE_STRING_FIELD(intermediateResultIdPrefix); WRITE_NODE_FIELD(subPlanList); diff --git a/src/include/distributed/multi_physical_planner.h b/src/include/distributed/multi_physical_planner.h index 26d074053..c457918db 100644 --- a/src/include/distributed/multi_physical_planner.h +++ b/src/include/distributed/multi_physical_planner.h @@ -361,19 +361,19 @@ typedef struct JoinSequenceNode /* - * InsertSelectMethod represents the method to use for INSERT INTO ... SELECT - * queries. + * ModifyWithSelectMethod represents the method to use for INSERT INTO ... SELECT + * or MERGE type of queries. * * Note that there is a third method which is not represented here, which is - * pushing down the INSERT INTO ... SELECT to workers. This method is executed - * similar to other distributed queries and doesn't need a special execution - * code, so we don't need to represent it here. + * pushing down the MERGE/INSERT INTO ... SELECT to workers. This method is + * executed similar to other distributed queries and doesn't need a special + * execution code, so we don't need to represent it here. */ -typedef enum InsertSelectMethod +typedef enum ModifyWithSelectMethod { - INSERT_SELECT_VIA_COORDINATOR, - INSERT_SELECT_REPARTITION -} InsertSelectMethod; + MODIFY_WITH_SELECT_VIA_COORDINATOR, + MODIFY_WITH_SELECT_REPARTITION +} ModifyWithSelectMethod; /* @@ -412,18 +412,22 @@ typedef struct DistributedPlan Oid targetRelationId; /* - * INSERT .. SELECT via the coordinator or repartition */ - Query *insertSelectQuery; - PlannedStmt *selectPlanForInsertSelect; - InsertSelectMethod insertSelectMethod; + * Modifications performed using the output of a source query via + * the coordinator or repartition. + */ + Query *modifyQueryViaCoordinatorOrRepartition; + PlannedStmt *selectPlanForModifyViaCoordinatorOrRepartition; + ModifyWithSelectMethod modifyWithSelectMethod; /* - * If intermediateResultIdPrefix is non-null, an INSERT ... SELECT - * via the coordinator is written to a set of intermediate results - * named according to _. - * That way we can run a distributed INSERT ... SELECT with - * RETURNING or ON CONFLICT from the intermediate results to the - * target relation. + * If intermediateResultIdPrefix is non-null, the source query + * results are written to a set of intermediate results named + * according to _. + * That way we can run a distributed modification query which + * requires evaluating source query results at the coordinator. + * Once results are captured in intermediate files, modification + * is done from the intermediate results into the target relation. + * */ char *intermediateResultIdPrefix; diff --git a/src/include/distributed/repartition_executor.h b/src/include/distributed/repartition_executor.h new file mode 100644 index 000000000..fea6d6525 --- /dev/null +++ b/src/include/distributed/repartition_executor.h @@ -0,0 +1,28 @@ +/*------------------------------------------------------------------------- + * + * repartition_executor.h + * + * Declarations for public functions and types related to repartition of + * select query results. + * + * Copyright (c) Citus Data, Inc. + * + *------------------------------------------------------------------------- + */ + +#ifndef REPARTITION_EXECUTOR_H +#define REPARTITION_EXECUTOR_H + +extern int DistributionColumnIndex(List *insertTargetList, Var *partitionColumn); +extern List * GenerateTaskListWithColocatedIntermediateResults(Oid targetRelationId, + Query * + modifyQueryViaCoordinatorOrRepartition, + char *resultIdPrefix); +extern List * GenerateTaskListWithRedistributedResults( + Query *modifyQueryViaCoordinatorOrRepartition, + CitusTableCacheEntry * + targetRelation, + List **redistributedResults, + bool useBinaryFormat); + +#endif /* REPARTITION_EXECUTOR_H */ From ac7f732be22914b35cb182eec66891bc002ed7a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20Ozan=20Akg=C3=BCl?= Date: Fri, 2 Jun 2023 11:57:53 +0300 Subject: [PATCH 061/118] Add Single Shard Table Tests for Dependency UDFs (#6960) This PR tests: - citus_get_all_dependencies_for_object - citus_get_dependencies_for_object - is_citus_depended_object --- .../expected/single_shard_table_udfs.out | 82 +++++++++++++++++++ .../regress/sql/single_shard_table_udfs.sql | 57 +++++++++++++ 2 files changed, 139 insertions(+) diff --git a/src/test/regress/expected/single_shard_table_udfs.out b/src/test/regress/expected/single_shard_table_udfs.out index f56e1baf2..3b7dd5034 100644 --- a/src/test/regress/expected/single_shard_table_udfs.out +++ b/src/test/regress/expected/single_shard_table_udfs.out @@ -24,6 +24,18 @@ CREATE OR REPLACE FUNCTION get_foreign_key_connected_relations(IN table_name reg RETURNS SETOF RECORD LANGUAGE C STRICT AS 'citus', $$get_foreign_key_connected_relations$$; +CREATE OR REPLACE FUNCTION citus_get_all_dependencies_for_object(classid oid, objid oid, objsubid int) +RETURNS SETOF RECORD +LANGUAGE C STRICT +AS 'citus', $$citus_get_all_dependencies_for_object$$; +CREATE OR REPLACE FUNCTION citus_get_dependencies_for_object(classid oid, objid oid, objsubid int) +RETURNS SETOF RECORD +LANGUAGE C STRICT +AS 'citus', $$citus_get_dependencies_for_object$$; +CREATE OR REPLACE FUNCTION pg_catalog.is_citus_depended_object(oid,oid) +RETURNS bool +LANGUAGE C +AS 'citus', $$is_citus_depended_object$$; -- test some other udf's with single shard tables CREATE TABLE null_dist_key_table(a int); SELECT create_distributed_table('null_dist_key_table', null, colocate_with=>'none', distribution_type=>null); @@ -870,5 +882,75 @@ SELECT oid::regclass::text FROM get_foreign_key_connected_relations('fkey_s1'::r fkey_s3 (4 rows) +--test dependency functions +CREATE TYPE dep_type AS (a INT); +CREATE TABLE dep_tbl(a INT, b dep_type); +SELECT create_distributed_table('dep_tbl', NULL, colocate_with:='none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE VIEW dep_view AS SELECT * FROM dep_tbl; +-- find all the dependencies of table dep_tbl +SELECT + pg_identify_object(t.classid, t.objid, t.objsubid) +FROM + (SELECT * FROM pg_get_object_address('table', '{dep_tbl}', '{}')) as addr +JOIN LATERAL + citus_get_all_dependencies_for_object(addr.classid, addr.objid, addr.objsubid) as t(classid oid, objid oid, objsubid int) +ON TRUE + ORDER BY 1; + pg_identify_object +--------------------------------------------------------------------- + ("composite type",null_dist_key_udfs,dep_type,null_dist_key_udfs.dep_type) + (schema,,null_dist_key_udfs,null_dist_key_udfs) + (type,null_dist_key_udfs,dep_type,null_dist_key_udfs.dep_type) +(3 rows) + +-- find all the dependencies of view dep_view +SELECT + pg_identify_object(t.classid, t.objid, t.objsubid) +FROM + (SELECT * FROM pg_get_object_address('view', '{dep_view}', '{}')) as addr +JOIN LATERAL + citus_get_all_dependencies_for_object(addr.classid, addr.objid, addr.objsubid) as t(classid oid, objid oid, objsubid int) +ON TRUE + ORDER BY 1; + pg_identify_object +--------------------------------------------------------------------- + ("composite type",null_dist_key_udfs,dep_type,null_dist_key_udfs.dep_type) + (schema,,null_dist_key_udfs,null_dist_key_udfs) + (table,null_dist_key_udfs,dep_tbl,null_dist_key_udfs.dep_tbl) + (type,null_dist_key_udfs,dep_type,null_dist_key_udfs.dep_type) +(4 rows) + +-- find non-distributed dependencies of table dep_tbl +SELECT + pg_identify_object(t.classid, t.objid, t.objsubid) +FROM + (SELECT * FROM pg_get_object_address('table', '{dep_tbl}', '{}')) as addr +JOIN LATERAL + citus_get_dependencies_for_object(addr.classid, addr.objid, addr.objsubid) as t(classid oid, objid oid, objsubid int) +ON TRUE + ORDER BY 1; + pg_identify_object +--------------------------------------------------------------------- +(0 rows) + +SET citus.hide_citus_dependent_objects TO true; +CREATE TABLE citus_dep_tbl (a noderole); +SELECT create_distributed_table('citus_dep_tbl', NULL, colocate_with:='none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT is_citus_depended_object('pg_class'::regclass, 'citus_dep_tbl'::regclass); + is_citus_depended_object +--------------------------------------------------------------------- + t +(1 row) + SET client_min_messages TO WARNING; DROP SCHEMA null_dist_key_udfs CASCADE; diff --git a/src/test/regress/sql/single_shard_table_udfs.sql b/src/test/regress/sql/single_shard_table_udfs.sql index a392252c2..1f8f7e8b9 100644 --- a/src/test/regress/sql/single_shard_table_udfs.sql +++ b/src/test/regress/sql/single_shard_table_udfs.sql @@ -24,6 +24,21 @@ RETURNS SETOF RECORD LANGUAGE C STRICT AS 'citus', $$get_foreign_key_connected_relations$$; +CREATE OR REPLACE FUNCTION citus_get_all_dependencies_for_object(classid oid, objid oid, objsubid int) +RETURNS SETOF RECORD +LANGUAGE C STRICT +AS 'citus', $$citus_get_all_dependencies_for_object$$; + +CREATE OR REPLACE FUNCTION citus_get_dependencies_for_object(classid oid, objid oid, objsubid int) +RETURNS SETOF RECORD +LANGUAGE C STRICT +AS 'citus', $$citus_get_dependencies_for_object$$; + +CREATE OR REPLACE FUNCTION pg_catalog.is_citus_depended_object(oid,oid) +RETURNS bool +LANGUAGE C +AS 'citus', $$is_citus_depended_object$$; + -- test some other udf's with single shard tables CREATE TABLE null_dist_key_table(a int); SELECT create_distributed_table('null_dist_key_table', null, colocate_with=>'none', distribution_type=>null); @@ -380,5 +395,47 @@ SELECT get_referenced_relation_id_list::regclass::text FROM get_referenced_relat SELECT oid::regclass::text FROM get_foreign_key_connected_relations('fkey_s1'::regclass) AS f(oid oid) ORDER BY 1; +--test dependency functions +CREATE TYPE dep_type AS (a INT); +CREATE TABLE dep_tbl(a INT, b dep_type); +SELECT create_distributed_table('dep_tbl', NULL, colocate_with:='none'); +CREATE VIEW dep_view AS SELECT * FROM dep_tbl; + +-- find all the dependencies of table dep_tbl +SELECT + pg_identify_object(t.classid, t.objid, t.objsubid) +FROM + (SELECT * FROM pg_get_object_address('table', '{dep_tbl}', '{}')) as addr +JOIN LATERAL + citus_get_all_dependencies_for_object(addr.classid, addr.objid, addr.objsubid) as t(classid oid, objid oid, objsubid int) +ON TRUE + ORDER BY 1; + +-- find all the dependencies of view dep_view +SELECT + pg_identify_object(t.classid, t.objid, t.objsubid) +FROM + (SELECT * FROM pg_get_object_address('view', '{dep_view}', '{}')) as addr +JOIN LATERAL + citus_get_all_dependencies_for_object(addr.classid, addr.objid, addr.objsubid) as t(classid oid, objid oid, objsubid int) +ON TRUE + ORDER BY 1; + +-- find non-distributed dependencies of table dep_tbl +SELECT + pg_identify_object(t.classid, t.objid, t.objsubid) +FROM + (SELECT * FROM pg_get_object_address('table', '{dep_tbl}', '{}')) as addr +JOIN LATERAL + citus_get_dependencies_for_object(addr.classid, addr.objid, addr.objsubid) as t(classid oid, objid oid, objsubid int) +ON TRUE + ORDER BY 1; + +SET citus.hide_citus_dependent_objects TO true; +CREATE TABLE citus_dep_tbl (a noderole); +SELECT create_distributed_table('citus_dep_tbl', NULL, colocate_with:='none'); + +SELECT is_citus_depended_object('pg_class'::regclass, 'citus_dep_tbl'::regclass); + SET client_min_messages TO WARNING; DROP SCHEMA null_dist_key_udfs CASCADE; From 3e183746b77b81bc33304d6d379f2c88741bc2f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20Ozan=20Akg=C3=BCl?= Date: Fri, 2 Jun 2023 13:46:14 +0300 Subject: [PATCH 062/118] Single Shard Misc UDFs 2 (#6963) Creating a second PR to make reviewing easier. This PR tests: - replicate_reference_tables - fix_partition_shard_index_names - isolate_tenant_to_new_shard - replicate_table_shards --- .../distributed/operations/shard_rebalancer.c | 5 + .../expected/single_shard_table_udfs.out | 118 +++++++++++++++++- .../regress/sql/single_shard_table_udfs.sql | 68 +++++++++- 3 files changed, 189 insertions(+), 2 deletions(-) diff --git a/src/backend/distributed/operations/shard_rebalancer.c b/src/backend/distributed/operations/shard_rebalancer.c index 0bb27934d..bc5b0af74 100644 --- a/src/backend/distributed/operations/shard_rebalancer.c +++ b/src/backend/distributed/operations/shard_rebalancer.c @@ -1178,6 +1178,11 @@ replicate_table_shards(PG_FUNCTION_ARGS) ArrayType *excludedShardArray = PG_GETARG_ARRAYTYPE_P(3); Oid shardReplicationModeOid = PG_GETARG_OID(4); + if (IsCitusTableType(relationId, SINGLE_SHARD_DISTRIBUTED)) + { + ereport(ERROR, (errmsg("cannot replicate single shard tables' shards"))); + } + char transferMode = LookupShardTransferMode(shardReplicationModeOid); EnsureReferenceTablesExistOnAllNodesExtended(transferMode); diff --git a/src/test/regress/expected/single_shard_table_udfs.out b/src/test/regress/expected/single_shard_table_udfs.out index 3b7dd5034..2a4e59f82 100644 --- a/src/test/regress/expected/single_shard_table_udfs.out +++ b/src/test/regress/expected/single_shard_table_udfs.out @@ -397,7 +397,7 @@ SELECT shardid, nodeport FROM pg_dist_shard_placement WHERE shardid > 1820000 OR (7 rows) -- verify we didn't break any colocations -SELECT logicalrelid, colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE '%single_shard_table_col%' ORDER BY colocationid; +SELECT logicalrelid, colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE '%single_shard_table_col%' ORDER BY colocationid, logicalrelid; logicalrelid | colocationid --------------------------------------------------------------------- single_shard_table_col1_1 | 198001 @@ -952,5 +952,121 @@ SELECT is_citus_depended_object('pg_class'::regclass, 'citus_dep_tbl'::regclass) t (1 row) +RESET citus.hide_citus_dependent_objects; +-- test replicate_reference_tables +SET client_min_messages TO WARNING; +DROP SCHEMA null_dist_key_udfs CASCADE; +RESET client_min_messages; +CREATE SCHEMA null_dist_key_udfs; +SET search_path TO null_dist_key_udfs; +SELECT citus_remove_node('localhost', :worker_2_port); + citus_remove_node +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE rep_ref (a INT UNIQUE); +SELECT create_reference_table('rep_ref'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE rep_sing (a INT); +SELECT create_distributed_table('rep_sing', NULL, colocate_with:='none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +ALTER TABLE rep_sing ADD CONSTRAINT rep_fkey FOREIGN KEY (a) REFERENCES rep_ref(a); +SELECT 1 FROM citus_add_node('localhost', :worker_2_port); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT count(*) FROM citus_shards WHERE table_name = 'rep_ref'::regclass AND nodeport = :worker_2_port; + count +--------------------------------------------------------------------- + 0 +(1 row) + +SELECT replicate_reference_tables('block_writes'); + replicate_reference_tables +--------------------------------------------------------------------- + +(1 row) + +SELECT count(*) FROM citus_shards WHERE table_name = 'rep_ref'::regclass AND nodeport = :worker_2_port; + count +--------------------------------------------------------------------- + 1 +(1 row) + +-- test fix_partition_shard_index_names +SET citus.next_shard_id TO 3820000; +CREATE TABLE part_tbl_sing (dist_col int, another_col int, partition_col timestamp) PARTITION BY RANGE (partition_col); +SELECT create_distributed_table('part_tbl_sing', NULL, colocate_with:='none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +-- create a partition with a long name and another with a short name +CREATE TABLE partition_table_with_very_long_name PARTITION OF part_tbl_sing FOR VALUES FROM ('2018-01-01') TO ('2019-01-01'); +CREATE TABLE p PARTITION OF part_tbl_sing FOR VALUES FROM ('2019-01-01') TO ('2020-01-01'); +-- create an index on parent table +-- we will see that it doesn't matter whether we name the index on parent or not +-- indexes auto-generated on partitions will not use this name +-- SELECT fix_partition_shard_index_names('dist_partitioned_table') will be executed +-- automatically at the end of the CREATE INDEX command +CREATE INDEX short ON part_tbl_sing USING btree (another_col, partition_col); +SELECT tablename, indexname FROM pg_indexes WHERE schemaname = 'null_dist_key_udfs' AND tablename SIMILAR TO 'p%' ORDER BY 1, 2; + tablename | indexname +--------------------------------------------------------------------- + p | p_another_col_partition_col_idx + part_tbl_sing | short + partition_table_with_very_long_name | partition_table_with_very_long_na_another_col_partition_col_idx +(3 rows) + +SELECT nodeport AS part_tbl_sing_port +FROM citus_shards +WHERE table_name = 'part_tbl_sing'::regclass AND + nodeport IN (:worker_1_port, :worker_2_port) \gset +\c - - - :part_tbl_sing_port +-- the names are generated correctly +-- shard id has been appended to all index names which didn't end in shard id +-- this goes in line with Citus's way of naming indexes of shards: always append shardid to the end +SELECT tablename, indexname FROM pg_indexes WHERE schemaname = 'null_dist_key_udfs' AND tablename SIMILAR TO 'p%\_\d*' ORDER BY 1, 2; + tablename | indexname +--------------------------------------------------------------------- + p_3820002 | p_another_col_partition_col_idx_3820002 + part_tbl_sing_3820000 | short_3820000 + partition_table_with_very_long_name_3820001 | partition_table_with_very_long_na_another_col__dd884a3b_3820001 +(3 rows) + +\c - - - :master_port +SET search_path TO null_dist_key_udfs; +--test isolate_tenant_to_new_shard +CREATE TABLE iso_tbl (a INT); +SELECT create_distributed_table('iso_tbl', NULL, colocate_with:='none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT isolate_tenant_to_new_shard('iso_tbl', 5); +ERROR: cannot isolate tenant because tenant isolation is only support for hash distributed tables +-- test replicate_table_shards +CREATE TABLE rep_tbl (a INT); +SELECT create_distributed_table('rep_tbl', NULL, colocate_with:='none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT replicate_table_shards('rep_tbl'); +ERROR: cannot replicate single shard tables' shards SET client_min_messages TO WARNING; DROP SCHEMA null_dist_key_udfs CASCADE; diff --git a/src/test/regress/sql/single_shard_table_udfs.sql b/src/test/regress/sql/single_shard_table_udfs.sql index 1f8f7e8b9..65b623a57 100644 --- a/src/test/regress/sql/single_shard_table_udfs.sql +++ b/src/test/regress/sql/single_shard_table_udfs.sql @@ -173,7 +173,7 @@ SELECT rebalance_table_shards(); SELECT shardid, nodeport FROM pg_dist_shard_placement WHERE shardid > 1820000 ORDER BY shardid; -- verify we didn't break any colocations -SELECT logicalrelid, colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE '%single_shard_table_col%' ORDER BY colocationid; +SELECT logicalrelid, colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE '%single_shard_table_col%' ORDER BY colocationid, logicalrelid; -- drop preexisting tables -- we can remove the drop commands once the issue is fixed: https://github.com/citusdata/citus/issues/6948 @@ -436,6 +436,72 @@ CREATE TABLE citus_dep_tbl (a noderole); SELECT create_distributed_table('citus_dep_tbl', NULL, colocate_with:='none'); SELECT is_citus_depended_object('pg_class'::regclass, 'citus_dep_tbl'::regclass); +RESET citus.hide_citus_dependent_objects; + +-- test replicate_reference_tables +SET client_min_messages TO WARNING; +DROP SCHEMA null_dist_key_udfs CASCADE; +RESET client_min_messages; +CREATE SCHEMA null_dist_key_udfs; +SET search_path TO null_dist_key_udfs; + +SELECT citus_remove_node('localhost', :worker_2_port); + +CREATE TABLE rep_ref (a INT UNIQUE); +SELECT create_reference_table('rep_ref'); + +CREATE TABLE rep_sing (a INT); +SELECT create_distributed_table('rep_sing', NULL, colocate_with:='none'); + +ALTER TABLE rep_sing ADD CONSTRAINT rep_fkey FOREIGN KEY (a) REFERENCES rep_ref(a); + +SELECT 1 FROM citus_add_node('localhost', :worker_2_port); + +SELECT count(*) FROM citus_shards WHERE table_name = 'rep_ref'::regclass AND nodeport = :worker_2_port; +SELECT replicate_reference_tables('block_writes'); +SELECT count(*) FROM citus_shards WHERE table_name = 'rep_ref'::regclass AND nodeport = :worker_2_port; + +-- test fix_partition_shard_index_names +SET citus.next_shard_id TO 3820000; +CREATE TABLE part_tbl_sing (dist_col int, another_col int, partition_col timestamp) PARTITION BY RANGE (partition_col); +SELECT create_distributed_table('part_tbl_sing', NULL, colocate_with:='none'); + +-- create a partition with a long name and another with a short name +CREATE TABLE partition_table_with_very_long_name PARTITION OF part_tbl_sing FOR VALUES FROM ('2018-01-01') TO ('2019-01-01'); +CREATE TABLE p PARTITION OF part_tbl_sing FOR VALUES FROM ('2019-01-01') TO ('2020-01-01'); + +-- create an index on parent table +-- we will see that it doesn't matter whether we name the index on parent or not +-- indexes auto-generated on partitions will not use this name +-- SELECT fix_partition_shard_index_names('dist_partitioned_table') will be executed +-- automatically at the end of the CREATE INDEX command +CREATE INDEX short ON part_tbl_sing USING btree (another_col, partition_col); + +SELECT tablename, indexname FROM pg_indexes WHERE schemaname = 'null_dist_key_udfs' AND tablename SIMILAR TO 'p%' ORDER BY 1, 2; + +SELECT nodeport AS part_tbl_sing_port +FROM citus_shards +WHERE table_name = 'part_tbl_sing'::regclass AND + nodeport IN (:worker_1_port, :worker_2_port) \gset + +\c - - - :part_tbl_sing_port +-- the names are generated correctly +-- shard id has been appended to all index names which didn't end in shard id +-- this goes in line with Citus's way of naming indexes of shards: always append shardid to the end +SELECT tablename, indexname FROM pg_indexes WHERE schemaname = 'null_dist_key_udfs' AND tablename SIMILAR TO 'p%\_\d*' ORDER BY 1, 2; + +\c - - - :master_port +SET search_path TO null_dist_key_udfs; + +--test isolate_tenant_to_new_shard +CREATE TABLE iso_tbl (a INT); +SELECT create_distributed_table('iso_tbl', NULL, colocate_with:='none'); +SELECT isolate_tenant_to_new_shard('iso_tbl', 5); + +-- test replicate_table_shards +CREATE TABLE rep_tbl (a INT); +SELECT create_distributed_table('rep_tbl', NULL, colocate_with:='none'); +SELECT replicate_table_shards('rep_tbl'); SET client_min_messages TO WARNING; DROP SCHEMA null_dist_key_udfs CASCADE; From f4b2494d0c251718c7676532079e1c70fcf786a6 Mon Sep 17 00:00:00 2001 From: ahmet gedemenli Date: Fri, 2 Jun 2023 14:05:15 +0300 Subject: [PATCH 063/118] Disable update_distributed_table_colocation for tenant tables --- .../distributed/commands/schema_based_sharding.c | 15 +++++++++++++++ src/backend/distributed/utils/colocation_utils.c | 2 ++ src/include/distributed/commands.h | 1 + .../regress/expected/schema_based_sharding.out | 16 ++++++++++++++-- src/test/regress/sql/schema_based_sharding.sql | 7 ++++++- 5 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/backend/distributed/commands/schema_based_sharding.c b/src/backend/distributed/commands/schema_based_sharding.c index 678835821..ce33b51a1 100644 --- a/src/backend/distributed/commands/schema_based_sharding.c +++ b/src/backend/distributed/commands/schema_based_sharding.c @@ -242,3 +242,18 @@ citus_internal_unregister_tenant_schema_globally(PG_FUNCTION_ARGS) PG_RETURN_VOID(); } + + +/* + * ErrorIfTenantTable errors out with the given operation name, + * if the given relation is a tenant table. + */ +void +ErrorIfTenantTable(Oid relationId, char *operationName) +{ + if (IsTenantSchema(get_rel_namespace(relationId))) + { + ereport(ERROR, (errmsg("%s is not allowed for %s because it is a tenant table", + get_rel_name(relationId), operationName))); + } +} diff --git a/src/backend/distributed/utils/colocation_utils.c b/src/backend/distributed/utils/colocation_utils.c index 8f8dade6b..dba791ba4 100644 --- a/src/backend/distributed/utils/colocation_utils.c +++ b/src/backend/distributed/utils/colocation_utils.c @@ -116,6 +116,7 @@ update_distributed_table_colocation(PG_FUNCTION_ARGS) text *colocateWithTableNameText = PG_GETARG_TEXT_P(1); EnsureTableOwner(targetRelationId); + ErrorIfTenantTable(targetRelationId, "update_distributed_table_colocation"); char *colocateWithTableName = text_to_cstring(colocateWithTableNameText); if (IsColocateWithNone(colocateWithTableName)) @@ -126,6 +127,7 @@ update_distributed_table_colocation(PG_FUNCTION_ARGS) else { Oid colocateWithTableId = ResolveRelationId(colocateWithTableNameText, false); + ErrorIfTenantTable(colocateWithTableId, "colocate_with"); EnsureTableOwner(colocateWithTableId); MarkTablesColocated(colocateWithTableId, targetRelationId); } diff --git a/src/include/distributed/commands.h b/src/include/distributed/commands.h index fd67da301..3064f83cd 100644 --- a/src/include/distributed/commands.h +++ b/src/include/distributed/commands.h @@ -789,5 +789,6 @@ extern bool IsTenantSchema(Oid schemaId); extern void ErrorIfIllegalPartitioningInTenantSchema(Oid parentRelationId, Oid partitionRelationId); extern void CreateTenantSchemaTable(Oid relationId); +extern void ErrorIfTenantTable(Oid relationId, char *operationName); #endif /*CITUS_COMMANDS_H */ diff --git a/src/test/regress/expected/schema_based_sharding.out b/src/test/regress/expected/schema_based_sharding.out index 6beb5ddd7..32ad6b1a2 100644 --- a/src/test/regress/expected/schema_based_sharding.out +++ b/src/test/regress/expected/schema_based_sharding.out @@ -34,6 +34,12 @@ SELECT 1 FROM citus_remove_node('localhost', :worker_2_port); (1 row) CREATE TABLE regular_schema.test_table(a int, b text); +SELECT create_distributed_table('regular_schema.test_table', 'a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + SET citus.enable_schema_based_sharding TO ON; -- show that regular_schema doesn't show up in pg_dist_tenant_schema SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'regular_schema'; @@ -68,6 +74,12 @@ SELECT create_reference_table('tenant_2.test_table'); ERROR: table "test_table" is already distributed SELECT citus_add_local_table_to_metadata('tenant_2.test_table'); ERROR: table "test_table" is already distributed +-- verify we don't allow update_distributed_table_colocation for tenant tables +SELECT update_distributed_table_colocation('tenant_2.test_table', colocate_with => 'none'); +ERROR: test_table is not allowed for update_distributed_table_colocation because it is a tenant table +-- verify we also don't allow colocate_with a tenant table +SELECT update_distributed_table_colocation('regular_schema.test_table', colocate_with => 'tenant_2.test_table'); +ERROR: test_table is not allowed for colocate_with because it is a tenant table -- (on coordinator) verify that colocation id is set for empty tenants too SELECT colocationid > 0 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text IN ('tenant_1', 'tenant_3'); @@ -235,8 +247,8 @@ SELECT EXISTS( (1 row) INSERT INTO tenant_4.another_partitioned_table VALUES (1, 'a'); -ERROR: insert or update on table "another_partitioned_table_child_1920008" violates foreign key constraint "another_partitioned_table_a_fkey_1920007" -DETAIL: Key (a)=(1) is not present in table "partitioned_table_1920005". +ERROR: insert or update on table "another_partitioned_table_child_1920040" violates foreign key constraint "another_partitioned_table_a_fkey_1920039" +DETAIL: Key (a)=(1) is not present in table "partitioned_table_1920037". CONTEXT: while executing command on localhost:xxxxx INSERT INTO tenant_4.partitioned_table VALUES (1, 'a'); INSERT INTO tenant_4.another_partitioned_table VALUES (1, 'a'); diff --git a/src/test/regress/sql/schema_based_sharding.sql b/src/test/regress/sql/schema_based_sharding.sql index 12dec9f0b..fc75ca4cc 100644 --- a/src/test/regress/sql/schema_based_sharding.sql +++ b/src/test/regress/sql/schema_based_sharding.sql @@ -25,7 +25,7 @@ SELECT citus_internal_unregister_tenant_schema_globally('regular_schema'::regnam SELECT 1 FROM citus_remove_node('localhost', :worker_2_port); CREATE TABLE regular_schema.test_table(a int, b text); - +SELECT create_distributed_table('regular_schema.test_table', 'a'); SET citus.enable_schema_based_sharding TO ON; -- show that regular_schema doesn't show up in pg_dist_tenant_schema @@ -55,6 +55,11 @@ SELECT create_distributed_table('tenant_2.test_table', 'a'); SELECT create_reference_table('tenant_2.test_table'); SELECT citus_add_local_table_to_metadata('tenant_2.test_table'); +-- verify we don't allow update_distributed_table_colocation for tenant tables +SELECT update_distributed_table_colocation('tenant_2.test_table', colocate_with => 'none'); +-- verify we also don't allow colocate_with a tenant table +SELECT update_distributed_table_colocation('regular_schema.test_table', colocate_with => 'tenant_2.test_table'); + -- (on coordinator) verify that colocation id is set for empty tenants too SELECT colocationid > 0 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text IN ('tenant_1', 'tenant_3'); From 4b67e398b11676e6e8d518a16cfa584b0d019d52 Mon Sep 17 00:00:00 2001 From: ahmet gedemenli Date: Fri, 2 Jun 2023 14:08:03 +0300 Subject: [PATCH 064/118] Disable undistribute_table for tenant tables --- src/backend/distributed/commands/alter_table.c | 2 ++ src/test/regress/expected/schema_based_sharding.out | 3 +++ src/test/regress/sql/schema_based_sharding.sql | 2 ++ 3 files changed, 7 insertions(+) diff --git a/src/backend/distributed/commands/alter_table.c b/src/backend/distributed/commands/alter_table.c index a0359d335..3728e7470 100644 --- a/src/backend/distributed/commands/alter_table.c +++ b/src/backend/distributed/commands/alter_table.c @@ -380,6 +380,8 @@ UndistributeTable(TableConversionParameters *params) "because the table is not distributed"))); } + ErrorIfTenantTable(params->relationId, "undistribute_table"); + if (!params->cascadeViaForeignKeys) { EnsureTableNotReferencing(params->relationId, UNDISTRIBUTE_TABLE); diff --git a/src/test/regress/expected/schema_based_sharding.out b/src/test/regress/expected/schema_based_sharding.out index 32ad6b1a2..0a529398a 100644 --- a/src/test/regress/expected/schema_based_sharding.out +++ b/src/test/regress/expected/schema_based_sharding.out @@ -80,6 +80,9 @@ ERROR: test_table is not allowed for update_distributed_table_colocation becaus -- verify we also don't allow colocate_with a tenant table SELECT update_distributed_table_colocation('regular_schema.test_table', colocate_with => 'tenant_2.test_table'); ERROR: test_table is not allowed for colocate_with because it is a tenant table +-- verify we don't allow undistribute_table for tenant tables +SELECT undistribute_table('tenant_2.test_table'); +ERROR: test_table is not allowed for undistribute_table because it is a tenant table -- (on coordinator) verify that colocation id is set for empty tenants too SELECT colocationid > 0 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text IN ('tenant_1', 'tenant_3'); diff --git a/src/test/regress/sql/schema_based_sharding.sql b/src/test/regress/sql/schema_based_sharding.sql index fc75ca4cc..469145a91 100644 --- a/src/test/regress/sql/schema_based_sharding.sql +++ b/src/test/regress/sql/schema_based_sharding.sql @@ -59,6 +59,8 @@ SELECT citus_add_local_table_to_metadata('tenant_2.test_table'); SELECT update_distributed_table_colocation('tenant_2.test_table', colocate_with => 'none'); -- verify we also don't allow colocate_with a tenant table SELECT update_distributed_table_colocation('regular_schema.test_table', colocate_with => 'tenant_2.test_table'); +-- verify we don't allow undistribute_table for tenant tables +SELECT undistribute_table('tenant_2.test_table'); -- (on coordinator) verify that colocation id is set for empty tenants too SELECT colocationid > 0 FROM pg_dist_tenant_schema From f68ea2000932b97d9b05ffe80024d6b32306aff5 Mon Sep 17 00:00:00 2001 From: ahmet gedemenli Date: Fri, 2 Jun 2023 14:24:14 +0300 Subject: [PATCH 065/118] Disable alter_distributed_table for tenant tables --- .../distributed/commands/alter_table.c | 22 +++++++++++++++++++ .../expected/schema_based_sharding.out | 6 +++++ .../regress/sql/schema_based_sharding.sql | 4 ++++ 3 files changed, 32 insertions(+) diff --git a/src/backend/distributed/commands/alter_table.c b/src/backend/distributed/commands/alter_table.c index 3728e7470..40324cfcb 100644 --- a/src/backend/distributed/commands/alter_table.c +++ b/src/backend/distributed/commands/alter_table.c @@ -196,6 +196,7 @@ static void EnsureTableNotReferencing(Oid relationId, char conversionType); static void EnsureTableNotReferenced(Oid relationId, char conversionType); static void EnsureTableNotForeign(Oid relationId); static void EnsureTableNotPartition(Oid relationId); +static void ErrorIfColocateWithTenantTable(char *colocateWith); static TableConversionState * CreateTableConversion(TableConversionParameters *params); static void CreateDistributedTableLike(TableConversionState *con); static void CreateCitusTableLike(TableConversionState *con); @@ -437,6 +438,9 @@ AlterDistributedTable(TableConversionParameters *params) "is not distributed"))); } + ErrorIfTenantTable(params->relationId, "alter_distributed_table"); + ErrorIfColocateWithTenantTable(params->colocateWith); + EnsureTableNotForeign(params->relationId); EnsureTableNotPartition(params->relationId); EnsureHashDistributedTable(params->relationId); @@ -1182,6 +1186,24 @@ EnsureTableNotPartition(Oid relationId) } +/* + * ErrorIfColocateWithTenantTable errors out if given colocateWith text refers to + * a tenant table. + */ +void +ErrorIfColocateWithTenantTable(char *colocateWith) +{ + if (colocateWith != NULL && + !IsColocateWithDefault(colocateWith) && + !IsColocateWithNone(colocateWith)) + { + text *colocateWithTableNameText = cstring_to_text(colocateWith); + Oid colocateWithTableId = ResolveRelationId(colocateWithTableNameText, false); + ErrorIfTenantTable(colocateWithTableId, "colocate_with"); + } +} + + TableConversionState * CreateTableConversion(TableConversionParameters *params) { diff --git a/src/test/regress/expected/schema_based_sharding.out b/src/test/regress/expected/schema_based_sharding.out index 0a529398a..84ca562e3 100644 --- a/src/test/regress/expected/schema_based_sharding.out +++ b/src/test/regress/expected/schema_based_sharding.out @@ -83,6 +83,12 @@ ERROR: test_table is not allowed for colocate_with because it is a tenant table -- verify we don't allow undistribute_table for tenant tables SELECT undistribute_table('tenant_2.test_table'); ERROR: test_table is not allowed for undistribute_table because it is a tenant table +-- verify we don't allow alter_distributed_table for tenant tables +SELECT alter_distributed_table('tenant_2.test_table', colocate_with => 'none'); +ERROR: test_table is not allowed for alter_distributed_table because it is a tenant table +-- verify we also don't allow colocate_with a tenant table +SELECT alter_distributed_table('regular_schema.test_table', colocate_with => 'tenant_2.test_table'); +ERROR: test_table is not allowed for colocate_with because it is a tenant table -- (on coordinator) verify that colocation id is set for empty tenants too SELECT colocationid > 0 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text IN ('tenant_1', 'tenant_3'); diff --git a/src/test/regress/sql/schema_based_sharding.sql b/src/test/regress/sql/schema_based_sharding.sql index 469145a91..f24628731 100644 --- a/src/test/regress/sql/schema_based_sharding.sql +++ b/src/test/regress/sql/schema_based_sharding.sql @@ -61,6 +61,10 @@ SELECT update_distributed_table_colocation('tenant_2.test_table', colocate_with SELECT update_distributed_table_colocation('regular_schema.test_table', colocate_with => 'tenant_2.test_table'); -- verify we don't allow undistribute_table for tenant tables SELECT undistribute_table('tenant_2.test_table'); +-- verify we don't allow alter_distributed_table for tenant tables +SELECT alter_distributed_table('tenant_2.test_table', colocate_with => 'none'); +-- verify we also don't allow colocate_with a tenant table +SELECT alter_distributed_table('regular_schema.test_table', colocate_with => 'tenant_2.test_table'); -- (on coordinator) verify that colocation id is set for empty tenants too SELECT colocationid > 0 FROM pg_dist_tenant_schema From fccfee08b6b29fba2a87127cc1cc9f85295a45a0 Mon Sep 17 00:00:00 2001 From: ahmet gedemenli Date: Fri, 2 Jun 2023 14:28:58 +0300 Subject: [PATCH 066/118] Style --- src/backend/distributed/commands/schema_based_sharding.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/distributed/commands/schema_based_sharding.c b/src/backend/distributed/commands/schema_based_sharding.c index ce33b51a1..f33debb34 100644 --- a/src/backend/distributed/commands/schema_based_sharding.c +++ b/src/backend/distributed/commands/schema_based_sharding.c @@ -254,6 +254,6 @@ ErrorIfTenantTable(Oid relationId, char *operationName) if (IsTenantSchema(get_rel_namespace(relationId))) { ereport(ERROR, (errmsg("%s is not allowed for %s because it is a tenant table", - get_rel_name(relationId), operationName))); + get_rel_name(relationId), operationName))); } } From 2bd6ff0e93c09f32fe534a40554bbdac23d3f390 Mon Sep 17 00:00:00 2001 From: ahmet gedemenli Date: Fri, 2 Jun 2023 15:25:14 +0300 Subject: [PATCH 067/118] Use schema name in the error msg --- .../distributed/commands/schema_based_sharding.c | 3 ++- src/test/regress/expected/schema_based_sharding.out | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/backend/distributed/commands/schema_based_sharding.c b/src/backend/distributed/commands/schema_based_sharding.c index f33debb34..b7f1ba70a 100644 --- a/src/backend/distributed/commands/schema_based_sharding.c +++ b/src/backend/distributed/commands/schema_based_sharding.c @@ -254,6 +254,7 @@ ErrorIfTenantTable(Oid relationId, char *operationName) if (IsTenantSchema(get_rel_namespace(relationId))) { ereport(ERROR, (errmsg("%s is not allowed for %s because it is a tenant table", - get_rel_name(relationId), operationName))); + generate_qualified_relation_name(relationId), + operationName))); } } diff --git a/src/test/regress/expected/schema_based_sharding.out b/src/test/regress/expected/schema_based_sharding.out index 84ca562e3..e55b1cd2b 100644 --- a/src/test/regress/expected/schema_based_sharding.out +++ b/src/test/regress/expected/schema_based_sharding.out @@ -76,19 +76,19 @@ SELECT citus_add_local_table_to_metadata('tenant_2.test_table'); ERROR: table "test_table" is already distributed -- verify we don't allow update_distributed_table_colocation for tenant tables SELECT update_distributed_table_colocation('tenant_2.test_table', colocate_with => 'none'); -ERROR: test_table is not allowed for update_distributed_table_colocation because it is a tenant table +ERROR: tenant_2.test_table is not allowed for update_distributed_table_colocation because it is a tenant table -- verify we also don't allow colocate_with a tenant table SELECT update_distributed_table_colocation('regular_schema.test_table', colocate_with => 'tenant_2.test_table'); -ERROR: test_table is not allowed for colocate_with because it is a tenant table +ERROR: tenant_2.test_table is not allowed for colocate_with because it is a tenant table -- verify we don't allow undistribute_table for tenant tables SELECT undistribute_table('tenant_2.test_table'); -ERROR: test_table is not allowed for undistribute_table because it is a tenant table +ERROR: tenant_2.test_table is not allowed for undistribute_table because it is a tenant table -- verify we don't allow alter_distributed_table for tenant tables SELECT alter_distributed_table('tenant_2.test_table', colocate_with => 'none'); -ERROR: test_table is not allowed for alter_distributed_table because it is a tenant table +ERROR: tenant_2.test_table is not allowed for alter_distributed_table because it is a tenant table -- verify we also don't allow colocate_with a tenant table SELECT alter_distributed_table('regular_schema.test_table', colocate_with => 'tenant_2.test_table'); -ERROR: test_table is not allowed for colocate_with because it is a tenant table +ERROR: tenant_2.test_table is not allowed for colocate_with because it is a tenant table -- (on coordinator) verify that colocation id is set for empty tenants too SELECT colocationid > 0 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text IN ('tenant_1', 'tenant_3'); From 3fda2c325413a3726824ce5f463d60ea84281eb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emel=20=C5=9Eim=C5=9Fek?= Date: Mon, 5 Jun 2023 10:37:48 +0300 Subject: [PATCH 068/118] Change test files in multi and multi-1 schedules to accommodate coordinator in the metadata. (#6939) Changes test files in multi and multi-1 schedules such that they accomodate coordinator in metadata. Changes fall into the following buckets: 1. When coordinator is in metadata, reference table shards are present in coordinator too. This changes test outputs checking the table size, shard numbers etc. for reference tables. 2. When coordinator is in metadata, postgres tables are converted to citus local tables whenever a foreign key relationship to them is created. This changes some test cases which tests it should not be possible to create foreign keys to postgres tables. 3. Remove lines that add/remove coordinator for testing purposes. --- src/test/regress/citus_tests/run_test.py | 4 + src/test/regress/columnar_schedule | 1 + src/test/regress/enterprise_schedule | 2 +- .../adaptive_executor_repartition.out | 3 +- src/test/regress/expected/add_coordinator.out | 7 + .../expected/alter_distributed_table.out | 8 +- .../alter_table_set_access_method.out | 6 - .../regress/expected/check_cluster_state.out | 6 + .../expected/citus_local_dist_joins.out | 7 - .../regress/expected/citus_table_triggers.out | 2 +- .../citus_update_table_statistics.out | 12 + .../expected/distributed_functions.out | 4 +- src/test/regress/expected/dml_recursive.out | 3 +- .../expected/fast_path_router_modify.out | 3 +- .../foreign_key_restriction_enforcement.out | 24 +- .../regress/expected/function_propagation.out | 7 - .../grant_on_foreign_server_propagation.out | 12 +- .../insert_select_into_local_table.out | 2 +- .../expected/insert_select_repartition.out | 12 +- .../expected/insert_select_repartition_0.out | 12 +- .../insert_select_single_shard_table.out | 13 - .../expected/intermediate_result_pruning.out | 2 + .../intermediate_result_pruning_0.out | 2 + .../regress/expected/intermediate_results.out | 2 + src/test/regress/expected/join_pushdown.out | 3 +- .../expected/limit_intermediate_size.out | 2 +- .../regress/expected/local_table_join.out | 28 +- .../regress/expected/logical_replication.out | 13 - src/test/regress/expected/merge.out | 13 - ...ter_table_add_constraints_without_name.out | 16 +- ...ter_table_add_foreign_key_without_name.out | 71 ++--- .../regress/expected/multi_citus_tools.out | 21 +- .../expected/multi_cluster_management.out | 42 ++- .../expected/multi_colocation_utils.out | 4 +- src/test/regress/expected/multi_copy.out | 3 +- .../expected/multi_create_table_superuser.out | 6 +- .../regress/expected/multi_drop_extension.out | 12 +- src/test/regress/expected/multi_explain.out | 70 ++--- .../multi_fix_partition_shard_index_names.out | 14 - .../regress/expected/multi_foreign_key.out | 26 +- .../regress/expected/multi_insert_select.out | 20 +- .../expected/multi_insert_select_0.out | 20 +- .../expected/multi_insert_select_conflict.out | 3 +- .../multi_insert_select_conflict_0.out | 3 +- .../regress/expected/multi_join_pruning.out | 4 + .../multi_level_recursive_queries.out | 7 +- .../regress/expected/multi_metadata_sync.out | 125 ++++---- .../expected/multi_metadata_sync_0.out | 125 ++++---- .../expected/multi_modifying_xacts.out | 8 +- .../regress/expected/multi_multiuser_auth.out | 2 +- .../multi_multiuser_master_protocol.out | 6 +- .../expected/multi_name_resolution.out | 3 +- .../multi_null_minmax_value_pruning.out | 96 +++++- .../regress/expected/multi_partitioning.out | 15 +- .../regress/expected/multi_poolinfo_usage.out | 2 +- .../expected/multi_read_from_secondaries.out | 7 +- .../expected/multi_real_time_transaction.out | 3 +- .../multi_remove_node_reference_table.out | 26 +- .../multi_repartition_join_planning.out | 191 +++++++++++- .../multi_repartition_join_pruning.out | 290 +++++++++++++++++- ...multi_repartition_join_task_assignment.out | 84 +++++ .../expected/multi_repartition_udt.out | 6 +- .../multi_replicate_reference_table.out | 48 +-- .../regress/expected/multi_router_planner.out | 169 +++++++++- .../regress/expected/multi_schema_support.out | 7 +- .../expected/multi_sequence_default.out | 13 - .../regress/expected/multi_simple_queries.out | 78 +++++ .../regress/expected/multi_size_queries.out | 14 +- src/test/regress/expected/multi_table_ddl.out | 6 + .../expected/multi_tenant_isolation.out | 11 +- .../multi_tenant_isolation_nonblocking.out | 6 + .../expected/multi_transaction_recovery.out | 17 - .../multi_transactional_drop_shards.out | 18 +- src/test/regress/expected/multi_truncate.out | 26 +- src/test/regress/expected/multi_utilities.out | 14 + src/test/regress/expected/pg12.out | 13 - src/test/regress/expected/pg14.out | 12 - src/test/regress/expected/pg15.out | 30 +- src/test/regress/expected/pgmerge.out | 13 - .../expected/propagate_foreign_servers.out | 7 - src/test/regress/expected/publication.out | 14 - src/test/regress/expected/publication_0.out | 13 - .../expected/query_single_shard_table.out | 13 - .../regress/expected/recurring_outer_join.out | 14 - ..._dml_with_different_planners_executors.out | 6 +- ...relation_planning_restriction_pushdown.out | 16 +- .../expected/recursive_view_local_table.out | 13 +- .../expected/relation_access_tracking.out | 16 +- .../regress/expected/remove_coordinator.out | 7 + .../remove_coordinator_from_metadata.out | 6 + .../replicated_table_disable_node.out | 4 +- .../expected/run_command_on_all_nodes.out | 12 + .../expected/sequential_modifications.out | 5 +- .../set_operation_and_local_tables.out | 32 +- src/test/regress/expected/set_operations.out | 61 +++- .../expected/shard_move_constraints.out | 3 +- .../shard_move_constraints_blocking.out | 3 +- .../expected/single_hash_repartition_join.out | 26 ++ .../regress/expected/subquery_and_cte.out | 11 +- src/test/regress/expected/subquery_append.out | 4 +- src/test/regress/expected/subquery_basics.out | 1 + src/test/regress/expected/subquery_view.out | 38 +-- src/test/regress/expected/tableam.out | 5 +- src/test/regress/expected/union_pushdown.out | 11 +- src/test/regress/expected/values.out | 6 +- src/test/regress/expected/with_dml.out | 6 +- src/test/regress/expected/with_executors.out | 4 +- src/test/regress/expected/with_join.out | 5 +- src/test/regress/expected/with_modifying.out | 2 +- src/test/regress/multi_1_schedule | 1 + src/test/regress/multi_mx_schedule | 1 + src/test/regress/multi_schedule | 1 + src/test/regress/split_schedule | 1 + src/test/regress/sql/add_coordinator.sql | 2 + .../regress/sql/alter_distributed_table.sql | 9 +- .../sql/alter_table_set_access_method.sql | 1 - src/test/regress/sql/check_cluster_state.sql | 1 + .../regress/sql/citus_local_dist_joins.sql | 3 - .../regress/sql/distributed_functions.sql | 5 +- .../foreign_key_restriction_enforcement.sql | 4 + src/test/regress/sql/function_propagation.sql | 2 - .../grant_on_foreign_server_propagation.sql | 3 +- .../sql/insert_select_single_shard_table.sql | 5 - src/test/regress/sql/intermediate_results.sql | 2 + .../regress/sql/limit_intermediate_size.sql | 2 +- src/test/regress/sql/local_table_join.sql | 9 +- src/test/regress/sql/logical_replication.sql | 4 - src/test/regress/sql/merge.sql | 2 - ...ter_table_add_constraints_without_name.sql | 6 - ...ter_table_add_foreign_key_without_name.sql | 6 +- .../regress/sql/multi_cluster_management.sql | 1 + src/test/regress/sql/multi_drop_extension.sql | 3 +- src/test/regress/sql/multi_explain.sql | 4 - .../multi_fix_partition_shard_index_names.sql | 5 - src/test/regress/sql/multi_foreign_key.sql | 5 - src/test/regress/sql/multi_join_pruning.sql | 5 + .../sql/multi_level_recursive_queries.sql | 2 +- src/test/regress/sql/multi_metadata_sync.sql | 3 +- src/test/regress/sql/multi_partitioning.sql | 3 - .../sql/multi_remove_node_reference_table.sql | 8 +- .../sql/multi_repartition_join_planning.sql | 1 + .../sql/multi_replicate_reference_table.sql | 8 +- .../regress/sql/multi_sequence_default.sql | 2 - src/test/regress/sql/multi_table_ddl.sql | 1 + .../regress/sql/multi_tenant_isolation.sql | 11 +- .../multi_tenant_isolation_nonblocking.sql | 3 + .../sql/multi_transaction_recovery.sql | 9 - .../sql/multi_transactional_drop_shards.sql | 3 - src/test/regress/sql/pg12.sql | 4 - src/test/regress/sql/pg14.sql | 3 - src/test/regress/sql/pg15.sql | 13 +- src/test/regress/sql/pgmerge.sql | 3 - .../regress/sql/propagate_foreign_servers.sql | 2 - src/test/regress/sql/publication.sql | 7 - .../regress/sql/query_single_shard_table.sql | 5 - src/test/regress/sql/recurring_outer_join.sql | 7 - ..._dml_with_different_planners_executors.sql | 3 +- .../sql/recursive_view_local_table.sql | 2 +- .../regress/sql/relation_access_tracking.sql | 1 + src/test/regress/sql/remove_coordinator.sql | 3 + .../sql/remove_coordinator_from_metadata.sql | 1 + .../regress/sql/run_command_on_all_nodes.sql | 5 + .../regress/sql/sequential_modifications.sql | 2 +- .../sql/set_operation_and_local_tables.sql | 3 +- src/test/regress/sql/set_operations.sql | 2 +- src/test/regress/sql/subquery_and_cte.sql | 3 +- src/test/regress/sql/subquery_append.sql | 1 + src/test/regress/sql/subquery_basics.sql | 1 + src/test/regress/sql/subquery_view.sql | 1 + src/test/regress/sql/union_pushdown.sql | 3 +- src/test/regress/sql/values.sql | 2 +- src/test/regress/sql/with_dml.sql | 2 +- src/test/regress/sql/with_executors.sql | 1 + 173 files changed, 1676 insertions(+), 956 deletions(-) create mode 100644 src/test/regress/expected/check_cluster_state.out create mode 100644 src/test/regress/expected/remove_coordinator_from_metadata.out create mode 100644 src/test/regress/sql/check_cluster_state.sql create mode 100644 src/test/regress/sql/remove_coordinator_from_metadata.sql diff --git a/src/test/regress/citus_tests/run_test.py b/src/test/regress/citus_tests/run_test.py index 04bf606f8..1ce2cfd9f 100755 --- a/src/test/regress/citus_tests/run_test.py +++ b/src/test/regress/citus_tests/run_test.py @@ -127,6 +127,9 @@ DEPS = { "multi_mx_function_table_reference", ], ), + "alter_distributed_table": TestDeps( + "minimal_schedule", ["multi_behavioral_analytics_create_table"] + ), "background_rebalance": TestDeps( None, [ @@ -144,6 +147,7 @@ DEPS = { worker_count=6, ), "function_propagation": TestDeps("minimal_schedule"), + "grant_on_foreign_server_propagation": TestDeps("minimal_schedule"), "multi_mx_modifying_xacts": TestDeps(None, ["multi_mx_create_table"]), "multi_mx_router_planner": TestDeps(None, ["multi_mx_create_table"]), "multi_mx_copy_data": TestDeps(None, ["multi_mx_create_table"]), diff --git a/src/test/regress/columnar_schedule b/src/test/regress/columnar_schedule index 11e9494d2..602af0fc7 100644 --- a/src/test/regress/columnar_schedule +++ b/src/test/regress/columnar_schedule @@ -2,6 +2,7 @@ test: multi_test_helpers multi_test_helpers_superuser columnar_test_helpers test: multi_cluster_management test: multi_test_catalog_views +test: remove_coordinator_from_metadata test: columnar_create test: columnar_load test: columnar_query diff --git a/src/test/regress/enterprise_schedule b/src/test/regress/enterprise_schedule index 55791d43a..9a832c4d6 100644 --- a/src/test/regress/enterprise_schedule +++ b/src/test/regress/enterprise_schedule @@ -16,11 +16,11 @@ test: add_coordinator test: citus_local_tables_ent -test: remove_coordinator # -------- test: publication test: logical_replication +test: check_cluster_state test: multi_create_table test: multi_create_table_superuser test: multi_create_role_dependency diff --git a/src/test/regress/expected/adaptive_executor_repartition.out b/src/test/regress/expected/adaptive_executor_repartition.out index a84677a35..3ac9e6a13 100644 --- a/src/test/regress/expected/adaptive_executor_repartition.out +++ b/src/test/regress/expected/adaptive_executor_repartition.out @@ -168,10 +168,11 @@ select count(*) from trips t1, cars r1, trips t2, cars r2 where t1.trip_id = t2. (1 row) DROP SCHEMA adaptive_executor CASCADE; -NOTICE: drop cascades to 6 other objects +NOTICE: drop cascades to 7 other objects DETAIL: drop cascades to table ab drop cascades to table single_hash_repartition_first drop cascades to table single_hash_repartition_second drop cascades to table ref_table +drop cascades to table ref_table_361397 drop cascades to table cars drop cascades to table trips diff --git a/src/test/regress/expected/add_coordinator.out b/src/test/regress/expected/add_coordinator.out index 01f3a682d..499669385 100644 --- a/src/test/regress/expected/add_coordinator.out +++ b/src/test/regress/expected/add_coordinator.out @@ -2,6 +2,13 @@ -- ADD_COORDINATOR -- -- node trying to add itself without specifying groupid => 0 should error out +-- first remove the coordinator to for testing master_add_node for coordinator +SELECT master_remove_node('localhost', :master_port); + master_remove_node +--------------------------------------------------------------------- + +(1 row) + SELECT master_add_node('localhost', :master_port); ERROR: Node cannot add itself as a worker. HINT: Add the node as a coordinator by using: SELECT citus_set_coordinator_host('localhost', 57636); diff --git a/src/test/regress/expected/alter_distributed_table.out b/src/test/regress/expected/alter_distributed_table.out index b8b86cd11..9d968dbb1 100644 --- a/src/test/regress/expected/alter_distributed_table.out +++ b/src/test/regress/expected/alter_distributed_table.out @@ -528,8 +528,8 @@ SELECT COUNT(DISTINCT colocationid) FROM pg_dist_partition WHERE logicalrelid::r -- test references CREATE TABLE referenced_dist_table (a INT UNIQUE); CREATE TABLE referenced_ref_table (a INT UNIQUE); -CREATE TABLE table_with_references (a1 INT UNIQUE REFERENCES referenced_dist_table(a), a2 INT REFERENCES referenced_ref_table(a)); -CREATE TABLE referencing_dist_table (a INT REFERENCES table_with_references(a1)); +CREATE TABLE table_with_references (a1 INT UNIQUE, a2 INT); +CREATE TABLE referencing_dist_table (a INT); SELECT create_distributed_table('referenced_dist_table', 'a', colocate_with:='none'); create_distributed_table --------------------------------------------------------------------- @@ -554,6 +554,9 @@ SELECT create_distributed_table('referencing_dist_table', 'a', colocate_with:='r (1 row) +ALTER TABLE table_with_references ADD FOREIGN KEY (a1) REFERENCES referenced_dist_table(a); +ALTER TABLE table_with_references ADD FOREIGN KEY (a2) REFERENCES referenced_ref_table(a); +ALTER TABLE referencing_dist_table ADD FOREIGN KEY (a) REFERENCES table_with_references(a1); SET client_min_messages TO WARNING; SELECT conrelid::regclass::text AS "Referencing Table", pg_get_constraintdef(oid, true) AS "Definition" FROM pg_constraint WHERE (conrelid::regclass::text = 'table_with_references' OR confrelid::regclass::text = 'table_with_references') AND contype = 'f' ORDER BY 1,2; @@ -1255,3 +1258,4 @@ SELECT run_command_on_workers($$SELECT count(*) FROM pg_matviews WHERE matviewna RESET search_path; DROP SCHEMA alter_distributed_table CASCADE; DROP SCHEMA schema_to_test_alter_dist_table CASCADE; +DROP USER alter_dist_table_test_user; diff --git a/src/test/regress/expected/alter_table_set_access_method.out b/src/test/regress/expected/alter_table_set_access_method.out index 63d0990d4..8a6f335a7 100644 --- a/src/test/regress/expected/alter_table_set_access_method.out +++ b/src/test/regress/expected/alter_table_set_access_method.out @@ -802,9 +802,3 @@ select alter_table_set_access_method('view_test_view','columnar'); ERROR: you cannot alter access method of a view SET client_min_messages TO WARNING; DROP SCHEMA alter_table_set_access_method CASCADE; -SELECT 1 FROM master_remove_node('localhost', :master_port); - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - diff --git a/src/test/regress/expected/check_cluster_state.out b/src/test/regress/expected/check_cluster_state.out new file mode 100644 index 000000000..c66119d52 --- /dev/null +++ b/src/test/regress/expected/check_cluster_state.out @@ -0,0 +1,6 @@ +SELECT count(*) >= 1 as coordinator_exists FROM pg_dist_node WHERE groupid = 0 AND isactive; + coordinator_exists +--------------------------------------------------------------------- + t +(1 row) + diff --git a/src/test/regress/expected/citus_local_dist_joins.out b/src/test/regress/expected/citus_local_dist_joins.out index 25833fc05..44101c925 100644 --- a/src/test/regress/expected/citus_local_dist_joins.out +++ b/src/test/regress/expected/citus_local_dist_joins.out @@ -1,7 +1,6 @@ CREATE SCHEMA citus_local_dist_joins; SET search_path TO citus_local_dist_joins; SET client_min_messages to ERROR; -SELECT master_add_node('localhost', :master_port, groupId => 0) AS coordinator_nodeid \gset CREATE TABLE citus_local(key int, value text); SELECT citus_add_local_table_to_metadata('citus_local'); citus_add_local_table_to_metadata @@ -523,11 +522,5 @@ ERROR: recursive complex joins are only supported when all distributed tables a RESET citus.local_table_join_policy; SET client_min_messages to ERROR; DROP TABLE citus_local; -SELECT master_remove_node('localhost', :master_port); - master_remove_node ---------------------------------------------------------------------- - -(1 row) - \set VERBOSITY terse DROP SCHEMA citus_local_dist_joins CASCADE; diff --git a/src/test/regress/expected/citus_table_triggers.out b/src/test/regress/expected/citus_table_triggers.out index 80273121e..80954f70f 100644 --- a/src/test/regress/expected/citus_table_triggers.out +++ b/src/test/regress/expected/citus_table_triggers.out @@ -155,4 +155,4 @@ SELECT master_get_table_ddl_events('test_table'); -- cleanup at exit DROP SCHEMA table_triggers_schema CASCADE; -NOTICE: drop cascades to 8 other objects +NOTICE: drop cascades to 9 other objects diff --git a/src/test/regress/expected/citus_update_table_statistics.out b/src/test/regress/expected/citus_update_table_statistics.out index 031104c53..d908e433d 100644 --- a/src/test/regress/expected/citus_update_table_statistics.out +++ b/src/test/regress/expected/citus_update_table_statistics.out @@ -64,6 +64,10 @@ SET citus.multi_shard_modify_mode TO sequential; SELECT citus_update_table_statistics('test_table_statistics_hash'); 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 SELECT 0::bigint, NULL::text, 0::bigint; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +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 SELECT 981000 AS shard_id, 'public.test_table_statistics_hash_981000' AS shard_name, pg_total_relation_size('public.test_table_statistics_hash_981000') UNION ALL SELECT 981001 AS shard_id, 'public.test_table_statistics_hash_981001' AS shard_name, pg_total_relation_size('public.test_table_statistics_hash_981001') UNION ALL SELECT 981002 AS shard_id, 'public.test_table_statistics_hash_981002' AS shard_name, pg_total_relation_size('public.test_table_statistics_hash_981002') UNION ALL SELECT 981003 AS shard_id, 'public.test_table_statistics_hash_981003' AS shard_name, pg_total_relation_size('public.test_table_statistics_hash_981003') UNION ALL SELECT 981004 AS shard_id, 'public.test_table_statistics_hash_981004' AS shard_name, pg_total_relation_size('public.test_table_statistics_hash_981004') UNION ALL SELECT 981005 AS shard_id, 'public.test_table_statistics_hash_981005' AS shard_name, pg_total_relation_size('public.test_table_statistics_hash_981005') UNION ALL SELECT 981006 AS shard_id, 'public.test_table_statistics_hash_981006' AS shard_name, pg_total_relation_size('public.test_table_statistics_hash_981006') UNION ALL SELECT 981007 AS shard_id, 'public.test_table_statistics_hash_981007' AS shard_name, pg_total_relation_size('public.test_table_statistics_hash_981007') UNION ALL SELECT 0::bigint, NULL::text, 0::bigint; DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;SELECT assign_distributed_transaction_id(xx, xx, 'xxxxxxx'); @@ -73,6 +77,8 @@ DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing COMMIT DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing COMMIT +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing COMMIT DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx citus_update_table_statistics --------------------------------------------------------------------- @@ -152,6 +158,10 @@ SET citus.multi_shard_modify_mode TO sequential; SELECT citus_update_table_statistics('test_table_statistics_append'); 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 SELECT 0::bigint, NULL::text, 0::bigint; +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +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 SELECT 981008 AS shard_id, 'public.test_table_statistics_append_981008' AS shard_name, pg_total_relation_size('public.test_table_statistics_append_981008') UNION ALL SELECT 981009 AS shard_id, 'public.test_table_statistics_append_981009' AS shard_name, pg_total_relation_size('public.test_table_statistics_append_981009') UNION ALL SELECT 0::bigint, NULL::text, 0::bigint; DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;SELECT assign_distributed_transaction_id(xx, xx, 'xxxxxxx'); @@ -161,6 +171,8 @@ DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing COMMIT DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing COMMIT +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing COMMIT DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx citus_update_table_statistics --------------------------------------------------------------------- diff --git a/src/test/regress/expected/distributed_functions.out b/src/test/regress/expected/distributed_functions.out index bc72af14c..668f97c3f 100644 --- a/src/test/regress/expected/distributed_functions.out +++ b/src/test/regress/expected/distributed_functions.out @@ -1118,7 +1118,7 @@ SET client_min_messages TO error; -- suppress cascading objects dropping DROP SCHEMA function_tests CASCADE; DROP SCHEMA function_tests2 CASCADE; -- clear objects -SELECT stop_metadata_sync_to_node(nodename,nodeport) FROM pg_dist_node WHERE isactive AND noderole = 'primary'; +SELECT stop_metadata_sync_to_node(nodename,nodeport) FROM pg_dist_node WHERE isactive AND noderole = 'primary' AND groupid <> 0; stop_metadata_sync_to_node --------------------------------------------------------------------- @@ -1144,7 +1144,7 @@ SELECT 1 FROM run_command_on_workers($$DROP USER functionuser$$); (2 rows) -- sync metadata again -SELECT start_metadata_sync_to_node(nodename,nodeport) FROM pg_dist_node WHERE isactive AND noderole = 'primary'; +SELECT start_metadata_sync_to_node(nodename,nodeport) FROM pg_dist_node WHERE isactive AND noderole = 'primary' AND groupid <> 0; start_metadata_sync_to_node --------------------------------------------------------------------- diff --git a/src/test/regress/expected/dml_recursive.out b/src/test/regress/expected/dml_recursive.out index b1d521ca2..cc4058def 100644 --- a/src/test/regress/expected/dml_recursive.out +++ b/src/test/regress/expected/dml_recursive.out @@ -357,9 +357,10 @@ DEBUG: generating subplan XXX_1 for subquery SELECT tenant_id FROM recursive_dm DEBUG: Plan XXX query after replacing subqueries and CTEs: UPDATE recursive_dml_queries.local_table SET id = 'citus_test'::text FROM (SELECT distributed_table_1.tenant_id, NULL::integer AS dept, NULL::jsonb AS info FROM (SELECT intermediate_result.tenant_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(tenant_id text)) distributed_table_1) distributed_table WHERE (distributed_table.tenant_id OPERATOR(pg_catalog.=) local_table.id) RESET client_min_messages; DROP SCHEMA recursive_dml_queries CASCADE; -NOTICE: drop cascades to 5 other objects +NOTICE: drop cascades to 6 other objects DETAIL: drop cascades to table distributed_table drop cascades to table second_distributed_table drop cascades to table reference_table +drop cascades to table reference_table_2370008 drop cascades to table local_table drop cascades to view tenant_ids diff --git a/src/test/regress/expected/fast_path_router_modify.out b/src/test/regress/expected/fast_path_router_modify.out index d27b0fb65..6934e6434 100644 --- a/src/test/regress/expected/fast_path_router_modify.out +++ b/src/test/regress/expected/fast_path_router_modify.out @@ -489,8 +489,9 @@ RESET citus.enable_fast_path_router_planner; RESET client_min_messages; RESET citus.log_remote_commands; DROP SCHEMA fast_path_router_modify CASCADE; -NOTICE: drop cascades to 4 other objects +NOTICE: drop cascades to 5 other objects DETAIL: drop cascades to table modify_fast_path drop cascades to table modify_fast_path_replication_2 drop cascades to table modify_fast_path_reference +drop cascades to table modify_fast_path_reference_1840008 drop cascades to function modify_fast_path_plpsql(integer,integer) diff --git a/src/test/regress/expected/foreign_key_restriction_enforcement.out b/src/test/regress/expected/foreign_key_restriction_enforcement.out index 15facd198..f8c9339a7 100644 --- a/src/test/regress/expected/foreign_key_restriction_enforcement.out +++ b/src/test/regress/expected/foreign_key_restriction_enforcement.out @@ -616,12 +616,15 @@ BEGIN; ALTER TABLE reference_table ALTER COLUMN id SET DATA TYPE smallint; DEBUG: rewriting table "reference_table" DEBUG: validating foreign key constraint "fkey" +DEBUG: rewriting table "reference_table_2380001" CREATE INDEX fkey_test_index_1 ON on_update_fkey_table(value_1); ROLLBACK; BEGIN; ALTER TABLE transitive_reference_table ALTER COLUMN id SET DATA TYPE smallint; DEBUG: rewriting table "transitive_reference_table" DEBUG: validating foreign key constraint "fkey" +DEBUG: rewriting table "transitive_reference_table_2380000" +DEBUG: validating foreign key constraint "fkey_xxxxxxx" CREATE INDEX fkey_test_index_1 ON on_update_fkey_table(value_1); ROLLBACK; -- case 4.6: DDL to reference table followed by a DDL to dist table, both touching fkey columns @@ -629,6 +632,7 @@ BEGIN; ALTER TABLE reference_table ALTER COLUMN id SET DATA TYPE smallint; DEBUG: rewriting table "reference_table" DEBUG: validating foreign key constraint "fkey" +DEBUG: rewriting table "reference_table_2380001" ALTER TABLE on_update_fkey_table ALTER COLUMN value_1 SET DATA TYPE smallint; DEBUG: rewriting table "on_update_fkey_table" DEBUG: validating foreign key constraint "fkey" @@ -637,6 +641,8 @@ BEGIN; ALTER TABLE transitive_reference_table ALTER COLUMN id SET DATA TYPE smallint; DEBUG: rewriting table "transitive_reference_table" DEBUG: validating foreign key constraint "fkey" +DEBUG: rewriting table "transitive_reference_table_2380000" +DEBUG: validating foreign key constraint "fkey_xxxxxxx" ALTER TABLE on_update_fkey_table ALTER COLUMN value_1 SET DATA TYPE smallint; DEBUG: rewriting table "on_update_fkey_table" DEBUG: validating foreign key constraint "fkey" @@ -672,12 +678,15 @@ BEGIN; ALTER TABLE reference_table ALTER COLUMN id SET DATA TYPE smallint; DEBUG: rewriting table "reference_table" DEBUG: validating foreign key constraint "fkey" +DEBUG: rewriting table "reference_table_2380001" TRUNCATE on_update_fkey_table; ROLLBACK; BEGIN; ALTER TABLE transitive_reference_table ALTER COLUMN id SET DATA TYPE smallint; DEBUG: rewriting table "transitive_reference_table" DEBUG: validating foreign key constraint "fkey" +DEBUG: rewriting table "transitive_reference_table_2380000" +DEBUG: validating foreign key constraint "fkey_xxxxxxx" TRUNCATE on_update_fkey_table; ROLLBACK; --------------------------------------------------------------------- @@ -834,6 +843,7 @@ BEGIN; TRUNCATE transitive_reference_table CASCADE; NOTICE: truncate cascades to table "reference_table" NOTICE: truncate cascades to table "on_update_fkey_table" +NOTICE: truncate cascades to table "reference_table_xxxxx" ROLLBACK; -- case 4.7: SELECT to a dist table is followed by a DROP -- DROP following SELECT is important as we error out after @@ -1101,6 +1111,12 @@ ROLLBACK; -- the fails since we're trying to switch sequential mode after -- already executed a parallel query BEGIN; + SELECT master_remove_node('localhost', :master_port); + master_remove_node +--------------------------------------------------------------------- + +(1 row) + CREATE TABLE test_table_1(id int PRIMARY KEY); SELECT create_reference_table('test_table_1'); create_reference_table @@ -1129,6 +1145,12 @@ ROLLBACK; -- same test with the above, but this time using -- sequential mode, succeeds BEGIN; + SELECT master_remove_node('localhost', :master_port); + master_remove_node +--------------------------------------------------------------------- + +(1 row) + SET LOCAL citus.multi_shard_modify_mode TO 'sequential'; CREATE TABLE test_table_1(id int PRIMARY KEY); SELECT create_reference_table('test_table_1'); @@ -1499,6 +1521,6 @@ ROLLBACK; RESET client_min_messages; \set VERBOSITY terse DROP SCHEMA test_fkey_to_ref_in_tx CASCADE; -NOTICE: drop cascades to 5 other objects +NOTICE: drop cascades to 7 other objects \set VERBOSITY default SET search_path TO public; diff --git a/src/test/regress/expected/function_propagation.out b/src/test/regress/expected/function_propagation.out index 8a588a3cf..d49f4fd10 100644 --- a/src/test/regress/expected/function_propagation.out +++ b/src/test/regress/expected/function_propagation.out @@ -864,13 +864,6 @@ BEGIN; (0 rows) CREATE TABLE citus_local_table_to_test_func(l1 int DEFAULT func_in_transaction_for_local_table()); - SET LOCAL client_min_messages TO WARNING; - SELECT 1 FROM master_add_node('localhost', :master_port, groupid => 0); - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - SELECT citus_add_local_table_to_metadata('citus_local_table_to_test_func'); citus_add_local_table_to_metadata --------------------------------------------------------------------- diff --git a/src/test/regress/expected/grant_on_foreign_server_propagation.out b/src/test/regress/expected/grant_on_foreign_server_propagation.out index b98130404..7c47a5524 100644 --- a/src/test/regress/expected/grant_on_foreign_server_propagation.out +++ b/src/test/regress/expected/grant_on_foreign_server_propagation.out @@ -5,6 +5,12 @@ CREATE SCHEMA "grant on server"; SET search_path TO "grant on server"; -- remove one of the worker nodes to test adding a new node later +SELECT 1 FROM citus_remove_node('localhost', :master_port); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + SELECT 1 FROM citus_remove_node('localhost', :worker_2_port); ?column? --------------------------------------------------------------------- @@ -173,9 +179,3 @@ SET client_min_messages TO ERROR; DROP SERVER "Foreign Server" CASCADE; DROP SCHEMA "grant on server" CASCADE; DROP ROLE role_test_servers, role_test_servers_2, ownerrole; -SELECT 1 FROM citus_remove_node('localhost', :master_port); - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - diff --git a/src/test/regress/expected/insert_select_into_local_table.out b/src/test/regress/expected/insert_select_into_local_table.out index f53348272..0e919b7cd 100644 --- a/src/test/regress/expected/insert_select_into_local_table.out +++ b/src/test/regress/expected/insert_select_into_local_table.out @@ -1112,4 +1112,4 @@ RETURNING *; ROLLBACK; \set VERBOSITY terse DROP SCHEMA insert_select_into_local_table CASCADE; -NOTICE: drop cascades to 12 other objects +NOTICE: drop cascades to 13 other objects diff --git a/src/test/regress/expected/insert_select_repartition.out b/src/test/regress/expected/insert_select_repartition.out index b97a82b63..88acc49e3 100644 --- a/src/test/regress/expected/insert_select_repartition.out +++ b/src/test/regress/expected/insert_select_repartition.out @@ -1092,14 +1092,14 @@ EXPLAIN (costs off) INSERT INTO test(y, x) SELECT a.x, b.y FROM test a JOIN test Custom Scan (Citus INSERT ... SELECT) INSERT/SELECT method: pull to coordinator -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 Tasks Shown: None, not supported for re-partition queries -> MapMergeJob Map Task Count: 3 - Merge Task Count: 4 + Merge Task Count: 6 -> MapMergeJob Map Task Count: 3 - Merge Task Count: 4 + Merge Task Count: 6 (11 rows) SET client_min_messages TO DEBUG1; @@ -1121,14 +1121,14 @@ EXPLAIN (costs off) INSERT INTO test SELECT a.* FROM test a JOIN test b USING (y Custom Scan (Citus INSERT ... SELECT) INSERT/SELECT method: pull to coordinator -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 Tasks Shown: None, not supported for re-partition queries -> MapMergeJob Map Task Count: 3 - Merge Task Count: 4 + Merge Task Count: 6 -> MapMergeJob Map Task Count: 3 - Merge Task Count: 4 + Merge Task Count: 6 (11 rows) SET client_min_messages TO DEBUG1; diff --git a/src/test/regress/expected/insert_select_repartition_0.out b/src/test/regress/expected/insert_select_repartition_0.out index 5bcb894cc..7217be3e9 100644 --- a/src/test/regress/expected/insert_select_repartition_0.out +++ b/src/test/regress/expected/insert_select_repartition_0.out @@ -1092,14 +1092,14 @@ EXPLAIN (costs off) INSERT INTO test(y, x) SELECT a.x, b.y FROM test a JOIN test Custom Scan (Citus INSERT ... SELECT) INSERT/SELECT method: pull to coordinator -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 Tasks Shown: None, not supported for re-partition queries -> MapMergeJob Map Task Count: 3 - Merge Task Count: 4 + Merge Task Count: 6 -> MapMergeJob Map Task Count: 3 - Merge Task Count: 4 + Merge Task Count: 6 (11 rows) SET client_min_messages TO DEBUG1; @@ -1121,14 +1121,14 @@ EXPLAIN (costs off) INSERT INTO test SELECT a.* FROM test a JOIN test b USING (y Custom Scan (Citus INSERT ... SELECT) INSERT/SELECT method: pull to coordinator -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 Tasks Shown: None, not supported for re-partition queries -> MapMergeJob Map Task Count: 3 - Merge Task Count: 4 + Merge Task Count: 6 -> MapMergeJob Map Task Count: 3 - Merge Task Count: 4 + Merge Task Count: 6 (11 rows) SET client_min_messages TO DEBUG1; diff --git a/src/test/regress/expected/insert_select_single_shard_table.out b/src/test/regress/expected/insert_select_single_shard_table.out index 219e7d5d9..d27bdcd73 100644 --- a/src/test/regress/expected/insert_select_single_shard_table.out +++ b/src/test/regress/expected/insert_select_single_shard_table.out @@ -2,13 +2,6 @@ CREATE SCHEMA insert_select_single_shard_table; SET search_path TO insert_select_single_shard_table; SET citus.next_shard_id TO 1820000; SET citus.shard_count TO 32; -SET client_min_messages TO WARNING; -SELECT 1 FROM citus_add_node('localhost', :master_port, groupid => 0); - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - SET client_min_messages TO NOTICE; CREATE TABLE nullkey_c1_t1(a int, b int); CREATE TABLE nullkey_c1_t2(a int, b int); @@ -823,9 +816,3 @@ DEBUG: Creating router plan SET client_min_messages TO WARNING; DROP SCHEMA insert_select_single_shard_table CASCADE; -SELECT citus_remove_node('localhost', :master_port); - citus_remove_node ---------------------------------------------------------------------- - -(1 row) - diff --git a/src/test/regress/expected/intermediate_result_pruning.out b/src/test/regress/expected/intermediate_result_pruning.out index e178765a8..5262ebc79 100644 --- a/src/test/regress/expected/intermediate_result_pruning.out +++ b/src/test/regress/expected/intermediate_result_pruning.out @@ -99,6 +99,7 @@ FROM DEBUG: generating subplan XXX_1 for CTE some_values_1: SELECT key, random() AS random FROM intermediate_result_pruning.table_1 WHERE (value OPERATOR(pg_catalog.=) ANY (ARRAY['3'::text, '4'::text])) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((SELECT intermediate_result.key, intermediate_result.random FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, random double precision)) some_values_1 JOIN intermediate_result_pruning.ref_table USING (key)) DEBUG: Subplan XXX_1 will be sent to localhost:xxxxx +DEBUG: Subplan XXX_1 will be sent to localhost:xxxxx DEBUG: Subplan XXX_1 will be sent to localhost:xxxxx count --------------------------------------------------------------------- @@ -386,6 +387,7 @@ DEBUG: Subplan XXX_1 will be sent to localhost:xxxxx DEBUG: Subplan XXX_2 will be sent to localhost:xxxxx DEBUG: Subplan XXX_2 will be sent to localhost:xxxxx DEBUG: Subplan XXX_3 will be sent to localhost:xxxxx +DEBUG: Subplan XXX_3 will be sent to localhost:xxxxx DEBUG: Subplan XXX_3 will be sent to localhost:xxxxx key | key | value --------------------------------------------------------------------- diff --git a/src/test/regress/expected/intermediate_result_pruning_0.out b/src/test/regress/expected/intermediate_result_pruning_0.out index ec4b489d0..ae1247545 100644 --- a/src/test/regress/expected/intermediate_result_pruning_0.out +++ b/src/test/regress/expected/intermediate_result_pruning_0.out @@ -99,6 +99,7 @@ FROM DEBUG: generating subplan XXX_1 for CTE some_values_1: SELECT key, random() AS random FROM intermediate_result_pruning.table_1 WHERE (value OPERATOR(pg_catalog.=) ANY (ARRAY['3'::text, '4'::text])) DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((SELECT intermediate_result.key, intermediate_result.random FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(key integer, random double precision)) some_values_1 JOIN intermediate_result_pruning.ref_table USING (key)) DEBUG: Subplan XXX_1 will be sent to localhost:xxxxx +DEBUG: Subplan XXX_1 will be sent to localhost:xxxxx DEBUG: Subplan XXX_1 will be sent to localhost:xxxxx count --------------------------------------------------------------------- @@ -386,6 +387,7 @@ DEBUG: Subplan XXX_1 will be sent to localhost:xxxxx DEBUG: Subplan XXX_2 will be sent to localhost:xxxxx DEBUG: Subplan XXX_2 will be sent to localhost:xxxxx DEBUG: Subplan XXX_3 will be sent to localhost:xxxxx +DEBUG: Subplan XXX_3 will be sent to localhost:xxxxx DEBUG: Subplan XXX_3 will be sent to localhost:xxxxx key | key | value --------------------------------------------------------------------- diff --git a/src/test/regress/expected/intermediate_results.out b/src/test/regress/expected/intermediate_results.out index bf13ce21c..8b2e200f7 100644 --- a/src/test/regress/expected/intermediate_results.out +++ b/src/test/regress/expected/intermediate_results.out @@ -672,3 +672,5 @@ COMMIT; SET client_min_messages TO ERROR; DROP SCHEMA other_schema CASCADE; DROP SCHEMA intermediate_results CASCADE; +DROP OWNED BY some_other_user; +DROP USER some_other_user; diff --git a/src/test/regress/expected/join_pushdown.out b/src/test/regress/expected/join_pushdown.out index c71478d30..02a16c195 100644 --- a/src/test/regress/expected/join_pushdown.out +++ b/src/test/regress/expected/join_pushdown.out @@ -463,10 +463,11 @@ SELECT * FROM abcd first join abcd second USING(b) join abcd third on first.b=th END; DROP SCHEMA join_schema CASCADE; -NOTICE: drop cascades to 6 other objects +NOTICE: drop cascades to 7 other objects DETAIL: drop cascades to table abcd drop cascades to table distributed_table drop cascades to table reference_table +drop cascades to table reference_table_9000004 drop cascades to table test_table_1 drop cascades to table test_table_2 drop cascades to view abcd_view diff --git a/src/test/regress/expected/limit_intermediate_size.out b/src/test/regress/expected/limit_intermediate_size.out index 662ce0e05..e6fd0e798 100644 --- a/src/test/regress/expected/limit_intermediate_size.out +++ b/src/test/regress/expected/limit_intermediate_size.out @@ -16,7 +16,7 @@ SELECT cte.user_id, cte.value_2 FROM cte,cte2 ORDER BY 1,2 LIMIT 10; ERROR: the intermediate result size exceeds citus.max_intermediate_result_size (currently 2 kB) DETAIL: Citus restricts the size of intermediate results of complex subqueries and CTEs to avoid accidentally pulling large result sets into once place. HINT: To run the current query, set citus.max_intermediate_result_size to a higher value or -1 to disable. -SET citus.max_intermediate_result_size TO 9; +SET citus.max_intermediate_result_size TO 17; WITH cte AS MATERIALIZED ( SELECT diff --git a/src/test/regress/expected/local_table_join.out b/src/test/regress/expected/local_table_join.out index 96b570ac3..7da341207 100644 --- a/src/test/regress/expected/local_table_join.out +++ b/src/test/regress/expected/local_table_join.out @@ -86,7 +86,13 @@ CREATE FOREIGN TABLE foreign_table ( CREATE MATERIALIZED VIEW mv1 AS SELECT * FROM postgres_table; CREATE MATERIALIZED VIEW mv2 AS SELECT * FROM distributed_table; SET client_min_messages TO DEBUG1; --- the user doesn't allow local / distributed table joinn +-- the user doesn't allow local / distributed table join +SELECT master_remove_node('localhost', :master_port); -- https://github.com/citusdata/citus/issues/6958 + master_remove_node +--------------------------------------------------------------------- + +(1 row) + SET citus.local_table_join_policy TO 'never'; SELECT count(*) FROM postgres_table JOIN distributed_table USING(key); ERROR: direct joins between distributed and local tables are not supported @@ -94,6 +100,12 @@ HINT: Use CTE's or subqueries to select from local tables and use them in joins SELECT count(*) FROM postgres_table JOIN reference_table USING(key); ERROR: direct joins between distributed and local tables are not supported HINT: Use CTE's or subqueries to select from local tables and use them in joins +SELECT citus_set_coordinator_host('localhost'); -- https://github.com/citusdata/citus/issues/6958 + citus_set_coordinator_host +--------------------------------------------------------------------- + +(1 row) + -- the user prefers local table recursively planned SET citus.local_table_join_policy TO 'prefer-local'; SELECT count(*) FROM postgres_table JOIN distributed_table USING(key); @@ -1586,6 +1598,12 @@ DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT 1 AS res FROM (1 row) ROLLBACK; +SELECT master_remove_node('localhost', :master_port); -- https://github.com/citusdata/citus/issues/6958 + master_remove_node +--------------------------------------------------------------------- + +(1 row) + BEGIN; SELECT create_reference_table('table1'); NOTICE: Copying data from local table... @@ -1632,7 +1650,13 @@ DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT 1 AS res FROM (1 row) ROLLBACK; +SELECT citus_set_coordinator_host('localhost'); -- https://github.com/citusdata/citus/issues/6958 + citus_set_coordinator_host +--------------------------------------------------------------------- + +(1 row) + RESET client_min_messages; \set VERBOSITY terse DROP SCHEMA local_table_join CASCADE; -NOTICE: drop cascades to 22 other objects +NOTICE: drop cascades to 23 other objects diff --git a/src/test/regress/expected/logical_replication.out b/src/test/regress/expected/logical_replication.out index 79108dd11..8a3e96da9 100644 --- a/src/test/regress/expected/logical_replication.out +++ b/src/test/regress/expected/logical_replication.out @@ -14,13 +14,6 @@ SELECT create_distributed_table('dist', 'id'); (1 row) INSERT INTO dist SELECT generate_series(1, 100); -SELECT 1 from citus_add_node('localhost', :master_port, groupId := 0); -NOTICE: localhost:xxxxx is the coordinator and already contains metadata, skipping syncing the metadata - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - -- Create a publiction and subscription (including replication slot) manually. -- This allows us to test the cleanup logic at the start of the shard move. \c - - - :worker_1_port @@ -97,12 +90,6 @@ select citus_move_shard_placement(6830002, 'localhost', :worker_1_port, 'localho (1 row) -SELECT citus_remove_node('localhost', :master_port); - citus_remove_node ---------------------------------------------------------------------- - -(1 row) - -- the subscription is still there, as there is no cleanup record for it -- we have created it manually SELECT count(*) from pg_subscription; diff --git a/src/test/regress/expected/merge.out b/src/test/regress/expected/merge.out index 3cf776ded..85d6daab6 100644 --- a/src/test/regress/expected/merge.out +++ b/src/test/regress/expected/merge.out @@ -20,13 +20,6 @@ SET citus.next_shard_id TO 4000000; SET citus.explain_all_tasks TO true; SET citus.shard_replication_factor TO 1; SET citus.max_adaptive_executor_pool_size TO 1; -SELECT 1 FROM master_add_node('localhost', :master_port, groupid => 0); -NOTICE: localhost:xxxxx is the coordinator and already contains metadata, skipping syncing the metadata - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - CREATE TABLE source ( order_id INT, @@ -3477,9 +3470,3 @@ drop cascades to table dist_colocated drop cascades to table dist_target drop cascades to table dist_source drop cascades to view show_tables -SELECT 1 FROM master_remove_node('localhost', :master_port); - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - diff --git a/src/test/regress/expected/multi_alter_table_add_constraints_without_name.out b/src/test/regress/expected/multi_alter_table_add_constraints_without_name.out index 54224c924..0b048946c 100644 --- a/src/test/regress/expected/multi_alter_table_add_constraints_without_name.out +++ b/src/test/regress/expected/multi_alter_table_add_constraints_without_name.out @@ -862,14 +862,6 @@ DROP TABLE AT_AddConstNoName.dist_partitioned_table; -- Test with Citus Local Tables -- Test "ADD PRIMARY KEY" \c - - :master_host :master_port -SET client_min_messages to ERROR; -SELECT 1 FROM master_add_node('localhost', :master_port, groupId => 0); - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - -RESET client_min_messages; CREATE TABLE AT_AddConstNoName.citus_local_table(id int, other_column int); SELECT citus_add_local_table_to_metadata('AT_AddConstNoName.citus_local_table'); citus_add_local_table_to_metadata @@ -1175,12 +1167,6 @@ SELECT con.conname (0 rows) \c - - :master_host :master_port -SELECT 1 FROM master_remove_node('localhost', :master_port); - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - -- Test with unusual table and column names CREATE TABLE AT_AddConstNoName."2nd table" ( "2nd id" INTEGER, "3rd id" INTEGER); SELECT create_distributed_table('AT_AddConstNoName."2nd table"','2nd id'); @@ -1315,7 +1301,7 @@ NOTICE: drop cascades to 7 other objects DETAIL: drop cascades to table at_addconstnoname.tbl drop cascades to table at_addconstnoname.products_ref_2 drop cascades to table at_addconstnoname.products_ref_3 -drop cascades to table at_addconstnoname.verylonglonglonglonglonglonglonglonglonglonglonglonglonglonglon drop cascades to table at_addconstnoname.products_ref_3_5410009 +drop cascades to table at_addconstnoname.verylonglonglonglonglonglonglonglonglonglonglonglonglonglonglon drop cascades to table at_addconstnoname.citus_local_partitioned_table drop cascades to table at_addconstnoname."2nd table" diff --git a/src/test/regress/expected/multi_alter_table_add_foreign_key_without_name.out b/src/test/regress/expected/multi_alter_table_add_foreign_key_without_name.out index c27e6a425..58571cc34 100644 --- a/src/test/regress/expected/multi_alter_table_add_foreign_key_without_name.out +++ b/src/test/regress/expected/multi_alter_table_add_foreign_key_without_name.out @@ -120,7 +120,7 @@ ERROR: cannot create foreign key constraint since relations are not colocated o DETAIL: A distributed table can only have foreign keys if it is referencing another colocated hash distributed table or a reference table DROP TABLE referencing_table; DROP TABLE referenced_table; --- test foreign constraint creation is not supported when one of the tables is not a citus table +-- test foreign constraint creation is supported when coordinator is in metadata CREATE TABLE referenced_local_table(id int PRIMARY KEY, other_column int); CREATE TABLE reference_table(id int, referencing_column int); SELECT create_reference_table('reference_table'); @@ -130,11 +130,12 @@ SELECT create_reference_table('reference_table'); (1 row) ALTER TABLE reference_table ADD FOREIGN KEY (referencing_column) REFERENCES referenced_local_table(id); -ERROR: referenced table "referenced_local_table" must be a distributed table or a reference table -DETAIL: To enforce foreign keys, the referencing and referenced rows need to be stored on the same node. -HINT: You could use SELECT create_reference_table('referenced_local_table') to replicate the referenced table to all nodes or consider dropping the foreign key DROP TABLE referenced_local_table; -DROP TABLE reference_table; +ERROR: cannot drop table referenced_local_table because other objects depend on it +DETAIL: constraint reference_table_referencing_column_fkey on table reference_table depends on table referenced_local_table +HINT: Use DROP ... CASCADE to drop the dependent objects too. +DROP TABLE reference_table CASCADE; +NOTICE: removing table at_add_fk.referenced_local_table from metadata as it is not connected to any reference tables via foreign keys -- test foreign constraint with correct conditions CREATE TABLE referenced_table(id int PRIMARY KEY, test_column int); CREATE TABLE referencing_table(id int, ref_id int); @@ -170,8 +171,8 @@ SELECT con.conname conname --------------------------------------------------------------------- referencing_table_ref_id_fkey - referencing_table_ref_id_fkey_1770033 - referencing_table_ref_id_fkey_1770035 + referencing_table_ref_id_fkey_1770034 + referencing_table_ref_id_fkey_1770036 (3 rows) \c - - :master_host :master_port @@ -198,8 +199,8 @@ SELECT con.conname conname --------------------------------------------------------------------- referencing_table_ref_id_fkey - referencing_table_ref_id_fkey_1770033 - referencing_table_ref_id_fkey_1770035 + referencing_table_ref_id_fkey_1770034 + referencing_table_ref_id_fkey_1770036 (3 rows) \c - - :master_host :master_port @@ -244,8 +245,8 @@ SELECT con.conname, con.confupdtype, con.confdeltype, con.confmatchtype conname | confupdtype | confdeltype | confmatchtype --------------------------------------------------------------------- referencing_table_ref_id_fkey | a | c | s - referencing_table_ref_id_fkey_1770041 | a | c | s - referencing_table_ref_id_fkey_1770043 | a | c | s + referencing_table_ref_id_fkey_1770042 | a | c | s + referencing_table_ref_id_fkey_1770044 | a | c | s (3 rows) \c - - :master_host :master_port @@ -272,8 +273,8 @@ SELECT con.conname, con.convalidated conname | convalidated --------------------------------------------------------------------- referencing_table_ref_id_fkey | f - referencing_table_ref_id_fkey_1770041 | f - referencing_table_ref_id_fkey_1770043 | f + referencing_table_ref_id_fkey_1770042 | f + referencing_table_ref_id_fkey_1770044 | f (3 rows) \c - - :master_host :master_port @@ -300,8 +301,8 @@ SELECT con.conname, con.confupdtype, con.confdeltype, con.confmatchtype conname | confupdtype | confdeltype | confmatchtype --------------------------------------------------------------------- referencing_table_ref_id_fkey | a | a | s - referencing_table_ref_id_fkey_1770041 | a | a | s - referencing_table_ref_id_fkey_1770043 | a | a | s + referencing_table_ref_id_fkey_1770042 | a | a | s + referencing_table_ref_id_fkey_1770044 | a | a | s (3 rows) \c - - :master_host :master_port @@ -328,8 +329,8 @@ SELECT con.conname, con.confupdtype, con.confdeltype, con.confmatchtype conname | confupdtype | confdeltype | confmatchtype --------------------------------------------------------------------- referencing_table_ref_id_fkey | a | r | s - referencing_table_ref_id_fkey_1770041 | a | r | s - referencing_table_ref_id_fkey_1770043 | a | r | s + referencing_table_ref_id_fkey_1770042 | a | r | s + referencing_table_ref_id_fkey_1770044 | a | r | s (3 rows) \c - - :master_host :master_port @@ -356,8 +357,8 @@ SELECT con.conname, con.confupdtype, con.confdeltype, con.confmatchtype conname | confupdtype | confdeltype | confmatchtype --------------------------------------------------------------------- referencing_table_ref_id_id_fkey | a | a | s - referencing_table_ref_id_id_fkey_1770041 | a | a | s - referencing_table_ref_id_id_fkey_1770043 | a | a | s + referencing_table_ref_id_id_fkey_1770042 | a | a | s + referencing_table_ref_id_id_fkey_1770044 | a | a | s (3 rows) \c - - :master_host :master_port @@ -384,8 +385,8 @@ SELECT con.conname, con.confupdtype, con.confdeltype, con.confmatchtype conname | confupdtype | confdeltype | confmatchtype --------------------------------------------------------------------- referencing_table_ref_id_id_fkey | r | a | s - referencing_table_ref_id_id_fkey_1770041 | r | a | s - referencing_table_ref_id_id_fkey_1770043 | r | a | s + referencing_table_ref_id_id_fkey_1770042 | r | a | s + referencing_table_ref_id_id_fkey_1770044 | r | a | s (3 rows) \c - - :master_host :master_port @@ -412,8 +413,8 @@ SELECT con.conname, con.confupdtype, con.confdeltype, con.confmatchtype conname | confupdtype | confdeltype | confmatchtype --------------------------------------------------------------------- referencing_table_ref_id_id_fkey | a | a | s - referencing_table_ref_id_id_fkey_1770041 | a | a | s - referencing_table_ref_id_id_fkey_1770043 | a | a | s + referencing_table_ref_id_id_fkey_1770042 | a | a | s + referencing_table_ref_id_id_fkey_1770044 | a | a | s (3 rows) \c - - :master_host :master_port @@ -440,8 +441,8 @@ SELECT con.conname, con.confupdtype, con.confdeltype, con.confmatchtype conname | confupdtype | confdeltype | confmatchtype --------------------------------------------------------------------- referencing_table_ref_id_id_fkey | a | a | f - referencing_table_ref_id_id_fkey_1770041 | a | a | f - referencing_table_ref_id_id_fkey_1770043 | a | a | f + referencing_table_ref_id_id_fkey_1770042 | a | a | f + referencing_table_ref_id_id_fkey_1770044 | a | a | f (3 rows) \c - - :master_host :master_port @@ -524,13 +525,6 @@ BEGIN; DROP TABLE dist_table CASCADE; DROP TABLE reference_table CASCADE; -- test ADD FOREIGN KEY from citus local to reference table -SELECT 1 FROM master_add_node('localhost', :master_port, groupId => 0); -NOTICE: localhost:xxxxx is the coordinator and already contains metadata, skipping syncing the metadata - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - CREATE TABLE citus_local_table(l1 int); SELECT citus_add_local_table_to_metadata('citus_local_table'); citus_add_local_table_to_metadata @@ -557,17 +551,12 @@ ALTER TABLE citus_local_table ADD FOREIGN KEY(l1) REFERENCES reference_table(r1) ALTER TABLE citus_local_table ADD FOREIGN KEY(l1) REFERENCES reference_table(r1) ON DELETE NO ACTION; ALTER TABLE citus_local_table ADD FOREIGN KEY(l1) REFERENCES reference_table(r1) ON DELETE RESTRICT; DROP TABLE citus_local_table CASCADE; -SELECT 1 FROM master_remove_node('localhost', :master_port); - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - RESET SEARCH_PATH; RESET client_min_messages; DROP SCHEMA at_add_fk CASCADE; -NOTICE: drop cascades to 4 other objects -DETAIL: drop cascades to table at_add_fk.referenced_table +NOTICE: drop cascades to 5 other objects +DETAIL: drop cascades to table at_add_fk.referenced_local_table +drop cascades to table at_add_fk.referenced_table drop cascades to table at_add_fk.referencing_table drop cascades to table at_add_fk.reference_table -drop cascades to table at_add_fk.reference_table_1770051 +drop cascades to table at_add_fk.reference_table_1770052 diff --git a/src/test/regress/expected/multi_citus_tools.out b/src/test/regress/expected/multi_citus_tools.out index 792839d87..eef7a98ca 100644 --- a/src/test/regress/expected/multi_citus_tools.out +++ b/src/test/regress/expected/multi_citus_tools.out @@ -547,7 +547,8 @@ WHERE isactive = 't' AND noderole='primary'; --------------------------------------------------------------------- t t -(2 rows) + t +(3 rows) CREATE TABLE distributed(id int, data text); SELECT create_distributed_table('distributed', 'id'); @@ -632,11 +633,16 @@ SELECT citus_check_connection_to_node('localhost', :worker_2_port); SELECT * FROM citus_check_cluster_node_health() ORDER BY 1,2,3,4; from_nodename | from_nodeport | to_nodename | to_nodeport | result --------------------------------------------------------------------- + localhost | 57636 | localhost | 57636 | t + localhost | 57636 | localhost | 57637 | t + localhost | 57636 | localhost | 57638 | t + localhost | 57637 | localhost | 57636 | t localhost | 57637 | localhost | 57637 | t localhost | 57637 | localhost | 57638 | t + localhost | 57638 | localhost | 57636 | t localhost | 57638 | localhost | 57637 | t localhost | 57638 | localhost | 57638 | t -(4 rows) +(9 rows) -- test cluster connectivity when we have broken nodes SET client_min_messages TO ERROR; @@ -648,23 +654,32 @@ INSERT INTO pg_dist_node VALUES SELECT * FROM citus_check_cluster_node_health() ORDER BY 5,1,2,3,4; from_nodename | from_nodeport | to_nodename | to_nodeport | result --------------------------------------------------------------------- + localhost | 57636 | localhost | 123456789 | f + localhost | 57636 | www.citusdata.com | 5432 | f localhost | 57637 | localhost | 123456789 | f localhost | 57637 | www.citusdata.com | 5432 | f localhost | 57638 | localhost | 123456789 | f localhost | 57638 | www.citusdata.com | 5432 | f + localhost | 57636 | localhost | 57636 | t + localhost | 57636 | localhost | 57637 | t + localhost | 57636 | localhost | 57638 | t + localhost | 57637 | localhost | 57636 | t localhost | 57637 | localhost | 57637 | t localhost | 57637 | localhost | 57638 | t + localhost | 57638 | localhost | 57636 | t localhost | 57638 | localhost | 57637 | t localhost | 57638 | localhost | 57638 | t + localhost | 123456789 | localhost | 57636 | localhost | 123456789 | localhost | 57637 | localhost | 123456789 | localhost | 57638 | localhost | 123456789 | localhost | 123456789 | localhost | 123456789 | www.citusdata.com | 5432 | + www.citusdata.com | 5432 | localhost | 57636 | www.citusdata.com | 5432 | localhost | 57637 | www.citusdata.com | 5432 | localhost | 57638 | www.citusdata.com | 5432 | localhost | 123456789 | www.citusdata.com | 5432 | www.citusdata.com | 5432 | -(16 rows) +(25 rows) ROLLBACK; RESET citus.node_connection_timeout; diff --git a/src/test/regress/expected/multi_cluster_management.out b/src/test/regress/expected/multi_cluster_management.out index 7d5d25d57..e58b02937 100644 --- a/src/test/regress/expected/multi_cluster_management.out +++ b/src/test/regress/expected/multi_cluster_management.out @@ -681,6 +681,12 @@ SELECT master_remove_node(nodename, nodeport) FROM pg_dist_node; (3 rows) +SELECT citus_set_coordinator_host('localhost'); + citus_set_coordinator_host +--------------------------------------------------------------------- + +(1 row) + SELECT 1 FROM master_add_node('localhost', :worker_1_port); ?column? --------------------------------------------------------------------- @@ -791,13 +797,13 @@ SELECT 1 FROM master_add_inactive_node('localhost', 9996, groupid => :worker_2_g SELECT master_add_inactive_node('localhost', 9999, groupid => :worker_2_group, nodecluster => 'olap', noderole => 'secondary'); master_add_inactive_node --------------------------------------------------------------------- - 22 + 23 (1 row) SELECT master_activate_node('localhost', 9999); master_activate_node --------------------------------------------------------------------- - 22 + 23 (1 row) SELECT citus_disable_node('localhost', 9999); @@ -831,17 +837,17 @@ CONTEXT: PL/pgSQL function citus_internal.pg_dist_node_trigger_func() line XX a INSERT INTO pg_dist_node (nodename, nodeport, groupid, noderole, nodecluster) VALUES ('localhost', 5000, 1000, 'primary', 'olap'); ERROR: new row for relation "pg_dist_node" violates check constraint "primaries_are_only_allowed_in_the_default_cluster" -DETAIL: Failing row contains (24, 1000, localhost, 5000, default, f, t, primary, olap, f, t). +DETAIL: Failing row contains (25, 1000, localhost, 5000, default, f, t, primary, olap, f, t). UPDATE pg_dist_node SET nodecluster = 'olap' WHERE nodeport = :worker_1_port; ERROR: new row for relation "pg_dist_node" violates check constraint "primaries_are_only_allowed_in_the_default_cluster" -DETAIL: Failing row contains (16, 14, localhost, 57637, default, f, t, primary, olap, f, t). +DETAIL: Failing row contains (17, 14, localhost, 57637, default, f, t, primary, olap, f, t). -- check that you /can/ add a secondary node to a non-default cluster SELECT groupid AS worker_2_group FROM pg_dist_node WHERE nodeport = :worker_2_port \gset SELECT master_add_node('localhost', 8888, groupid => :worker_1_group, noderole => 'secondary', nodecluster=> 'olap'); master_add_node --------------------------------------------------------------------- - 25 + 26 (1 row) -- check that super-long cluster names are truncated @@ -854,13 +860,13 @@ SELECT master_add_node('localhost', 8887, groupid => :worker_1_group, noderole = ); master_add_node --------------------------------------------------------------------- - 26 + 27 (1 row) SELECT * FROM pg_dist_node WHERE nodeport=8887; nodeid | groupid | nodename | nodeport | noderack | hasmetadata | isactive | noderole | nodecluster | metadatasynced | shouldhaveshards --------------------------------------------------------------------- - 26 | 14 | localhost | 8887 | default | f | t | secondary | thisisasixtyfourcharacterstringrepeatedfourtimestomake256chars. | f | t + 27 | 14 | localhost | 8887 | default | f | t | secondary | thisisasixtyfourcharacterstringrepeatedfourtimestomake256chars. | f | t (1 row) -- don't remove the secondary and unavailable nodes, check that no commands are sent to @@ -869,13 +875,13 @@ SELECT * FROM pg_dist_node WHERE nodeport=8887; SELECT master_add_secondary_node('localhost', 9995, 'localhost', :worker_1_port); master_add_secondary_node --------------------------------------------------------------------- - 27 + 28 (1 row) SELECT master_add_secondary_node('localhost', 9994, primaryname => 'localhost', primaryport => :worker_2_port); master_add_secondary_node --------------------------------------------------------------------- - 28 + 29 (1 row) SELECT master_add_secondary_node('localhost', 9993, 'localhost', 2000); @@ -883,7 +889,7 @@ ERROR: node at "localhost:xxxxx" does not exist SELECT master_add_secondary_node('localhost', 9992, 'localhost', :worker_1_port, nodecluster => 'second-cluster'); master_add_secondary_node --------------------------------------------------------------------- - 29 + 30 (1 row) SELECT nodeid AS worker_1_node FROM pg_dist_node WHERE nodeport=9992 \gset @@ -941,7 +947,7 @@ SELECT master_update_node(:worker_1_node, 'somehost', 9000); SELECT * FROM pg_dist_node WHERE nodeid = :worker_1_node; nodeid | groupid | nodename | nodeport | noderack | hasmetadata | isactive | noderole | nodecluster | metadatasynced | shouldhaveshards --------------------------------------------------------------------- - 16 | 14 | somehost | 9000 | default | f | t | primary | default | f | t + 17 | 14 | somehost | 9000 | default | f | t | primary | default | f | t (1 row) -- cleanup @@ -954,7 +960,7 @@ SELECT master_update_node(:worker_1_node, 'localhost', :worker_1_port); SELECT * FROM pg_dist_node WHERE nodeid = :worker_1_node; nodeid | groupid | nodename | nodeport | noderack | hasmetadata | isactive | noderole | nodecluster | metadatasynced | shouldhaveshards --------------------------------------------------------------------- - 16 | 14 | localhost | 57637 | default | f | t | primary | default | f | t + 17 | 14 | localhost | 57637 | default | f | t | primary | default | f | t (1 row) SET client_min_messages TO ERROR; @@ -963,7 +969,8 @@ SELECT start_metadata_sync_to_node(nodename, nodeport) FROM pg_dist_node WHERE i --------------------------------------------------------------------- -(2 rows) + +(3 rows) RESET client_min_messages; SET citus.shard_replication_factor TO 1; @@ -1044,9 +1051,10 @@ FROM pg_dist_shard JOIN pg_dist_shard_placement USING (shardid) WHERE logicalrelid = 'test_ref'::regclass GROUP BY nodeport ORDER BY nodeport; nodeport | count --------------------------------------------------------------------- + 57636 | 1 57637 | 1 57638 | 1 -(2 rows) +(3 rows) -- cleanup for next test DROP TABLE test_dist, test_ref, test_dist_colocated, test_dist_non_colocated, test_dist_colocated_with_non_colocated; @@ -1088,9 +1096,10 @@ FROM pg_dist_shard JOIN pg_dist_shard_placement USING (shardid) WHERE logicalrelid = 'test_ref'::regclass GROUP BY nodeport ORDER BY nodeport; nodeport | count --------------------------------------------------------------------- + 57636 | 1 57637 | 1 57638 | 1 -(2 rows) +(3 rows) SELECT * from master_set_node_property('localhost', :worker_2_port, 'shouldhaveshards', true); master_set_node_property @@ -1114,9 +1123,10 @@ FROM pg_dist_shard JOIN pg_dist_shard_placement USING (shardid) WHERE logicalrelid = 'test_ref'::regclass GROUP BY nodeport ORDER BY nodeport; nodeport | count --------------------------------------------------------------------- + 57636 | 1 57637 | 1 57638 | 1 -(2 rows) +(3 rows) SELECT create_distributed_table('test_dist_colocated', 'x'); create_distributed_table diff --git a/src/test/regress/expected/multi_colocation_utils.out b/src/test/regress/expected/multi_colocation_utils.out index 7415983a2..9ef2896ff 100644 --- a/src/test/regress/expected/multi_colocation_utils.out +++ b/src/test/regress/expected/multi_colocation_utils.out @@ -825,11 +825,13 @@ ORDER BY table1_group_default | 1300070 | t | 57638 | -715827883 | 715827881 table1_group_default | 1300071 | t | 57637 | 715827882 | 2147483647 table1_group_default | 1300071 | t | 57638 | 715827882 | 2147483647 + table1_groupf | 1300080 | t | 57636 | | table1_groupf | 1300080 | t | 57637 | | table1_groupf | 1300080 | t | 57638 | | + table2_groupf | 1300081 | t | 57636 | | table2_groupf | 1300081 | t | 57637 | | table2_groupf | 1300081 | t | 57638 | | -(92 rows) +(94 rows) -- reset colocation ids to test update_distributed_table_colocation ALTER SEQUENCE pg_catalog.pg_dist_colocationid_seq RESTART 1; diff --git a/src/test/regress/expected/multi_copy.out b/src/test/regress/expected/multi_copy.out index 2b302965c..abd58eb1d 100644 --- a/src/test/regress/expected/multi_copy.out +++ b/src/test/regress/expected/multi_copy.out @@ -756,9 +756,10 @@ SELECT shardid, shardstate, nodename, nodeport WHERE logicalrelid = 'numbers_reference'::regclass order by placementid; shardid | shardstate | nodename | nodeport --------------------------------------------------------------------- + 560165 | 1 | localhost | 57636 560165 | 1 | localhost | 57637 560165 | 1 | localhost | 57638 -(2 rows) +(3 rows) -- try to insert into numbers_hash_other. copy should fail and rollback -- since it can not insert into either copies of a shard. shards are expected to diff --git a/src/test/regress/expected/multi_create_table_superuser.out b/src/test/regress/expected/multi_create_table_superuser.out index e15b6359e..1f756d688 100644 --- a/src/test/regress/expected/multi_create_table_superuser.out +++ b/src/test/regress/expected/multi_create_table_superuser.out @@ -642,13 +642,15 @@ DROP TABLE tt1; DROP TABLE tt2; DROP TABLE alter_replica_table; DROP SCHEMA sc CASCADE; -NOTICE: drop cascades to 2 other objects +NOTICE: drop cascades to 3 other objects DETAIL: drop cascades to table sc.ref +drop cascades to table sc.ref_360102 drop cascades to table sc.hash DROP SCHEMA sc2 CASCADE; -NOTICE: drop cascades to 2 other objects +NOTICE: drop cascades to 3 other objects DETAIL: drop cascades to table sc2.hash drop cascades to table sc2.ref +drop cascades to table sc2.ref_360111 DROP SCHEMA sc3 CASCADE; NOTICE: drop cascades to table sc3.alter_replica_table DROP SCHEMA sc4 CASCADE; diff --git a/src/test/regress/expected/multi_drop_extension.out b/src/test/regress/expected/multi_drop_extension.out index 112fa7f70..909ad2f87 100644 --- a/src/test/regress/expected/multi_drop_extension.out +++ b/src/test/regress/expected/multi_drop_extension.out @@ -21,12 +21,6 @@ BEGIN; SET client_min_messages TO ERROR; SET search_path TO public; CREATE EXTENSION citus; - SELECT 1 FROM master_add_node('localhost', :master_port, groupId => 0); - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - create table l1 (a int unique); SELECT create_reference_table('l1'); create_reference_table @@ -136,6 +130,12 @@ ROLLBACK TO SAVEPOINT s3; ROLLBACK; CREATE EXTENSION citus; -- re-add the nodes to the cluster +SELECT citus_set_coordinator_host('localhost'); + citus_set_coordinator_host +--------------------------------------------------------------------- + +(1 row) + SELECT 1 FROM master_add_node('localhost', :worker_1_port); ?column? --------------------------------------------------------------------- diff --git a/src/test/regress/expected/multi_explain.out b/src/test/regress/expected/multi_explain.out index 7b533a642..f9fc5a164 100644 --- a/src/test/regress/expected/multi_explain.out +++ b/src/test/regress/expected/multi_explain.out @@ -331,16 +331,16 @@ BEGIN; SET LOCAL citus.enable_repartition_joins TO true; EXPLAIN (COSTS off, ANALYZE on, TIMING off, SUMMARY off) SELECT count(*) FROM t1, t2 WHERE t1.a=t2.b; Aggregate (actual rows=1 loops=1) - -> Custom Scan (Citus Adaptive) (actual rows=4 loops=1) - Task Count: 4 - Tuple data received from nodes: 32 bytes + -> Custom Scan (Citus Adaptive) (actual rows=6 loops=1) + Task Count: 6 + Tuple data received from nodes: 48 bytes Tasks Shown: None, not supported for re-partition queries -> MapMergeJob Map Task Count: 3 - Merge Task Count: 4 + Merge Task Count: 6 -> MapMergeJob Map Task Count: 3 - Merge Task Count: 4 + Merge Task Count: 6 -- Confirm repartiton join in distributed subplan works EXPLAIN (COSTS off, ANALYZE on, TIMING off, SUMMARY off) WITH repartition AS (SELECT count(*) FROM t1, t2 WHERE t1.a=t2.b) @@ -350,16 +350,16 @@ Custom Scan (Citus Adaptive) (actual rows=1 loops=1) Intermediate Data Size: 14 bytes Result destination: Write locally -> Aggregate (actual rows=1 loops=1) - -> Custom Scan (Citus Adaptive) (actual rows=4 loops=1) - Task Count: 4 - Tuple data received from nodes: 32 bytes + -> Custom Scan (Citus Adaptive) (actual rows=6 loops=1) + Task Count: 6 + Tuple data received from nodes: 48 bytes Tasks Shown: None, not supported for re-partition queries -> MapMergeJob Map Task Count: 3 - Merge Task Count: 4 + Merge Task Count: 6 -> MapMergeJob Map Task Count: 3 - Merge Task Count: 4 + Merge Task Count: 6 Task Count: 1 Tuple data received from nodes: 8 bytes Tasks Shown: All @@ -1108,20 +1108,20 @@ EXPLAIN (COSTS FALSE) AND l_suppkey = s_suppkey; Aggregate -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 Tasks Shown: None, not supported for re-partition queries -> MapMergeJob - Map Task Count: 4 - Merge Task Count: 4 + Map Task Count: 6 + Merge Task Count: 6 -> MapMergeJob Map Task Count: 2 - Merge Task Count: 4 + Merge Task Count: 6 -> MapMergeJob Map Task Count: 1 - Merge Task Count: 4 + Merge Task Count: 6 -> MapMergeJob Map Task Count: 1 - Merge Task Count: 4 + Merge Task Count: 6 EXPLAIN (COSTS FALSE, FORMAT JSON) SELECT count(*) FROM lineitem, orders, customer_append, supplier_single_shard @@ -1142,26 +1142,26 @@ EXPLAIN (COSTS FALSE, FORMAT JSON) "Parallel Aware": false, "Distributed Query": { "Job": { - "Task Count": 4, + "Task Count": 6, "Tasks Shown": "None, not supported for re-partition queries", "Dependent Jobs": [ { - "Map Task Count": 4, - "Merge Task Count": 4, + "Map Task Count": 6, + "Merge Task Count": 6, "Dependent Jobs": [ { "Map Task Count": 2, - "Merge Task Count": 4 + "Merge Task Count": 6 }, { "Map Task Count": 1, - "Merge Task Count": 4 + "Merge Task Count": 6 } ] }, { "Map Task Count": 1, - "Merge Task Count": 4 + "Merge Task Count": 6 } ] } @@ -1198,26 +1198,26 @@ EXPLAIN (COSTS FALSE, FORMAT XML) false - 4 + 6 None, not supported for re-partition queries - 4 - 4 + 6 + 6 2 - 4 + 6 1 - 4 + 6 1 - 4 + 6 @@ -1264,13 +1264,13 @@ EXPLAIN (COSTS FALSE, FORMAT YAML) Parallel Aware: false Distributed Query: Job: - Task Count: 4 + Task Count: 6 Tasks Shown: "None, not supported for re-partition queries" Dependent Jobs: - Map Task Count: 2 - Merge Task Count: 4 + Merge Task Count: 6 - Map Task Count: 1 - Merge Task Count: 4 + Merge Task Count: 6 -- ensure local plans display correctly CREATE TABLE lineitem_clone (LIKE lineitem); EXPLAIN (COSTS FALSE) SELECT avg(l_linenumber) FROM lineitem_clone; @@ -2317,7 +2317,7 @@ SELECT count(distinct a) from r NATURAL JOIN ref_table; Custom Scan (Citus Adaptive) (actual rows=1 loops=1) -> Distributed Subplan XXX_1 Intermediate Data Size: 220 bytes - Result destination: Send to 2 nodes + Result destination: Send to 3 nodes -> Custom Scan (Citus Adaptive) (actual rows=10 loops=1) Task Count: 4 Tuple data received from nodes: 120 bytes @@ -3146,8 +3146,6 @@ Custom Scan (Citus Adaptive) (actual rows=0 loops=1) -- check when auto explain + analyze is enabled, we do not allow local execution. CREATE SCHEMA test_auto_explain; SET search_path TO 'test_auto_explain'; -SELECT citus_set_coordinator_host('localhost'); - CREATE TABLE test_ref_table (key int PRIMARY KEY); SELECT create_reference_table('test_ref_table'); @@ -3157,9 +3155,5 @@ set auto_explain.log_analyze to true; -- the following should not be locally executed since explain analyze is on select * from test_ref_table; DROP SCHEMA test_auto_explain CASCADE; -select master_remove_node('localhost', :master_port); - -SELECT public.wait_until_metadata_sync(30000); - SET client_min_messages TO ERROR; DROP SCHEMA multi_explain CASCADE; diff --git a/src/test/regress/expected/multi_fix_partition_shard_index_names.out b/src/test/regress/expected/multi_fix_partition_shard_index_names.out index 5f7526982..e6795317c 100644 --- a/src/test/regress/expected/multi_fix_partition_shard_index_names.out +++ b/src/test/regress/expected/multi_fix_partition_shard_index_names.out @@ -495,14 +495,6 @@ SET search_path TO fix_idx_names, public; DROP TABLE dist_partitioned_table; SET citus.next_shard_id TO 910040; -- test with citus local table -SET client_min_messages TO WARNING; -SELECT 1 FROM citus_add_node('localhost', :master_port, groupid=>0); - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - -RESET client_min_messages; CREATE TABLE date_partitioned_citus_local_table( measureid integer, eventdate date, @@ -750,9 +742,3 @@ ALTER TABLE parent_table DROP CONSTRAINT pkey_cst CASCADE; ALTER TABLE parent_table DROP CONSTRAINT unique_cst CASCADE; SET client_min_messages TO WARNING; DROP SCHEMA fix_idx_names CASCADE; -SELECT citus_remove_node('localhost', :master_port); - citus_remove_node ---------------------------------------------------------------------- - -(1 row) - diff --git a/src/test/regress/expected/multi_foreign_key.out b/src/test/regress/expected/multi_foreign_key.out index 7efa9d61c..832be2740 100644 --- a/src/test/regress/expected/multi_foreign_key.out +++ b/src/test/regress/expected/multi_foreign_key.out @@ -856,11 +856,16 @@ SELECT create_reference_table('reference_table_second'); CREATE TABLE referenced_local_table(id int PRIMARY KEY, other_column int); DROP TABLE reference_table CASCADE; NOTICE: drop cascades to constraint reference_table_second_referencing_column_fkey on table reference_table_second +NOTICE: drop cascades to constraint reference_table_second_referencing_column_fkey_1350654 on table public.reference_table_second_1350654 +CONTEXT: SQL statement "SELECT citus_drop_all_shards(v_obj.objid, v_obj.schema_name, v_obj.object_name, drop_shards_metadata_only := false)" +PL/pgSQL function citus_drop_trigger() line XX at PERFORM CREATE TABLE reference_table(id int, referencing_column int REFERENCES referenced_local_table(id)); SELECT create_reference_table('reference_table'); -ERROR: referenced table "referenced_local_table" must be a distributed table or a reference table -DETAIL: To enforce foreign keys, the referencing and referenced rows need to be stored on the same node. -HINT: You could use SELECT create_reference_table('referenced_local_table') to replicate the referenced table to all nodes or consider dropping the foreign key + create_reference_table +--------------------------------------------------------------------- + +(1 row) + -- test foreign key creation on CREATE TABLE on self referencing reference table CREATE TABLE self_referencing_reference_table( id int, @@ -877,6 +882,7 @@ SELECT create_reference_table('self_referencing_reference_table'); -- test foreign key creation on ALTER TABLE from reference table DROP TABLE reference_table; +NOTICE: removing table public.referenced_local_table from metadata as it is not connected to any reference tables via foreign keys CREATE TABLE reference_table(id int PRIMARY KEY, referencing_column int); SELECT create_reference_table('reference_table'); create_reference_table @@ -911,6 +917,9 @@ DROP TABLE reference_table CASCADE; NOTICE: drop cascades to 2 other objects DETAIL: drop cascades to constraint fk on table references_to_reference_table drop cascades to constraint fk on table reference_table_second +NOTICE: drop cascades to constraint fk_1350663 on table public.reference_table_second_1350663 +CONTEXT: SQL statement "SELECT citus_drop_all_shards(v_obj.objid, v_obj.schema_name, v_obj.object_name, drop_shards_metadata_only := false)" +PL/pgSQL function citus_drop_trigger() line XX at PERFORM CREATE TABLE reference_table(id int PRIMARY KEY, referencing_column int); SELECT create_reference_table('reference_table'); create_reference_table @@ -919,9 +928,6 @@ SELECT create_reference_table('reference_table'); (1 row) ALTER TABLE reference_table ADD CONSTRAINT fk FOREIGN KEY(referencing_column) REFERENCES referenced_local_table(id); -ERROR: referenced table "referenced_local_table" must be a distributed table or a reference table -DETAIL: To enforce foreign keys, the referencing and referenced rows need to be stored on the same node. -HINT: You could use SELECT create_reference_table('referenced_local_table') to replicate the referenced table to all nodes or consider dropping the foreign key -- test foreign key creation on ALTER TABLE on self referencing reference table DROP TABLE self_referencing_reference_table; CREATE TABLE self_referencing_reference_table( @@ -1187,12 +1193,7 @@ CREATE TABLE set_on_default_test_referencing( REFERENCES set_on_default_test_referenced(col_1, col_3) ON UPDATE SET DEFAULT ); --- from distributed / reference to reference, fkey exists before calling the UDFs -SELECT create_distributed_table('set_on_default_test_referencing', 'col_1'); ERROR: cannot create foreign key constraint since Citus does not support ON DELETE / UPDATE SET DEFAULT actions on the columns that default to sequences -SELECT create_reference_table('set_on_default_test_referencing'); -ERROR: cannot create foreign key constraint since Citus does not support ON DELETE / UPDATE SET DEFAULT actions on the columns that default to sequences -DROP TABLE set_on_default_test_referencing; CREATE TABLE set_on_default_test_referencing( col_1 serial, col_2 int, col_3 int, col_4 int ); @@ -1276,3 +1277,6 @@ ERROR: cannot create foreign key constraint since Citus does not support ON DEL -- we no longer need those tables DROP TABLE referenced_by_reference_table, references_to_reference_table, reference_table, reference_table_second, referenced_local_table, self_referencing_reference_table, dropfkeytest2, set_on_default_test_referenced, set_on_default_test_referencing; +NOTICE: drop cascades to constraint fk_1350664 on table public.reference_table_1350664 +CONTEXT: SQL statement "SELECT citus_drop_all_shards(v_obj.objid, v_obj.schema_name, v_obj.object_name, drop_shards_metadata_only := false)" +PL/pgSQL function citus_drop_trigger() line XX at PERFORM diff --git a/src/test/regress/expected/multi_insert_select.out b/src/test/regress/expected/multi_insert_select.out index abebf314e..ac339a620 100644 --- a/src/test/regress/expected/multi_insert_select.out +++ b/src/test/regress/expected/multi_insert_select.out @@ -884,7 +884,7 @@ $Q$); Custom Scan (Citus INSERT ... SELECT) INSERT/SELECT method: pull to coordinator -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 (4 rows) -- EXPLAIN ANALYZE is not supported for INSERT ... SELECT via coordinator @@ -921,7 +921,7 @@ $Q$); Custom Scan (Citus INSERT ... SELECT) INSERT/SELECT method: pull to coordinator -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 (4 rows) -- make things a bit more complicate with IN clauses @@ -940,7 +940,7 @@ $Q$); Custom Scan (Citus INSERT ... SELECT) INSERT/SELECT method: pull to coordinator -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 (4 rows) -- implicit join on non partition column should also not be pushed down, @@ -959,7 +959,7 @@ $Q$); Custom Scan (Citus INSERT ... SELECT) INSERT/SELECT method: pull to coordinator -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 (4 rows) RESET client_min_messages; @@ -981,7 +981,7 @@ $Q$); Custom Scan (Citus INSERT ... SELECT) INSERT/SELECT method: pull to coordinator -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 (4 rows) -- foo is not joined on the partition key so the query is not @@ -1046,7 +1046,7 @@ $Q$); Custom Scan (Citus INSERT ... SELECT) INSERT/SELECT method: pull to coordinator -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 (4 rows) -- foo is not joined on the partition key so the query is not @@ -1437,7 +1437,7 @@ $Q$); Group Key: remote_scan.id Filter: (pg_catalog.sum(remote_scan.worker_column_4) > '10'::numeric) -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 (11 rows) -- cannot push down since foo doesn't have en equi join @@ -1514,7 +1514,7 @@ $Q$); -> HashAggregate Group Key: remote_scan.user_id -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 (6 rows) -- join among reference_ids and averages is not on the partition key @@ -1576,7 +1576,7 @@ $Q$); Custom Scan (Citus INSERT ... SELECT) INSERT/SELECT method: pull to coordinator -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 (4 rows) -- Selected value in the WHERE is not partition key, so we cannot use distributed @@ -3276,7 +3276,7 @@ $$); Custom Scan (Citus INSERT ... SELECT) INSERT/SELECT method: pull to coordinator -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 (4 rows) -- For INSERT SELECT, when a lateral query references an outer query, push-down is possible even if limit clause exists in the lateral query. diff --git a/src/test/regress/expected/multi_insert_select_0.out b/src/test/regress/expected/multi_insert_select_0.out index ee2341759..a4988bceb 100644 --- a/src/test/regress/expected/multi_insert_select_0.out +++ b/src/test/regress/expected/multi_insert_select_0.out @@ -884,7 +884,7 @@ $Q$); Custom Scan (Citus INSERT ... SELECT) INSERT/SELECT method: pull to coordinator -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 (4 rows) -- EXPLAIN ANALYZE is not supported for INSERT ... SELECT via coordinator @@ -921,7 +921,7 @@ $Q$); Custom Scan (Citus INSERT ... SELECT) INSERT/SELECT method: pull to coordinator -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 (4 rows) -- make things a bit more complicate with IN clauses @@ -940,7 +940,7 @@ $Q$); Custom Scan (Citus INSERT ... SELECT) INSERT/SELECT method: pull to coordinator -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 (4 rows) -- implicit join on non partition column should also not be pushed down, @@ -959,7 +959,7 @@ $Q$); Custom Scan (Citus INSERT ... SELECT) INSERT/SELECT method: pull to coordinator -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 (4 rows) RESET client_min_messages; @@ -981,7 +981,7 @@ $Q$); Custom Scan (Citus INSERT ... SELECT) INSERT/SELECT method: pull to coordinator -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 (4 rows) -- foo is not joined on the partition key so the query is not @@ -1046,7 +1046,7 @@ $Q$); Custom Scan (Citus INSERT ... SELECT) INSERT/SELECT method: pull to coordinator -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 (4 rows) -- foo is not joined on the partition key so the query is not @@ -1437,7 +1437,7 @@ $Q$); Group Key: remote_scan.id Filter: (pg_catalog.sum(remote_scan.worker_column_4) > '10'::numeric) -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 (11 rows) -- cannot push down since foo doesn't have en equi join @@ -1514,7 +1514,7 @@ $Q$); -> HashAggregate Group Key: remote_scan.user_id -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 (6 rows) -- join among reference_ids and averages is not on the partition key @@ -1576,7 +1576,7 @@ $Q$); Custom Scan (Citus INSERT ... SELECT) INSERT/SELECT method: pull to coordinator -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 (4 rows) -- Selected value in the WHERE is not partition key, so we cannot use distributed @@ -3276,7 +3276,7 @@ $$); Custom Scan (Citus INSERT ... SELECT) INSERT/SELECT method: pull to coordinator -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 (4 rows) -- For INSERT SELECT, when a lateral query references an outer query, push-down is possible even if limit clause exists in the lateral query. diff --git a/src/test/regress/expected/multi_insert_select_conflict.out b/src/test/regress/expected/multi_insert_select_conflict.out index f344a8b79..5c06719d3 100644 --- a/src/test/regress/expected/multi_insert_select_conflict.out +++ b/src/test/regress/expected/multi_insert_select_conflict.out @@ -589,8 +589,9 @@ DEBUG: Collecting INSERT ... SELECT results on coordinator RESET client_min_messages; DROP SCHEMA on_conflict CASCADE; -NOTICE: drop cascades to 7 other objects +NOTICE: drop cascades to 8 other objects DETAIL: drop cascades to table test_ref_table +drop cascades to table test_ref_table_1900012 drop cascades to table source_table_3 drop cascades to table source_table_4 drop cascades to table target_table_2 diff --git a/src/test/regress/expected/multi_insert_select_conflict_0.out b/src/test/regress/expected/multi_insert_select_conflict_0.out index 42b5aed31..4c2add1d7 100644 --- a/src/test/regress/expected/multi_insert_select_conflict_0.out +++ b/src/test/regress/expected/multi_insert_select_conflict_0.out @@ -589,8 +589,9 @@ DEBUG: Collecting INSERT ... SELECT results on coordinator RESET client_min_messages; DROP SCHEMA on_conflict CASCADE; -NOTICE: drop cascades to 7 other objects +NOTICE: drop cascades to 8 other objects DETAIL: drop cascades to table test_ref_table +drop cascades to table test_ref_table_1900012 drop cascades to table source_table_3 drop cascades to table source_table_4 drop cascades to table target_table_2 diff --git a/src/test/regress/expected/multi_join_pruning.out b/src/test/regress/expected/multi_join_pruning.out index efc6954ad..27fdc3980 100644 --- a/src/test/regress/expected/multi_join_pruning.out +++ b/src/test/regress/expected/multi_join_pruning.out @@ -140,3 +140,7 @@ DEBUG: join prunable for intervals [BA1000U2AMO4ZGX,BZZXSP27F21T6] and [AA1000U explain statements for distributed queries are not enabled (3 rows) +SET client_min_messages TO WARNING; +DROP TABLE varchar_partitioned_table; +DROP TABLE array_partitioned_table; +DROP TABLE composite_partitioned_table; diff --git a/src/test/regress/expected/multi_level_recursive_queries.out b/src/test/regress/expected/multi_level_recursive_queries.out index b2bf0a49c..e0a4d44a9 100644 --- a/src/test/regress/expected/multi_level_recursive_queries.out +++ b/src/test/regress/expected/multi_level_recursive_queries.out @@ -298,10 +298,5 @@ DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT avg(table_5.i DEBUG: generating subplan XXX_1 for subquery SELECT table_6.id FROM ((SELECT intermediate_result.id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(id integer)) table_6 JOIN multi_recursive.dist0 table_8 USING (id)) WHERE (table_8.id OPERATOR(pg_catalog.<) 0) ORDER BY table_6.id DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT avg(table_5.id) AS avg FROM ((SELECT intermediate_result.id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(id integer)) table_5 JOIN multi_recursive.dist0 table_9 USING (id)) ERROR: recursive complex joins are only supported when all distributed tables are co-located and joined on their distribution columns -RESET client_min_messages; +SET client_min_messages TO WARNING; DROP SCHEMA multi_recursive CASCADE; -NOTICE: drop cascades to 4 other objects -DETAIL: drop cascades to table tbl_dist1 -drop cascades to table tbl_ref1 -drop cascades to table dist0 -drop cascades to table dist1 diff --git a/src/test/regress/expected/multi_metadata_sync.out b/src/test/regress/expected/multi_metadata_sync.out index 626584de6..e92b9aa1b 100644 --- a/src/test/regress/expected/multi_metadata_sync.out +++ b/src/test/regress/expected/multi_metadata_sync.out @@ -69,7 +69,7 @@ ALTER ROLE CURRENT_USER WITH PASSWORD 'dummypassword'; -- Show that, with no MX tables, activate node snapshot contains only the delete commands, -- pg_dist_node entries, pg_dist_object entries and roles. SELECT unnest(activate_node_snapshot()) order by 1; - unnest + unnest --------------------------------------------------------------------- ALTER DATABASE regression OWNER TO postgres; CALL pg_catalog.worker_drop_all_shell_tables(true) @@ -85,7 +85,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; GRANT CREATE ON SCHEMA public TO pg_database_owner; GRANT USAGE ON SCHEMA public TO PUBLIC; GRANT USAGE ON SCHEMA public TO pg_database_owner; - INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (1, 1, 'localhost', 57637, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE),(2, 2, 'localhost', 57638, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE) + INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (1, 0, 'localhost', 57636, 'default', TRUE, TRUE, TRUE, 'primary'::noderole, 'default', FALSE),(2, 1, 'localhost', 57637, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE),(3, 2, 'localhost', 57638, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE) RESET ROLE RESET ROLE SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') @@ -98,9 +98,9 @@ SELECT unnest(activate_node_snapshot()) order by 1; SET citus.enable_ddl_propagation TO 'on' SET citus.enable_ddl_propagation TO 'on' UPDATE pg_dist_local_group SET groupid = 1 - UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 1 + UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 2 + UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 2 + UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 2 WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('database', ARRAY['regression']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('role', ARRAY['postgres']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('schema', ARRAY['public']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; @@ -158,7 +158,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; GRANT CREATE ON SCHEMA public TO pg_database_owner; GRANT USAGE ON SCHEMA public TO PUBLIC; GRANT USAGE ON SCHEMA public TO pg_database_owner; - INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (1, 1, 'localhost', 57637, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE),(2, 2, 'localhost', 57638, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE) + INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (1, 0, 'localhost', 57636, 'default', TRUE, TRUE, TRUE, 'primary'::noderole, 'default', FALSE),(2, 1, 'localhost', 57637, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE),(3, 2, 'localhost', 57638, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE) RESET ROLE RESET ROLE SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') @@ -180,9 +180,9 @@ SELECT unnest(activate_node_snapshot()) order by 1; SET citus.enable_ddl_propagation TO 'on' SET citus.enable_ddl_propagation TO 'on' UPDATE pg_dist_local_group SET groupid = 1 - UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 1 + UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 2 + UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 2 + UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 2 WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (2, 8, 1, 'integer'::regtype, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (3, 1, 2, 0, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('database', ARRAY['regression']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; @@ -226,7 +226,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; GRANT CREATE ON SCHEMA public TO pg_database_owner; GRANT USAGE ON SCHEMA public TO PUBLIC; GRANT USAGE ON SCHEMA public TO pg_database_owner; - INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (1, 1, 'localhost', 57637, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE),(2, 2, 'localhost', 57638, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE) + INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (1, 0, 'localhost', 57636, 'default', TRUE, TRUE, TRUE, 'primary'::noderole, 'default', FALSE),(2, 1, 'localhost', 57637, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE),(3, 2, 'localhost', 57638, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE) RESET ROLE RESET ROLE SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') @@ -245,9 +245,9 @@ SELECT unnest(activate_node_snapshot()) order by 1; SET citus.enable_ddl_propagation TO 'on' SET citus.enable_ddl_propagation TO 'on' UPDATE pg_dist_local_group SET groupid = 1 - UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 1 + UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 2 + UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 2 + UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 2 WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (2, 8, 1, 'integer'::regtype, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('database', ARRAY['regression']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('role', ARRAY['postgres']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; @@ -287,7 +287,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; GRANT CREATE ON SCHEMA public TO pg_database_owner; GRANT USAGE ON SCHEMA public TO PUBLIC; GRANT USAGE ON SCHEMA public TO pg_database_owner; - INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (1, 1, 'localhost', 57637, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE),(2, 2, 'localhost', 57638, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE) + INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (1, 0, 'localhost', 57636, 'default', TRUE, TRUE, TRUE, 'primary'::noderole, 'default', FALSE),(2, 1, 'localhost', 57637, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE),(3, 2, 'localhost', 57638, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE) RESET ROLE RESET ROLE SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') @@ -306,9 +306,9 @@ SELECT unnest(activate_node_snapshot()) order by 1; SET citus.enable_ddl_propagation TO 'on' SET citus.enable_ddl_propagation TO 'on' UPDATE pg_dist_local_group SET groupid = 1 - UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 1 + UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 2 + UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 2 + UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 2 WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (2, 8, 1, 'integer'::regtype, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('database', ARRAY['regression']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('role', ARRAY['postgres']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; @@ -355,7 +355,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; GRANT CREATE ON SCHEMA public TO pg_database_owner; GRANT USAGE ON SCHEMA public TO PUBLIC; GRANT USAGE ON SCHEMA public TO pg_database_owner; - INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (1, 1, 'localhost', 57637, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE),(2, 2, 'localhost', 57638, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE) + INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (1, 0, 'localhost', 57636, 'default', TRUE, TRUE, TRUE, 'primary'::noderole, 'default', FALSE),(2, 1, 'localhost', 57637, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE),(3, 2, 'localhost', 57638, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE) RESET ROLE RESET ROLE SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') @@ -374,9 +374,9 @@ SELECT unnest(activate_node_snapshot()) order by 1; SET citus.enable_ddl_propagation TO 'on' SET citus.enable_ddl_propagation TO 'on' UPDATE pg_dist_local_group SET groupid = 1 - UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 1 + UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 2 + UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 2 + UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 2 WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (2, 8, 1, 'integer'::regtype, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('database', ARRAY['regression']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('role', ARRAY['postgres']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; @@ -416,7 +416,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; GRANT CREATE ON SCHEMA public TO pg_database_owner; GRANT USAGE ON SCHEMA public TO PUBLIC; GRANT USAGE ON SCHEMA public TO pg_database_owner; - INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (1, 1, 'localhost', 57637, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE),(2, 2, 'localhost', 57638, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE) + INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (1, 0, 'localhost', 57636, 'default', TRUE, TRUE, TRUE, 'primary'::noderole, 'default', FALSE),(2, 1, 'localhost', 57637, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE),(3, 2, 'localhost', 57638, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE) RESET ROLE RESET ROLE SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') @@ -435,9 +435,9 @@ SELECT unnest(activate_node_snapshot()) order by 1; SET citus.enable_ddl_propagation TO 'on' SET citus.enable_ddl_propagation TO 'on' UPDATE pg_dist_local_group SET groupid = 1 - UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 1 + UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 2 + UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 2 + UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 2 WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (2, 8, 1, 'integer'::regtype, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('database', ARRAY['regression']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('role', ARRAY['postgres']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; @@ -451,11 +451,11 @@ SELECT unnest(activate_node_snapshot()) order by 1; (54 rows) -- Test start_metadata_sync_to_node and citus_activate_node UDFs --- Ensure that hasmetadata=false for all nodes +-- Ensure that hasmetadata=false for all nodes except for the coordinator node SELECT count(*) FROM pg_dist_node WHERE hasmetadata=true; count --------------------------------------------------------------------- - 0 + 1 (1 row) -- Show that metadata can not be synced on secondary node @@ -463,7 +463,7 @@ SELECT groupid AS worker_1_group FROM pg_dist_node WHERE nodeport = :worker_1_po SELECT master_add_node('localhost', 8888, groupid => :worker_1_group, noderole => 'secondary'); master_add_node --------------------------------------------------------------------- - 4 + 5 (1 row) SELECT start_metadata_sync_to_node('localhost', 8888); @@ -495,7 +495,7 @@ SELECT hasmetadata FROM pg_dist_node WHERE nodeport = 8888; SELECT master_add_secondary_node('localhost', 8889, 'localhost', :worker_1_port, nodecluster => 'second-cluster'); master_add_secondary_node --------------------------------------------------------------------- - 5 + 6 (1 row) \c - - - :master_port @@ -509,7 +509,7 @@ SELECT 1 FROM citus_activate_node('localhost', :worker_1_port); SELECT nodeid, hasmetadata FROM pg_dist_node WHERE nodename='localhost' AND nodeport=:worker_1_port; nodeid | hasmetadata --------------------------------------------------------------------- - 1 | t + 2 | t (1 row) -- Check that the metadata has been copied to the worker @@ -523,11 +523,12 @@ SELECT * FROM pg_dist_local_group; SELECT * FROM pg_dist_node ORDER BY nodeid; nodeid | groupid | nodename | nodeport | noderack | hasmetadata | isactive | noderole | nodecluster | metadatasynced | shouldhaveshards --------------------------------------------------------------------- - 1 | 1 | localhost | 57637 | default | t | t | primary | default | t | t - 2 | 2 | localhost | 57638 | default | f | t | primary | default | f | t - 4 | 1 | localhost | 8888 | default | f | t | secondary | default | f | t - 5 | 1 | localhost | 8889 | default | f | t | secondary | second-cluster | f | t -(4 rows) + 1 | 0 | localhost | 57636 | default | t | t | primary | default | t | f + 2 | 1 | localhost | 57637 | default | t | t | primary | default | t | t + 3 | 2 | localhost | 57638 | default | f | t | primary | default | f | t + 5 | 1 | localhost | 8888 | default | f | t | secondary | default | f | t + 6 | 1 | localhost | 8889 | default | f | t | secondary | second-cluster | f | t +(5 rows) SELECT * FROM pg_dist_partition WHERE logicalrelid::text LIKE 'mx_testing_schema%' ORDER BY logicalrelid::text; logicalrelid | partmethod | partkey | colocationid | repmodel | autoconverted @@ -661,11 +662,12 @@ SELECT * FROM pg_dist_local_group; SELECT * FROM pg_dist_node ORDER BY nodeid; nodeid | groupid | nodename | nodeport | noderack | hasmetadata | isactive | noderole | nodecluster | metadatasynced | shouldhaveshards --------------------------------------------------------------------- - 1 | 1 | localhost | 57637 | default | t | t | primary | default | t | t - 2 | 2 | localhost | 57638 | default | f | t | primary | default | f | t - 4 | 1 | localhost | 8888 | default | f | t | secondary | default | f | t - 5 | 1 | localhost | 8889 | default | f | t | secondary | second-cluster | f | t -(4 rows) + 1 | 0 | localhost | 57636 | default | t | t | primary | default | t | f + 2 | 1 | localhost | 57637 | default | t | t | primary | default | t | t + 3 | 2 | localhost | 57638 | default | f | t | primary | default | f | t + 5 | 1 | localhost | 8888 | default | f | t | secondary | default | f | t + 6 | 1 | localhost | 8889 | default | f | t | secondary | second-cluster | f | t +(5 rows) SELECT * FROM pg_dist_partition WHERE logicalrelid::text LIKE 'mx_testing_schema%' ORDER BY logicalrelid::text; logicalrelid | partmethod | partkey | colocationid | repmodel | autoconverted @@ -1509,7 +1511,7 @@ SELECT create_distributed_table('mx_table', 'a'); SELECT master_add_node('localhost', :worker_2_port); master_add_node --------------------------------------------------------------------- - 6 + 7 (1 row) \c - mx_user - :worker_1_port @@ -1620,9 +1622,10 @@ ORDER BY nodeport; logicalrelid | partmethod | repmodel | shardid | placementid | nodename | nodeport --------------------------------------------------------------------- - mx_ref | n | t | 1310074 | 100074 | localhost | 57637 - mx_ref | n | t | 1310074 | 100075 | localhost | 57638 -(2 rows) + mx_ref | n | t | 1310074 | 100074 | localhost | 57636 + mx_ref | n | t | 1310074 | 100075 | localhost | 57637 + mx_ref | n | t | 1310074 | 100076 | localhost | 57638 +(3 rows) SELECT shardid AS ref_table_shardid FROM pg_dist_shard WHERE logicalrelid='mx_ref'::regclass \gset -- make sure we have the pg_dist_colocation record on the worker @@ -1716,8 +1719,9 @@ FROM pg_dist_shard NATURAL JOIN pg_dist_shard_placement WHERE logicalrelid='mx_ref'::regclass; shardid | nodename | nodeport --------------------------------------------------------------------- + 1310075 | localhost | 57636 1310075 | localhost | 57637 -(1 row) +(2 rows) \c - - - :worker_1_port SELECT shardid, nodename, nodeport @@ -1725,15 +1729,16 @@ FROM pg_dist_shard NATURAL JOIN pg_dist_shard_placement WHERE logicalrelid='mx_ref'::regclass; shardid | nodename | nodeport --------------------------------------------------------------------- + 1310075 | localhost | 57636 1310075 | localhost | 57637 -(1 row) +(2 rows) \c - - - :master_port SET client_min_messages TO ERROR; SELECT master_add_node('localhost', :worker_2_port); master_add_node --------------------------------------------------------------------- - 7 + 8 (1 row) RESET client_min_messages; @@ -1743,8 +1748,9 @@ WHERE logicalrelid='mx_ref'::regclass ORDER BY shardid, nodeport; shardid | nodename | nodeport --------------------------------------------------------------------- + 1310075 | localhost | 57636 1310075 | localhost | 57637 -(1 row) +(2 rows) \c - - - :worker_1_port SELECT shardid, nodename, nodeport @@ -1753,8 +1759,9 @@ WHERE logicalrelid='mx_ref'::regclass ORDER BY shardid, nodeport; shardid | nodename | nodeport --------------------------------------------------------------------- + 1310075 | localhost | 57636 1310075 | localhost | 57637 -(1 row) +(2 rows) -- Get the metadata back into a consistent state \c - - - :master_port @@ -1862,10 +1869,6 @@ HINT: If the node is up, wait until metadata gets synced to it and try again. ALTER TABLE dist_table_1 ADD COLUMN b int; ERROR: localhost:xxxxx is a metadata node, but is out of sync HINT: If the node is up, wait until metadata gets synced to it and try again. -SELECT master_add_node('localhost', :master_port, groupid => 0); -NOTICE: localhost:xxxxx is the coordinator and already contains metadata, skipping syncing the metadata -ERROR: localhost:xxxxx is a metadata node, but is out of sync -HINT: If the node is up, wait until metadata gets synced to it and try again. SELECT citus_disable_node_and_wait('localhost', :worker_1_port); ERROR: disabling the first worker node in the metadata is not allowed DETAIL: Citus uses the first worker node in the metadata for certain internal operations when replicated tables are modified. Synchronous mode ensures that all nodes have the same view of the first worker node, which is used for certain locking operations. @@ -1918,7 +1921,7 @@ SELECT wait_until_metadata_sync(60000); SELECT master_add_node('localhost', :worker_2_port); master_add_node --------------------------------------------------------------------- - 7 + 8 (1 row) CREATE SEQUENCE mx_test_sequence_0; @@ -1989,7 +1992,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; GRANT CREATE ON SCHEMA public TO pg_database_owner; GRANT USAGE ON SCHEMA public TO PUBLIC; GRANT USAGE ON SCHEMA public TO pg_database_owner; - INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (4, 1, 'localhost', 8888, 'default', FALSE, FALSE, TRUE, 'secondary'::noderole, 'default', TRUE),(5, 1, 'localhost', 8889, 'default', FALSE, FALSE, TRUE, 'secondary'::noderole, 'second-cluster', TRUE),(1, 1, 'localhost', 57637, 'default', TRUE, TRUE, TRUE, 'primary'::noderole, 'default', TRUE),(7, 5, 'localhost', 57638, 'default', TRUE, TRUE, TRUE, 'primary'::noderole, 'default', TRUE) + INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (5, 1, 'localhost', 8888, 'default', FALSE, FALSE, TRUE, 'secondary'::noderole, 'default', TRUE),(6, 1, 'localhost', 8889, 'default', FALSE, FALSE, TRUE, 'secondary'::noderole, 'second-cluster', TRUE),(1, 0, 'localhost', 57636, 'default', TRUE, TRUE, TRUE, 'primary'::noderole, 'default', FALSE),(2, 1, 'localhost', 57637, 'default', TRUE, TRUE, TRUE, 'primary'::noderole, 'default', TRUE),(8, 5, 'localhost', 57638, 'default', TRUE, TRUE, TRUE, 'primary'::noderole, 'default', TRUE) RESET ROLE RESET ROLE SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') @@ -2025,9 +2028,9 @@ SELECT unnest(activate_node_snapshot()) order by 1; SET citus.enable_ddl_propagation TO 'on' SET citus.enable_ddl_propagation TO 'on' UPDATE pg_dist_local_group SET groupid = 1 - UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 1 + UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 2 + UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 2 + UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 2 WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (10009, 1, -1, 0, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (10010, 4, 1, 'integer'::regtype, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('database', ARRAY['regression']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; @@ -2050,9 +2053,9 @@ SELECT unnest(activate_node_snapshot()) order by 1; WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310000, 0, 1, 100000), (1310001, 0, 5, 100001), (1310002, 0, 1, 100002), (1310003, 0, 5, 100003), (1310004, 0, 1, 100004), (1310005, 0, 5, 100005), (1310006, 0, 1, 100006), (1310007, 0, 5, 100007)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310022, 0, 1, 100022), (1310023, 0, 5, 100023), (1310024, 0, 1, 100024), (1310025, 0, 5, 100025), (1310026, 0, 1, 100026)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310027, 0, 1, 100027), (1310028, 0, 5, 100028), (1310029, 0, 1, 100029), (1310030, 0, 5, 100030), (1310031, 0, 1, 100031)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; - WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310075, 0, 1, 100076), (1310075, 0, 5, 100077)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; - WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310076, 0, 1, 100078), (1310077, 0, 5, 100079), (1310078, 0, 1, 100080), (1310079, 0, 5, 100081)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; - WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310085, 0, 1, 100088), (1310086, 0, 5, 100089), (1310087, 0, 1, 100090), (1310088, 0, 5, 100091)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; + WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310075, 0, 0, 100077), (1310075, 0, 1, 100078), (1310075, 0, 5, 100079)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; + WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310076, 0, 1, 100080), (1310077, 0, 5, 100081), (1310078, 0, 1, 100082), (1310079, 0, 5, 100083)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; + WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310085, 0, 1, 100091), (1310086, 0, 5, 100092), (1310087, 0, 1, 100093), (1310088, 0, 5, 100094)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('mx_test_schema_1.mx_table_1'::regclass, 1310022, 't'::"char", '-2147483648', '-1288490190'), ('mx_test_schema_1.mx_table_1'::regclass, 1310023, 't'::"char", '-1288490189', '-429496731'), ('mx_test_schema_1.mx_table_1'::regclass, 1310024, 't'::"char", '-429496730', '429496728'), ('mx_test_schema_1.mx_table_1'::regclass, 1310025, 't'::"char", '429496729', '1288490187'), ('mx_test_schema_1.mx_table_1'::regclass, 1310026, 't'::"char", '1288490188', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('mx_test_schema_2.mx_table_2'::regclass, 1310027, 't'::"char", '-2147483648', '-1288490190'), ('mx_test_schema_2.mx_table_2'::regclass, 1310028, 't'::"char", '-1288490189', '-429496731'), ('mx_test_schema_2.mx_table_2'::regclass, 1310029, 't'::"char", '-429496730', '429496728'), ('mx_test_schema_2.mx_table_2'::regclass, 1310030, 't'::"char", '429496729', '1288490187'), ('mx_test_schema_2.mx_table_2'::regclass, 1310031, 't'::"char", '1288490188', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('mx_testing_schema.mx_test_table'::regclass, 1310000, 't'::"char", '-2147483648', '-1610612737'), ('mx_testing_schema.mx_test_table'::regclass, 1310001, 't'::"char", '-1610612736', '-1073741825'), ('mx_testing_schema.mx_test_table'::regclass, 1310002, 't'::"char", '-1073741824', '-536870913'), ('mx_testing_schema.mx_test_table'::regclass, 1310003, 't'::"char", '-536870912', '-1'), ('mx_testing_schema.mx_test_table'::regclass, 1310004, 't'::"char", '0', '536870911'), ('mx_testing_schema.mx_test_table'::regclass, 1310005, 't'::"char", '536870912', '1073741823'), ('mx_testing_schema.mx_test_table'::regclass, 1310006, 't'::"char", '1073741824', '1610612735'), ('mx_testing_schema.mx_test_table'::regclass, 1310007, 't'::"char", '1610612736', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; diff --git a/src/test/regress/expected/multi_metadata_sync_0.out b/src/test/regress/expected/multi_metadata_sync_0.out index 341a0f319..9ab50664c 100644 --- a/src/test/regress/expected/multi_metadata_sync_0.out +++ b/src/test/regress/expected/multi_metadata_sync_0.out @@ -69,7 +69,7 @@ ALTER ROLE CURRENT_USER WITH PASSWORD 'dummypassword'; -- Show that, with no MX tables, activate node snapshot contains only the delete commands, -- pg_dist_node entries, pg_dist_object entries and roles. SELECT unnest(activate_node_snapshot()) order by 1; - unnest + unnest --------------------------------------------------------------------- ALTER DATABASE regression OWNER TO postgres; CALL pg_catalog.worker_drop_all_shell_tables(true) @@ -85,7 +85,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; GRANT CREATE ON SCHEMA public TO postgres; GRANT USAGE ON SCHEMA public TO PUBLIC; GRANT USAGE ON SCHEMA public TO postgres; - INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (1, 1, 'localhost', 57637, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE),(2, 2, 'localhost', 57638, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE) + INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (1, 0, 'localhost', 57636, 'default', TRUE, TRUE, TRUE, 'primary'::noderole, 'default', FALSE),(2, 1, 'localhost', 57637, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE),(3, 2, 'localhost', 57638, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE) RESET ROLE RESET ROLE SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') @@ -98,9 +98,9 @@ SELECT unnest(activate_node_snapshot()) order by 1; SET citus.enable_ddl_propagation TO 'on' SET citus.enable_ddl_propagation TO 'on' UPDATE pg_dist_local_group SET groupid = 1 - UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 1 + UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 2 + UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 2 + UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 2 WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('database', ARRAY['regression']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('role', ARRAY['postgres']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('schema', ARRAY['public']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; @@ -158,7 +158,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; GRANT CREATE ON SCHEMA public TO postgres; GRANT USAGE ON SCHEMA public TO PUBLIC; GRANT USAGE ON SCHEMA public TO postgres; - INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (1, 1, 'localhost', 57637, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE),(2, 2, 'localhost', 57638, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE) + INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (1, 0, 'localhost', 57636, 'default', TRUE, TRUE, TRUE, 'primary'::noderole, 'default', FALSE),(2, 1, 'localhost', 57637, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE),(3, 2, 'localhost', 57638, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE) RESET ROLE RESET ROLE SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') @@ -180,9 +180,9 @@ SELECT unnest(activate_node_snapshot()) order by 1; SET citus.enable_ddl_propagation TO 'on' SET citus.enable_ddl_propagation TO 'on' UPDATE pg_dist_local_group SET groupid = 1 - UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 1 + UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 2 + UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 2 + UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 2 WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (2, 8, 1, 'integer'::regtype, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (3, 1, 2, 0, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('database', ARRAY['regression']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; @@ -226,7 +226,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; GRANT CREATE ON SCHEMA public TO postgres; GRANT USAGE ON SCHEMA public TO PUBLIC; GRANT USAGE ON SCHEMA public TO postgres; - INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (1, 1, 'localhost', 57637, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE),(2, 2, 'localhost', 57638, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE) + INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (1, 0, 'localhost', 57636, 'default', TRUE, TRUE, TRUE, 'primary'::noderole, 'default', FALSE),(2, 1, 'localhost', 57637, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE),(3, 2, 'localhost', 57638, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE) RESET ROLE RESET ROLE SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') @@ -245,9 +245,9 @@ SELECT unnest(activate_node_snapshot()) order by 1; SET citus.enable_ddl_propagation TO 'on' SET citus.enable_ddl_propagation TO 'on' UPDATE pg_dist_local_group SET groupid = 1 - UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 1 + UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 2 + UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 2 + UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 2 WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (2, 8, 1, 'integer'::regtype, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('database', ARRAY['regression']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('role', ARRAY['postgres']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; @@ -287,7 +287,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; GRANT CREATE ON SCHEMA public TO postgres; GRANT USAGE ON SCHEMA public TO PUBLIC; GRANT USAGE ON SCHEMA public TO postgres; - INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (1, 1, 'localhost', 57637, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE),(2, 2, 'localhost', 57638, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE) + INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (1, 0, 'localhost', 57636, 'default', TRUE, TRUE, TRUE, 'primary'::noderole, 'default', FALSE),(2, 1, 'localhost', 57637, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE),(3, 2, 'localhost', 57638, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE) RESET ROLE RESET ROLE SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') @@ -306,9 +306,9 @@ SELECT unnest(activate_node_snapshot()) order by 1; SET citus.enable_ddl_propagation TO 'on' SET citus.enable_ddl_propagation TO 'on' UPDATE pg_dist_local_group SET groupid = 1 - UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 1 + UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 2 + UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 2 + UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 2 WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (2, 8, 1, 'integer'::regtype, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('database', ARRAY['regression']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('role', ARRAY['postgres']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; @@ -355,7 +355,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; GRANT CREATE ON SCHEMA public TO postgres; GRANT USAGE ON SCHEMA public TO PUBLIC; GRANT USAGE ON SCHEMA public TO postgres; - INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (1, 1, 'localhost', 57637, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE),(2, 2, 'localhost', 57638, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE) + INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (1, 0, 'localhost', 57636, 'default', TRUE, TRUE, TRUE, 'primary'::noderole, 'default', FALSE),(2, 1, 'localhost', 57637, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE),(3, 2, 'localhost', 57638, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE) RESET ROLE RESET ROLE SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') @@ -374,9 +374,9 @@ SELECT unnest(activate_node_snapshot()) order by 1; SET citus.enable_ddl_propagation TO 'on' SET citus.enable_ddl_propagation TO 'on' UPDATE pg_dist_local_group SET groupid = 1 - UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 1 + UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 2 + UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 2 + UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 2 WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (2, 8, 1, 'integer'::regtype, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('database', ARRAY['regression']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('role', ARRAY['postgres']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; @@ -416,7 +416,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; GRANT CREATE ON SCHEMA public TO postgres; GRANT USAGE ON SCHEMA public TO PUBLIC; GRANT USAGE ON SCHEMA public TO postgres; - INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (1, 1, 'localhost', 57637, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE),(2, 2, 'localhost', 57638, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE) + INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (1, 0, 'localhost', 57636, 'default', TRUE, TRUE, TRUE, 'primary'::noderole, 'default', FALSE),(2, 1, 'localhost', 57637, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE),(3, 2, 'localhost', 57638, 'default', FALSE, FALSE, TRUE, 'primary'::noderole, 'default', TRUE) RESET ROLE RESET ROLE SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') @@ -435,9 +435,9 @@ SELECT unnest(activate_node_snapshot()) order by 1; SET citus.enable_ddl_propagation TO 'on' SET citus.enable_ddl_propagation TO 'on' UPDATE pg_dist_local_group SET groupid = 1 - UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 1 + UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 2 + UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 2 + UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 2 WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (2, 8, 1, 'integer'::regtype, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('database', ARRAY['regression']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('role', ARRAY['postgres']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; @@ -451,11 +451,11 @@ SELECT unnest(activate_node_snapshot()) order by 1; (54 rows) -- Test start_metadata_sync_to_node and citus_activate_node UDFs --- Ensure that hasmetadata=false for all nodes +-- Ensure that hasmetadata=false for all nodes except for the coordinator node SELECT count(*) FROM pg_dist_node WHERE hasmetadata=true; count --------------------------------------------------------------------- - 0 + 1 (1 row) -- Show that metadata can not be synced on secondary node @@ -463,7 +463,7 @@ SELECT groupid AS worker_1_group FROM pg_dist_node WHERE nodeport = :worker_1_po SELECT master_add_node('localhost', 8888, groupid => :worker_1_group, noderole => 'secondary'); master_add_node --------------------------------------------------------------------- - 4 + 5 (1 row) SELECT start_metadata_sync_to_node('localhost', 8888); @@ -495,7 +495,7 @@ SELECT hasmetadata FROM pg_dist_node WHERE nodeport = 8888; SELECT master_add_secondary_node('localhost', 8889, 'localhost', :worker_1_port, nodecluster => 'second-cluster'); master_add_secondary_node --------------------------------------------------------------------- - 5 + 6 (1 row) \c - - - :master_port @@ -509,7 +509,7 @@ SELECT 1 FROM citus_activate_node('localhost', :worker_1_port); SELECT nodeid, hasmetadata FROM pg_dist_node WHERE nodename='localhost' AND nodeport=:worker_1_port; nodeid | hasmetadata --------------------------------------------------------------------- - 1 | t + 2 | t (1 row) -- Check that the metadata has been copied to the worker @@ -523,11 +523,12 @@ SELECT * FROM pg_dist_local_group; SELECT * FROM pg_dist_node ORDER BY nodeid; nodeid | groupid | nodename | nodeport | noderack | hasmetadata | isactive | noderole | nodecluster | metadatasynced | shouldhaveshards --------------------------------------------------------------------- - 1 | 1 | localhost | 57637 | default | t | t | primary | default | t | t - 2 | 2 | localhost | 57638 | default | f | t | primary | default | f | t - 4 | 1 | localhost | 8888 | default | f | t | secondary | default | f | t - 5 | 1 | localhost | 8889 | default | f | t | secondary | second-cluster | f | t -(4 rows) + 1 | 0 | localhost | 57636 | default | t | t | primary | default | t | f + 2 | 1 | localhost | 57637 | default | t | t | primary | default | t | t + 3 | 2 | localhost | 57638 | default | f | t | primary | default | f | t + 5 | 1 | localhost | 8888 | default | f | t | secondary | default | f | t + 6 | 1 | localhost | 8889 | default | f | t | secondary | second-cluster | f | t +(5 rows) SELECT * FROM pg_dist_partition WHERE logicalrelid::text LIKE 'mx_testing_schema%' ORDER BY logicalrelid::text; logicalrelid | partmethod | partkey | colocationid | repmodel | autoconverted @@ -661,11 +662,12 @@ SELECT * FROM pg_dist_local_group; SELECT * FROM pg_dist_node ORDER BY nodeid; nodeid | groupid | nodename | nodeport | noderack | hasmetadata | isactive | noderole | nodecluster | metadatasynced | shouldhaveshards --------------------------------------------------------------------- - 1 | 1 | localhost | 57637 | default | t | t | primary | default | t | t - 2 | 2 | localhost | 57638 | default | f | t | primary | default | f | t - 4 | 1 | localhost | 8888 | default | f | t | secondary | default | f | t - 5 | 1 | localhost | 8889 | default | f | t | secondary | second-cluster | f | t -(4 rows) + 1 | 0 | localhost | 57636 | default | t | t | primary | default | t | f + 2 | 1 | localhost | 57637 | default | t | t | primary | default | t | t + 3 | 2 | localhost | 57638 | default | f | t | primary | default | f | t + 5 | 1 | localhost | 8888 | default | f | t | secondary | default | f | t + 6 | 1 | localhost | 8889 | default | f | t | secondary | second-cluster | f | t +(5 rows) SELECT * FROM pg_dist_partition WHERE logicalrelid::text LIKE 'mx_testing_schema%' ORDER BY logicalrelid::text; logicalrelid | partmethod | partkey | colocationid | repmodel | autoconverted @@ -1509,7 +1511,7 @@ SELECT create_distributed_table('mx_table', 'a'); SELECT master_add_node('localhost', :worker_2_port); master_add_node --------------------------------------------------------------------- - 6 + 7 (1 row) \c - mx_user - :worker_1_port @@ -1620,9 +1622,10 @@ ORDER BY nodeport; logicalrelid | partmethod | repmodel | shardid | placementid | nodename | nodeport --------------------------------------------------------------------- - mx_ref | n | t | 1310074 | 100074 | localhost | 57637 - mx_ref | n | t | 1310074 | 100075 | localhost | 57638 -(2 rows) + mx_ref | n | t | 1310074 | 100074 | localhost | 57636 + mx_ref | n | t | 1310074 | 100075 | localhost | 57637 + mx_ref | n | t | 1310074 | 100076 | localhost | 57638 +(3 rows) SELECT shardid AS ref_table_shardid FROM pg_dist_shard WHERE logicalrelid='mx_ref'::regclass \gset -- make sure we have the pg_dist_colocation record on the worker @@ -1716,8 +1719,9 @@ FROM pg_dist_shard NATURAL JOIN pg_dist_shard_placement WHERE logicalrelid='mx_ref'::regclass; shardid | nodename | nodeport --------------------------------------------------------------------- + 1310075 | localhost | 57636 1310075 | localhost | 57637 -(1 row) +(2 rows) \c - - - :worker_1_port SELECT shardid, nodename, nodeport @@ -1725,15 +1729,16 @@ FROM pg_dist_shard NATURAL JOIN pg_dist_shard_placement WHERE logicalrelid='mx_ref'::regclass; shardid | nodename | nodeport --------------------------------------------------------------------- + 1310075 | localhost | 57636 1310075 | localhost | 57637 -(1 row) +(2 rows) \c - - - :master_port SET client_min_messages TO ERROR; SELECT master_add_node('localhost', :worker_2_port); master_add_node --------------------------------------------------------------------- - 7 + 8 (1 row) RESET client_min_messages; @@ -1743,8 +1748,9 @@ WHERE logicalrelid='mx_ref'::regclass ORDER BY shardid, nodeport; shardid | nodename | nodeport --------------------------------------------------------------------- + 1310075 | localhost | 57636 1310075 | localhost | 57637 -(1 row) +(2 rows) \c - - - :worker_1_port SELECT shardid, nodename, nodeport @@ -1753,8 +1759,9 @@ WHERE logicalrelid='mx_ref'::regclass ORDER BY shardid, nodeport; shardid | nodename | nodeport --------------------------------------------------------------------- + 1310075 | localhost | 57636 1310075 | localhost | 57637 -(1 row) +(2 rows) -- Get the metadata back into a consistent state \c - - - :master_port @@ -1862,10 +1869,6 @@ HINT: If the node is up, wait until metadata gets synced to it and try again. ALTER TABLE dist_table_1 ADD COLUMN b int; ERROR: localhost:xxxxx is a metadata node, but is out of sync HINT: If the node is up, wait until metadata gets synced to it and try again. -SELECT master_add_node('localhost', :master_port, groupid => 0); -NOTICE: localhost:xxxxx is the coordinator and already contains metadata, skipping syncing the metadata -ERROR: localhost:xxxxx is a metadata node, but is out of sync -HINT: If the node is up, wait until metadata gets synced to it and try again. SELECT citus_disable_node_and_wait('localhost', :worker_1_port); ERROR: disabling the first worker node in the metadata is not allowed DETAIL: Citus uses the first worker node in the metadata for certain internal operations when replicated tables are modified. Synchronous mode ensures that all nodes have the same view of the first worker node, which is used for certain locking operations. @@ -1918,7 +1921,7 @@ SELECT wait_until_metadata_sync(60000); SELECT master_add_node('localhost', :worker_2_port); master_add_node --------------------------------------------------------------------- - 7 + 8 (1 row) CREATE SEQUENCE mx_test_sequence_0; @@ -1989,7 +1992,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; GRANT CREATE ON SCHEMA public TO postgres; GRANT USAGE ON SCHEMA public TO PUBLIC; GRANT USAGE ON SCHEMA public TO postgres; - INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (4, 1, 'localhost', 8888, 'default', FALSE, FALSE, TRUE, 'secondary'::noderole, 'default', TRUE),(5, 1, 'localhost', 8889, 'default', FALSE, FALSE, TRUE, 'secondary'::noderole, 'second-cluster', TRUE),(1, 1, 'localhost', 57637, 'default', TRUE, TRUE, TRUE, 'primary'::noderole, 'default', TRUE),(7, 5, 'localhost', 57638, 'default', TRUE, TRUE, TRUE, 'primary'::noderole, 'default', TRUE) + INSERT INTO pg_dist_node (nodeid, groupid, nodename, nodeport, noderack, hasmetadata, metadatasynced, isactive, noderole, nodecluster, shouldhaveshards) VALUES (5, 1, 'localhost', 8888, 'default', FALSE, FALSE, TRUE, 'secondary'::noderole, 'default', TRUE),(6, 1, 'localhost', 8889, 'default', FALSE, FALSE, TRUE, 'secondary'::noderole, 'second-cluster', TRUE),(1, 0, 'localhost', 57636, 'default', TRUE, TRUE, TRUE, 'primary'::noderole, 'default', FALSE),(2, 1, 'localhost', 57637, 'default', TRUE, TRUE, TRUE, 'primary'::noderole, 'default', TRUE),(8, 5, 'localhost', 57638, 'default', TRUE, TRUE, TRUE, 'primary'::noderole, 'default', TRUE) RESET ROLE RESET ROLE SELECT alter_role_if_exists('postgres', 'ALTER ROLE postgres SET lc_messages = ''C''') @@ -2025,9 +2028,9 @@ SELECT unnest(activate_node_snapshot()) order by 1; SET citus.enable_ddl_propagation TO 'on' SET citus.enable_ddl_propagation TO 'on' UPDATE pg_dist_local_group SET groupid = 1 - UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 1 - UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 1 + UPDATE pg_dist_node SET hasmetadata = TRUE WHERE nodeid = 2 + UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 2 + UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 2 WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (10009, 1, -1, 0, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (10010, 4, 1, 'integer'::regtype, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('database', ARRAY['regression']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; @@ -2050,9 +2053,9 @@ SELECT unnest(activate_node_snapshot()) order by 1; WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310000, 0, 1, 100000), (1310001, 0, 5, 100001), (1310002, 0, 1, 100002), (1310003, 0, 5, 100003), (1310004, 0, 1, 100004), (1310005, 0, 5, 100005), (1310006, 0, 1, 100006), (1310007, 0, 5, 100007)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310022, 0, 1, 100022), (1310023, 0, 5, 100023), (1310024, 0, 1, 100024), (1310025, 0, 5, 100025), (1310026, 0, 1, 100026)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310027, 0, 1, 100027), (1310028, 0, 5, 100028), (1310029, 0, 1, 100029), (1310030, 0, 5, 100030), (1310031, 0, 1, 100031)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; - WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310075, 0, 1, 100076), (1310075, 0, 5, 100077)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; - WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310076, 0, 1, 100078), (1310077, 0, 5, 100079), (1310078, 0, 1, 100080), (1310079, 0, 5, 100081)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; - WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310085, 0, 1, 100088), (1310086, 0, 5, 100089), (1310087, 0, 1, 100090), (1310088, 0, 5, 100091)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; + WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310075, 0, 0, 100077), (1310075, 0, 1, 100078), (1310075, 0, 5, 100079)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; + WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310076, 0, 1, 100080), (1310077, 0, 5, 100081), (1310078, 0, 1, 100082), (1310079, 0, 5, 100083)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; + WITH placement_data(shardid, shardlength, groupid, placementid) AS (VALUES (1310085, 0, 1, 100091), (1310086, 0, 5, 100092), (1310087, 0, 1, 100093), (1310088, 0, 5, 100094)) SELECT citus_internal_add_placement_metadata(shardid, shardlength, groupid, placementid) FROM placement_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('mx_test_schema_1.mx_table_1'::regclass, 1310022, 't'::"char", '-2147483648', '-1288490190'), ('mx_test_schema_1.mx_table_1'::regclass, 1310023, 't'::"char", '-1288490189', '-429496731'), ('mx_test_schema_1.mx_table_1'::regclass, 1310024, 't'::"char", '-429496730', '429496728'), ('mx_test_schema_1.mx_table_1'::regclass, 1310025, 't'::"char", '429496729', '1288490187'), ('mx_test_schema_1.mx_table_1'::regclass, 1310026, 't'::"char", '1288490188', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('mx_test_schema_2.mx_table_2'::regclass, 1310027, 't'::"char", '-2147483648', '-1288490190'), ('mx_test_schema_2.mx_table_2'::regclass, 1310028, 't'::"char", '-1288490189', '-429496731'), ('mx_test_schema_2.mx_table_2'::regclass, 1310029, 't'::"char", '-429496730', '429496728'), ('mx_test_schema_2.mx_table_2'::regclass, 1310030, 't'::"char", '429496729', '1288490187'), ('mx_test_schema_2.mx_table_2'::regclass, 1310031, 't'::"char", '1288490188', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; WITH shard_data(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) AS (VALUES ('mx_testing_schema.mx_test_table'::regclass, 1310000, 't'::"char", '-2147483648', '-1610612737'), ('mx_testing_schema.mx_test_table'::regclass, 1310001, 't'::"char", '-1610612736', '-1073741825'), ('mx_testing_schema.mx_test_table'::regclass, 1310002, 't'::"char", '-1073741824', '-536870913'), ('mx_testing_schema.mx_test_table'::regclass, 1310003, 't'::"char", '-536870912', '-1'), ('mx_testing_schema.mx_test_table'::regclass, 1310004, 't'::"char", '0', '536870911'), ('mx_testing_schema.mx_test_table'::regclass, 1310005, 't'::"char", '536870912', '1073741823'), ('mx_testing_schema.mx_test_table'::regclass, 1310006, 't'::"char", '1073741824', '1610612735'), ('mx_testing_schema.mx_test_table'::regclass, 1310007, 't'::"char", '1610612736', '2147483647')) SELECT citus_internal_add_shard_metadata(relationname, shardid, storagetype, shardminvalue, shardmaxvalue) FROM shard_data; diff --git a/src/test/regress/expected/multi_modifying_xacts.out b/src/test/regress/expected/multi_modifying_xacts.out index 99cdc9ce4..5eba6e21d 100644 --- a/src/test/regress/expected/multi_modifying_xacts.out +++ b/src/test/regress/expected/multi_modifying_xacts.out @@ -914,7 +914,7 @@ GROUP BY s.logicalrelid, sp.shardstate ORDER BY s.logicalrelid, sp.shardstate; logicalrelid | shardstate | count --------------------------------------------------------------------- - reference_modifying_xacts | 1 | 2 + reference_modifying_xacts | 1 | 3 (1 row) -- for the time-being drop the constraint @@ -1021,7 +1021,7 @@ GROUP BY s.logicalrelid, sp.shardstate ORDER BY s.logicalrelid, sp.shardstate; logicalrelid | shardstate | count --------------------------------------------------------------------- - reference_modifying_xacts | 1 | 2 + reference_modifying_xacts | 1 | 3 hash_modifying_xacts | 1 | 4 (2 rows) @@ -1070,7 +1070,7 @@ GROUP BY s.logicalrelid, sp.shardstate ORDER BY s.logicalrelid, sp.shardstate; logicalrelid | shardstate | count --------------------------------------------------------------------- - reference_modifying_xacts | 1 | 2 + reference_modifying_xacts | 1 | 3 hash_modifying_xacts | 1 | 4 (2 rows) @@ -1235,7 +1235,7 @@ GROUP BY s.logicalrelid, sp.shardstate ORDER BY s.logicalrelid, sp.shardstate; logicalrelid | shardstate | count --------------------------------------------------------------------- - reference_failure_test | 1 | 2 + reference_failure_test | 1 | 3 (1 row) -- any failure rollbacks the transaction diff --git a/src/test/regress/expected/multi_multiuser_auth.out b/src/test/regress/expected/multi_multiuser_auth.out index 4b7c6fcc7..8dd9b8ba7 100644 --- a/src/test/regress/expected/multi_multiuser_auth.out +++ b/src/test/regress/expected/multi_multiuser_auth.out @@ -15,7 +15,7 @@ SELECT nodeid AS worker_1_id FROM pg_dist_node WHERE nodename = 'localhost' AND nodeport = :worker_1_port; worker_1_id --------------------------------------------------------------------- - 16 + 17 (1 row) \gset diff --git a/src/test/regress/expected/multi_multiuser_master_protocol.out b/src/test/regress/expected/multi_multiuser_master_protocol.out index 0ea53b339..a6bddb7f2 100644 --- a/src/test/regress/expected/multi_multiuser_master_protocol.out +++ b/src/test/regress/expected/multi_multiuser_master_protocol.out @@ -355,9 +355,10 @@ SELECT * FROM run_command_on_placements('multiuser_schema.reference_table', $$ s ORDER BY nodename, nodeport, shardid; nodename | nodeport | shardid | success | result --------------------------------------------------------------------- + localhost | 57636 | 109094 | t | t localhost | 57637 | 109094 | t | t localhost | 57638 | 109094 | t | t -(2 rows) +(3 rows) -- create another table in the schema, verify select is not granted CREATE TABLE multiuser_schema.another_table(a int, b int); @@ -483,9 +484,10 @@ ORDER BY nodename, nodeport, shardid; (6 rows) DROP SCHEMA multiuser_schema CASCADE; -NOTICE: drop cascades to 3 other objects +NOTICE: drop cascades to 4 other objects DETAIL: drop cascades to table multiuser_schema.hash_table drop cascades to table multiuser_schema.reference_table +drop cascades to table multiuser_schema.reference_table_109094 drop cascades to table multiuser_schema.another_table DROP SCHEMA multiuser_second_schema CASCADE; NOTICE: drop cascades to table multiuser_second_schema.hash_table diff --git a/src/test/regress/expected/multi_name_resolution.out b/src/test/regress/expected/multi_name_resolution.out index 890c336bf..5c59a10e6 100644 --- a/src/test/regress/expected/multi_name_resolution.out +++ b/src/test/regress/expected/multi_name_resolution.out @@ -36,6 +36,7 @@ WHERE bar.id_deep = join_alias.id_deep; (0 rows) DROP SCHEMA multi_name_resolution CASCADE; -NOTICE: drop cascades to 2 other objects +NOTICE: drop cascades to 3 other objects DETAIL: drop cascades to table namenest1 drop cascades to table namenest2 +drop cascades to table namenest2_2250000000010 diff --git a/src/test/regress/expected/multi_null_minmax_value_pruning.out b/src/test/regress/expected/multi_null_minmax_value_pruning.out index a531b065f..5b0ad79f4 100644 --- a/src/test/regress/expected/multi_null_minmax_value_pruning.out +++ b/src/test/regress/expected/multi_null_minmax_value_pruning.out @@ -104,15 +104,33 @@ LOG: join order: [ "lineitem" ][ dual partition join "orders" ] DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 3 DEBUG: pruning merge fetch taskId 2 @@ -129,18 +147,26 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 12 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 12 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 18 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 18 QUERY PLAN --------------------------------------------------------------------- Aggregate -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 Tasks Shown: None, not supported for re-partition queries -> MapMergeJob Map Task Count: 2 - Merge Task Count: 4 + Merge Task Count: 6 -> MapMergeJob Map Task Count: 2 - Merge Task Count: 4 + Merge Task Count: 6 (10 rows) -- Next, set the maximum value for another shard to null. Then check that we @@ -169,15 +195,33 @@ LOG: join order: [ "lineitem" ][ dual partition join "orders" ] DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 3 DEBUG: pruning merge fetch taskId 2 @@ -194,18 +238,26 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 12 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 12 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 18 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 18 QUERY PLAN --------------------------------------------------------------------- Aggregate -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 Tasks Shown: None, not supported for re-partition queries -> MapMergeJob Map Task Count: 2 - Merge Task Count: 4 + Merge Task Count: 6 -> MapMergeJob Map Task Count: 2 - Merge Task Count: 4 + Merge Task Count: 6 (10 rows) -- Last, set the minimum value to 0 and check that we don't treat it as null. We @@ -232,15 +284,33 @@ LOG: join order: [ "lineitem" ][ dual partition join "orders" ] DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 3 DEBUG: pruning merge fetch taskId 2 @@ -257,18 +327,26 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 12 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 12 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 18 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 18 QUERY PLAN --------------------------------------------------------------------- Aggregate -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 Tasks Shown: None, not supported for re-partition queries -> MapMergeJob Map Task Count: 2 - Merge Task Count: 4 + Merge Task Count: 6 -> MapMergeJob Map Task Count: 2 - Merge Task Count: 4 + Merge Task Count: 6 (10 rows) RESET client_min_messages; diff --git a/src/test/regress/expected/multi_partitioning.out b/src/test/regress/expected/multi_partitioning.out index db79e075a..47139614d 100644 --- a/src/test/regress/expected/multi_partitioning.out +++ b/src/test/regress/expected/multi_partitioning.out @@ -1952,6 +1952,8 @@ DEBUG: switching to sequential query execution mode DETAIL: Table "" is modified, which might lead to data inconsistencies or distributed deadlocks via parallel accesses to hash distributed tables due to foreign keys. Any parallel modification to those hash distributed tables in the same transaction can only be executed in sequential query execution mode CONTEXT: SQL statement "SELECT citus_drop_all_shards(v_obj.objid, v_obj.schema_name, v_obj.object_name, drop_shards_metadata_only := false)" PL/pgSQL function citus_drop_trigger() line XX at PERFORM +CONTEXT: SQL statement "SELECT citus_drop_all_shards(v_obj.objid, v_obj.schema_name, v_obj.object_name, drop_shards_metadata_only := false)" +PL/pgSQL function citus_drop_trigger() line XX at PERFORM DEBUG: drop cascades to 2 other objects DETAIL: drop cascades to constraint partitioning_reference_fkey_1660302 on table partitioning_schema.partitioning_test_1660302 drop cascades to constraint partitioning_reference_fkey_1660304 on table partitioning_schema.partitioning_test_1660304 @@ -3772,13 +3774,6 @@ BEGIN; ROLLBACK; DROP TABLE pi_table; -- 6) test with citus local table -select 1 from citus_add_node('localhost', :master_port, groupid=>0); -NOTICE: localhost:xxxxx is the coordinator and already contains metadata, skipping syncing the metadata - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - CREATE TABLE date_partitioned_citus_local_table( measureid integer, eventdate date, @@ -4214,12 +4209,6 @@ DROP TABLE date_partitioned_table_to_exp; DROP TABLE date_partitioned_citus_local_table CASCADE; DROP TABLE date_partitioned_citus_local_table_2; set client_min_messages to notice; -SELECT citus_remove_node('localhost', :master_port); - citus_remove_node ---------------------------------------------------------------------- - -(1 row) - -- d) invalid tables for helper UDFs CREATE TABLE multiple_partition_column_table( event_id bigserial, diff --git a/src/test/regress/expected/multi_poolinfo_usage.out b/src/test/regress/expected/multi_poolinfo_usage.out index b428409ff..ee98f0df7 100644 --- a/src/test/regress/expected/multi_poolinfo_usage.out +++ b/src/test/regress/expected/multi_poolinfo_usage.out @@ -9,7 +9,7 @@ SET citus.next_shard_id TO 20000000; SELECT nodeid AS worker_1_id FROM pg_dist_node WHERE nodename = 'localhost' AND nodeport = :worker_1_port; worker_1_id --------------------------------------------------------------------- - 16 + 17 (1 row) \gset diff --git a/src/test/regress/expected/multi_read_from_secondaries.out b/src/test/regress/expected/multi_read_from_secondaries.out index 5c69458e4..9652b6520 100644 --- a/src/test/regress/expected/multi_read_from_secondaries.out +++ b/src/test/regress/expected/multi_read_from_secondaries.out @@ -27,9 +27,10 @@ INSERT INTO source_table (a, b) VALUES (10, 10); SELECT nodeid, groupid, nodename, nodeport, noderack, isactive, noderole, nodecluster FROM pg_dist_node ORDER BY 1, 2; nodeid | groupid | nodename | nodeport | noderack | isactive | noderole | nodecluster --------------------------------------------------------------------- - 1 | 1 | localhost | 57637 | default | t | primary | default - 2 | 2 | localhost | 57638 | default | t | primary | default -(2 rows) + 1 | 0 | localhost | 57636 | default | t | primary | default + 2 | 1 | localhost | 57637 | default | t | primary | default + 3 | 2 | localhost | 57638 | default | t | primary | default +(3 rows) UPDATE pg_dist_node SET noderole = 'secondary'; \c "dbname=regression options='-c\ citus.use_secondary_nodes=always'" diff --git a/src/test/regress/expected/multi_real_time_transaction.out b/src/test/regress/expected/multi_real_time_transaction.out index 633d00dab..f348430e1 100644 --- a/src/test/regress/expected/multi_real_time_transaction.out +++ b/src/test/regress/expected/multi_real_time_transaction.out @@ -668,8 +668,9 @@ SELECT id, pg_advisory_xact_lock(16) FROM test_table ORDER BY id; END; DROP SCHEMA multi_real_time_transaction CASCADE; -NOTICE: drop cascades to 4 other objects +NOTICE: drop cascades to 5 other objects DETAIL: drop cascades to table test_table drop cascades to table co_test_table drop cascades to table ref_test_table +drop cascades to table ref_test_table_1610008 drop cascades to function insert_row_test(name) diff --git a/src/test/regress/expected/multi_remove_node_reference_table.out b/src/test/regress/expected/multi_remove_node_reference_table.out index 98fd168b5..44233266a 100644 --- a/src/test/regress/expected/multi_remove_node_reference_table.out +++ b/src/test/regress/expected/multi_remove_node_reference_table.out @@ -218,10 +218,24 @@ WHERE colocationid IN 1 | -1 | 0 (1 row) +-- test that we cannot remove a node if it has the only placement for a shard +SELECT master_remove_node('localhost', :master_port); + master_remove_node +--------------------------------------------------------------------- + +(1 row) + SELECT master_remove_node('localhost', :worker_1_port); ERROR: cannot remove or disable the node localhost:xxxxx because because it contains the only shard placement for shard xxxxx DETAIL: One of the table(s) that prevents the operation complete successfully is public.remove_node_reference_table HINT: To proceed, either drop the tables or use undistribute_table() function to convert them to local tables +-- restore the coordinator +SELECT citus_set_coordinator_host('localhost'); + citus_set_coordinator_host +--------------------------------------------------------------------- + +(1 row) + \c - - - :worker_1_port SELECT COUNT(*) FROM pg_dist_node WHERE nodeport = :worker_2_port; count @@ -972,12 +986,6 @@ ORDER BY shardid ASC; (0 rows) \c - - - :master_port -SELECT 1 FROM citus_set_coordinator_host('localhost', :master_port); - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - SELECT citus_disable_node('localhost', :worker_2_port); citus_disable_node --------------------------------------------------------------------- @@ -1004,12 +1012,6 @@ SELECT hasmetadata, metadatasynced FROM pg_dist_node WHERE nodeport = :master_po t | t (1 row) -SELECT 1 FROM citus_remove_node('localhost', :master_port); - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - SELECT shardid, shardstate, shardlength, nodename, nodeport FROM diff --git a/src/test/regress/expected/multi_repartition_join_planning.out b/src/test/regress/expected/multi_repartition_join_planning.out index 13f569a4e..237fe906b 100644 --- a/src/test/regress/expected/multi_repartition_join_planning.out +++ b/src/test/regress/expected/multi_repartition_join_planning.out @@ -7,6 +7,7 @@ SET citus.next_shard_id TO 690000; SET citus.enable_unique_job_ids TO off; SET citus.enable_repartition_joins to ON; +SET citus.shard_replication_factor to 1; create schema repartition_join; DROP TABLE IF EXISTS repartition_join.order_line; NOTICE: table "order_line" does not exist, skipping @@ -69,15 +70,33 @@ DEBUG: join prunable for intervals [0,2147483647] and [-2147483648,-1] DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 3 DEBUG: pruning merge fetch taskId 2 @@ -94,34 +113,68 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 12 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 12 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 18 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 18 DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 -DETAIL: Creating dependency on merge taskId 13 +DETAIL: Creating dependency on merge taskId 19 DEBUG: pruning merge fetch taskId 2 DETAIL: Creating dependency on merge taskId 4 DEBUG: pruning merge fetch taskId 4 -DETAIL: Creating dependency on merge taskId 18 +DETAIL: Creating dependency on merge taskId 26 DEBUG: pruning merge fetch taskId 5 DETAIL: Creating dependency on merge taskId 8 DEBUG: pruning merge fetch taskId 7 -DETAIL: Creating dependency on merge taskId 23 +DETAIL: Creating dependency on merge taskId 33 DEBUG: pruning merge fetch taskId 8 DETAIL: Creating dependency on merge taskId 12 DEBUG: pruning merge fetch taskId 10 -DETAIL: Creating dependency on merge taskId 28 +DETAIL: Creating dependency on merge taskId 40 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 16 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 47 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 20 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 54 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 24 l_partkey | o_orderkey | count --------------------------------------------------------------------- 18 | 12005 | 1 @@ -170,15 +223,33 @@ DEBUG: Router planner cannot handle multi-shard select queries DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 3 DEBUG: pruning merge fetch taskId 2 @@ -195,6 +266,14 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 12 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 12 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 18 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 18 l_partkey | o_orderkey | count --------------------------------------------------------------------- (0 rows) @@ -214,15 +293,33 @@ DEBUG: Router planner cannot handle multi-shard select queries DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 3 DEBUG: pruning merge fetch taskId 2 @@ -239,6 +336,14 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 12 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 12 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 18 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 18 o_orderkey | o_shippriority | count --------------------------------------------------------------------- (0 rows) @@ -260,15 +365,33 @@ DEBUG: Router planner cannot handle multi-shard select queries DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 3 DEBUG: pruning merge fetch taskId 2 @@ -285,6 +408,14 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 12 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 12 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 18 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 18 o_orderkey | o_shippriority | count --------------------------------------------------------------------- (0 rows) @@ -304,15 +435,33 @@ DEBUG: Router planner cannot handle multi-shard select queries DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 3 DEBUG: pruning merge fetch taskId 2 @@ -329,6 +478,14 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 12 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 12 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 18 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 18 o_orderkey | any_value --------------------------------------------------------------------- (0 rows) @@ -346,15 +503,33 @@ DEBUG: Router planner cannot handle multi-shard select queries DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 5 DEBUG: pruning merge fetch taskId 2 @@ -371,6 +546,14 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 20 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 20 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 25 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 25 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 30 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 30 s_i_id --------------------------------------------------------------------- (0 rows) diff --git a/src/test/regress/expected/multi_repartition_join_pruning.out b/src/test/regress/expected/multi_repartition_join_pruning.out index 8c0a26800..b5c571f2a 100644 --- a/src/test/regress/expected/multi_repartition_join_pruning.out +++ b/src/test/regress/expected/multi_repartition_join_pruning.out @@ -17,15 +17,33 @@ DEBUG: Router planner does not support append-partitioned tables. DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 3 DEBUG: pruning merge fetch taskId 2 @@ -42,18 +60,26 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 12 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 16 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 20 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 18 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 24 QUERY PLAN --------------------------------------------------------------------- Aggregate -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 Tasks Shown: None, not supported for re-partition queries -> MapMergeJob Map Task Count: 2 - Merge Task Count: 4 + Merge Task Count: 6 -> MapMergeJob Map Task Count: 3 - Merge Task Count: 4 + Merge Task Count: 6 (10 rows) SELECT @@ -66,15 +92,33 @@ DEBUG: Router planner does not support append-partitioned tables. DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 3 DEBUG: pruning merge fetch taskId 2 @@ -91,6 +135,14 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 12 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 16 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 20 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 18 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 24 count --------------------------------------------------------------------- 2985 @@ -110,15 +162,33 @@ DEBUG: Router planner does not support append-partitioned tables. DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 3 DEBUG: pruning merge fetch taskId 2 @@ -135,18 +205,26 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 12 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 16 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 20 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 18 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 24 QUERY PLAN --------------------------------------------------------------------- Aggregate -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 Tasks Shown: None, not supported for re-partition queries -> MapMergeJob Map Task Count: 2 - Merge Task Count: 4 + Merge Task Count: 6 -> MapMergeJob Map Task Count: 3 - Merge Task Count: 4 + Merge Task Count: 6 (10 rows) SELECT @@ -160,15 +238,33 @@ DEBUG: Router planner does not support append-partitioned tables. DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 3 DEBUG: pruning merge fetch taskId 2 @@ -185,6 +281,14 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 12 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 16 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 20 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 18 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 24 count --------------------------------------------------------------------- 0 @@ -204,15 +308,33 @@ DEBUG: Router planner does not support append-partitioned tables. DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 3 DEBUG: pruning merge fetch taskId 2 @@ -229,18 +351,26 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 12 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 16 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 20 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 18 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 24 QUERY PLAN --------------------------------------------------------------------- Aggregate -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 Tasks Shown: None, not supported for re-partition queries -> MapMergeJob Map Task Count: 2 - Merge Task Count: 4 + Merge Task Count: 6 -> MapMergeJob Map Task Count: 3 - Merge Task Count: 4 + Merge Task Count: 6 (10 rows) SELECT @@ -254,15 +384,33 @@ DEBUG: Router planner does not support append-partitioned tables. DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 3 DEBUG: pruning merge fetch taskId 2 @@ -279,6 +427,14 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 12 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 16 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 20 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 18 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 24 count --------------------------------------------------------------------- 0 @@ -298,15 +454,33 @@ DEBUG: Router planner does not support append-partitioned tables. DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 3 DEBUG: pruning merge fetch taskId 2 @@ -323,18 +497,26 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 12 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 16 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 20 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 18 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 24 QUERY PLAN --------------------------------------------------------------------- Aggregate -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 Tasks Shown: None, not supported for re-partition queries -> MapMergeJob Map Task Count: 2 - Merge Task Count: 4 + Merge Task Count: 6 -> MapMergeJob Map Task Count: 3 - Merge Task Count: 4 + Merge Task Count: 6 (10 rows) SELECT @@ -347,15 +529,33 @@ DEBUG: Router planner does not support append-partitioned tables. DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 3 DEBUG: pruning merge fetch taskId 2 @@ -372,6 +572,14 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 12 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 16 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 20 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 18 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 24 count --------------------------------------------------------------------- 125 @@ -391,15 +599,33 @@ DEBUG: Router planner does not support append-partitioned tables. DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 3 DEBUG: pruning merge fetch taskId 2 @@ -416,18 +642,26 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 12 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 16 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 20 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 18 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 24 QUERY PLAN --------------------------------------------------------------------- Aggregate -> Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 Tasks Shown: None, not supported for re-partition queries -> MapMergeJob Map Task Count: 2 - Merge Task Count: 4 + Merge Task Count: 6 -> MapMergeJob Map Task Count: 3 - Merge Task Count: 4 + Merge Task Count: 6 (10 rows) SELECT @@ -441,15 +675,33 @@ DEBUG: Router planner does not support append-partitioned tables. DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 3 DEBUG: pruning merge fetch taskId 2 @@ -466,6 +718,14 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 12 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 16 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 20 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 18 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 24 count --------------------------------------------------------------------- 0 diff --git a/src/test/regress/expected/multi_repartition_join_task_assignment.out b/src/test/regress/expected/multi_repartition_join_task_assignment.out index 3fbe9121b..713a68e6b 100644 --- a/src/test/regress/expected/multi_repartition_join_task_assignment.out +++ b/src/test/regress/expected/multi_repartition_join_task_assignment.out @@ -30,15 +30,33 @@ DEBUG: assigned task to node localhost:xxxxx DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 3 DEBUG: pruning merge fetch taskId 2 @@ -55,6 +73,16 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 12 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 16 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 20 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 18 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 24 +DEBUG: assigned task to node localhost:xxxxx +DEBUG: assigned task to node localhost:xxxxx DEBUG: assigned task to node localhost:xxxxx DEBUG: assigned task to node localhost:xxxxx DEBUG: assigned task to node localhost:xxxxx @@ -88,15 +116,33 @@ DEBUG: assigned task to node localhost:xxxxx DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 4 DEBUG: pruning merge fetch taskId 2 @@ -113,6 +159,16 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 16 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 12 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 20 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 24 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 18 +DEBUG: assigned task to node localhost:xxxxx +DEBUG: assigned task to node localhost:xxxxx DEBUG: assigned task to node localhost:xxxxx DEBUG: assigned task to node localhost:xxxxx DEBUG: assigned task to node localhost:xxxxx @@ -143,15 +199,33 @@ DEBUG: assigned task to node localhost:xxxxx DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 3 DEBUG: pruning merge fetch taskId 2 @@ -168,6 +242,16 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 12 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 16 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 20 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 18 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 24 +DEBUG: assigned task to node localhost:xxxxx +DEBUG: assigned task to node localhost:xxxxx DEBUG: assigned task to node localhost:xxxxx DEBUG: assigned task to node localhost:xxxxx DEBUG: assigned task to node localhost:xxxxx diff --git a/src/test/regress/expected/multi_repartition_udt.out b/src/test/regress/expected/multi_repartition_udt.out index 437e188ee..35d3bd80f 100644 --- a/src/test/regress/expected/multi_repartition_udt.out +++ b/src/test/regress/expected/multi_repartition_udt.out @@ -155,14 +155,14 @@ LOG: join order: [ "repartition_udt" ][ dual partition join "repartition_udt_ot QUERY PLAN --------------------------------------------------------------------- Custom Scan (Citus Adaptive) - Task Count: 4 + Task Count: 6 Tasks Shown: None, not supported for re-partition queries -> MapMergeJob Map Task Count: 3 - Merge Task Count: 4 + Merge Task Count: 6 -> MapMergeJob Map Task Count: 5 - Merge Task Count: 4 + Merge Task Count: 6 (9 rows) SELECT * FROM repartition_udt JOIN repartition_udt_other diff --git a/src/test/regress/expected/multi_replicate_reference_table.out b/src/test/regress/expected/multi_replicate_reference_table.out index e16726171..3d8d8a787 100644 --- a/src/test/regress/expected/multi_replicate_reference_table.out +++ b/src/test/regress/expected/multi_replicate_reference_table.out @@ -284,7 +284,7 @@ DROP TABLE replicate_reference_table_rollback; SELECT count(*) FROM pg_dist_node; count --------------------------------------------------------------------- - 1 + 2 (1 row) -- test whether we can create distributed objects on a single worker node @@ -376,12 +376,6 @@ SELECT citus_add_node('localhost', :worker_2_port); (1 row) -- required for create_distributed_table_concurrently -SELECT 1 FROM citus_set_coordinator_host('localhost', :master_port); - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - SET citus.shard_replication_factor TO 1; CREATE TABLE distributed_table_cdtc(column1 int primary key); SELECT create_distributed_table_concurrently('distributed_table_cdtc', 'column1'); @@ -391,12 +385,6 @@ SELECT create_distributed_table_concurrently('distributed_table_cdtc', 'column1' (1 row) RESET citus.shard_replication_factor; -SELECT citus_remove_node('localhost', :master_port); - citus_remove_node ---------------------------------------------------------------------- - -(1 row) - SELECT shardid, shardstate, shardlength, nodename, nodeport FROM @@ -712,12 +700,22 @@ SELECT master_remove_node('localhost', :worker_2_port); CREATE TABLE ref_table_1(id int primary key, v int); CREATE TABLE ref_table_2(id int primary key, v int references ref_table_1(id)); CREATE TABLE ref_table_3(id int primary key, v int references ref_table_2(id)); -SELECT create_reference_table('ref_table_1'), - create_reference_table('ref_table_2'), - create_reference_table('ref_table_3'); - create_reference_table | create_reference_table | create_reference_table +SELECT create_reference_table('ref_table_1'); + create_reference_table --------------------------------------------------------------------- - | | + +(1 row) + +SELECT create_reference_table('ref_table_2'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_reference_table('ref_table_3'); + create_reference_table +--------------------------------------------------------------------- + (1 row) -- status before master_add_node @@ -795,7 +793,7 @@ WHERE ORDER BY 1,4,5; shardid | shardstate | shardlength | nodename | nodeport --------------------------------------------------------------------- - 1370019 | 1 | 0 | localhost | 57637 + 1370021 | 1 | 0 | localhost | 57637 (1 row) -- we should see the two shard placements after activation @@ -820,7 +818,7 @@ WHERE ORDER BY 1,4,5; shardid | shardstate | shardlength | nodename | nodeport --------------------------------------------------------------------- - 1370019 | 1 | 0 | localhost | 57637 + 1370021 | 1 | 0 | localhost | 57637 (1 row) SELECT 1 FROM master_remove_node('localhost', :worker_2_port); @@ -850,7 +848,7 @@ HINT: Add the target node via SELECT citus_add_node('localhost', 57638); SELECT citus_add_secondary_node('localhost', :worker_2_port, 'localhost', :worker_1_port); citus_add_secondary_node --------------------------------------------------------------------- - 1370014 + 1370013 (1 row) SELECT citus_copy_shard_placement( @@ -1139,8 +1137,10 @@ select 1 FROM master_add_node('localhost', :worker_2_port); BEGIN; DROP TABLE test; CREATE TABLE test (x int, y int references ref(a)); -SELECT create_distributed_table('test','x'); ERROR: canceling the transaction since it was involved in a distributed deadlock +DETAIL: When adding a foreign key from a local table to a reference table, Citus applies a conversion to all the local tables in the foreign key graph +SELECT create_distributed_table('test','x'); +ERROR: current transaction is aborted, commands ignored until end of transaction block END; -- verify the split fails if we still need to replicate reference tables SELECT citus_remove_node('localhost', :worker_2_port); @@ -1158,7 +1158,7 @@ SELECT create_distributed_table('test','x'); SELECT citus_add_node('localhost', :worker_2_port); citus_add_node --------------------------------------------------------------------- - 1370022 + 1370020 (1 row) SELECT @@ -1194,7 +1194,7 @@ errors_received := 0; RAISE '(%/1) failed to add node', errors_received; END; $$; -ERROR: (1/1) failed to add node +ERROR: (0/1) failed to add node -- drop unnecassary tables DROP TABLE initially_not_replicated_reference_table; -- reload pg_dist_shard_placement table diff --git a/src/test/regress/expected/multi_router_planner.out b/src/test/regress/expected/multi_router_planner.out index a2f840aa6..edfc728db 100644 --- a/src/test/regress/expected/multi_router_planner.out +++ b/src/test/regress/expected/multi_router_planner.out @@ -794,15 +794,33 @@ DEBUG: push down of limit count: 3 DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 2 DEBUG: pruning merge fetch taskId 2 @@ -819,6 +837,14 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 8 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 8 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 10 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 10 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 12 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 12 ERROR: the query contains a join that requires repartitioning HINT: Set citus.enable_repartition_joins to on to enable repartitioning RESET citus.enable_non_colocated_router_query_pushdown; @@ -1517,15 +1543,33 @@ DEBUG: router planner does not support queries that reference non-colocated dis DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 2 DEBUG: pruning merge fetch taskId 2 @@ -1542,6 +1586,14 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 8 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 8 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 10 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 10 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 12 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 12 ERROR: the query contains a join that requires repartitioning HINT: Set citus.enable_repartition_joins to on to enable repartitioning SELECT a.author_id as first_author, b.word_count as second_word_count @@ -1652,11 +1704,6 @@ DETAIL: A command for a distributed function is run. To make sure subsequent co SELECT 1 FROM authors_reference r JOIN ( SELECT s.datid FROM number1() s LEFT JOIN pg_database d ON s.datid = d.oid ) num_db ON (r.id = num_db.datid) LIMIT 1; -DEBUG: found no worker with all shard placements -DEBUG: generating subplan XXX_1 for subquery SELECT datid FROM multi_router_planner.number1() s(datid) -DEBUG: Creating router plan -DEBUG: generating subplan XXX_2 for subquery SELECT s.datid FROM ((SELECT intermediate_result.datid FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(datid integer)) s LEFT JOIN pg_database d ON (((s.datid)::oid OPERATOR(pg_catalog.=) d.oid))) -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT 1 FROM (multi_router_planner.authors_reference r JOIN (SELECT intermediate_result.datid FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(datid integer)) num_db ON ((r.id OPERATOR(pg_catalog.=) num_db.datid))) LIMIT 1 DEBUG: Creating router plan ?column? --------------------------------------------------------------------- @@ -1666,11 +1713,6 @@ DEBUG: Creating router plan CREATE VIEW num_db AS SELECT s.datid FROM number1() s LEFT JOIN pg_database d ON s.datid = d.oid; SELECT 1 FROM authors_reference r JOIN num_db ON (r.id = num_db.datid) LIMIT 1; -DEBUG: found no worker with all shard placements -DEBUG: generating subplan XXX_1 for subquery SELECT datid FROM multi_router_planner.number1() s(datid) -DEBUG: Creating router plan -DEBUG: generating subplan XXX_2 for subquery SELECT s.datid FROM ((SELECT intermediate_result.datid FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(datid integer)) s LEFT JOIN pg_database d ON (((s.datid)::oid OPERATOR(pg_catalog.=) d.oid))) -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT 1 FROM (multi_router_planner.authors_reference r JOIN (SELECT intermediate_result.datid FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(datid integer)) num_db ON ((r.id OPERATOR(pg_catalog.=) num_db.datid))) LIMIT 1 DEBUG: Creating router plan ?column? --------------------------------------------------------------------- @@ -1679,9 +1721,6 @@ DEBUG: Creating router plan -- with a CTE in a view WITH cte AS MATERIALIZED (SELECT * FROM num_db) SELECT 1 FROM authors_reference r JOIN cte ON (r.id = cte.datid) LIMIT 1; -DEBUG: found no worker with all shard placements -DEBUG: generating subplan XXX_1 for CTE cte: SELECT datid FROM (SELECT s.datid FROM (multi_router_planner.number1() s(datid) LEFT JOIN pg_database d ON (((s.datid)::oid OPERATOR(pg_catalog.=) d.oid)))) num_db -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT 1 FROM (multi_router_planner.authors_reference r JOIN (SELECT intermediate_result.datid FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(datid integer)) cte ON ((r.id OPERATOR(pg_catalog.=) cte.datid))) LIMIT 1 DEBUG: Creating router plan ?column? --------------------------------------------------------------------- @@ -1897,15 +1936,33 @@ DEBUG: Router planner cannot handle multi-shard select queries DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 2 DEBUG: pruning merge fetch taskId 2 @@ -1922,6 +1979,14 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 8 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 20 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 10 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 25 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 12 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 30 id | author_id | title | word_count | name | id --------------------------------------------------------------------- (0 rows) @@ -1935,15 +2000,33 @@ DEBUG: Router planner cannot handle multi-shard select queries DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 5 DEBUG: pruning merge fetch taskId 2 @@ -1960,6 +2043,14 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 20 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 20 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 25 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 25 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 30 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 30 id | author_id | title | word_count | name | id --------------------------------------------------------------------- (0 rows) @@ -1993,15 +2084,33 @@ DEBUG: router planner does not support queries that reference non-colocated dis DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 2 DEBUG: pruning merge fetch taskId 2 @@ -2018,6 +2127,14 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 8 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 20 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 10 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 25 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 12 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 30 id | author_id | title | word_count | name | id --------------------------------------------------------------------- (0 rows) @@ -2030,15 +2147,33 @@ DEBUG: router planner does not support queries that reference non-colocated dis DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 2 DEBUG: pruning merge fetch taskId 2 @@ -2055,6 +2190,14 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 8 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 20 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 10 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 25 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 12 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 30 id | author_id | title | word_count | name | id --------------------------------------------------------------------- (0 rows) diff --git a/src/test/regress/expected/multi_schema_support.out b/src/test/regress/expected/multi_schema_support.out index b4003f258..9128491c9 100644 --- a/src/test/regress/expected/multi_schema_support.out +++ b/src/test/regress/expected/multi_schema_support.out @@ -1153,7 +1153,12 @@ SELECT create_reference_table('schema_with_user.test_table'); SET citus.next_shard_id TO 1197000; -- we do not use run_command_on_coordinator_and_workers here because when there is CASCADE, it causes deadlock DROP OWNED BY "test-user" CASCADE; -NOTICE: drop cascades to table schema_with_user.test_table +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to table schema_with_user.test_table +drop cascades to table schema_with_user.test_table_1190039 +NOTICE: schema "schema_with_user" does not exist, skipping +CONTEXT: SQL statement "SELECT citus_drop_all_shards(v_obj.objid, v_obj.schema_name, v_obj.object_name, drop_shards_metadata_only := false)" +PL/pgSQL function citus_drop_trigger() line XX at PERFORM DROP USER "test-user"; DROP FUNCTION run_command_on_coordinator_and_workers(p_sql text); -- test run_command_on_* UDFs with schema diff --git a/src/test/regress/expected/multi_sequence_default.out b/src/test/regress/expected/multi_sequence_default.out index c22e8109b..57305befa 100644 --- a/src/test/regress/expected/multi_sequence_default.out +++ b/src/test/regress/expected/multi_sequence_default.out @@ -9,13 +9,6 @@ SET citus.shard_replication_factor TO 1; CREATE SCHEMA sequence_default; SET search_path = sequence_default, public; -- test both distributed and citus local tables -SELECT 1 FROM citus_add_node('localhost', :master_port, groupId => 0); -NOTICE: localhost:xxxxx is the coordinator and already contains metadata, skipping syncing the metadata - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - -- Cannot add a column involving DEFAULT nextval('..') because the table is not empty CREATE SEQUENCE seq_0; CREATE SEQUENCE seq_0_local_table; @@ -891,10 +884,4 @@ DROP TABLE test_seq_dist; DROP TABLE sequence_default.seq_test_7_par; SET client_min_messages TO error; -- suppress cascading objects dropping DROP SCHEMA sequence_default CASCADE; -SELECT master_remove_node('localhost', :master_port); - master_remove_node ---------------------------------------------------------------------- - -(1 row) - SET search_path TO public; diff --git a/src/test/regress/expected/multi_simple_queries.out b/src/test/regress/expected/multi_simple_queries.out index 646c42599..d48f935c6 100644 --- a/src/test/regress/expected/multi_simple_queries.out +++ b/src/test/regress/expected/multi_simple_queries.out @@ -507,15 +507,33 @@ DEBUG: push down of limit count: 3 DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 2 DEBUG: pruning merge fetch taskId 2 @@ -532,6 +550,14 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 8 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 8 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 10 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 10 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 12 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 12 ERROR: the query contains a join that requires repartitioning HINT: Set citus.enable_repartition_joins to on to enable repartitioning -- but they can be executed via repartition join planner @@ -545,15 +571,33 @@ DEBUG: push down of limit count: 3 DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 2 DEBUG: pruning merge fetch taskId 2 @@ -570,6 +614,14 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 8 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 8 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 10 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 10 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 12 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 12 first_author | second_word_count --------------------------------------------------------------------- 10 | 19519 @@ -655,15 +707,33 @@ DEBUG: Router planner cannot handle multi-shard select queries DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 2 DEBUG: pruning merge fetch taskId 2 @@ -680,6 +750,14 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 8 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 12 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 10 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 15 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 12 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 18 ERROR: the query contains a join that requires repartitioning HINT: Set citus.enable_repartition_joins to on to enable repartitioning -- system columns from shard tables can be queried and retrieved diff --git a/src/test/regress/expected/multi_size_queries.out b/src/test/regress/expected/multi_size_queries.out index 97036b1db..2ff8d9c4b 100644 --- a/src/test/regress/expected/multi_size_queries.out +++ b/src/test/regress/expected/multi_size_queries.out @@ -75,7 +75,7 @@ SELECT citus_table_size('customer_copy_hash'), citus_table_size('supplier'); citus_table_size | citus_table_size | citus_table_size --------------------------------------------------------------------- - 548864 | 548864 | 442368 + 548864 | 548864 | 655360 (1 row) CREATE INDEX index_1 on customer_copy_hash(c_custkey); @@ -104,19 +104,19 @@ VACUUM (FULL) supplier; SELECT citus_table_size('supplier'); citus_table_size --------------------------------------------------------------------- - 376832 + 565248 (1 row) SELECT citus_relation_size('supplier'); citus_relation_size --------------------------------------------------------------------- - 376832 + 565248 (1 row) SELECT citus_total_relation_size('supplier'); citus_total_relation_size --------------------------------------------------------------------- - 376832 + 565248 (1 row) CREATE INDEX index_2 on supplier(s_suppkey); @@ -124,19 +124,19 @@ VACUUM (FULL) supplier; SELECT citus_table_size('supplier'); citus_table_size --------------------------------------------------------------------- - 376832 + 565248 (1 row) SELECT citus_relation_size('supplier'); citus_relation_size --------------------------------------------------------------------- - 376832 + 565248 (1 row) SELECT citus_total_relation_size('supplier'); citus_total_relation_size --------------------------------------------------------------------- - 458752 + 688128 (1 row) -- Test inside the transaction diff --git a/src/test/regress/expected/multi_table_ddl.out b/src/test/regress/expected/multi_table_ddl.out index 4a2f68162..2db4a7797 100644 --- a/src/test/regress/expected/multi_table_ddl.out +++ b/src/test/regress/expected/multi_table_ddl.out @@ -78,6 +78,12 @@ SELECT * FROM pg_dist_shard_placement; DROP EXTENSION citus; CREATE EXTENSION citus; -- re-add the nodes to the cluster +SELECT 1 FROM citus_set_coordinator_host('localhost'); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + SELECT 1 FROM master_add_node('localhost', :worker_1_port); ?column? --------------------------------------------------------------------- diff --git a/src/test/regress/expected/multi_tenant_isolation.out b/src/test/regress/expected/multi_tenant_isolation.out index b370ba6c6..5af7acac8 100644 --- a/src/test/regress/expected/multi_tenant_isolation.out +++ b/src/test/regress/expected/multi_tenant_isolation.out @@ -986,20 +986,25 @@ SELECT create_distributed_table('test_colocated_table_1', 'id', colocate_with => (1 row) -CREATE TABLE test_colocated_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_reference_table_fkey(id), FOREIGN KEY(id) REFERENCES test_colocated_table_1(id)); +CREATE TABLE test_colocated_table_2(id int PRIMARY KEY, value_1 int); SELECT create_distributed_table('test_colocated_table_2', 'id', colocate_with => 'test_colocated_table_1'); create_distributed_table --------------------------------------------------------------------- (1 row) -CREATE TABLE test_colocated_table_3(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_reference_table_fkey(id), FOREIGN KEY(id) REFERENCES test_colocated_table_1(id), FOREIGN KEY(id) REFERENCES test_colocated_table_2(id)); +ALTER TABLE test_colocated_table_2 ADD FOREIGN KEY(value_1) REFERENCES test_reference_table_fkey(id); +ALTER TABLE test_colocated_table_2 ADD FOREIGN KEY(id) REFERENCES test_colocated_table_1(id); +CREATE TABLE test_colocated_table_3(id int PRIMARY KEY, value_1 int); SELECT create_distributed_table('test_colocated_table_3', 'id', colocate_with => 'test_colocated_table_1'); create_distributed_table --------------------------------------------------------------------- (1 row) +ALTER TABLE test_colocated_table_3 ADD FOREIGN KEY(value_1) REFERENCES test_reference_table_fkey(id); +ALTER TABLE test_colocated_table_3 ADD FOREIGN KEY(id) REFERENCES test_colocated_table_1(id); +ALTER TABLE test_colocated_table_3 ADD FOREIGN KEY(id) REFERENCES test_colocated_table_2(id); INSERT INTO test_reference_table_fkey SELECT i FROM generate_series (0, 100) i; INSERT INTO test_colocated_table_1 SELECT i, i FROM generate_series (0, 100) i; INSERT INTO test_colocated_table_2 SELECT i, i FROM generate_series (0, 100) i; @@ -1159,7 +1164,7 @@ SELECT 1 FROM master_add_node('localhost', :master_port, groupId=>0); SELECT count(*) FROM pg_dist_shard NATURAL JOIN pg_dist_shard_placement WHERE logicalrelid = 'ref_table'::regclass; count --------------------------------------------------------------------- - 2 + 3 (1 row) \c - mx_isolation_role_ent - :master_port diff --git a/src/test/regress/expected/multi_tenant_isolation_nonblocking.out b/src/test/regress/expected/multi_tenant_isolation_nonblocking.out index dbd15b056..3ec16e6ee 100644 --- a/src/test/regress/expected/multi_tenant_isolation_nonblocking.out +++ b/src/test/regress/expected/multi_tenant_isolation_nonblocking.out @@ -1275,3 +1275,9 @@ SELECT count(*) FROM pg_catalog.pg_dist_partition WHERE colocationid > 0; TRUNCATE TABLE pg_catalog.pg_dist_colocation; ALTER SEQUENCE pg_catalog.pg_dist_colocationid_seq RESTART 100; ALTER SEQUENCE pg_catalog.pg_dist_placement_placementid_seq RESTART :last_placement_id; +SELECT citus_set_coordinator_host('localhost'); + citus_set_coordinator_host +--------------------------------------------------------------------- + +(1 row) + diff --git a/src/test/regress/expected/multi_transaction_recovery.out b/src/test/regress/expected/multi_transaction_recovery.out index 509a87acf..85144944d 100644 --- a/src/test/regress/expected/multi_transaction_recovery.out +++ b/src/test/regress/expected/multi_transaction_recovery.out @@ -1,16 +1,5 @@ -- Tests for prepared transaction recovery SET citus.next_shard_id TO 1220000; --- reference tables can have placements on the coordinator. Add it so --- verify we recover transactions which do DML on coordinator placements --- properly. -SET client_min_messages TO ERROR; -SELECT 1 FROM master_add_node('localhost', :master_port, groupid => 0); - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - -RESET client_min_messages; -- enforce 1 connection per placement since -- the tests are prepared for that SET citus.force_max_query_parallelization TO ON; @@ -516,9 +505,3 @@ DROP TABLE test_recovery; DROP TABLE test_recovery_single; DROP TABLE test_2pcskip; DROP TABLE test_reference; -SELECT 1 FROM master_remove_node('localhost', :master_port); - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - diff --git a/src/test/regress/expected/multi_transactional_drop_shards.out b/src/test/regress/expected/multi_transactional_drop_shards.out index dca2466d6..761275dff 100644 --- a/src/test/regress/expected/multi_transactional_drop_shards.out +++ b/src/test/regress/expected/multi_transactional_drop_shards.out @@ -374,9 +374,10 @@ ORDER BY shardid, nodename, nodeport; shardid | shardstate | nodename | nodeport --------------------------------------------------------------------- + 1410006 | 1 | localhost | 57636 1410006 | 1 | localhost | 57637 1410006 | 1 | localhost | 57638 -(2 rows) +(3 rows) -- verify table is not dropped \dt transactional_drop_reference @@ -516,7 +517,7 @@ SET citus.override_table_visibility TO false; (8 rows) \ds transactional_drop_serial_column2_seq - List of relations + List of relations Schema | Name | Type | Owner --------------------------------------------------------------------- public | transactional_drop_serial_column2_seq | sequence | postgres @@ -670,13 +671,6 @@ ORDER BY \c - - - :master_port SET client_min_messages TO WARNING; --- try using the coordinator as a worker and then dropping the table -SELECT 1 FROM master_add_node('localhost', :master_port, groupid := 0); - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - CREATE TABLE citus_local (id serial, k int); SELECT create_distributed_table('citus_local', 'id'); create_distributed_table @@ -686,12 +680,6 @@ SELECT create_distributed_table('citus_local', 'id'); INSERT INTO citus_local (k) VALUES (2); DROP TABLE citus_local; -SELECT master_remove_node('localhost', :master_port); - master_remove_node ---------------------------------------------------------------------- - -(1 row) - -- clean the workspace DROP TABLE transactional_drop_shards, transactional_drop_reference; -- test DROP TABLE as a non-superuser in a transaction block diff --git a/src/test/regress/expected/multi_truncate.out b/src/test/regress/expected/multi_truncate.out index 9b47237ea..f08f0c3c7 100644 --- a/src/test/regress/expected/multi_truncate.out +++ b/src/test/regress/expected/multi_truncate.out @@ -432,8 +432,12 @@ CREATE TABLE dist(id int, ref_id int REFERENCES ref(id)); INSERT INTO dist SELECT x,x FROM generate_series(1,10000) x; -- test that we do not cascade truncates to local referencing tables SELECT truncate_local_data_after_distributing_table('ref'); -ERROR: cannot truncate a table referenced in a foreign key constraint by a local table -DETAIL: Table "dist" references "ref" +NOTICE: truncate cascades to table "dist" + truncate_local_data_after_distributing_table +--------------------------------------------------------------------- + +(1 row) + -- test that we allow distributing tables that have foreign keys to reference tables SELECT create_distributed_table('dist','id'); NOTICE: Copying data from local table... @@ -461,11 +465,12 @@ NOTICE: truncate cascades to table "dist" (1 row) SELECT * FROM table_sizes; - name | has_data + name | has_data --------------------------------------------------------------------- - dist | f - ref | f -(2 rows) + dist | f + ref | f + ref_1210032 | t +(3 rows) ROLLBACK; -- the following should truncate dist table only @@ -477,11 +482,12 @@ SELECT truncate_local_data_after_distributing_table('dist'); (1 row) SELECT * FROM table_sizes; - name | has_data + name | has_data --------------------------------------------------------------------- - dist | f - ref | t -(2 rows) + dist | f + ref | f + ref_1210032 | t +(3 rows) ROLLBACK; DROP TABLE ref, dist; diff --git a/src/test/regress/expected/multi_utilities.out b/src/test/regress/expected/multi_utilities.out index 93021a067..b82e54f16 100644 --- a/src/test/regress/expected/multi_utilities.out +++ b/src/test/regress/expected/multi_utilities.out @@ -370,6 +370,8 @@ NOTICE: issuing VACUUM multi_utilities.reference_vacuum_table_970000 DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing VACUUM multi_utilities.reference_vacuum_table_970000 DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing VACUUM multi_utilities.reference_vacuum_table_970000 +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -- should propagate to all workers because table is distributed table VACUUM distributed_vacuum_table; NOTICE: issuing VACUUM multi_utilities.distributed_vacuum_table_970001 @@ -382,12 +384,16 @@ NOTICE: issuing VACUUM multi_utilities.reference_vacuum_table_970000 DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing VACUUM multi_utilities.reference_vacuum_table_970000 DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing VACUUM multi_utilities.reference_vacuum_table_970000 +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -- only reference_vacuum_table should propagate VACUUM local_vacuum_table, reference_vacuum_table; NOTICE: issuing VACUUM multi_utilities.reference_vacuum_table_970000 DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing VACUUM multi_utilities.reference_vacuum_table_970000 DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing VACUUM multi_utilities.reference_vacuum_table_970000 +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -- vacuum (disable_page_skipping) aggressively process pages of the relation, it does not respect visibility map VACUUM (DISABLE_PAGE_SKIPPING true) local_vacuum_table; VACUUM (DISABLE_PAGE_SKIPPING false) local_vacuum_table; @@ -440,6 +446,8 @@ NOTICE: issuing VACUUM (ANALYZE) multi_utilities.reference_vacuum_table_970000 DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing VACUUM (ANALYZE) multi_utilities.reference_vacuum_table_970000 DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing VACUUM (ANALYZE) multi_utilities.reference_vacuum_table_970000 +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -- give enough time for stats to be updated.(updated per 500ms by default) select pg_sleep(1); pg_sleep @@ -499,6 +507,8 @@ NOTICE: issuing ANALYZE multi_utilities.reference_analyze_table_970002 DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing ANALYZE multi_utilities.reference_analyze_table_970002 DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ANALYZE multi_utilities.reference_analyze_table_970002 +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -- should propagate to all workers because table is distributed table ANALYZE distributed_analyze_table; NOTICE: issuing ANALYZE multi_utilities.distributed_analyze_table_970003 @@ -511,12 +521,16 @@ NOTICE: issuing ANALYZE multi_utilities.reference_analyze_table_970002 DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing ANALYZE multi_utilities.reference_analyze_table_970002 DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ANALYZE multi_utilities.reference_analyze_table_970002 +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -- only reference_analyze_table should propagate ANALYZE local_analyze_table, reference_analyze_table; NOTICE: issuing ANALYZE multi_utilities.reference_analyze_table_970002 DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing ANALYZE multi_utilities.reference_analyze_table_970002 DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx +NOTICE: issuing ANALYZE multi_utilities.reference_analyze_table_970002 +DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -- should not propagate because ddl propagation is disabled SET citus.enable_ddl_propagation TO OFF; ANALYZE distributed_analyze_table; diff --git a/src/test/regress/expected/pg12.out b/src/test/regress/expected/pg12.out index e8ebe3f3f..8999038ec 100644 --- a/src/test/regress/expected/pg12.out +++ b/src/test/regress/expected/pg12.out @@ -404,13 +404,6 @@ where val = 'asdf'; 3 (1 row) -SELECT 1 FROM citus_add_node('localhost', :master_port, groupId => 0); -NOTICE: localhost:xxxxx is the coordinator and already contains metadata, skipping syncing the metadata - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - BEGIN; CREATE TABLE generated_stored_col_test (x int, y int generated always as (x+1) stored); SELECT citus_add_local_table_to_metadata('generated_stored_col_test'); @@ -639,12 +632,6 @@ NOTICE: renaming the new table to test_pg12.generated_stored_ref (4 rows) ROLLBACK; -SELECT citus_remove_node('localhost', :master_port); - citus_remove_node ---------------------------------------------------------------------- - -(1 row) - CREATE TABLE superuser_columnar_table (a int) USING columnar; CREATE USER read_access; SET ROLE read_access; diff --git a/src/test/regress/expected/pg14.out b/src/test/regress/expected/pg14.out index ae5fe8bdc..e4f94c053 100644 --- a/src/test/regress/expected/pg14.out +++ b/src/test/regress/expected/pg14.out @@ -1332,12 +1332,6 @@ set client_min_messages to error; drop schema pg14 cascade; create schema pg14; set search_path to pg14; -select 1 from citus_add_node('localhost',:master_port,groupid=>0); - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - -- test adding foreign table to metadata with the guc -- will test truncating foreign tables later CREATE TABLE foreign_table_test (id integer NOT NULL, data text, a bigserial); @@ -1505,9 +1499,3 @@ set client_min_messages to error; drop extension postgres_fdw cascade; drop schema pg14 cascade; reset client_min_messages; -select 1 from citus_remove_node('localhost',:master_port); - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - diff --git a/src/test/regress/expected/pg15.out b/src/test/regress/expected/pg15.out index 4d1040a7e..68c4c4466 100644 --- a/src/test/regress/expected/pg15.out +++ b/src/test/regress/expected/pg15.out @@ -218,6 +218,9 @@ BEGIN; NOTICE: drop cascades to 2 other objects DETAIL: drop cascades to column col_3 of table generated_stored_ref drop cascades to column col_5 of table generated_stored_ref +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to column col_3 of table generated_stored_ref_960016 +drop cascades to column col_5 of table generated_stored_ref_960016 ALTER TABLE generated_stored_ref DROP COLUMN col_4; -- show that undistribute_table works fine SELECT undistribute_table('generated_stored_ref'); @@ -269,15 +272,6 @@ CREATE TABLE tbl2 -- on local tables works fine MERGE INTO tbl1 USING tbl2 ON (true) WHEN MATCHED THEN DELETE; --- add coordinator node as a worker -SET client_min_messages to ERROR; -SELECT 1 FROM master_add_node('localhost', :master_port, groupId => 0); - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - -RESET client_min_messages; -- one table is Citus local table, fails SELECT citus_add_local_table_to_metadata('tbl1'); citus_add_local_table_to_metadata @@ -398,12 +392,6 @@ SET search_path TO pg15; SET client_min_messages to ERROR; DROP TABLE FKTABLE_local, PKTABLE_local; RESET client_min_messages; -SELECT 1 FROM citus_remove_node('localhost', :master_port); - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - SELECT create_distributed_table('tbl1', 'x'); create_distributed_table --------------------------------------------------------------------- @@ -880,8 +868,8 @@ SELECT create_reference_table('FKTABLE'); SELECT pg_get_constraintdef(oid) FROM pg_constraint WHERE conrelid = 'fktable'::regclass::oid ORDER BY oid; pg_get_constraintdef --------------------------------------------------------------------- - FOREIGN KEY (tid, fk_id_del_set_null) REFERENCES pktable(tid, id) ON DELETE SET NULL (fk_id_del_set_null) FOREIGN KEY (tid, fk_id_del_set_default) REFERENCES pktable(tid, id) ON DELETE SET DEFAULT (fk_id_del_set_default) + FOREIGN KEY (tid, fk_id_del_set_null) REFERENCES pktable(tid, id) ON DELETE SET NULL (fk_id_del_set_null) (2 rows) \c - - - :worker_1_port @@ -1274,6 +1262,7 @@ SELECT create_reference_table('set_on_default_test_referenced'); (1 row) +-- should error since col_3 defaults to a sequence CREATE TABLE set_on_default_test_referencing( col_1 int, col_2 int, col_3 serial, col_4 int, FOREIGN KEY(col_1, col_3) @@ -1281,10 +1270,7 @@ CREATE TABLE set_on_default_test_referencing( ON DELETE SET DEFAULT (col_1) ON UPDATE SET DEFAULT ); --- should error since col_3 defaults to a sequence -SELECT create_reference_table('set_on_default_test_referencing'); ERROR: cannot create foreign key constraint since Citus does not support ON DELETE / UPDATE SET DEFAULT actions on the columns that default to sequences -DROP TABLE set_on_default_test_referencing; CREATE TABLE set_on_default_test_referencing( col_1 int, col_2 int, col_3 serial, col_4 int, FOREIGN KEY(col_1, col_3) @@ -1447,12 +1433,6 @@ NOTICE: renaming the new table to pg15.foreign_table_test (1 row) -SELECT 1 FROM citus_remove_node('localhost', :master_port); - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - DROP SERVER foreign_server CASCADE; NOTICE: drop cascades to 2 other objects -- PG15 now supports specifying oid on CREATE DATABASE diff --git a/src/test/regress/expected/pgmerge.out b/src/test/regress/expected/pgmerge.out index 6bdb7f771..7742610f4 100644 --- a/src/test/regress/expected/pgmerge.out +++ b/src/test/regress/expected/pgmerge.out @@ -15,13 +15,6 @@ SET search_path TO pgmerge_schema; SET citus.use_citus_managed_tables to true; \set SHOW_CONTEXT errors SET citus.next_shard_id TO 4001000; -SELECT 1 FROM master_add_node('localhost', :master_port, groupid => 0); -NOTICE: localhost:xxxxx is the coordinator and already contains metadata, skipping syncing the metadata - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - CREATE USER regress_merge_privs; CREATE USER regress_merge_no_privs; DROP TABLE IF EXISTS target; @@ -2133,9 +2126,3 @@ drop cascades to table source2 drop cascades to function merge_trigfunc() DROP USER regress_merge_privs; DROP USER regress_merge_no_privs; -SELECT 1 FROM master_remove_node('localhost', :master_port); - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - diff --git a/src/test/regress/expected/propagate_foreign_servers.out b/src/test/regress/expected/propagate_foreign_servers.out index 551d1dde7..076973da3 100644 --- a/src/test/regress/expected/propagate_foreign_servers.out +++ b/src/test/regress/expected/propagate_foreign_servers.out @@ -30,13 +30,6 @@ CREATE FOREIGN TABLE foreign_table ( ) SERVER foreign_server_dependent_schema OPTIONS (schema_name 'test_dependent_schema', table_name 'foreign_table_test'); -SELECT 1 FROM citus_add_node('localhost', :master_port, groupId=>0); -NOTICE: localhost:xxxxx is the coordinator and already contains metadata, skipping syncing the metadata - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - -- verify that the aggregate is propagated to the new node SELECT run_command_on_workers($$select aggfnoid from pg_aggregate where aggfnoid::text like '%propagate_foreign_server.array_agg%';$$); run_command_on_workers diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out index a267cbe71..7fc75637d 100644 --- a/src/test/regress/expected/publication.out +++ b/src/test/regress/expected/publication.out @@ -2,13 +2,6 @@ CREATE SCHEMA publication; CREATE SCHEMA "publication-1"; SET search_path TO publication; SET citus.shard_replication_factor TO 1; --- for citus_add_local_table_to_metadata / create_distributed_table_concurrently -SELECT citus_set_coordinator_host('localhost', :master_port); - citus_set_coordinator_host ---------------------------------------------------------------------- - -(1 row) - CREATE OR REPLACE FUNCTION activate_node_snapshot() RETURNS text[] LANGUAGE C STRICT @@ -264,7 +257,6 @@ SELECT substring(:'server_version', '\d+')::int >= 15 AS server_version_ge_15 SET client_min_messages TO ERROR; DROP SCHEMA publication CASCADE; DROP SCHEMA "publication-1" CASCADE; -SELECT citus_remove_node('localhost', :master_port); \q \endif -- recreate a mixed publication @@ -371,9 +363,3 @@ DROP PUBLICATION pubpartitioned; SET client_min_messages TO ERROR; DROP SCHEMA publication CASCADE; DROP SCHEMA "publication-1" CASCADE; -SELECT citus_remove_node('localhost', :master_port); - citus_remove_node ---------------------------------------------------------------------- - -(1 row) - diff --git a/src/test/regress/expected/publication_0.out b/src/test/regress/expected/publication_0.out index 617950a76..02978ff65 100644 --- a/src/test/regress/expected/publication_0.out +++ b/src/test/regress/expected/publication_0.out @@ -2,13 +2,6 @@ CREATE SCHEMA publication; CREATE SCHEMA "publication-1"; SET search_path TO publication; SET citus.shard_replication_factor TO 1; --- for citus_add_local_table_to_metadata / create_distributed_table_concurrently -SELECT citus_set_coordinator_host('localhost', :master_port); - citus_set_coordinator_host ---------------------------------------------------------------------- - -(1 row) - CREATE OR REPLACE FUNCTION activate_node_snapshot() RETURNS text[] LANGUAGE C STRICT @@ -264,10 +257,4 @@ SELECT substring(:'server_version', '\d+')::int >= 15 AS server_version_ge_15 SET client_min_messages TO ERROR; DROP SCHEMA publication CASCADE; DROP SCHEMA "publication-1" CASCADE; -SELECT citus_remove_node('localhost', :master_port); - citus_remove_node ---------------------------------------------------------------------- - -(1 row) - \q diff --git a/src/test/regress/expected/query_single_shard_table.out b/src/test/regress/expected/query_single_shard_table.out index ff04ad50e..992b91f9f 100644 --- a/src/test/regress/expected/query_single_shard_table.out +++ b/src/test/regress/expected/query_single_shard_table.out @@ -2,13 +2,6 @@ CREATE SCHEMA query_single_shard_table; SET search_path TO query_single_shard_table; SET citus.next_shard_id TO 1620000; SET citus.shard_count TO 32; -SET client_min_messages TO WARNING; -SELECT 1 FROM citus_add_node('localhost', :master_port, groupid => 0); - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - SET client_min_messages TO NOTICE; CREATE TABLE nullkey_c1_t1(a int, b int); CREATE TABLE nullkey_c1_t2(a int, b int); @@ -1879,9 +1872,3 @@ DEBUG: Creating router plan SET client_min_messages TO ERROR; DROP SCHEMA query_single_shard_table CASCADE; -SELECT citus_remove_node('localhost', :master_port); - citus_remove_node ---------------------------------------------------------------------- - -(1 row) - diff --git a/src/test/regress/expected/recurring_outer_join.out b/src/test/regress/expected/recurring_outer_join.out index aa8cb906d..4ff353838 100644 --- a/src/test/regress/expected/recurring_outer_join.out +++ b/src/test/regress/expected/recurring_outer_join.out @@ -2,14 +2,6 @@ CREATE SCHEMA recurring_outer_join; SET search_path TO recurring_outer_join; SET citus.next_shard_id TO 1520000; SET citus.shard_count TO 32; --- idempotently add node to allow this test to run without add_coordinator -SET client_min_messages TO WARNING; -SELECT 1 FROM citus_add_node('localhost', :master_port, groupid => 0); - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - SET client_min_messages TO DEBUG1; CREATE TABLE dist_1 (a int, b int); SELECT create_distributed_table('dist_1', 'a'); @@ -2012,9 +2004,3 @@ DEBUG: performing repartitioned INSERT ... SELECT ROLLBACK; SET client_min_messages TO ERROR; DROP SCHEMA recurring_outer_join CASCADE; -SELECT master_remove_node('localhost', :master_port); - master_remove_node ---------------------------------------------------------------------- - -(1 row) - diff --git a/src/test/regress/expected/recursive_dml_with_different_planners_executors.out b/src/test/regress/expected/recursive_dml_with_different_planners_executors.out index 4532b3bb2..029d7b451 100644 --- a/src/test/regress/expected/recursive_dml_with_different_planners_executors.out +++ b/src/test/regress/expected/recursive_dml_with_different_planners_executors.out @@ -71,10 +71,6 @@ UPDATE distributed_table SET dept = foo.max_dept FROM ) as foo WHERE foo.max_dept >= dept and tenant_id = '8'; DEBUG: generating subplan XXX_1 for subquery SELECT max(dept) AS max_dept FROM (SELECT DISTINCT distributed_table_1.tenant_id, distributed_table_1.dept FROM recursive_dml_with_different_planner_executors.distributed_table distributed_table_1) distributed_table WHERE (tenant_id OPERATOR(pg_catalog.=) ANY (SELECT second_distributed_table.tenant_id FROM recursive_dml_with_different_planner_executors.second_distributed_table WHERE (second_distributed_table.dept OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4])))) DEBUG: Plan XXX query after replacing subqueries and CTEs: UPDATE recursive_dml_with_different_planner_executors.distributed_table SET dept = foo.max_dept FROM (SELECT intermediate_result.max_dept FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(max_dept integer)) foo WHERE ((foo.max_dept OPERATOR(pg_catalog.>=) distributed_table.dept) AND (distributed_table.tenant_id OPERATOR(pg_catalog.=) '8'::text)) -RESET client_min_messages; +SET client_min_messages TO WARNING; DROP SCHEMA recursive_dml_with_different_planner_executors CASCADE; -NOTICE: drop cascades to 3 other objects -DETAIL: drop cascades to table distributed_table -drop cascades to table second_distributed_table -drop cascades to table reference_table SET search_path TO public; diff --git a/src/test/regress/expected/recursive_relation_planning_restriction_pushdown.out b/src/test/regress/expected/recursive_relation_planning_restriction_pushdown.out index 6a41c735a..26c4e09b4 100644 --- a/src/test/regress/expected/recursive_relation_planning_restriction_pushdown.out +++ b/src/test/regress/expected/recursive_relation_planning_restriction_pushdown.out @@ -491,18 +491,14 @@ SELECT MAX(x) FROM ( UNION ALL SELECT 1 as x FROM (SELECT 1 FROM tbl1, tbl2 WHERE b > 0) AS s1 WHERE false ) as res; -DEBUG: Wrapping relation "tbl2" to a subquery -DEBUG: generating subplan XXX_1 for subquery SELECT b FROM push_down_filters.tbl2 WHERE (b OPERATOR(pg_catalog.>) 0) -DEBUG: Wrapping relation "tbl2" to a subquery -DEBUG: generating subplan XXX_2 for subquery SELECT b FROM push_down_filters.tbl2 WHERE false -DEBUG: generating subplan XXX_3 for subquery SELECT 1 AS x FROM (SELECT 1 FROM push_down_filters.tbl1, (SELECT tbl2_1.b FROM (SELECT intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(b integer)) tbl2_1) tbl2 WHERE (tbl2.b OPERATOR(pg_catalog.>) 0)) s1("?column?") WHERE true UNION ALL SELECT 1 AS x FROM (SELECT 1 FROM push_down_filters.tbl1, (SELECT tbl2_1.b FROM (SELECT intermediate_result.b FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(b integer)) tbl2_1) tbl2 WHERE (tbl2.b OPERATOR(pg_catalog.>) 0)) s1("?column?") WHERE false -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT max(x) AS max FROM (SELECT intermediate_result.x FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(x integer)) res max --------------------------------------------------------------------- 1 (1 row) DROP TABLE tbl1, tbl2; +CONTEXT: SQL statement "SELECT citus_drop_all_shards(v_obj.objid, v_obj.schema_name, v_obj.object_name, drop_shards_metadata_only := false)" +PL/pgSQL function citus_drop_trigger() line XX at PERFORM CREATE table tbl2(a int, b int, d int); CREATE table tbl1(a int, b int, c int); INSERT INTO tbl1 VALUES (1,1,1); @@ -563,12 +559,6 @@ SELECT 1 as x FROM (SELECT 1 FROM tbl1, tbl2 WHERE tbl2.b > 0) AS s1 WHERE true UNION ALL SELECT 1 as x FROM (SELECT 1 FROM tbl1, tbl2 WHERE tbl2.b > 0) AS s1 WHERE false ) as res; -DEBUG: Wrapping relation "tbl2" to a subquery -DEBUG: generating subplan XXX_1 for subquery SELECT b FROM push_down_filters.tbl2 WHERE (b OPERATOR(pg_catalog.>) 0) -DEBUG: Wrapping relation "tbl2" to a subquery -DEBUG: generating subplan XXX_2 for subquery SELECT b FROM push_down_filters.tbl2 WHERE false -DEBUG: generating subplan XXX_3 for subquery SELECT 1 AS x FROM (SELECT 1 FROM push_down_filters.tbl1, (SELECT NULL::integer AS a, tbl2_1.b, NULL::integer AS d FROM (SELECT intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(b integer)) tbl2_1) tbl2 WHERE (tbl2.b OPERATOR(pg_catalog.>) 0)) s1("?column?") WHERE true UNION ALL SELECT 1 AS x FROM (SELECT 1 FROM push_down_filters.tbl1, (SELECT NULL::integer AS a, tbl2_1.b, NULL::integer AS d FROM (SELECT intermediate_result.b FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(b integer)) tbl2_1) tbl2 WHERE (tbl2.b OPERATOR(pg_catalog.>) 0)) s1("?column?") WHERE false -DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT max(x) AS max FROM (SELECT intermediate_result.x FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(x integer)) res max --------------------------------------------------------------------- 1 @@ -577,4 +567,4 @@ DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT max(x) AS max \set VERBOSITY terse RESET client_min_messages; DROP SCHEMA push_down_filters CASCADE; -NOTICE: drop cascades to 7 other objects +NOTICE: drop cascades to 8 other objects diff --git a/src/test/regress/expected/recursive_view_local_table.out b/src/test/regress/expected/recursive_view_local_table.out index b4ef802b4..dd11b103a 100644 --- a/src/test/regress/expected/recursive_view_local_table.out +++ b/src/test/regress/expected/recursive_view_local_table.out @@ -152,8 +152,11 @@ SELECT ref_table.* FROM ref_table JOIN (SELECT * FROM recursive_defined_non_recu (3 rows) SELECT ref_table.* FROM ref_table WHERE EXISTS (SELECT * FROM local_table l WHERE l.a = ref_table.a); -ERROR: direct joins between distributed and local tables are not supported -HINT: Use CTE's or subqueries to select from local tables and use them in joins + a | b +--------------------------------------------------------------------- + 1 | 1 +(1 row) + SELECT ref_table.* FROM ref_table WHERE EXISTS (SELECT * FROM local_table l WHERE l.a = ref_table.a) AND false; a | b --------------------------------------------------------------------- @@ -196,9 +199,5 @@ SELECT ref_table.* FROM ref_table WHERE EXISTS (SELECT * FROM recursive_defined_ --------------------------------------------------------------------- (0 rows) +SET client_min_messages TO WARNING; DROP SCHEMA postgres_local_table CASCADE; -NOTICE: drop cascades to 4 other objects -DETAIL: drop cascades to table local_table -drop cascades to view recursive_view -drop cascades to view recursive_defined_non_recursive_view -drop cascades to table ref_table diff --git a/src/test/regress/expected/relation_access_tracking.out b/src/test/regress/expected/relation_access_tracking.out index 0b8c1d05b..052c456e5 100644 --- a/src/test/regress/expected/relation_access_tracking.out +++ b/src/test/regress/expected/relation_access_tracking.out @@ -1020,20 +1020,6 @@ HINT: To remove the local data, run: SELECT truncate_local_data_after_distribut (1 row) COMMIT; +SET client_min_messages TO WARNING; SET search_path TO 'public'; DROP SCHEMA access_tracking CASCADE; -NOTICE: drop cascades to 14 other objects -DETAIL: drop cascades to function access_tracking.relation_select_access_mode(oid) -drop cascades to function access_tracking.relation_dml_access_mode(oid) -drop cascades to function access_tracking.relation_ddl_access_mode(oid) -drop cascades to function access_tracking.distributed_relation(text) -drop cascades to function access_tracking.relation_access_mode_to_text(text,integer) -drop cascades to view access_tracking.relation_accesses -drop cascades to table access_tracking.table_1 -drop cascades to table access_tracking.table_2 -drop cascades to table access_tracking.table_4 -drop cascades to table access_tracking.table_5 -drop cascades to table access_tracking.table_6 -drop cascades to table access_tracking.table_7 -drop cascades to table access_tracking.partitioning_test -drop cascades to table access_tracking.table_3 diff --git a/src/test/regress/expected/remove_coordinator.out b/src/test/regress/expected/remove_coordinator.out index e59a1f89e..0226a7cd0 100644 --- a/src/test/regress/expected/remove_coordinator.out +++ b/src/test/regress/expected/remove_coordinator.out @@ -5,3 +5,10 @@ SELECT master_remove_node('localhost', :master_port); (1 row) +-- restore coordinator for the rest of the tests +SELECT citus_set_coordinator_host('localhost', :master_port); + citus_set_coordinator_host +--------------------------------------------------------------------- + +(1 row) + diff --git a/src/test/regress/expected/remove_coordinator_from_metadata.out b/src/test/regress/expected/remove_coordinator_from_metadata.out new file mode 100644 index 000000000..5b062ed6a --- /dev/null +++ b/src/test/regress/expected/remove_coordinator_from_metadata.out @@ -0,0 +1,6 @@ +SELECT master_remove_node('localhost', :master_port); + master_remove_node +--------------------------------------------------------------------- + +(1 row) + diff --git a/src/test/regress/expected/replicated_table_disable_node.out b/src/test/regress/expected/replicated_table_disable_node.out index 60de41f08..be1ad92b3 100644 --- a/src/test/regress/expected/replicated_table_disable_node.out +++ b/src/test/regress/expected/replicated_table_disable_node.out @@ -38,7 +38,7 @@ SELECT count(*) FROM pg_dist_placement p JOIN pg_dist_node n USING(groupid) AND p.shardid IN (101500, 101501, 101502); count --------------------------------------------------------------------- - 3 + 4 (1 row) \c - - - :worker_1_port @@ -47,7 +47,7 @@ SELECT count(*) FROM pg_dist_placement p JOIN pg_dist_node n USING(groupid) AND p.shardid IN (101500, 101501, 101502); count --------------------------------------------------------------------- - 3 + 4 (1 row) SET search_path TO disable_node_with_replicated_tables; diff --git a/src/test/regress/expected/run_command_on_all_nodes.out b/src/test/regress/expected/run_command_on_all_nodes.out index 76c42ad23..e95989d84 100644 --- a/src/test/regress/expected/run_command_on_all_nodes.out +++ b/src/test/regress/expected/run_command_on_all_nodes.out @@ -1,5 +1,11 @@ CREATE SCHEMA run_command_on_all_nodes; SET search_path TO run_command_on_all_nodes; +SELECT master_remove_node('localhost', :master_port); + master_remove_node +--------------------------------------------------------------------- + +(1 row) + -- check coordinator isn't in metadata SELECT count(*) != 0 AS "Coordinator is in Metadata" FROM pg_dist_node @@ -205,3 +211,9 @@ DROP SCHEMA run_command_on_all_nodes CASCADE; NOTICE: drop cascades to 2 other objects DETAIL: drop cascades to table run_command_on_all_nodes.tbl drop cascades to table run_command_on_all_nodes.test +SELECT citus_set_coordinator_host('localhost'); + citus_set_coordinator_host +--------------------------------------------------------------------- + +(1 row) + diff --git a/src/test/regress/expected/sequential_modifications.out b/src/test/regress/expected/sequential_modifications.out index e5092ae56..c0acde1da 100644 --- a/src/test/regress/expected/sequential_modifications.out +++ b/src/test/regress/expected/sequential_modifications.out @@ -22,7 +22,7 @@ $$ DECLARE result bool; BEGIN - SELECT tx_count = worker_count FROM (SELECT count(*) as tx_count FROM pg_dist_transaction WHERE gid LIKE 'citus_%_' || pg_backend_pid() || '%_%') as s1, (SELECT count(*) as worker_count FROM pg_dist_node WHERE noderole = 'primary') as s2 INTO result; + SELECT tx_count = worker_count FROM (SELECT count(*) as tx_count FROM pg_dist_transaction WHERE gid LIKE 'citus_%_' || pg_backend_pid() || '%_%') as s1, (SELECT count(*) as worker_count FROM pg_dist_node WHERE noderole = 'primary' AND groupid <> 0 ) as s2 INTO result; RETURN result; END; $$ @@ -669,13 +669,14 @@ HINT: To remove the local data, run: SELECT truncate_local_data_after_distribut ABORT; SET search_path TO 'public'; DROP SCHEMA test_seq_ddl CASCADE; -NOTICE: drop cascades to 11 other objects +NOTICE: drop cascades to 12 other objects DETAIL: drop cascades to function test_seq_ddl.distributed_2pcs_are_equal_to_worker_count() drop cascades to function test_seq_ddl.distributed_2pcs_are_equal_to_placement_count() drop cascades to function test_seq_ddl.no_distributed_2pcs() drop cascades to function test_seq_ddl.set_local_multi_shard_modify_mode_to_sequential() drop cascades to table test_seq_ddl.test_table drop cascades to table test_seq_ddl.ref_test +drop cascades to table test_seq_ddl.ref_test_16004 drop cascades to table test_seq_ddl.test_table_rep_2 drop cascades to table test_seq_ddl.test_seq_truncate drop cascades to table test_seq_ddl.test_seq_truncate_rep_2 diff --git a/src/test/regress/expected/set_operation_and_local_tables.out b/src/test/regress/expected/set_operation_and_local_tables.out index 92cde1148..db9b36506 100644 --- a/src/test/regress/expected/set_operation_and_local_tables.out +++ b/src/test/regress/expected/set_operation_and_local_tables.out @@ -321,15 +321,33 @@ DEBUG: push down of limit count: 2 DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 5 DEBUG: pruning merge fetch taskId 2 @@ -346,6 +364,14 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 20 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 20 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 25 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 25 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 30 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 30 DEBUG: generating subplan XXX_1 for subquery SELECT t1.x FROM recursive_set_local.test t1, recursive_set_local.test t2 WHERE (t1.x OPERATOR(pg_catalog.=) t2.y) LIMIT 2 DEBUG: generating subplan XXX_2 for subquery SELECT x FROM recursive_set_local.local_test DEBUG: Router planner cannot handle multi-shard select queries @@ -360,9 +386,5 @@ DEBUG: Creating router plan 1 (2 rows) -RESET client_min_messages; +SET client_min_messages TO WARNING; DROP SCHEMA recursive_set_local CASCADE; -NOTICE: drop cascades to 3 other objects -DETAIL: drop cascades to table test -drop cascades to table ref -drop cascades to table local_test diff --git a/src/test/regress/expected/set_operations.out b/src/test/regress/expected/set_operations.out index 8580fd51a..a0dad36a8 100644 --- a/src/test/regress/expected/set_operations.out +++ b/src/test/regress/expected/set_operations.out @@ -916,15 +916,33 @@ DEBUG: push down of limit count: 0 DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 5 DEBUG: pruning merge fetch taskId 2 @@ -941,6 +959,14 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 20 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 20 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 25 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 25 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 30 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 30 DEBUG: generating subplan XXX_1 for subquery SELECT t1.x FROM recursive_union.test t1, recursive_union.test t2 WHERE (t1.x OPERATOR(pg_catalog.=) t2.y) LIMIT 0 DEBUG: Router planner cannot handle multi-shard select queries DEBUG: generating subplan XXX_2 for subquery SELECT x FROM recursive_union.test @@ -957,15 +983,33 @@ DEBUG: Router planner cannot handle multi-shard select queries DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 5 DEBUG: pruning merge fetch taskId 2 @@ -982,6 +1026,14 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 20 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 20 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 25 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 25 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 30 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 30 DEBUG: generating subplan XXX_1 for subquery SELECT t1.x FROM recursive_union.test t1, recursive_union.test t2 WHERE (t1.x OPERATOR(pg_catalog.=) t2.y) DEBUG: Router planner cannot handle multi-shard select queries DEBUG: generating subplan XXX_2 for subquery SELECT x FROM recursive_union.test @@ -1098,12 +1150,5 @@ DEBUG: Creating router plan 2 | 2 (2 rows) -RESET client_min_messages; +SET client_min_messages TO WARNING; DROP SCHEMA recursive_union CASCADE; -NOTICE: drop cascades to 6 other objects -DETAIL: drop cascades to table test -drop cascades to table ref -drop cascades to table test_not_colocated -drop cascades to view set_view_recursive -drop cascades to view set_view_pushdown -drop cascades to view set_view_recursive_second diff --git a/src/test/regress/expected/shard_move_constraints.out b/src/test/regress/expected/shard_move_constraints.out index 931e55644..72b49f262 100644 --- a/src/test/regress/expected/shard_move_constraints.out +++ b/src/test/regress/expected/shard_move_constraints.out @@ -472,12 +472,13 @@ SELECT start_metadata_sync_to_node('localhost', :worker_2_port); (1 row) DROP SCHEMA "shard Move Fkeys Indexes" CASCADE; -NOTICE: drop cascades to 7 other objects +NOTICE: drop cascades to 8 other objects DETAIL: drop cascades to extension btree_gist drop cascades to table "shard Move Fkeys Indexes".sensors drop cascades to table "shard Move Fkeys Indexes".colocated_dist_table drop cascades to table "shard Move Fkeys Indexes".colocated_partitioned_table drop cascades to table "shard Move Fkeys Indexes".reference_table +drop cascades to table "shard Move Fkeys Indexes".reference_table_8970028 drop cascades to table "shard Move Fkeys Indexes".index_backed_rep_identity drop cascades to table "shard Move Fkeys Indexes".multiple_unique_keys DROP ROLE mx_rebalancer_role_ent; diff --git a/src/test/regress/expected/shard_move_constraints_blocking.out b/src/test/regress/expected/shard_move_constraints_blocking.out index 82d925821..5f1b91cb6 100644 --- a/src/test/regress/expected/shard_move_constraints_blocking.out +++ b/src/test/regress/expected/shard_move_constraints_blocking.out @@ -358,10 +358,11 @@ ALTER TABLE sensors_2020_01_01 DROP CONSTRAINT fkey_from_child_to_child; -- cleanup \c - postgres - :master_port DROP SCHEMA "blocking shard Move Fkeys Indexes" CASCADE; -NOTICE: drop cascades to 5 other objects +NOTICE: drop cascades to 6 other objects DETAIL: drop cascades to table "blocking shard Move Fkeys Indexes".sensors drop cascades to table "blocking shard Move Fkeys Indexes".colocated_dist_table drop cascades to table "blocking shard Move Fkeys Indexes".colocated_partitioned_table drop cascades to table "blocking shard Move Fkeys Indexes".reference_table +drop cascades to table "blocking shard Move Fkeys Indexes".reference_table_8970028 drop cascades to table "blocking shard Move Fkeys Indexes".index_backed_rep_identity DROP ROLE mx_rebalancer_blocking_role_ent; diff --git a/src/test/regress/expected/single_hash_repartition_join.out b/src/test/regress/expected/single_hash_repartition_join.out index 31a5c7e9f..7f7586355 100644 --- a/src/test/regress/expected/single_hash_repartition_join.out +++ b/src/test/regress/expected/single_hash_repartition_join.out @@ -196,15 +196,33 @@ DETAIL: Creating dependency on merge taskId 20 DEBUG: join prunable for task partitionId 0 and 1 DEBUG: join prunable for task partitionId 0 and 2 DEBUG: join prunable for task partitionId 0 and 3 +DEBUG: join prunable for task partitionId 0 and 4 +DEBUG: join prunable for task partitionId 0 and 5 DEBUG: join prunable for task partitionId 1 and 0 DEBUG: join prunable for task partitionId 1 and 2 DEBUG: join prunable for task partitionId 1 and 3 +DEBUG: join prunable for task partitionId 1 and 4 +DEBUG: join prunable for task partitionId 1 and 5 DEBUG: join prunable for task partitionId 2 and 0 DEBUG: join prunable for task partitionId 2 and 1 DEBUG: join prunable for task partitionId 2 and 3 +DEBUG: join prunable for task partitionId 2 and 4 +DEBUG: join prunable for task partitionId 2 and 5 DEBUG: join prunable for task partitionId 3 and 0 DEBUG: join prunable for task partitionId 3 and 1 DEBUG: join prunable for task partitionId 3 and 2 +DEBUG: join prunable for task partitionId 3 and 4 +DEBUG: join prunable for task partitionId 3 and 5 +DEBUG: join prunable for task partitionId 4 and 0 +DEBUG: join prunable for task partitionId 4 and 1 +DEBUG: join prunable for task partitionId 4 and 2 +DEBUG: join prunable for task partitionId 4 and 3 +DEBUG: join prunable for task partitionId 4 and 5 +DEBUG: join prunable for task partitionId 5 and 0 +DEBUG: join prunable for task partitionId 5 and 1 +DEBUG: join prunable for task partitionId 5 and 2 +DEBUG: join prunable for task partitionId 5 and 3 +DEBUG: join prunable for task partitionId 5 and 4 DEBUG: pruning merge fetch taskId 1 DETAIL: Creating dependency on merge taskId 9 DEBUG: pruning merge fetch taskId 2 @@ -221,6 +239,14 @@ DEBUG: pruning merge fetch taskId 10 DETAIL: Creating dependency on merge taskId 24 DEBUG: pruning merge fetch taskId 11 DETAIL: Creating dependency on merge taskId 20 +DEBUG: pruning merge fetch taskId 13 +DETAIL: Creating dependency on merge taskId 29 +DEBUG: pruning merge fetch taskId 14 +DETAIL: Creating dependency on merge taskId 25 +DEBUG: pruning merge fetch taskId 16 +DETAIL: Creating dependency on merge taskId 34 +DEBUG: pruning merge fetch taskId 17 +DETAIL: Creating dependency on merge taskId 30 ERROR: the query contains a join that requires repartitioning HINT: Set citus.enable_repartition_joins to on to enable repartitioning -- single hash repartitioning is not supported between different column types diff --git a/src/test/regress/expected/subquery_and_cte.out b/src/test/regress/expected/subquery_and_cte.out index 4360bb69e..c15e9b9d7 100644 --- a/src/test/regress/expected/subquery_and_cte.out +++ b/src/test/regress/expected/subquery_and_cte.out @@ -718,15 +718,6 @@ END; $$; ERROR: (3/3) failed to execute one of the tasks CONTEXT: PL/pgSQL function inline_code_block line XX at RAISE -SET client_min_messages TO DEFAULT; +SET client_min_messages TO WARNING; DROP SCHEMA subquery_and_ctes CASCADE; -NOTICE: drop cascades to 8 other objects -DETAIL: drop cascades to table users_table -drop cascades to table events_table -drop cascades to table users_table_local -drop cascades to table dist_table -drop cascades to function func() -drop cascades to table ref_table_1 -drop cascades to table ref_table_2 -drop cascades to table dist SET search_path TO public; diff --git a/src/test/regress/expected/subquery_append.out b/src/test/regress/expected/subquery_append.out index 493c0bc37..381c467a7 100644 --- a/src/test/regress/expected/subquery_append.out +++ b/src/test/regress/expected/subquery_append.out @@ -224,7 +224,5 @@ SELECT count(*) FROM append_table WHERE extra = 1; UPDATE append_table a sET extra = 1 FROM append_table b WHERE a.key = b.key; ERROR: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns END; +SET client_min_messages TO WARNING; DROP SCHEMA subquery_append CASCADE; -NOTICE: drop cascades to 2 other objects -DETAIL: drop cascades to table append_table -drop cascades to table ref_table diff --git a/src/test/regress/expected/subquery_basics.out b/src/test/regress/expected/subquery_basics.out index 7a4fb77b7..9e2b226eb 100644 --- a/src/test/regress/expected/subquery_basics.out +++ b/src/test/regress/expected/subquery_basics.out @@ -579,6 +579,7 @@ DEBUG: Wrapping relation "dist" "d1" to a subquery DEBUG: generating subplan XXX_1 for subquery SELECT id FROM public.dist d1 WHERE true DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT (SELECT dist.id FROM public.dist WHERE (dist.id OPERATOR(pg_catalog.>) d1.id) GROUP BY dist.id) AS id FROM (public.ref FULL JOIN (SELECT d1_1.id, NULL::integer AS value FROM (SELECT intermediate_result.id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(id integer)) d1_1) d1 USING (id)) ERROR: correlated subqueries are not supported when the FROM clause contains a reference table +SET client_min_messages TO WARNING; DROP TABLE dist; DROP TABLE ref; DROP TABLE local; diff --git a/src/test/regress/expected/subquery_view.out b/src/test/regress/expected/subquery_view.out index 32354e329..3de55b3aa 100644 --- a/src/test/regress/expected/subquery_view.out +++ b/src/test/regress/expected/subquery_view.out @@ -584,16 +584,11 @@ EXPLAIN (COSTS OFF) WITH cte AS ( FROM pg_stat_activity ) SELECT * FROM reference_table JOIN cte USING (text_col); $Q$); - coordinator_plan_with_subplans + coordinator_plan_with_subplans --------------------------------------------------------------------- Custom Scan (Citus Adaptive) - -> Distributed Subplan XXX_1 - -> Function Scan on pg_stat_get_activity s - -> Distributed Subplan XXX_2 - -> Custom Scan (Citus Adaptive) - Task Count: 1 Task Count: 1 -(7 rows) +(2 rows) CREATE OR REPLACE VIEW view_on_views AS SELECT pg_stat_activity.application_name, pg_locks.pid FROM pg_stat_activity, pg_locks; SELECT public.coordinator_plan_with_subplans($Q$ @@ -602,35 +597,12 @@ EXPLAIN (COSTS OFF) WITH cte AS ( FROM view_on_views ) SELECT * FROM reference_table JOIN cte USING (text_col); $Q$); - coordinator_plan_with_subplans + coordinator_plan_with_subplans --------------------------------------------------------------------- Custom Scan (Citus Adaptive) - -> Distributed Subplan XXX_1 - -> Nested Loop - -> Function Scan on pg_stat_get_activity s - -> Function Scan on pg_lock_status l Task Count: 1 -(6 rows) +(2 rows) +SET client_min_messages TO WARNING; DROP SCHEMA subquery_view CASCADE; -NOTICE: drop cascades to 19 other objects -DETAIL: drop cascades to table users_table_local -drop cascades to table events_table_local -drop cascades to view view_without_subquery -drop cascades to view view_without_subquery_second -drop cascades to view subquery_limit -drop cascades to view subquery_non_p_key_group_by -drop cascades to view final_query_router -drop cascades to view final_query_realtime -drop cascades to view subquery_in_where -drop cascades to view subquery_from_from_where -drop cascades to view subquery_from_from_where_local_table -drop cascades to view repartition_view -drop cascades to view all_executors_view -drop cascades to view subquery_and_ctes -drop cascades to view subquery_and_ctes_second -drop cascades to view deep_subquery -drop cascades to view result_of_view_is_also_recursively_planned -drop cascades to table reference_table -drop cascades to view view_on_views SET search_path TO public; diff --git a/src/test/regress/expected/tableam.out b/src/test/regress/expected/tableam.out index 36f3729dd..47f4c241d 100644 --- a/src/test/regress/expected/tableam.out +++ b/src/test/regress/expected/tableam.out @@ -96,9 +96,7 @@ HINT: To remove the local data, run: SELECT truncate_local_data_after_distribut select * from test_ref; WARNING: fake_scan_getnextslot -DETAIL: from localhost:xxxxx WARNING: fake_scan_getnextslot -DETAIL: from localhost:xxxxx a --------------------------------------------------------------------- 1 @@ -109,6 +107,7 @@ WARNING: fake_tuple_insert DETAIL: from localhost:xxxxx WARNING: fake_tuple_insert DETAIL: from localhost:xxxxx +WARNING: fake_tuple_insert -- we should error on following, since this AM is append only SET client_min_messages TO ERROR; delete from test_ref; @@ -292,4 +291,4 @@ ERROR: specifying a table access method is not supported on a partitioned table ALTER EXTENSION citus DROP ACCESS METHOD fake_am; NOTICE: Citus does not propagate adding/dropping member objects drop schema test_tableam cascade; -NOTICE: drop cascades to 5 other objects +NOTICE: drop cascades to 6 other objects diff --git a/src/test/regress/expected/union_pushdown.out b/src/test/regress/expected/union_pushdown.out index cbee11f8e..040535b75 100644 --- a/src/test/regress/expected/union_pushdown.out +++ b/src/test/regress/expected/union_pushdown.out @@ -1469,14 +1469,5 @@ $$); f (1 row) -RESET client_min_messages; +SET client_min_messages TO WARNING; DROP SCHEMA union_pushdown CASCADE; -NOTICE: drop cascades to 8 other objects -DETAIL: drop cascades to table users_table_part -drop cascades to table events_table_part -drop cascades to table events_table_ref -drop cascades to table events_table_local -drop cascades to table test_a -drop cascades to table test_b -drop cascades to type comp_type -drop cascades to view v diff --git a/src/test/regress/expected/values.out b/src/test/regress/expected/values.out index ad5f8a911..575fd6866 100644 --- a/src/test/regress/expected/values.out +++ b/src/test/regress/expected/values.out @@ -636,9 +636,5 @@ DEBUG: CTE cte_1 is going to be inlined via distributed planning (1 row) COMMIT; -RESET client_min_messages; +SET client_min_messages TO WARNING; DROP SCHEMA values_subquery CASCADE; -NOTICE: drop cascades to 3 other objects -DETAIL: drop cascades to table test_values -drop cascades to table test_values_ref -drop cascades to function fixed_volatile_value() diff --git a/src/test/regress/expected/with_dml.out b/src/test/regress/expected/with_dml.out index f2743a8d9..4f2170082 100644 --- a/src/test/regress/expected/with_dml.out +++ b/src/test/regress/expected/with_dml.out @@ -176,9 +176,5 @@ WITH ids_to_delete AS ( SELECT id FROM reference_table ) DELETE FROM reference_table WHERE id = ANY(SELECT id FROM ids_to_delete); -RESET client_min_messages; +SET client_min_messages TO WARNING; DROP SCHEMA with_dml CASCADE; -NOTICE: drop cascades to 3 other objects -DETAIL: drop cascades to table distributed_table -drop cascades to table second_distributed_table -drop cascades to table reference_table diff --git a/src/test/regress/expected/with_executors.out b/src/test/regress/expected/with_executors.out index df1c80625..0ef716cac 100644 --- a/src/test/regress/expected/with_executors.out +++ b/src/test/regress/expected/with_executors.out @@ -427,7 +427,5 @@ WHERE 4365606 (1 row) +SET client_min_messages TO WARNING; DROP SCHEMA with_executors CASCADE; -NOTICE: drop cascades to 2 other objects -DETAIL: drop cascades to table local_table -drop cascades to table ref_table diff --git a/src/test/regress/expected/with_join.out b/src/test/regress/expected/with_join.out index 23cafd2cc..c5df67ca7 100644 --- a/src/test/regress/expected/with_join.out +++ b/src/test/regress/expected/with_join.out @@ -385,9 +385,12 @@ join cte_1 ON cte_1.col1=d1.distrib_col; RESET client_min_messages; DROP SCHEMA with_join CASCADE; -NOTICE: drop cascades to 5 other objects +NOTICE: drop cascades to 8 other objects DETAIL: drop cascades to table reference_table +drop cascades to table reference_table_1501000 drop cascades to table distributed_1 drop cascades to table distributed_2 drop cascades to table reference_1 drop cascades to table reference_2 +drop cascades to table reference_1_1501009 +drop cascades to table reference_2_1501010 diff --git a/src/test/regress/expected/with_modifying.out b/src/test/regress/expected/with_modifying.out index 997c62f93..70418251b 100644 --- a/src/test/regress/expected/with_modifying.out +++ b/src/test/regress/expected/with_modifying.out @@ -1083,4 +1083,4 @@ WITH mb AS (DELETE FROM modify_table WHERE id = 3 RETURNING NULL, NULL) SELECT * \set VERBOSITY terse DROP SCHEMA with_modifying CASCADE; -NOTICE: drop cascades to 9 other objects +NOTICE: drop cascades to 10 other objects diff --git a/src/test/regress/multi_1_schedule b/src/test/regress/multi_1_schedule index 5a8a6b635..b9a1db0e2 100644 --- a/src/test/regress/multi_1_schedule +++ b/src/test/regress/multi_1_schedule @@ -374,3 +374,4 @@ test: ensure_no_intermediate_data_leak test: ensure_no_shared_connection_leak test: check_mx +test: check_cluster_state diff --git a/src/test/regress/multi_mx_schedule b/src/test/regress/multi_mx_schedule index 181f9288f..682379b78 100644 --- a/src/test/regress/multi_mx_schedule +++ b/src/test/regress/multi_mx_schedule @@ -17,6 +17,7 @@ test: multi_extension test: multi_test_helpers multi_test_helpers_superuser test: multi_mx_node_metadata test: multi_cluster_management +test: remove_coordinator_from_metadata test: multi_mx_function_table_reference test: multi_test_catalog_views diff --git a/src/test/regress/multi_schedule b/src/test/regress/multi_schedule index 67fb48fa2..527dec8f7 100644 --- a/src/test/regress/multi_schedule +++ b/src/test/regress/multi_schedule @@ -128,3 +128,4 @@ test: check_mx test: generated_identity test: drop_database +test: check_cluster_state diff --git a/src/test/regress/split_schedule b/src/test/regress/split_schedule index 2510260fb..b47acd828 100644 --- a/src/test/regress/split_schedule +++ b/src/test/regress/split_schedule @@ -2,6 +2,7 @@ # Include tests from 'minimal_schedule' for setup. test: multi_test_helpers multi_test_helpers_superuser columnar_test_helpers test: multi_cluster_management +test: remove_coordinator_from_metadata test: multi_test_catalog_views test: tablespace # Helpers for foreign key catalogs. diff --git a/src/test/regress/sql/add_coordinator.sql b/src/test/regress/sql/add_coordinator.sql index 2dba78064..81b77bfcd 100644 --- a/src/test/regress/sql/add_coordinator.sql +++ b/src/test/regress/sql/add_coordinator.sql @@ -3,6 +3,8 @@ -- -- node trying to add itself without specifying groupid => 0 should error out +-- first remove the coordinator to for testing master_add_node for coordinator +SELECT master_remove_node('localhost', :master_port); SELECT master_add_node('localhost', :master_port); SELECT master_add_node('localhost', :master_port, groupid => 0) AS master_nodeid \gset diff --git a/src/test/regress/sql/alter_distributed_table.sql b/src/test/regress/sql/alter_distributed_table.sql index 348aba7b1..0577421de 100644 --- a/src/test/regress/sql/alter_distributed_table.sql +++ b/src/test/regress/sql/alter_distributed_table.sql @@ -195,14 +195,18 @@ SELECT COUNT(DISTINCT colocationid) FROM pg_dist_partition WHERE logicalrelid::r -- test references CREATE TABLE referenced_dist_table (a INT UNIQUE); CREATE TABLE referenced_ref_table (a INT UNIQUE); -CREATE TABLE table_with_references (a1 INT UNIQUE REFERENCES referenced_dist_table(a), a2 INT REFERENCES referenced_ref_table(a)); -CREATE TABLE referencing_dist_table (a INT REFERENCES table_with_references(a1)); +CREATE TABLE table_with_references (a1 INT UNIQUE, a2 INT); +CREATE TABLE referencing_dist_table (a INT); SELECT create_distributed_table('referenced_dist_table', 'a', colocate_with:='none'); SELECT create_reference_table('referenced_ref_table'); SELECT create_distributed_table('table_with_references', 'a1', colocate_with:='referenced_dist_table'); SELECT create_distributed_table('referencing_dist_table', 'a', colocate_with:='referenced_dist_table'); +ALTER TABLE table_with_references ADD FOREIGN KEY (a1) REFERENCES referenced_dist_table(a); +ALTER TABLE table_with_references ADD FOREIGN KEY (a2) REFERENCES referenced_ref_table(a); +ALTER TABLE referencing_dist_table ADD FOREIGN KEY (a) REFERENCES table_with_references(a1); + SET client_min_messages TO WARNING; SELECT conrelid::regclass::text AS "Referencing Table", pg_get_constraintdef(oid, true) AS "Definition" FROM pg_constraint WHERE (conrelid::regclass::text = 'table_with_references' OR confrelid::regclass::text = 'table_with_references') AND contype = 'f' ORDER BY 1,2; @@ -477,3 +481,4 @@ RESET search_path; DROP SCHEMA alter_distributed_table CASCADE; DROP SCHEMA schema_to_test_alter_dist_table CASCADE; +DROP USER alter_dist_table_test_user; diff --git a/src/test/regress/sql/alter_table_set_access_method.sql b/src/test/regress/sql/alter_table_set_access_method.sql index 87055e364..d7720cfda 100644 --- a/src/test/regress/sql/alter_table_set_access_method.sql +++ b/src/test/regress/sql/alter_table_set_access_method.sql @@ -278,4 +278,3 @@ select alter_table_set_access_method('view_test_view','columnar'); SET client_min_messages TO WARNING; DROP SCHEMA alter_table_set_access_method CASCADE; -SELECT 1 FROM master_remove_node('localhost', :master_port); diff --git a/src/test/regress/sql/check_cluster_state.sql b/src/test/regress/sql/check_cluster_state.sql new file mode 100644 index 000000000..bcdd8f1a7 --- /dev/null +++ b/src/test/regress/sql/check_cluster_state.sql @@ -0,0 +1 @@ +SELECT count(*) >= 1 as coordinator_exists FROM pg_dist_node WHERE groupid = 0 AND isactive; diff --git a/src/test/regress/sql/citus_local_dist_joins.sql b/src/test/regress/sql/citus_local_dist_joins.sql index c68fbbfd8..2a7bbacf9 100644 --- a/src/test/regress/sql/citus_local_dist_joins.sql +++ b/src/test/regress/sql/citus_local_dist_joins.sql @@ -2,8 +2,6 @@ CREATE SCHEMA citus_local_dist_joins; SET search_path TO citus_local_dist_joins; SET client_min_messages to ERROR; -SELECT master_add_node('localhost', :master_port, groupId => 0) AS coordinator_nodeid \gset - CREATE TABLE citus_local(key int, value text); SELECT citus_add_local_table_to_metadata('citus_local'); @@ -273,6 +271,5 @@ RESET citus.local_table_join_policy; SET client_min_messages to ERROR; DROP TABLE citus_local; -SELECT master_remove_node('localhost', :master_port); \set VERBOSITY terse DROP SCHEMA citus_local_dist_joins CASCADE; diff --git a/src/test/regress/sql/distributed_functions.sql b/src/test/regress/sql/distributed_functions.sql index 18198a217..9eb2fe730 100644 --- a/src/test/regress/sql/distributed_functions.sql +++ b/src/test/regress/sql/distributed_functions.sql @@ -696,7 +696,7 @@ DROP SCHEMA function_tests CASCADE; DROP SCHEMA function_tests2 CASCADE; -- clear objects -SELECT stop_metadata_sync_to_node(nodename,nodeport) FROM pg_dist_node WHERE isactive AND noderole = 'primary'; +SELECT stop_metadata_sync_to_node(nodename,nodeport) FROM pg_dist_node WHERE isactive AND noderole = 'primary' AND groupid <> 0; -- This is hacky, but we should clean-up the resources as below \c - - - :worker_1_port @@ -710,7 +710,8 @@ TRUNCATE pg_dist_node; SET client_min_messages TO ERROR; DROP USER functionuser; DROP ROLE r1; + SELECT 1 FROM run_command_on_workers($$DROP USER functionuser$$); -- sync metadata again -SELECT start_metadata_sync_to_node(nodename,nodeport) FROM pg_dist_node WHERE isactive AND noderole = 'primary'; +SELECT start_metadata_sync_to_node(nodename,nodeport) FROM pg_dist_node WHERE isactive AND noderole = 'primary' AND groupid <> 0; diff --git a/src/test/regress/sql/foreign_key_restriction_enforcement.sql b/src/test/regress/sql/foreign_key_restriction_enforcement.sql index cdef1c798..f6c7bd68b 100644 --- a/src/test/regress/sql/foreign_key_restriction_enforcement.sql +++ b/src/test/regress/sql/foreign_key_restriction_enforcement.sql @@ -680,6 +680,8 @@ ROLLBACK; -- the fails since we're trying to switch sequential mode after -- already executed a parallel query BEGIN; + SELECT master_remove_node('localhost', :master_port); + CREATE TABLE test_table_1(id int PRIMARY KEY); SELECT create_reference_table('test_table_1'); @@ -697,6 +699,8 @@ ROLLBACK; -- same test with the above, but this time using -- sequential mode, succeeds BEGIN; + SELECT master_remove_node('localhost', :master_port); + SET LOCAL citus.multi_shard_modify_mode TO 'sequential'; CREATE TABLE test_table_1(id int PRIMARY KEY); SELECT create_reference_table('test_table_1'); diff --git a/src/test/regress/sql/function_propagation.sql b/src/test/regress/sql/function_propagation.sql index cd718d17a..d3e4a74d4 100644 --- a/src/test/regress/sql/function_propagation.sql +++ b/src/test/regress/sql/function_propagation.sql @@ -565,8 +565,6 @@ BEGIN; SELECT pg_identify_object_as_address(classid, objid, objsubid) from pg_catalog.pg_dist_object where objid = 'function_propagation_schema.func_in_transaction_for_local_table'::regproc::oid; CREATE TABLE citus_local_table_to_test_func(l1 int DEFAULT func_in_transaction_for_local_table()); - SET LOCAL client_min_messages TO WARNING; - SELECT 1 FROM master_add_node('localhost', :master_port, groupid => 0); SELECT citus_add_local_table_to_metadata('citus_local_table_to_test_func'); -- Function should be marked as distributed after distributing the table that depends on it diff --git a/src/test/regress/sql/grant_on_foreign_server_propagation.sql b/src/test/regress/sql/grant_on_foreign_server_propagation.sql index d2ecd482b..df0d01f62 100644 --- a/src/test/regress/sql/grant_on_foreign_server_propagation.sql +++ b/src/test/regress/sql/grant_on_foreign_server_propagation.sql @@ -6,6 +6,7 @@ CREATE SCHEMA "grant on server"; SET search_path TO "grant on server"; -- remove one of the worker nodes to test adding a new node later +SELECT 1 FROM citus_remove_node('localhost', :master_port); SELECT 1 FROM citus_remove_node('localhost', :worker_2_port); select 1 from citus_add_node('localhost',:master_port,groupId=>0); @@ -133,5 +134,3 @@ SET client_min_messages TO ERROR; DROP SERVER "Foreign Server" CASCADE; DROP SCHEMA "grant on server" CASCADE; DROP ROLE role_test_servers, role_test_servers_2, ownerrole; - -SELECT 1 FROM citus_remove_node('localhost', :master_port); diff --git a/src/test/regress/sql/insert_select_single_shard_table.sql b/src/test/regress/sql/insert_select_single_shard_table.sql index 6593ab90b..3ea036772 100644 --- a/src/test/regress/sql/insert_select_single_shard_table.sql +++ b/src/test/regress/sql/insert_select_single_shard_table.sql @@ -4,9 +4,6 @@ SET search_path TO insert_select_single_shard_table; SET citus.next_shard_id TO 1820000; SET citus.shard_count TO 32; -SET client_min_messages TO WARNING; -SELECT 1 FROM citus_add_node('localhost', :master_port, groupid => 0); - SET client_min_messages TO NOTICE; CREATE TABLE nullkey_c1_t1(a int, b int); @@ -472,5 +469,3 @@ SELECT * FROM upsert_test_3 ORDER BY key_1, key_2; SET client_min_messages TO WARNING; DROP SCHEMA insert_select_single_shard_table CASCADE; - -SELECT citus_remove_node('localhost', :master_port); diff --git a/src/test/regress/sql/intermediate_results.sql b/src/test/regress/sql/intermediate_results.sql index 44eadf0e5..4cd54b29b 100644 --- a/src/test/regress/sql/intermediate_results.sql +++ b/src/test/regress/sql/intermediate_results.sql @@ -337,3 +337,5 @@ COMMIT; SET client_min_messages TO ERROR; DROP SCHEMA other_schema CASCADE; DROP SCHEMA intermediate_results CASCADE; +DROP OWNED BY some_other_user; +DROP USER some_other_user; diff --git a/src/test/regress/sql/limit_intermediate_size.sql b/src/test/regress/sql/limit_intermediate_size.sql index 5b013bf75..8f64c31fd 100644 --- a/src/test/regress/sql/limit_intermediate_size.sql +++ b/src/test/regress/sql/limit_intermediate_size.sql @@ -17,7 +17,7 @@ cte2 AS MATERIALIZED ( SELECT cte.user_id, cte.value_2 FROM cte,cte2 ORDER BY 1,2 LIMIT 10; -SET citus.max_intermediate_result_size TO 9; +SET citus.max_intermediate_result_size TO 17; WITH cte AS MATERIALIZED ( SELECT diff --git a/src/test/regress/sql/local_table_join.sql b/src/test/regress/sql/local_table_join.sql index 96b51ff69..8d0d7d332 100644 --- a/src/test/regress/sql/local_table_join.sql +++ b/src/test/regress/sql/local_table_join.sql @@ -60,11 +60,16 @@ CREATE MATERIALIZED VIEW mv2 AS SELECT * FROM distributed_table; SET client_min_messages TO DEBUG1; --- the user doesn't allow local / distributed table joinn +-- the user doesn't allow local / distributed table join + +SELECT master_remove_node('localhost', :master_port); -- https://github.com/citusdata/citus/issues/6958 + SET citus.local_table_join_policy TO 'never'; SELECT count(*) FROM postgres_table JOIN distributed_table USING(key); SELECT count(*) FROM postgres_table JOIN reference_table USING(key); +SELECT citus_set_coordinator_host('localhost'); -- https://github.com/citusdata/citus/issues/6958 + -- the user prefers local table recursively planned SET citus.local_table_join_policy TO 'prefer-local'; SELECT count(*) FROM postgres_table JOIN distributed_table USING(key); @@ -466,6 +471,7 @@ SELECT create_distributed_table('table2', 'a'); SELECT 1 AS res FROM table2 RIGHT JOIN (SELECT 1 FROM table1, table2) AS sub1 ON false; ROLLBACK; +SELECT master_remove_node('localhost', :master_port); -- https://github.com/citusdata/citus/issues/6958 BEGIN; SELECT create_reference_table('table1'); SELECT 1 AS res FROM table2 RIGHT JOIN (SELECT 1 FROM table1, table2) AS sub1 ON false; @@ -476,6 +482,7 @@ SELECT create_reference_table('table2'); SELECT 1 AS res FROM table2 RIGHT JOIN (SELECT 1 FROM table1, table2) AS sub1 ON false; ROLLBACK; +SELECT citus_set_coordinator_host('localhost'); -- https://github.com/citusdata/citus/issues/6958 RESET client_min_messages; \set VERBOSITY terse diff --git a/src/test/regress/sql/logical_replication.sql b/src/test/regress/sql/logical_replication.sql index e78b0a393..3f8e048ca 100644 --- a/src/test/regress/sql/logical_replication.sql +++ b/src/test/regress/sql/logical_replication.sql @@ -15,8 +15,6 @@ SELECT oid AS postgres_oid FROM pg_roles where rolname = 'postgres' \gset SELECT create_distributed_table('dist', 'id'); INSERT INTO dist SELECT generate_series(1, 100); -SELECT 1 from citus_add_node('localhost', :master_port, groupId := 0); - -- Create a publiction and subscription (including replication slot) manually. -- This allows us to test the cleanup logic at the start of the shard move. \c - - - :worker_1_port @@ -55,8 +53,6 @@ SET search_path TO logical_replication; select citus_move_shard_placement(6830002, 'localhost', :worker_1_port, 'localhost', :worker_2_port, 'force_logical'); -SELECT citus_remove_node('localhost', :master_port); - -- the subscription is still there, as there is no cleanup record for it -- we have created it manually SELECT count(*) from pg_subscription; diff --git a/src/test/regress/sql/merge.sql b/src/test/regress/sql/merge.sql index f10ab6c99..911642812 100644 --- a/src/test/regress/sql/merge.sql +++ b/src/test/regress/sql/merge.sql @@ -21,7 +21,6 @@ SET citus.next_shard_id TO 4000000; SET citus.explain_all_tasks TO true; SET citus.shard_replication_factor TO 1; SET citus.max_adaptive_executor_pool_size TO 1; -SELECT 1 FROM master_add_node('localhost', :master_port, groupid => 0); CREATE TABLE source ( @@ -2166,4 +2165,3 @@ SET search_path TO merge_schema; DROP SERVER foreign_server CASCADE; DROP FUNCTION merge_when_and_write(); DROP SCHEMA merge_schema CASCADE; -SELECT 1 FROM master_remove_node('localhost', :master_port); diff --git a/src/test/regress/sql/multi_alter_table_add_constraints_without_name.sql b/src/test/regress/sql/multi_alter_table_add_constraints_without_name.sql index fe8bb4b20..700e37f6e 100644 --- a/src/test/regress/sql/multi_alter_table_add_constraints_without_name.sql +++ b/src/test/regress/sql/multi_alter_table_add_constraints_without_name.sql @@ -620,10 +620,6 @@ DROP TABLE AT_AddConstNoName.dist_partitioned_table; -- Test "ADD PRIMARY KEY" \c - - :master_host :master_port -SET client_min_messages to ERROR; -SELECT 1 FROM master_add_node('localhost', :master_port, groupId => 0); -RESET client_min_messages; - CREATE TABLE AT_AddConstNoName.citus_local_table(id int, other_column int); SELECT citus_add_local_table_to_metadata('AT_AddConstNoName.citus_local_table'); @@ -821,8 +817,6 @@ SELECT con.conname WHERE rel.relname LIKE 'longlonglonglonglonglonglonglonglong%' ORDER BY con.conname ASC; \c - - :master_host :master_port -SELECT 1 FROM master_remove_node('localhost', :master_port); - -- Test with unusual table and column names CREATE TABLE AT_AddConstNoName."2nd table" ( "2nd id" INTEGER, "3rd id" INTEGER); SELECT create_distributed_table('AT_AddConstNoName."2nd table"','2nd id'); diff --git a/src/test/regress/sql/multi_alter_table_add_foreign_key_without_name.sql b/src/test/regress/sql/multi_alter_table_add_foreign_key_without_name.sql index 330ac0c45..03df65709 100644 --- a/src/test/regress/sql/multi_alter_table_add_foreign_key_without_name.sql +++ b/src/test/regress/sql/multi_alter_table_add_foreign_key_without_name.sql @@ -69,14 +69,14 @@ ALTER TABLE referencing_table ADD FOREIGN KEY (id) REFERENCES referenced_table(i DROP TABLE referencing_table; DROP TABLE referenced_table; --- test foreign constraint creation is not supported when one of the tables is not a citus table +-- test foreign constraint creation is supported when coordinator is in metadata CREATE TABLE referenced_local_table(id int PRIMARY KEY, other_column int); CREATE TABLE reference_table(id int, referencing_column int); SELECT create_reference_table('reference_table'); ALTER TABLE reference_table ADD FOREIGN KEY (referencing_column) REFERENCES referenced_local_table(id); DROP TABLE referenced_local_table; -DROP TABLE reference_table; +DROP TABLE reference_table CASCADE; -- test foreign constraint with correct conditions CREATE TABLE referenced_table(id int PRIMARY KEY, test_column int); @@ -352,7 +352,6 @@ DROP TABLE dist_table CASCADE; DROP TABLE reference_table CASCADE; -- test ADD FOREIGN KEY from citus local to reference table -SELECT 1 FROM master_add_node('localhost', :master_port, groupId => 0); CREATE TABLE citus_local_table(l1 int); SELECT citus_add_local_table_to_metadata('citus_local_table'); @@ -373,7 +372,6 @@ ALTER TABLE citus_local_table ADD FOREIGN KEY(l1) REFERENCES reference_table(r1) ALTER TABLE citus_local_table ADD FOREIGN KEY(l1) REFERENCES reference_table(r1) ON DELETE RESTRICT; DROP TABLE citus_local_table CASCADE; -SELECT 1 FROM master_remove_node('localhost', :master_port); RESET SEARCH_PATH; RESET client_min_messages; diff --git a/src/test/regress/sql/multi_cluster_management.sql b/src/test/regress/sql/multi_cluster_management.sql index 367fa9d58..9ec0eb28e 100644 --- a/src/test/regress/sql/multi_cluster_management.sql +++ b/src/test/regress/sql/multi_cluster_management.sql @@ -272,6 +272,7 @@ SELECT nodename, nodeport FROM pg_dist_node WHERE nodename='localhost' AND nodep \c - - - :master_port SELECT master_remove_node(nodename, nodeport) FROM pg_dist_node; +SELECT citus_set_coordinator_host('localhost'); SELECT 1 FROM master_add_node('localhost', :worker_1_port); SELECT 1 FROM master_add_node('localhost', :worker_2_port); diff --git a/src/test/regress/sql/multi_drop_extension.sql b/src/test/regress/sql/multi_drop_extension.sql index 29da58dc4..0bb3c3ecd 100644 --- a/src/test/regress/sql/multi_drop_extension.sql +++ b/src/test/regress/sql/multi_drop_extension.sql @@ -23,8 +23,6 @@ BEGIN; SET search_path TO public; CREATE EXTENSION citus; - SELECT 1 FROM master_add_node('localhost', :master_port, groupId => 0); - create table l1 (a int unique); SELECT create_reference_table('l1'); @@ -135,6 +133,7 @@ ROLLBACK; CREATE EXTENSION citus; -- re-add the nodes to the cluster +SELECT citus_set_coordinator_host('localhost'); SELECT 1 FROM master_add_node('localhost', :worker_1_port); SELECT 1 FROM master_add_node('localhost', :worker_2_port); diff --git a/src/test/regress/sql/multi_explain.sql b/src/test/regress/sql/multi_explain.sql index 931fb02bc..dd4615434 100644 --- a/src/test/regress/sql/multi_explain.sql +++ b/src/test/regress/sql/multi_explain.sql @@ -1151,8 +1151,6 @@ EXPLAIN :default_analyze_flags EXECUTE q2('(1)'); CREATE SCHEMA test_auto_explain; SET search_path TO 'test_auto_explain'; -SELECT citus_set_coordinator_host('localhost'); - CREATE TABLE test_ref_table (key int PRIMARY KEY); SELECT create_reference_table('test_ref_table'); @@ -1164,8 +1162,6 @@ set auto_explain.log_analyze to true; select * from test_ref_table; DROP SCHEMA test_auto_explain CASCADE; -select master_remove_node('localhost', :master_port); -SELECT public.wait_until_metadata_sync(30000); SET client_min_messages TO ERROR; DROP SCHEMA multi_explain CASCADE; diff --git a/src/test/regress/sql/multi_fix_partition_shard_index_names.sql b/src/test/regress/sql/multi_fix_partition_shard_index_names.sql index 5f87708a2..d0f789cd9 100644 --- a/src/test/regress/sql/multi_fix_partition_shard_index_names.sql +++ b/src/test/regress/sql/multi_fix_partition_shard_index_names.sql @@ -278,10 +278,6 @@ DROP TABLE dist_partitioned_table; SET citus.next_shard_id TO 910040; -- test with citus local table -SET client_min_messages TO WARNING; -SELECT 1 FROM citus_add_node('localhost', :master_port, groupid=>0); -RESET client_min_messages; - CREATE TABLE date_partitioned_citus_local_table( measureid integer, eventdate date, @@ -345,4 +341,3 @@ ALTER TABLE parent_table DROP CONSTRAINT unique_cst CASCADE; SET client_min_messages TO WARNING; DROP SCHEMA fix_idx_names CASCADE; -SELECT citus_remove_node('localhost', :master_port); diff --git a/src/test/regress/sql/multi_foreign_key.sql b/src/test/regress/sql/multi_foreign_key.sql index 041202dff..328df1fb0 100644 --- a/src/test/regress/sql/multi_foreign_key.sql +++ b/src/test/regress/sql/multi_foreign_key.sql @@ -709,11 +709,6 @@ CREATE TABLE set_on_default_test_referencing( ON UPDATE SET DEFAULT ); --- from distributed / reference to reference, fkey exists before calling the UDFs -SELECT create_distributed_table('set_on_default_test_referencing', 'col_1'); -SELECT create_reference_table('set_on_default_test_referencing'); - -DROP TABLE set_on_default_test_referencing; CREATE TABLE set_on_default_test_referencing( col_1 serial, col_2 int, col_3 int, col_4 int ); diff --git a/src/test/regress/sql/multi_join_pruning.sql b/src/test/regress/sql/multi_join_pruning.sql index d655f7c52..1b8f22706 100644 --- a/src/test/regress/sql/multi_join_pruning.sql +++ b/src/test/regress/sql/multi_join_pruning.sql @@ -66,3 +66,8 @@ EXPLAIN (COSTS OFF) SELECT count(*) FROM varchar_partitioned_table table1, varchar_partitioned_table table2 WHERE table1.varchar_column = table2.varchar_column; + +SET client_min_messages TO WARNING; +DROP TABLE varchar_partitioned_table; +DROP TABLE array_partitioned_table; +DROP TABLE composite_partitioned_table; diff --git a/src/test/regress/sql/multi_level_recursive_queries.sql b/src/test/regress/sql/multi_level_recursive_queries.sql index 29db13b6e..a708dd3dc 100644 --- a/src/test/regress/sql/multi_level_recursive_queries.sql +++ b/src/test/regress/sql/multi_level_recursive_queries.sql @@ -170,5 +170,5 @@ SELECT avg(table_5.id) FROM ( ) AS table_5 INNER JOIN dist0 AS table_9 USING (id); -RESET client_min_messages; +SET client_min_messages TO WARNING; DROP SCHEMA multi_recursive CASCADE; diff --git a/src/test/regress/sql/multi_metadata_sync.sql b/src/test/regress/sql/multi_metadata_sync.sql index b03843edc..c1a0a6a9b 100644 --- a/src/test/regress/sql/multi_metadata_sync.sql +++ b/src/test/regress/sql/multi_metadata_sync.sql @@ -95,7 +95,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; -- Test start_metadata_sync_to_node and citus_activate_node UDFs --- Ensure that hasmetadata=false for all nodes +-- Ensure that hasmetadata=false for all nodes except for the coordinator node SELECT count(*) FROM pg_dist_node WHERE hasmetadata=true; -- Show that metadata can not be synced on secondary node @@ -770,7 +770,6 @@ SELECT create_reference_table('dist_table_2'); ALTER TABLE dist_table_1 ADD COLUMN b int; -SELECT master_add_node('localhost', :master_port, groupid => 0); SELECT citus_disable_node_and_wait('localhost', :worker_1_port); SELECT citus_disable_node_and_wait('localhost', :worker_2_port); SELECT master_remove_node('localhost', :worker_1_port); diff --git a/src/test/regress/sql/multi_partitioning.sql b/src/test/regress/sql/multi_partitioning.sql index 85a3ece66..6fbd92638 100644 --- a/src/test/regress/sql/multi_partitioning.sql +++ b/src/test/regress/sql/multi_partitioning.sql @@ -1786,7 +1786,6 @@ ROLLBACK; DROP TABLE pi_table; -- 6) test with citus local table -select 1 from citus_add_node('localhost', :master_port, groupid=>0); CREATE TABLE date_partitioned_citus_local_table( measureid integer, eventdate date, @@ -1938,8 +1937,6 @@ DROP TABLE date_partitioned_citus_local_table CASCADE; DROP TABLE date_partitioned_citus_local_table_2; set client_min_messages to notice; -SELECT citus_remove_node('localhost', :master_port); - -- d) invalid tables for helper UDFs CREATE TABLE multiple_partition_column_table( event_id bigserial, diff --git a/src/test/regress/sql/multi_remove_node_reference_table.sql b/src/test/regress/sql/multi_remove_node_reference_table.sql index fbc9f9524..cc8a67239 100644 --- a/src/test/regress/sql/multi_remove_node_reference_table.sql +++ b/src/test/regress/sql/multi_remove_node_reference_table.sql @@ -107,8 +107,13 @@ WHERE colocationid IN FROM pg_dist_partition WHERE logicalrelid = 'remove_node_reference_table'::regclass); +-- test that we cannot remove a node if it has the only placement for a shard +SELECT master_remove_node('localhost', :master_port); SELECT master_remove_node('localhost', :worker_1_port); +-- restore the coordinator +SELECT citus_set_coordinator_host('localhost'); + \c - - - :worker_1_port SELECT COUNT(*) FROM pg_dist_node WHERE nodeport = :worker_2_port; @@ -574,7 +579,6 @@ WHERE ORDER BY shardid ASC; \c - - - :master_port -SELECT 1 FROM citus_set_coordinator_host('localhost', :master_port); SELECT citus_disable_node('localhost', :worker_2_port); SELECT public.wait_until_metadata_sync(); @@ -584,8 +588,6 @@ SELECT COUNT(*) FROM pg_dist_node WHERE nodeport = :worker_2_port; -- never mark coordinator metadatasynced = false SELECT hasmetadata, metadatasynced FROM pg_dist_node WHERE nodeport = :master_port; -SELECT 1 FROM citus_remove_node('localhost', :master_port); - SELECT shardid, shardstate, shardlength, nodename, nodeport diff --git a/src/test/regress/sql/multi_repartition_join_planning.sql b/src/test/regress/sql/multi_repartition_join_planning.sql index 30cfc7156..c2f379d23 100644 --- a/src/test/regress/sql/multi_repartition_join_planning.sql +++ b/src/test/regress/sql/multi_repartition_join_planning.sql @@ -9,6 +9,7 @@ SET citus.next_shard_id TO 690000; SET citus.enable_unique_job_ids TO off; SET citus.enable_repartition_joins to ON; +SET citus.shard_replication_factor to 1; create schema repartition_join; DROP TABLE IF EXISTS repartition_join.order_line; diff --git a/src/test/regress/sql/multi_replicate_reference_table.sql b/src/test/regress/sql/multi_replicate_reference_table.sql index 39aaf44c6..4d5594126 100644 --- a/src/test/regress/sql/multi_replicate_reference_table.sql +++ b/src/test/regress/sql/multi_replicate_reference_table.sql @@ -238,14 +238,12 @@ SELECT create_reference_table('replicate_reference_table_cdtc'); SELECT citus_add_node('localhost', :worker_2_port); -- required for create_distributed_table_concurrently -SELECT 1 FROM citus_set_coordinator_host('localhost', :master_port); SET citus.shard_replication_factor TO 1; CREATE TABLE distributed_table_cdtc(column1 int primary key); SELECT create_distributed_table_concurrently('distributed_table_cdtc', 'column1'); RESET citus.shard_replication_factor; -SELECT citus_remove_node('localhost', :master_port); SELECT shardid, shardstate, shardlength, nodename, nodeport @@ -456,9 +454,9 @@ CREATE TABLE ref_table_1(id int primary key, v int); CREATE TABLE ref_table_2(id int primary key, v int references ref_table_1(id)); CREATE TABLE ref_table_3(id int primary key, v int references ref_table_2(id)); -SELECT create_reference_table('ref_table_1'), - create_reference_table('ref_table_2'), - create_reference_table('ref_table_3'); +SELECT create_reference_table('ref_table_1'); +SELECT create_reference_table('ref_table_2'); +SELECT create_reference_table('ref_table_3'); -- status before master_add_node SELECT diff --git a/src/test/regress/sql/multi_sequence_default.sql b/src/test/regress/sql/multi_sequence_default.sql index 3b1dd188b..b41aba577 100644 --- a/src/test/regress/sql/multi_sequence_default.sql +++ b/src/test/regress/sql/multi_sequence_default.sql @@ -12,7 +12,6 @@ SET search_path = sequence_default, public; -- test both distributed and citus local tables -SELECT 1 FROM citus_add_node('localhost', :master_port, groupId => 0); -- Cannot add a column involving DEFAULT nextval('..') because the table is not empty CREATE SEQUENCE seq_0; CREATE SEQUENCE seq_0_local_table; @@ -451,5 +450,4 @@ DROP TABLE test_seq_dist; DROP TABLE sequence_default.seq_test_7_par; SET client_min_messages TO error; -- suppress cascading objects dropping DROP SCHEMA sequence_default CASCADE; -SELECT master_remove_node('localhost', :master_port); SET search_path TO public; diff --git a/src/test/regress/sql/multi_table_ddl.sql b/src/test/regress/sql/multi_table_ddl.sql index fc6539ac9..ee826add0 100644 --- a/src/test/regress/sql/multi_table_ddl.sql +++ b/src/test/regress/sql/multi_table_ddl.sql @@ -58,6 +58,7 @@ DROP EXTENSION citus; CREATE EXTENSION citus; -- re-add the nodes to the cluster +SELECT 1 FROM citus_set_coordinator_host('localhost'); SELECT 1 FROM master_add_node('localhost', :worker_1_port); SELECT 1 FROM master_add_node('localhost', :worker_2_port); diff --git a/src/test/regress/sql/multi_tenant_isolation.sql b/src/test/regress/sql/multi_tenant_isolation.sql index 52ce044dc..c3e51b6cc 100644 --- a/src/test/regress/sql/multi_tenant_isolation.sql +++ b/src/test/regress/sql/multi_tenant_isolation.sql @@ -497,12 +497,19 @@ SELECT create_reference_table('test_reference_table_fkey'); CREATE TABLE test_colocated_table_1(id int PRIMARY KEY, value_1 int, FOREIGN KEY(id) REFERENCES test_colocated_table_1(id)); SELECT create_distributed_table('test_colocated_table_1', 'id', colocate_with => 'NONE'); -CREATE TABLE test_colocated_table_2(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_reference_table_fkey(id), FOREIGN KEY(id) REFERENCES test_colocated_table_1(id)); +CREATE TABLE test_colocated_table_2(id int PRIMARY KEY, value_1 int); SELECT create_distributed_table('test_colocated_table_2', 'id', colocate_with => 'test_colocated_table_1'); -CREATE TABLE test_colocated_table_3(id int PRIMARY KEY, value_1 int, FOREIGN KEY(value_1) REFERENCES test_reference_table_fkey(id), FOREIGN KEY(id) REFERENCES test_colocated_table_1(id), FOREIGN KEY(id) REFERENCES test_colocated_table_2(id)); +ALTER TABLE test_colocated_table_2 ADD FOREIGN KEY(value_1) REFERENCES test_reference_table_fkey(id); +ALTER TABLE test_colocated_table_2 ADD FOREIGN KEY(id) REFERENCES test_colocated_table_1(id); + +CREATE TABLE test_colocated_table_3(id int PRIMARY KEY, value_1 int); SELECT create_distributed_table('test_colocated_table_3', 'id', colocate_with => 'test_colocated_table_1'); +ALTER TABLE test_colocated_table_3 ADD FOREIGN KEY(value_1) REFERENCES test_reference_table_fkey(id); +ALTER TABLE test_colocated_table_3 ADD FOREIGN KEY(id) REFERENCES test_colocated_table_1(id); +ALTER TABLE test_colocated_table_3 ADD FOREIGN KEY(id) REFERENCES test_colocated_table_2(id); + INSERT INTO test_reference_table_fkey SELECT i FROM generate_series (0, 100) i; INSERT INTO test_colocated_table_1 SELECT i, i FROM generate_series (0, 100) i; INSERT INTO test_colocated_table_2 SELECT i, i FROM generate_series (0, 100) i; diff --git a/src/test/regress/sql/multi_tenant_isolation_nonblocking.sql b/src/test/regress/sql/multi_tenant_isolation_nonblocking.sql index f74835108..1299c9282 100644 --- a/src/test/regress/sql/multi_tenant_isolation_nonblocking.sql +++ b/src/test/regress/sql/multi_tenant_isolation_nonblocking.sql @@ -607,3 +607,6 @@ TRUNCATE TABLE pg_catalog.pg_dist_colocation; ALTER SEQUENCE pg_catalog.pg_dist_colocationid_seq RESTART 100; ALTER SEQUENCE pg_catalog.pg_dist_placement_placementid_seq RESTART :last_placement_id; + +SELECT citus_set_coordinator_host('localhost'); + diff --git a/src/test/regress/sql/multi_transaction_recovery.sql b/src/test/regress/sql/multi_transaction_recovery.sql index 333807267..b1072fe6b 100644 --- a/src/test/regress/sql/multi_transaction_recovery.sql +++ b/src/test/regress/sql/multi_transaction_recovery.sql @@ -1,13 +1,6 @@ -- Tests for prepared transaction recovery SET citus.next_shard_id TO 1220000; --- reference tables can have placements on the coordinator. Add it so --- verify we recover transactions which do DML on coordinator placements --- properly. -SET client_min_messages TO ERROR; -SELECT 1 FROM master_add_node('localhost', :master_port, groupid => 0); -RESET client_min_messages; - -- enforce 1 connection per placement since -- the tests are prepared for that SET citus.force_max_query_parallelization TO ON; @@ -264,5 +257,3 @@ DROP TABLE test_recovery; DROP TABLE test_recovery_single; DROP TABLE test_2pcskip; DROP TABLE test_reference; - -SELECT 1 FROM master_remove_node('localhost', :master_port); diff --git a/src/test/regress/sql/multi_transactional_drop_shards.sql b/src/test/regress/sql/multi_transactional_drop_shards.sql index dd7ba43a5..3ab463b7f 100644 --- a/src/test/regress/sql/multi_transactional_drop_shards.sql +++ b/src/test/regress/sql/multi_transactional_drop_shards.sql @@ -379,13 +379,10 @@ ORDER BY \c - - - :master_port SET client_min_messages TO WARNING; --- try using the coordinator as a worker and then dropping the table -SELECT 1 FROM master_add_node('localhost', :master_port, groupid := 0); CREATE TABLE citus_local (id serial, k int); SELECT create_distributed_table('citus_local', 'id'); INSERT INTO citus_local (k) VALUES (2); DROP TABLE citus_local; -SELECT master_remove_node('localhost', :master_port); -- clean the workspace DROP TABLE transactional_drop_shards, transactional_drop_reference; diff --git a/src/test/regress/sql/pg12.sql b/src/test/regress/sql/pg12.sql index 5624a70eb..a86dbbb42 100644 --- a/src/test/regress/sql/pg12.sql +++ b/src/test/regress/sql/pg12.sql @@ -267,8 +267,6 @@ select count(*) from col_test where val = 'asdf'; -SELECT 1 FROM citus_add_node('localhost', :master_port, groupId => 0); - BEGIN; CREATE TABLE generated_stored_col_test (x int, y int generated always as (x+1) stored); SELECT citus_add_local_table_to_metadata('generated_stored_col_test'); @@ -374,8 +372,6 @@ BEGIN; SELECT * FROM generated_stored_ref; ROLLBACK; -SELECT citus_remove_node('localhost', :master_port); - CREATE TABLE superuser_columnar_table (a int) USING columnar; CREATE USER read_access; diff --git a/src/test/regress/sql/pg14.sql b/src/test/regress/sql/pg14.sql index 77f3e1cc5..be4d2f72d 100644 --- a/src/test/regress/sql/pg14.sql +++ b/src/test/regress/sql/pg14.sql @@ -671,8 +671,6 @@ drop schema pg14 cascade; create schema pg14; set search_path to pg14; -select 1 from citus_add_node('localhost',:master_port,groupid=>0); - -- test adding foreign table to metadata with the guc -- will test truncating foreign tables later CREATE TABLE foreign_table_test (id integer NOT NULL, data text, a bigserial); @@ -786,4 +784,3 @@ set client_min_messages to error; drop extension postgres_fdw cascade; drop schema pg14 cascade; reset client_min_messages; -select 1 from citus_remove_node('localhost',:master_port); diff --git a/src/test/regress/sql/pg15.sql b/src/test/regress/sql/pg15.sql index b82b0d745..e29ceff28 100644 --- a/src/test/regress/sql/pg15.sql +++ b/src/test/regress/sql/pg15.sql @@ -179,11 +179,6 @@ CREATE TABLE tbl2 MERGE INTO tbl1 USING tbl2 ON (true) WHEN MATCHED THEN DELETE; --- add coordinator node as a worker -SET client_min_messages to ERROR; -SELECT 1 FROM master_add_node('localhost', :master_port, groupId => 0); -RESET client_min_messages; - -- one table is Citus local table, fails SELECT citus_add_local_table_to_metadata('tbl1'); @@ -254,8 +249,6 @@ SET client_min_messages to ERROR; DROP TABLE FKTABLE_local, PKTABLE_local; RESET client_min_messages; -SELECT 1 FROM citus_remove_node('localhost', :master_port); - SELECT create_distributed_table('tbl1', 'x'); SELECT create_distributed_table('tbl2', 'x'); @@ -810,6 +803,7 @@ CREATE TABLE set_on_default_test_referenced( ); SELECT create_reference_table('set_on_default_test_referenced'); +-- should error since col_3 defaults to a sequence CREATE TABLE set_on_default_test_referencing( col_1 int, col_2 int, col_3 serial, col_4 int, FOREIGN KEY(col_1, col_3) @@ -818,10 +812,6 @@ CREATE TABLE set_on_default_test_referencing( ON UPDATE SET DEFAULT ); --- should error since col_3 defaults to a sequence -SELECT create_reference_table('set_on_default_test_referencing'); - -DROP TABLE set_on_default_test_referencing; CREATE TABLE set_on_default_test_referencing( col_1 int, col_2 int, col_3 serial, col_4 int, FOREIGN KEY(col_1, col_3) @@ -921,7 +911,6 @@ SELECT * FROM foreign_table WHERE c1::text LIKE 'foo' LIMIT 1; -- ERROR; cast no RESET citus.use_citus_managed_tables; SELECT undistribute_table('foreign_table'); SELECT undistribute_table('foreign_table_test'); -SELECT 1 FROM citus_remove_node('localhost', :master_port); DROP SERVER foreign_server CASCADE; -- PG15 now supports specifying oid on CREATE DATABASE diff --git a/src/test/regress/sql/pgmerge.sql b/src/test/regress/sql/pgmerge.sql index 9b828f27e..86dc15040 100644 --- a/src/test/regress/sql/pgmerge.sql +++ b/src/test/regress/sql/pgmerge.sql @@ -19,8 +19,6 @@ SET citus.use_citus_managed_tables to true; SET citus.next_shard_id TO 4001000; -SELECT 1 FROM master_add_node('localhost', :master_port, groupid => 0); - CREATE USER regress_merge_privs; CREATE USER regress_merge_no_privs; DROP TABLE IF EXISTS target; @@ -1374,4 +1372,3 @@ REVOKE ALL ON SCHEMA pgmerge_schema FROM regress_merge_no_privs; DROP SCHEMA pgmerge_schema CASCADE; DROP USER regress_merge_privs; DROP USER regress_merge_no_privs; -SELECT 1 FROM master_remove_node('localhost', :master_port); diff --git a/src/test/regress/sql/propagate_foreign_servers.sql b/src/test/regress/sql/propagate_foreign_servers.sql index 32cba12ef..a9f93a702 100644 --- a/src/test/regress/sql/propagate_foreign_servers.sql +++ b/src/test/regress/sql/propagate_foreign_servers.sql @@ -29,8 +29,6 @@ CREATE FOREIGN TABLE foreign_table ( SERVER foreign_server_dependent_schema OPTIONS (schema_name 'test_dependent_schema', table_name 'foreign_table_test'); -SELECT 1 FROM citus_add_node('localhost', :master_port, groupId=>0); - -- verify that the aggregate is propagated to the new node SELECT run_command_on_workers($$select aggfnoid from pg_aggregate where aggfnoid::text like '%propagate_foreign_server.array_agg%';$$); diff --git a/src/test/regress/sql/publication.sql b/src/test/regress/sql/publication.sql index 3fd6128b8..488c0408c 100644 --- a/src/test/regress/sql/publication.sql +++ b/src/test/regress/sql/publication.sql @@ -3,9 +3,6 @@ CREATE SCHEMA "publication-1"; SET search_path TO publication; SET citus.shard_replication_factor TO 1; --- for citus_add_local_table_to_metadata / create_distributed_table_concurrently -SELECT citus_set_coordinator_host('localhost', :master_port); - CREATE OR REPLACE FUNCTION activate_node_snapshot() RETURNS text[] LANGUAGE C STRICT @@ -187,8 +184,6 @@ SELECT substring(:'server_version', '\d+')::int >= 15 AS server_version_ge_15 SET client_min_messages TO ERROR; DROP SCHEMA publication CASCADE; DROP SCHEMA "publication-1" CASCADE; - -SELECT citus_remove_node('localhost', :master_port); \q \endif @@ -265,5 +260,3 @@ DROP PUBLICATION pubpartitioned; SET client_min_messages TO ERROR; DROP SCHEMA publication CASCADE; DROP SCHEMA "publication-1" CASCADE; - -SELECT citus_remove_node('localhost', :master_port); diff --git a/src/test/regress/sql/query_single_shard_table.sql b/src/test/regress/sql/query_single_shard_table.sql index 0a05558af..f1a04c9e3 100644 --- a/src/test/regress/sql/query_single_shard_table.sql +++ b/src/test/regress/sql/query_single_shard_table.sql @@ -4,9 +4,6 @@ SET search_path TO query_single_shard_table; SET citus.next_shard_id TO 1620000; SET citus.shard_count TO 32; -SET client_min_messages TO WARNING; -SELECT 1 FROM citus_add_node('localhost', :master_port, groupid => 0); - SET client_min_messages TO NOTICE; CREATE TABLE nullkey_c1_t1(a int, b int); @@ -1175,5 +1172,3 @@ LIMIT 10; SET client_min_messages TO ERROR; DROP SCHEMA query_single_shard_table CASCADE; - -SELECT citus_remove_node('localhost', :master_port); diff --git a/src/test/regress/sql/recurring_outer_join.sql b/src/test/regress/sql/recurring_outer_join.sql index e26df4b86..595d734ec 100644 --- a/src/test/regress/sql/recurring_outer_join.sql +++ b/src/test/regress/sql/recurring_outer_join.sql @@ -4,12 +4,7 @@ SET search_path TO recurring_outer_join; SET citus.next_shard_id TO 1520000; SET citus.shard_count TO 32; --- idempotently add node to allow this test to run without add_coordinator -SET client_min_messages TO WARNING; -SELECT 1 FROM citus_add_node('localhost', :master_port, groupid => 0); - SET client_min_messages TO DEBUG1; - CREATE TABLE dist_1 (a int, b int); SELECT create_distributed_table('dist_1', 'a'); INSERT INTO dist_1 VALUES @@ -1026,5 +1021,3 @@ ROLLBACK; SET client_min_messages TO ERROR; DROP SCHEMA recurring_outer_join CASCADE; - -SELECT master_remove_node('localhost', :master_port); diff --git a/src/test/regress/sql/recursive_dml_with_different_planners_executors.sql b/src/test/regress/sql/recursive_dml_with_different_planners_executors.sql index 179fcb198..e5fb8a6c5 100644 --- a/src/test/regress/sql/recursive_dml_with_different_planners_executors.sql +++ b/src/test/regress/sql/recursive_dml_with_different_planners_executors.sql @@ -60,7 +60,6 @@ UPDATE distributed_table SET dept = foo.max_dept FROM (SELECT tenant_id FROM second_distributed_table WHERE dept IN (1, 2, 3, 4)) ) as foo WHERE foo.max_dept >= dept and tenant_id = '8'; - -RESET client_min_messages; +SET client_min_messages TO WARNING; DROP SCHEMA recursive_dml_with_different_planner_executors CASCADE; SET search_path TO public; diff --git a/src/test/regress/sql/recursive_view_local_table.sql b/src/test/regress/sql/recursive_view_local_table.sql index c33a95e99..e813dc455 100644 --- a/src/test/regress/sql/recursive_view_local_table.sql +++ b/src/test/regress/sql/recursive_view_local_table.sql @@ -51,5 +51,5 @@ SELECT ref_table.* FROM ref_table WHERE EXISTS (SELECT * FROM recursive_defined_ SELECT ref_table.* FROM ref_table WHERE EXISTS (SELECT * FROM recursive_defined_non_recursive_view l WHERE l.c = ref_table.a) AND false; SELECT ref_table.* FROM ref_table WHERE EXISTS (SELECT * FROM recursive_defined_non_recursive_view l WHERE l.c = ref_table.a AND false); - +SET client_min_messages TO WARNING; DROP SCHEMA postgres_local_table CASCADE; diff --git a/src/test/regress/sql/relation_access_tracking.sql b/src/test/regress/sql/relation_access_tracking.sql index 3a4581e59..f0f132e6b 100644 --- a/src/test/regress/sql/relation_access_tracking.sql +++ b/src/test/regress/sql/relation_access_tracking.sql @@ -583,5 +583,6 @@ BEGIN; SELECT * FROM relation_accesses WHERE table_name IN ('table_3') ORDER BY 1; COMMIT; +SET client_min_messages TO WARNING; SET search_path TO 'public'; DROP SCHEMA access_tracking CASCADE; diff --git a/src/test/regress/sql/remove_coordinator.sql b/src/test/regress/sql/remove_coordinator.sql index 2db26d4d7..b0df327d1 100644 --- a/src/test/regress/sql/remove_coordinator.sql +++ b/src/test/regress/sql/remove_coordinator.sql @@ -1,2 +1,5 @@ -- removing coordinator from pg_dist_node should update pg_dist_colocation SELECT master_remove_node('localhost', :master_port); + +-- restore coordinator for the rest of the tests +SELECT citus_set_coordinator_host('localhost', :master_port); diff --git a/src/test/regress/sql/remove_coordinator_from_metadata.sql b/src/test/regress/sql/remove_coordinator_from_metadata.sql new file mode 100644 index 000000000..8ec16cfaf --- /dev/null +++ b/src/test/regress/sql/remove_coordinator_from_metadata.sql @@ -0,0 +1 @@ +SELECT master_remove_node('localhost', :master_port); diff --git a/src/test/regress/sql/run_command_on_all_nodes.sql b/src/test/regress/sql/run_command_on_all_nodes.sql index 0004a74e7..9b7b083af 100644 --- a/src/test/regress/sql/run_command_on_all_nodes.sql +++ b/src/test/regress/sql/run_command_on_all_nodes.sql @@ -1,6 +1,8 @@ CREATE SCHEMA run_command_on_all_nodes; SET search_path TO run_command_on_all_nodes; +SELECT master_remove_node('localhost', :master_port); + -- check coordinator isn't in metadata SELECT count(*) != 0 AS "Coordinator is in Metadata" FROM pg_dist_node @@ -85,3 +87,6 @@ SELECT success, result FROM run_command_on_all_nodes($$select count(*) from run_ SELECT success, result FROM run_command_on_all_nodes($$create index on run_command_on_all_nodes.test (x)$$); DROP SCHEMA run_command_on_all_nodes CASCADE; + +SELECT citus_set_coordinator_host('localhost'); + diff --git a/src/test/regress/sql/sequential_modifications.sql b/src/test/regress/sql/sequential_modifications.sql index 79e0a1211..3d56a20ee 100644 --- a/src/test/regress/sql/sequential_modifications.sql +++ b/src/test/regress/sql/sequential_modifications.sql @@ -25,7 +25,7 @@ $$ DECLARE result bool; BEGIN - SELECT tx_count = worker_count FROM (SELECT count(*) as tx_count FROM pg_dist_transaction WHERE gid LIKE 'citus_%_' || pg_backend_pid() || '%_%') as s1, (SELECT count(*) as worker_count FROM pg_dist_node WHERE noderole = 'primary') as s2 INTO result; + SELECT tx_count = worker_count FROM (SELECT count(*) as tx_count FROM pg_dist_transaction WHERE gid LIKE 'citus_%_' || pg_backend_pid() || '%_%') as s1, (SELECT count(*) as worker_count FROM pg_dist_node WHERE noderole = 'primary' AND groupid <> 0 ) as s2 INTO result; RETURN result; END; $$ diff --git a/src/test/regress/sql/set_operation_and_local_tables.sql b/src/test/regress/sql/set_operation_and_local_tables.sql index cbc024729..ab044d019 100644 --- a/src/test/regress/sql/set_operation_and_local_tables.sql +++ b/src/test/regress/sql/set_operation_and_local_tables.sql @@ -97,6 +97,5 @@ SELECT * FROM ((SELECT x FROM test) UNION (SELECT x FROM (SELECT x FROM local_te -- repartition is recursively planned before the set operation (SELECT x FROM test) INTERSECT (SELECT t1.x FROM test as t1, test as t2 WHERE t1.x = t2.y LIMIT 2) INTERSECT (((SELECT x FROM local_test) UNION ALL (SELECT x FROM test)) INTERSECT (SELECT i FROM generate_series(0, 100) i)) ORDER BY 1 DESC; - -RESET client_min_messages; +SET client_min_messages TO WARNING; DROP SCHEMA recursive_set_local CASCADE; diff --git a/src/test/regress/sql/set_operations.sql b/src/test/regress/sql/set_operations.sql index 9f66f4dfb..633b5c0b5 100644 --- a/src/test/regress/sql/set_operations.sql +++ b/src/test/regress/sql/set_operations.sql @@ -200,5 +200,5 @@ SELECT * FROM set_view_recursive_second ORDER BY 1,2; SELECT * FROM (SELECT * FROM test UNION SELECT * FROM test_not_colocated) u ORDER BY 1,2; SELECT * FROM (SELECT * FROM test UNION ALL SELECT * FROM test_not_colocated) u ORDER BY 1,2; -RESET client_min_messages; +SET client_min_messages TO WARNING; DROP SCHEMA recursive_union CASCADE; diff --git a/src/test/regress/sql/subquery_and_cte.sql b/src/test/regress/sql/subquery_and_cte.sql index 47bfe7184..1644c5fcc 100644 --- a/src/test/regress/sql/subquery_and_cte.sql +++ b/src/test/regress/sql/subquery_and_cte.sql @@ -510,7 +510,6 @@ RAISE '(%/3) failed to execute one of the tasks', errors_received; END; $$; -SET client_min_messages TO DEFAULT; - +SET client_min_messages TO WARNING; DROP SCHEMA subquery_and_ctes CASCADE; SET search_path TO public; diff --git a/src/test/regress/sql/subquery_append.sql b/src/test/regress/sql/subquery_append.sql index 4210f61ef..ed456fc19 100644 --- a/src/test/regress/sql/subquery_append.sql +++ b/src/test/regress/sql/subquery_append.sql @@ -87,4 +87,5 @@ SELECT count(*) FROM append_table WHERE extra = 1; UPDATE append_table a sET extra = 1 FROM append_table b WHERE a.key = b.key; END; +SET client_min_messages TO WARNING; DROP SCHEMA subquery_append CASCADE; diff --git a/src/test/regress/sql/subquery_basics.sql b/src/test/regress/sql/subquery_basics.sql index cfc02521f..0acd584fe 100644 --- a/src/test/regress/sql/subquery_basics.sql +++ b/src/test/regress/sql/subquery_basics.sql @@ -391,6 +391,7 @@ WHERE -- sublinks in the targetlist are not supported SELECT (SELECT id FROM dist WHERE dist.id > d1.id GROUP BY id) FROM ref FULL JOIN dist d1 USING (id); +SET client_min_messages TO WARNING; DROP TABLE dist; DROP TABLE ref; DROP TABLE local; diff --git a/src/test/regress/sql/subquery_view.sql b/src/test/regress/sql/subquery_view.sql index 8f57ef5a3..23732d7e8 100644 --- a/src/test/regress/sql/subquery_view.sql +++ b/src/test/regress/sql/subquery_view.sql @@ -443,5 +443,6 @@ EXPLAIN (COSTS OFF) WITH cte AS ( ) SELECT * FROM reference_table JOIN cte USING (text_col); $Q$); +SET client_min_messages TO WARNING; DROP SCHEMA subquery_view CASCADE; SET search_path TO public; diff --git a/src/test/regress/sql/union_pushdown.sql b/src/test/regress/sql/union_pushdown.sql index 884d93600..57099f060 100644 --- a/src/test/regress/sql/union_pushdown.sql +++ b/src/test/regress/sql/union_pushdown.sql @@ -1109,6 +1109,5 @@ SELECT k, COUNT(*) FROM v GROUP BY k ORDER BY k; $$); - -RESET client_min_messages; +SET client_min_messages TO WARNING; DROP SCHEMA union_pushdown CASCADE; diff --git a/src/test/regress/sql/values.sql b/src/test/regress/sql/values.sql index 4a5bb8352..6ff2282dc 100644 --- a/src/test/regress/sql/values.sql +++ b/src/test/regress/sql/values.sql @@ -393,5 +393,5 @@ BEGIN; COMMIT; -RESET client_min_messages; +SET client_min_messages TO WARNING; DROP SCHEMA values_subquery CASCADE; diff --git a/src/test/regress/sql/with_dml.sql b/src/test/regress/sql/with_dml.sql index 8602a961b..40f9fe3ed 100644 --- a/src/test/regress/sql/with_dml.sql +++ b/src/test/regress/sql/with_dml.sql @@ -157,5 +157,5 @@ WITH ids_to_delete AS ( ) DELETE FROM reference_table WHERE id = ANY(SELECT id FROM ids_to_delete); -RESET client_min_messages; +SET client_min_messages TO WARNING; DROP SCHEMA with_dml CASCADE; diff --git a/src/test/regress/sql/with_executors.sql b/src/test/regress/sql/with_executors.sql index dec5fcd9b..897c79cdc 100644 --- a/src/test/regress/sql/with_executors.sql +++ b/src/test/regress/sql/with_executors.sql @@ -334,4 +334,5 @@ FROM WHERE users_table.user_id = cte_merge.u_id; +SET client_min_messages TO WARNING; DROP SCHEMA with_executors CASCADE; From 48f068d08e77f69e1acc1d04d3247969db927678 Mon Sep 17 00:00:00 2001 From: Naisila Puka <37271756+naisila@users.noreply.github.com> Date: Mon, 5 Jun 2023 13:25:21 +0300 Subject: [PATCH 069/118] Remove AssertArg and AssertState (#6970) PG16 removed them. They were already identical to Assert. We can merge this directly to main branch Relevant PG commit: https://github.com/postgres/postgres/commit/b1099eca8f38ff5cfaf0901bb91cb6a22f909bc6 b1099eca8f38ff5cfaf0901bb91cb6a22f909bc6 Co-authored-by: onderkalaci --- src/backend/distributed/commands/collation.c | 2 +- src/backend/distributed/commands/sequence.c | 2 +- src/backend/distributed/commands/utility_hook.c | 4 ++-- src/backend/distributed/commands/variableset.c | 2 +- src/backend/distributed/connection/connection_configuration.c | 2 +- src/backend/distributed/planner/fast_path_router_planner.c | 2 +- src/backend/distributed/planner/insert_select_planner.c | 2 +- src/backend/distributed/planner/multi_logical_optimizer.c | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/backend/distributed/commands/collation.c b/src/backend/distributed/commands/collation.c index 879dbeeba..eb4dd9654 100644 --- a/src/backend/distributed/commands/collation.c +++ b/src/backend/distributed/commands/collation.c @@ -109,7 +109,7 @@ CreateCollationDDLInternal(Oid collationId, Oid *collowner, char **quotedCollati colliculocale = NULL; } - AssertArg((collcollate && collctype) || colliculocale); + Assert((collcollate && collctype) || colliculocale); #else /* diff --git a/src/backend/distributed/commands/sequence.c b/src/backend/distributed/commands/sequence.c index f1757bb62..9289dcd58 100644 --- a/src/backend/distributed/commands/sequence.c +++ b/src/backend/distributed/commands/sequence.c @@ -222,7 +222,7 @@ ExtractDefaultColumnsAndOwnedSequences(Oid relationId, List **columnNameList, bool ColumnDefaultsToNextVal(Oid relationId, AttrNumber attrNumber) { - AssertArg(AttributeNumberIsValid(attrNumber)); + Assert(AttributeNumberIsValid(attrNumber)); Relation relation = RelationIdGetRelation(relationId); Node *defExpr = build_column_default(relation, attrNumber); diff --git a/src/backend/distributed/commands/utility_hook.c b/src/backend/distributed/commands/utility_hook.c index 212091569..6393fdd71 100644 --- a/src/backend/distributed/commands/utility_hook.c +++ b/src/backend/distributed/commands/utility_hook.c @@ -501,8 +501,8 @@ ProcessUtilityInternal(PlannedStmt *pstmt, VariableSetStmt *setStmt = (VariableSetStmt *) parsetree; /* at present, we only implement the NONE and LOCAL behaviors */ - AssertState(PropagateSetCommands == PROPSETCMD_NONE || - PropagateSetCommands == PROPSETCMD_LOCAL); + Assert(PropagateSetCommands == PROPSETCMD_NONE || + PropagateSetCommands == PROPSETCMD_LOCAL); if (IsMultiStatementTransaction() && ShouldPropagateSetCommand(setStmt)) { diff --git a/src/backend/distributed/commands/variableset.c b/src/backend/distributed/commands/variableset.c index a086274e6..277f5b63f 100644 --- a/src/backend/distributed/commands/variableset.c +++ b/src/backend/distributed/commands/variableset.c @@ -89,7 +89,7 @@ PostprocessVariableSetStmt(VariableSetStmt *setStmt, const char *setStmtString) List *connectionList = NIL; /* at present we only support SET LOCAL and SET TRANSACTION */ - AssertArg(ShouldPropagateSetCommand(setStmt)); + Assert(ShouldPropagateSetCommand(setStmt)); /* haven't seen any SET stmts so far in this (sub-)xact: initialize StringInfo */ if (activeSetStmts == NULL) diff --git a/src/backend/distributed/connection/connection_configuration.c b/src/backend/distributed/connection/connection_configuration.c index 63dfebf8b..ff63c717c 100644 --- a/src/backend/distributed/connection/connection_configuration.c +++ b/src/backend/distributed/connection/connection_configuration.c @@ -183,7 +183,7 @@ CheckConninfo(const char *conninfo, const char **allowedConninfoKeywords, const char *prev = allowedConninfoKeywords[keywordIdx - 1]; const char *curr = allowedConninfoKeywords[keywordIdx]; - AssertArg(strcmp(prev, curr) < 0); + Assert(strcmp(prev, curr) < 0); } #endif diff --git a/src/backend/distributed/planner/fast_path_router_planner.c b/src/backend/distributed/planner/fast_path_router_planner.c index 2be4a5626..7b97d3ff5 100644 --- a/src/backend/distributed/planner/fast_path_router_planner.c +++ b/src/backend/distributed/planner/fast_path_router_planner.c @@ -108,7 +108,7 @@ GeneratePlaceHolderPlannedStmt(Query *parse) Node *distKey PG_USED_FOR_ASSERTS_ONLY = NULL; - AssertArg(FastPathRouterQuery(parse, &distKey)); + Assert(FastPathRouterQuery(parse, &distKey)); /* there is only a single relation rte */ scanNode->scanrelid = 1; diff --git a/src/backend/distributed/planner/insert_select_planner.c b/src/backend/distributed/planner/insert_select_planner.c index 8a58b0f13..c25fb393d 100644 --- a/src/backend/distributed/planner/insert_select_planner.c +++ b/src/backend/distributed/planner/insert_select_planner.c @@ -622,7 +622,7 @@ DistributedInsertSelectSupported(Query *queryTree, RangeTblEntry *insertRte, ListCell *rangeTableCell = NULL; /* we only do this check for INSERT ... SELECT queries */ - AssertArg(InsertSelectIntoCitusTable(queryTree)); + Assert(InsertSelectIntoCitusTable(queryTree)); Query *subquery = subqueryRte->subquery; diff --git a/src/backend/distributed/planner/multi_logical_optimizer.c b/src/backend/distributed/planner/multi_logical_optimizer.c index 851afc4b6..e0548049f 100644 --- a/src/backend/distributed/planner/multi_logical_optimizer.c +++ b/src/backend/distributed/planner/multi_logical_optimizer.c @@ -2985,7 +2985,7 @@ AppendTargetEntryToGroupClause(TargetEntry *targetEntry, Expr *targetExpr PG_USED_FOR_ASSERTS_ONLY = targetEntry->expr; /* we currently only support appending Var target entries */ - AssertArg(IsA(targetExpr, Var)); + Assert(IsA(targetExpr, Var)); Var *targetColumn = (Var *) targetEntry->expr; SortGroupClause *groupByClause = CreateSortGroupClause(targetColumn); From 1c9e3fabc2485b3c1f541364a0aa7ef4e3b5725f Mon Sep 17 00:00:00 2001 From: Naisila Puka <37271756+naisila@users.noreply.github.com> Date: Mon, 5 Jun 2023 14:03:39 +0300 Subject: [PATCH 070/118] Bump PGversions for CI tests (#6969) Postgres got minor updates in May, this starts using the images with the latest version for our tests. These new Postgres versions didn't cause any compilation issues or test failures. Depends on https://github.com/citusdata/the-process/pull/136 --- .circleci/config.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1a73a4b96..d99398790 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,19 +6,19 @@ orbs: parameters: image_suffix: type: string - default: '-vabeb997' + default: '-vf5dc39a' pg13_version: type: string - default: '13.10' + default: '13.11' pg14_version: type: string - default: '14.7' + default: '14.8' pg15_version: type: string - default: '15.2' + default: '15.3' upgrade_pg_versions: type: string - default: '13.10-14.7-15.2' + default: '13.11-14.8-15.3' style_checker_tools_version: type: string default: '0.8.18' From f6a516dab5cbaeb90dbea42d2766676fc7d59fcd Mon Sep 17 00:00:00 2001 From: Teja Mupparti Date: Fri, 2 Jun 2023 09:53:40 -0700 Subject: [PATCH 071/118] Refactor repartitioning code into generic format --- .../executor/insert_select_executor.c | 72 ------------------ .../executor/repartition_executor.c | 76 +++++++++++++++++++ .../planner/insert_select_planner.c | 1 + src/backend/distributed/shared_library_init.c | 2 +- .../distributed/insert_select_executor.h | 3 - .../distributed/repartition_executor.h | 4 + 6 files changed, 82 insertions(+), 76 deletions(-) diff --git a/src/backend/distributed/executor/insert_select_executor.c b/src/backend/distributed/executor/insert_select_executor.c index 71e66567a..58312ba19 100644 --- a/src/backend/distributed/executor/insert_select_executor.c +++ b/src/backend/distributed/executor/insert_select_executor.c @@ -419,25 +419,6 @@ PartitionColumnIndexFromColumnList(Oid relationId, List *columnNameList) } -/* - * IsSupportedRedistributionTarget determines whether re-partitioning into the - * given target relation is supported. - */ -bool -IsSupportedRedistributionTarget(Oid targetRelationId) -{ - CitusTableCacheEntry *tableEntry = GetCitusTableCacheEntry(targetRelationId); - - if (!IsCitusTableTypeCacheEntry(tableEntry, HASH_DISTRIBUTED) && - !IsCitusTableTypeCacheEntry(tableEntry, RANGE_DISTRIBUTED)) - { - return false; - } - - return true; -} - - /* * DistributionColumnIndex finds the index of given distribution column in the * given target list. @@ -461,59 +442,6 @@ DistributionColumnIndex(List *insertTargetList, Var *partitionColumn) } -/* - * IsRedistributablePlan returns true if the given plan is a redistrituable plan. - */ -bool -IsRedistributablePlan(Plan *selectPlan) -{ - if (!EnableRepartitionedInsertSelect) - { - return false; - } - - /* don't redistribute if query is not distributed or requires merge on coordinator */ - if (!IsCitusCustomScan(selectPlan)) - { - return false; - } - - DistributedPlan *distSelectPlan = - GetDistributedPlan((CustomScan *) selectPlan); - Job *distSelectJob = distSelectPlan->workerJob; - List *distSelectTaskList = distSelectJob->taskList; - - /* - * Don't use redistribution if only one task. This is to keep the existing - * behaviour for CTEs that the last step is a read_intermediate_result() - * call. It doesn't hurt much in other cases too. - */ - if (list_length(distSelectTaskList) <= 1) - { - return false; - } - - /* don't use redistribution for repartition joins for now */ - if (distSelectJob->dependentJobList != NIL) - { - return false; - } - - if (distSelectPlan->combineQuery != NULL) - { - Query *combineQuery = (Query *) distSelectPlan->combineQuery; - - if (contain_nextval_expression_walker((Node *) combineQuery->targetList, NULL)) - { - /* nextval needs to be evaluated on the coordinator */ - return false; - } - } - - return true; -} - - /* * WrapTaskListForProjection wraps task query string to only select given * projected columns. It modifies the taskList. diff --git a/src/backend/distributed/executor/repartition_executor.c b/src/backend/distributed/executor/repartition_executor.c index 2d70a1356..b35527b99 100644 --- a/src/backend/distributed/executor/repartition_executor.c +++ b/src/backend/distributed/executor/repartition_executor.c @@ -15,6 +15,7 @@ #include "nodes/makefuncs.h" #include "nodes/parsenodes.h" +#include "distributed/citus_custom_scan.h" #include "distributed/intermediate_results.h" #include "distributed/listutils.h" #include "distributed/multi_physical_planner.h" @@ -24,6 +25,81 @@ #include "distributed/resource_lock.h" +/* + * IsSupportedRedistributionTarget determines whether re-partitioning into the + * given target relation is supported. + */ +bool +IsSupportedRedistributionTarget(Oid targetRelationId) +{ + CitusTableCacheEntry *tableEntry = GetCitusTableCacheEntry(targetRelationId); + + if (!IsCitusTableTypeCacheEntry(tableEntry, HASH_DISTRIBUTED) && + !IsCitusTableTypeCacheEntry(tableEntry, RANGE_DISTRIBUTED)) + { + return false; + } + + return true; +} + + +/* + * IsRedistributablePlan returns true if the given plan is a distributable plan. + */ +bool +IsRedistributablePlan(Plan *selectPlan) +{ + if (!EnableRepartitionedInsertSelect) + { + return false; + } + + /* + * Don't redistribute if query is not distributed or requires + * merge on coordinator + */ + if (!IsCitusCustomScan(selectPlan)) + { + return false; + } + + DistributedPlan *distSelectPlan = + GetDistributedPlan((CustomScan *) selectPlan); + Job *distSelectJob = distSelectPlan->workerJob; + List *distSelectTaskList = distSelectJob->taskList; + + /* + * Don't use redistribution if only one task. This is to keep the existing + * behaviour for CTEs that the last step is a read_intermediate_result() + * call. It doesn't hurt much in other cases too. + */ + if (list_length(distSelectTaskList) <= 1) + { + return false; + } + + /* don't use redistribution for repartition joins for now */ + if (distSelectJob->dependentJobList != NIL) + { + return false; + } + + if (distSelectPlan->combineQuery != NULL) + { + Query *combineQuery = (Query *) distSelectPlan->combineQuery; + + if (contain_nextval_expression_walker((Node *) combineQuery->targetList, NULL)) + { + /* nextval needs to be evaluated on the coordinator */ + return false; + } + } + + return true; +} + + /* * GenerateTaskListWithColocatedIntermediateResults generates a list of tasks * for a query that inserts into a target relation and selects from a set of diff --git a/src/backend/distributed/planner/insert_select_planner.c b/src/backend/distributed/planner/insert_select_planner.c index c25fb393d..4f24d396c 100644 --- a/src/backend/distributed/planner/insert_select_planner.c +++ b/src/backend/distributed/planner/insert_select_planner.c @@ -31,6 +31,7 @@ #include "distributed/pg_dist_partition.h" #include "distributed/query_pushdown_planning.h" #include "distributed/recursive_planning.h" +#include "distributed/repartition_executor.h" #include "distributed/resource_lock.h" #include "distributed/version_compat.h" #include "nodes/makefuncs.h" diff --git a/src/backend/distributed/shared_library_init.c b/src/backend/distributed/shared_library_init.c index 24cd7acd6..8f6485c25 100644 --- a/src/backend/distributed/shared_library_init.c +++ b/src/backend/distributed/shared_library_init.c @@ -44,7 +44,7 @@ #include "distributed/cte_inline.h" #include "distributed/distributed_deadlock_detection.h" #include "distributed/errormessage.h" -#include "distributed/insert_select_executor.h" +#include "distributed/repartition_executor.h" #include "distributed/intermediate_result_pruning.h" #include "distributed/local_multi_copy.h" #include "distributed/local_executor.h" diff --git a/src/include/distributed/insert_select_executor.h b/src/include/distributed/insert_select_executor.h index 6e84b80f2..1b08f5a94 100644 --- a/src/include/distributed/insert_select_executor.h +++ b/src/include/distributed/insert_select_executor.h @@ -16,11 +16,8 @@ #include "executor/execdesc.h" -extern bool EnableRepartitionedInsertSelect; extern TupleTableSlot * NonPushableInsertSelectExecScan(CustomScanState *node); -extern bool IsSupportedRedistributionTarget(Oid targetRelationId); -extern bool IsRedistributablePlan(Plan *selectPlan); #endif /* INSERT_SELECT_EXECUTOR_H */ diff --git a/src/include/distributed/repartition_executor.h b/src/include/distributed/repartition_executor.h index fea6d6525..98173b828 100644 --- a/src/include/distributed/repartition_executor.h +++ b/src/include/distributed/repartition_executor.h @@ -13,6 +13,8 @@ #ifndef REPARTITION_EXECUTOR_H #define REPARTITION_EXECUTOR_H +extern bool EnableRepartitionedInsertSelect; + extern int DistributionColumnIndex(List *insertTargetList, Var *partitionColumn); extern List * GenerateTaskListWithColocatedIntermediateResults(Oid targetRelationId, Query * @@ -24,5 +26,7 @@ extern List * GenerateTaskListWithRedistributedResults( targetRelation, List **redistributedResults, bool useBinaryFormat); +extern bool IsSupportedRedistributionTarget(Oid targetRelationId); +extern bool IsRedistributablePlan(Plan *selectPlan); #endif /* REPARTITION_EXECUTOR_H */ From c2f117c5597af5ba024fd18384f31ae637910e4d Mon Sep 17 00:00:00 2001 From: Naisila Puka <37271756+naisila@users.noreply.github.com> Date: Tue, 6 Jun 2023 14:17:51 +0300 Subject: [PATCH 072/118] Citus Revise tree-walk APIs to include context (#6975) Without revising there are Warnings in PG16 build Relevant PG commit https://github.com/postgres/postgres/commit/1c27d16e6e5c1f463bbe1e9ece88dda811235165 1c27d16e6e5c1f463bbe1e9ece88dda811235165 --- src/backend/distributed/planner/cte_inline.c | 6 +++--- .../distributed/planner/multi_physical_planner.c | 12 ++++++------ .../distributed/planner/multi_router_planner.c | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/backend/distributed/planner/cte_inline.c b/src/backend/distributed/planner/cte_inline.c index 2356ebf48..ce258916d 100644 --- a/src/backend/distributed/planner/cte_inline.c +++ b/src/backend/distributed/planner/cte_inline.c @@ -43,7 +43,7 @@ static bool contain_dml_walker(Node *node, void *context); /* the following utility functions are related to Citus' logic */ static bool RecursivelyInlineCteWalker(Node *node, void *context); static void InlineCTEsInQueryTree(Query *query); -static bool QueryTreeContainsInlinableCteWalker(Node *node); +static bool QueryTreeContainsInlinableCteWalker(Node *node, void *context); /* @@ -135,7 +135,7 @@ InlineCTEsInQueryTree(Query *query) bool QueryTreeContainsInlinableCTE(Query *queryTree) { - return QueryTreeContainsInlinableCteWalker((Node *) queryTree); + return QueryTreeContainsInlinableCteWalker((Node *) queryTree, NULL); } @@ -144,7 +144,7 @@ QueryTreeContainsInlinableCTE(Query *queryTree) * the (sub)queries in the node contains at least one CTE. */ static bool -QueryTreeContainsInlinableCteWalker(Node *node) +QueryTreeContainsInlinableCteWalker(Node *node, void *context) { if (node == NULL) { diff --git a/src/backend/distributed/planner/multi_physical_planner.c b/src/backend/distributed/planner/multi_physical_planner.c index 24fbd9935..8ca51a0a4 100644 --- a/src/backend/distributed/planner/multi_physical_planner.c +++ b/src/backend/distributed/planner/multi_physical_planner.c @@ -234,8 +234,8 @@ static List * FetchEqualityAttrNumsForRTEBoolExpr(BoolExpr *boolExpr); static List * FetchEqualityAttrNumsForList(List *nodeList); static int PartitionColumnIndex(Var *targetVar, List *targetList); static List * GetColumnOriginalIndexes(Oid relationId); -static bool QueryTreeHasImproperForDeparseNodes(Node *inputNode); -static Node * AdjustImproperForDeparseNodes(Node *inputNode); +static bool QueryTreeHasImproperForDeparseNodes(Node *inputNode, void *context); +static Node * AdjustImproperForDeparseNodes(Node *inputNode, void *context); static bool IsImproperForDeparseRelabelTypeNode(Node *inputNode); static bool IsImproperForDeparseCoerceViaIONode(Node *inputNode); static CollateExpr * RelabelTypeToCollateExpr(RelabelType *relabelType); @@ -2698,9 +2698,9 @@ SqlTaskList(Job *job) * the query sublinks, and we don't want to do that unless necessary, as it * would be inefficient. */ - if (QueryTreeHasImproperForDeparseNodes((Node *) jobQuery)) + if (QueryTreeHasImproperForDeparseNodes((Node *) jobQuery, NULL)) { - jobQuery = (Query *) AdjustImproperForDeparseNodes((Node *) jobQuery); + jobQuery = (Query *) AdjustImproperForDeparseNodes((Node *) jobQuery, NULL); } ListCell *fragmentCombinationCell = NULL; @@ -5621,7 +5621,7 @@ TaskListHighestTaskId(List *taskList) * CoerceViaIONodes which are improper for deparse */ static bool -QueryTreeHasImproperForDeparseNodes(Node *inputNode) +QueryTreeHasImproperForDeparseNodes(Node *inputNode, void *context) { if (inputNode == NULL) { @@ -5653,7 +5653,7 @@ QueryTreeHasImproperForDeparseNodes(Node *inputNode) * Details will be written in comments in the corresponding if conditions. */ static Node * -AdjustImproperForDeparseNodes(Node *inputNode) +AdjustImproperForDeparseNodes(Node *inputNode, void *context) { if (inputNode == NULL) { diff --git a/src/backend/distributed/planner/multi_router_planner.c b/src/backend/distributed/planner/multi_router_planner.c index 47769f5bb..cc9d9732c 100644 --- a/src/backend/distributed/planner/multi_router_planner.c +++ b/src/backend/distributed/planner/multi_router_planner.c @@ -154,7 +154,7 @@ static DeferredErrorMessage * DeferErrorIfUnsupportedRouterPlannableSelectQuery( static DeferredErrorMessage * ErrorIfQueryHasUnroutableModifyingCTE(Query *queryTree); #if PG_VERSION_NUM >= PG_VERSION_14 static DeferredErrorMessage * ErrorIfQueryHasCTEWithSearchClause(Query *queryTree); -static bool ContainsSearchClauseWalker(Node *node); +static bool ContainsSearchClauseWalker(Node *node, void *context); #endif static bool SelectsFromDistributedTable(List *rangeTableList, Query *query); static ShardPlacement * CreateDummyPlacement(bool hasLocalRelation); @@ -3929,7 +3929,7 @@ ErrorIfQueryHasUnroutableModifyingCTE(Query *queryTree) static DeferredErrorMessage * ErrorIfQueryHasCTEWithSearchClause(Query *queryTree) { - if (ContainsSearchClauseWalker((Node *) queryTree)) + if (ContainsSearchClauseWalker((Node *) queryTree, NULL)) { return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, "CTEs with search clauses are not supported", @@ -3944,7 +3944,7 @@ ErrorIfQueryHasCTEWithSearchClause(Query *queryTree) * CommonTableExprs with search clause */ static bool -ContainsSearchClauseWalker(Node *node) +ContainsSearchClauseWalker(Node *node, void *context) { if (node == NULL) { From 7e486345f1eb04805e66206bfdfa02912e918282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20Ozan=20Akg=C3=BCl?= Date: Tue, 6 Jun 2023 16:20:11 +0300 Subject: [PATCH 073/118] Fix citus_table_type column in citus_tables and citus_shards views for single shard tables (#6971) `citus_table_type` column of `citus_tables` and `citus_shards` will show "schema" for tenants schema tables and "distributed" for single shard tables that are not in a tenant schema. --- .../distributed/sql/citus--11.3-1--12.0-1.sql | 3 ++ .../sql/downgrades/citus--12.0-1--11.3-1.sql | 3 ++ .../sql/udfs/citus_shards/12.0-1.sql | 49 ++++++++++++++++++ .../sql/udfs/citus_shards/latest.sql | 6 ++- .../sql/udfs/citus_tables/12.0-1.sql | 51 +++++++++++++++++++ .../sql/udfs/citus_tables/latest.sql | 7 ++- .../expected/schema_based_sharding.out | 28 +++++++++- .../regress/sql/schema_based_sharding.sql | 15 +++++- 8 files changed, 157 insertions(+), 5 deletions(-) create mode 100644 src/backend/distributed/sql/udfs/citus_shards/12.0-1.sql create mode 100644 src/backend/distributed/sql/udfs/citus_tables/12.0-1.sql diff --git a/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql b/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql index 9f96b01cc..d241fd235 100644 --- a/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql +++ b/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql @@ -23,3 +23,6 @@ GRANT SELECT ON pg_catalog.pg_dist_tenant_schema TO public; -- udfs used to modify pg_dist_tenant_schema globally via drop trigger #include "udfs/citus_internal_unregister_tenant_schema_globally/12.0-1.sql" #include "udfs/citus_drop_trigger/12.0-1.sql" + +#include "udfs/citus_tables/12.0-1.sql" +#include "udfs/citus_shards/12.0-1.sql" diff --git a/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql b/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql index 42eab88d2..1cddc488c 100644 --- a/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql +++ b/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql @@ -40,4 +40,7 @@ DROP FUNCTION pg_catalog.citus_internal_unregister_tenant_schema_globally(Oid, t #include "../udfs/citus_drop_trigger/10.2-1.sql" +#include "../udfs/citus_tables/11.1-1.sql" +#include "../udfs/citus_shards/11.1-1.sql" + DROP TABLE pg_catalog.pg_dist_tenant_schema; diff --git a/src/backend/distributed/sql/udfs/citus_shards/12.0-1.sql b/src/backend/distributed/sql/udfs/citus_shards/12.0-1.sql new file mode 100644 index 000000000..8215ab64a --- /dev/null +++ b/src/backend/distributed/sql/udfs/citus_shards/12.0-1.sql @@ -0,0 +1,49 @@ +CREATE OR REPLACE VIEW pg_catalog.citus_shards AS +SELECT + pg_dist_shard.logicalrelid AS table_name, + pg_dist_shard.shardid, + shard_name(pg_dist_shard.logicalrelid, pg_dist_shard.shardid) as shard_name, + CASE WHEN colocationid IN (SELECT colocationid FROM pg_dist_tenant_schema) THEN 'schema' + WHEN partkey IS NOT NULL THEN 'distributed' + WHEN repmodel = 't' THEN 'reference' + WHEN colocationid = 0 THEN 'local' + ELSE 'distributed' END AS citus_table_type, + colocationid AS colocation_id, + pg_dist_node.nodename, + pg_dist_node.nodeport, + size as shard_size +FROM + pg_dist_shard +JOIN + pg_dist_placement +ON + pg_dist_shard.shardid = pg_dist_placement.shardid +JOIN + pg_dist_node +ON + pg_dist_placement.groupid = pg_dist_node.groupid +JOIN + pg_dist_partition +ON + pg_dist_partition.logicalrelid = pg_dist_shard.logicalrelid +LEFT JOIN + (SELECT (regexp_matches(table_name,'_(\d+)$'))[1]::int as shard_id, max(size) as size from citus_shard_sizes() GROUP BY shard_id) as shard_sizes +ON + pg_dist_shard.shardid = shard_sizes.shard_id +WHERE + pg_dist_placement.shardstate = 1 +AND + -- filter out tables owned by extensions + pg_dist_partition.logicalrelid NOT IN ( + SELECT + objid + FROM + pg_depend + WHERE + classid = 'pg_class'::regclass AND refclassid = 'pg_extension'::regclass AND deptype = 'e' + ) +ORDER BY + pg_dist_shard.logicalrelid::text, shardid +; + +GRANT SELECT ON pg_catalog.citus_shards TO public; diff --git a/src/backend/distributed/sql/udfs/citus_shards/latest.sql b/src/backend/distributed/sql/udfs/citus_shards/latest.sql index 08e039899..8215ab64a 100644 --- a/src/backend/distributed/sql/udfs/citus_shards/latest.sql +++ b/src/backend/distributed/sql/udfs/citus_shards/latest.sql @@ -3,7 +3,11 @@ SELECT pg_dist_shard.logicalrelid AS table_name, pg_dist_shard.shardid, shard_name(pg_dist_shard.logicalrelid, pg_dist_shard.shardid) as shard_name, - CASE WHEN partkey IS NOT NULL THEN 'distributed' WHEN repmodel = 't' THEN 'reference' ELSE 'local' END AS citus_table_type, + CASE WHEN colocationid IN (SELECT colocationid FROM pg_dist_tenant_schema) THEN 'schema' + WHEN partkey IS NOT NULL THEN 'distributed' + WHEN repmodel = 't' THEN 'reference' + WHEN colocationid = 0 THEN 'local' + ELSE 'distributed' END AS citus_table_type, colocationid AS colocation_id, pg_dist_node.nodename, pg_dist_node.nodeport, diff --git a/src/backend/distributed/sql/udfs/citus_tables/12.0-1.sql b/src/backend/distributed/sql/udfs/citus_tables/12.0-1.sql new file mode 100644 index 000000000..565b263f7 --- /dev/null +++ b/src/backend/distributed/sql/udfs/citus_tables/12.0-1.sql @@ -0,0 +1,51 @@ +DO $$ +declare +citus_tables_create_query text; +BEGIN +citus_tables_create_query=$CTCQ$ + CREATE OR REPLACE VIEW %I.citus_tables AS + SELECT + logicalrelid AS table_name, + CASE WHEN colocationid IN (SELECT colocationid FROM pg_dist_tenant_schema) THEN 'schema' + WHEN partkey IS NOT NULL THEN 'distributed' + WHEN repmodel = 't' THEN 'reference' + WHEN colocationid = 0 THEN 'local' + ELSE 'distributed' + END AS citus_table_type, + coalesce(column_to_column_name(logicalrelid, partkey), '') AS distribution_column, + colocationid AS colocation_id, + pg_size_pretty(citus_total_relation_size(logicalrelid, fail_on_error := false)) AS table_size, + (select count(*) from pg_dist_shard where logicalrelid = p.logicalrelid) AS shard_count, + pg_get_userbyid(relowner) AS table_owner, + amname AS access_method + FROM + pg_dist_partition p + JOIN + pg_class c ON (p.logicalrelid = c.oid) + LEFT JOIN + pg_am a ON (a.oid = c.relam) + WHERE + -- filter out tables owned by extensions + logicalrelid NOT IN ( + SELECT + objid + FROM + pg_depend + WHERE + classid = 'pg_class'::regclass AND refclassid = 'pg_extension'::regclass AND deptype = 'e' + ) + ORDER BY + logicalrelid::text; +$CTCQ$; + +IF EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'public') THEN + EXECUTE format(citus_tables_create_query, 'public'); + GRANT SELECT ON public.citus_tables TO public; +ELSE + EXECUTE format(citus_tables_create_query, 'citus'); + ALTER VIEW citus.citus_tables SET SCHEMA pg_catalog; + GRANT SELECT ON pg_catalog.citus_tables TO public; +END IF; + +END; +$$; diff --git a/src/backend/distributed/sql/udfs/citus_tables/latest.sql b/src/backend/distributed/sql/udfs/citus_tables/latest.sql index ee66852a4..565b263f7 100644 --- a/src/backend/distributed/sql/udfs/citus_tables/latest.sql +++ b/src/backend/distributed/sql/udfs/citus_tables/latest.sql @@ -6,8 +6,11 @@ citus_tables_create_query=$CTCQ$ CREATE OR REPLACE VIEW %I.citus_tables AS SELECT logicalrelid AS table_name, - CASE WHEN partkey IS NOT NULL THEN 'distributed' ELSE - CASE when repmodel = 't' THEN 'reference' ELSE 'local' END + CASE WHEN colocationid IN (SELECT colocationid FROM pg_dist_tenant_schema) THEN 'schema' + WHEN partkey IS NOT NULL THEN 'distributed' + WHEN repmodel = 't' THEN 'reference' + WHEN colocationid = 0 THEN 'local' + ELSE 'distributed' END AS citus_table_type, coalesce(column_to_column_name(logicalrelid, partkey), '') AS distribution_column, colocationid AS colocation_id, diff --git a/src/test/regress/expected/schema_based_sharding.out b/src/test/regress/expected/schema_based_sharding.out index e55b1cd2b..7c17e5bb8 100644 --- a/src/test/regress/expected/schema_based_sharding.out +++ b/src/test/regress/expected/schema_based_sharding.out @@ -1379,8 +1379,34 @@ SELECT pg_reload_conf(); (1 row) \c - - - :master_port +SET search_path TO regular_schema; +CREATE TABLE type_sing(a INT); +SELECT create_distributed_table('type_sing', NULL, colocate_with:='none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SET citus.enable_schema_based_sharding TO ON; +CREATE SCHEMA type_sch; +CREATE TABLE type_sch.tbl (a INT); +SELECT table_name, citus_table_type FROM public.citus_tables WHERE table_name::text LIKE 'type_%'; + table_name | citus_table_type +--------------------------------------------------------------------- + type_sch.tbl | schema + type_sing | distributed +(2 rows) + +SELECT table_name, citus_table_type FROM citus_shards WHERE table_name::text LIKE 'type_%' AND nodeport IN (:worker_1_port, :worker_2_port); + table_name | citus_table_type +--------------------------------------------------------------------- + type_sch.tbl | schema + type_sing | distributed +(2 rows) + +RESET citus.enable_schema_based_sharding; SET client_min_messages TO WARNING; -DROP SCHEMA regular_schema, tenant_3, tenant_5, tenant_7, tenant_6 CASCADE; +DROP SCHEMA regular_schema, tenant_3, tenant_5, tenant_7, tenant_6, type_sch CASCADE; SELECT citus_remove_node('localhost', :master_port); citus_remove_node --------------------------------------------------------------------- diff --git a/src/test/regress/sql/schema_based_sharding.sql b/src/test/regress/sql/schema_based_sharding.sql index f24628731..27741473b 100644 --- a/src/test/regress/sql/schema_based_sharding.sql +++ b/src/test/regress/sql/schema_based_sharding.sql @@ -945,8 +945,21 @@ ALTER SYSTEM RESET citus.enable_schema_based_sharding; SELECT pg_reload_conf(); \c - - - :master_port +SET search_path TO regular_schema; + +CREATE TABLE type_sing(a INT); +SELECT create_distributed_table('type_sing', NULL, colocate_with:='none'); + +SET citus.enable_schema_based_sharding TO ON; +CREATE SCHEMA type_sch; +CREATE TABLE type_sch.tbl (a INT); + +SELECT table_name, citus_table_type FROM public.citus_tables WHERE table_name::text LIKE 'type_%'; +SELECT table_name, citus_table_type FROM citus_shards WHERE table_name::text LIKE 'type_%' AND nodeport IN (:worker_1_port, :worker_2_port); + +RESET citus.enable_schema_based_sharding; SET client_min_messages TO WARNING; -DROP SCHEMA regular_schema, tenant_3, tenant_5, tenant_7, tenant_6 CASCADE; +DROP SCHEMA regular_schema, tenant_3, tenant_5, tenant_7, tenant_6, type_sch CASCADE; SELECT citus_remove_node('localhost', :master_port); From 3f7bc0cbf5b7858e9cd788726551c1495a2c9495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20Ozan=20Akg=C3=BCl?= Date: Tue, 6 Jun 2023 17:55:40 +0300 Subject: [PATCH 074/118] Single Shard Partition Column UDFs (#6964) This PR fixes and tests: - debug_equality_expression - partition_column_id --- .../distributed/test/distribution_metadata.c | 4 +++ .../distributed/test/prune_shard_list.c | 4 +++ .../expected/single_shard_table_udfs.out | 28 +++++++++++++++++++ .../regress/sql/single_shard_table_udfs.sql | 20 +++++++++++++ 4 files changed, 56 insertions(+) diff --git a/src/backend/distributed/test/distribution_metadata.c b/src/backend/distributed/test/distribution_metadata.c index cb378e9e3..c3bc7fb51 100644 --- a/src/backend/distributed/test/distribution_metadata.c +++ b/src/backend/distributed/test/distribution_metadata.c @@ -170,6 +170,10 @@ partition_column_id(PG_FUNCTION_ARGS) { Oid distributedTableId = PG_GETARG_OID(0); uint32 rangeTableId = 1; + if (!IsCitusTableType(distributedTableId, HASH_DISTRIBUTED)) + { + ereport(ERROR, (errmsg("table needs to be hash distributed"))); + } Var *partitionColumn = PartitionColumn(distributedTableId, rangeTableId); PG_RETURN_INT16((int16) partitionColumn->varattno); diff --git a/src/backend/distributed/test/prune_shard_list.c b/src/backend/distributed/test/prune_shard_list.c index d83a645dc..a9f5e4a88 100644 --- a/src/backend/distributed/test/prune_shard_list.c +++ b/src/backend/distributed/test/prune_shard_list.c @@ -139,6 +139,10 @@ debug_equality_expression(PG_FUNCTION_ARGS) { Oid distributedTableId = PG_GETARG_OID(0); uint32 rangeTableId = 1; + if (!IsCitusTableType(distributedTableId, HASH_DISTRIBUTED)) + { + ereport(ERROR, (errmsg("table needs to be hash distributed"))); + } Var *partitionColumn = PartitionColumn(distributedTableId, rangeTableId); OpExpr *equalityExpression = MakeOpExpression(partitionColumn, BTEqualStrategyNumber); diff --git a/src/test/regress/expected/single_shard_table_udfs.out b/src/test/regress/expected/single_shard_table_udfs.out index 2a4e59f82..861ba1477 100644 --- a/src/test/regress/expected/single_shard_table_udfs.out +++ b/src/test/regress/expected/single_shard_table_udfs.out @@ -1068,5 +1068,33 @@ SELECT create_distributed_table('rep_tbl', NULL, colocate_with:='none'); SELECT replicate_table_shards('rep_tbl'); ERROR: cannot replicate single shard tables' shards +-- test debug_equality_expression +CREATE FUNCTION debug_equality_expression(regclass) +RETURNS cstring +AS 'citus' +LANGUAGE C STRICT; +CREATE TABLE debug_tbl (a INT); +SELECT create_distributed_table ('debug_tbl', NULL, colocate_with:='none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT debug_equality_expression('debug_tbl'::regclass); +ERROR: table needs to be hash distributed +-- test partition_column_id +CREATE FUNCTION partition_column_id(regclass) +RETURNS smallint +AS 'citus' +LANGUAGE C STRICT; +CREATE TABLE partcol_tbl (a INT); +SELECT create_distributed_table ('partcol_tbl', NULL, colocate_with:='none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT partition_column_id('partcol_tbl'::regclass); +ERROR: table needs to be hash distributed SET client_min_messages TO WARNING; DROP SCHEMA null_dist_key_udfs CASCADE; diff --git a/src/test/regress/sql/single_shard_table_udfs.sql b/src/test/regress/sql/single_shard_table_udfs.sql index 65b623a57..8d9057aca 100644 --- a/src/test/regress/sql/single_shard_table_udfs.sql +++ b/src/test/regress/sql/single_shard_table_udfs.sql @@ -503,5 +503,25 @@ CREATE TABLE rep_tbl (a INT); SELECT create_distributed_table('rep_tbl', NULL, colocate_with:='none'); SELECT replicate_table_shards('rep_tbl'); +-- test debug_equality_expression +CREATE FUNCTION debug_equality_expression(regclass) +RETURNS cstring +AS 'citus' +LANGUAGE C STRICT; + +CREATE TABLE debug_tbl (a INT); +SELECT create_distributed_table ('debug_tbl', NULL, colocate_with:='none'); +SELECT debug_equality_expression('debug_tbl'::regclass); + +-- test partition_column_id +CREATE FUNCTION partition_column_id(regclass) +RETURNS smallint +AS 'citus' +LANGUAGE C STRICT; + +CREATE TABLE partcol_tbl (a INT); +SELECT create_distributed_table ('partcol_tbl', NULL, colocate_with:='none'); +SELECT partition_column_id('partcol_tbl'::regclass); + SET client_min_messages TO WARNING; DROP SCHEMA null_dist_key_udfs CASCADE; From 8d8968ae630ad846e6cde2cb1bbe2abd89a9adb9 Mon Sep 17 00:00:00 2001 From: Ahmet Gedemenli Date: Wed, 7 Jun 2023 11:02:53 +0300 Subject: [PATCH 075/118] Disable ALTER TABLE .. SET SCHEMA for tenant tables (#6973) Disables `ALTER TABLE .. SET SCHEMA` for tenant tables. Disables `ALTER TABLE .. SET SCHEMA` for tenant schemas. --- .../commands/schema_based_sharding.c | 17 +++++++++++++++++ src/backend/distributed/commands/table.c | 4 ++++ src/include/distributed/commands.h | 1 + .../regress/expected/schema_based_sharding.out | 9 +++++++++ src/test/regress/sql/schema_based_sharding.sql | 6 ++++++ 5 files changed, 37 insertions(+) diff --git a/src/backend/distributed/commands/schema_based_sharding.c b/src/backend/distributed/commands/schema_based_sharding.c index b7f1ba70a..77daaa629 100644 --- a/src/backend/distributed/commands/schema_based_sharding.c +++ b/src/backend/distributed/commands/schema_based_sharding.c @@ -258,3 +258,20 @@ ErrorIfTenantTable(Oid relationId, char *operationName) operationName))); } } + + +/* + * ErrorIfTenantSchema errors out with the given operation name, + * if the given schema is a tenant schema. + */ +void +ErrorIfTenantSchema(Oid nspOid, char *operationName) +{ + if (IsTenantSchema(nspOid)) + { + ereport(ERROR, (errmsg( + "%s is not allowed for %s because it is a distributed schema", + get_namespace_name(nspOid), + operationName))); + } +} diff --git a/src/backend/distributed/commands/table.c b/src/backend/distributed/commands/table.c index 2286c292a..df0aa757b 100644 --- a/src/backend/distributed/commands/table.c +++ b/src/backend/distributed/commands/table.c @@ -2293,6 +2293,10 @@ PreprocessAlterTableSchemaStmt(Node *node, const char *queryString, return NIL; } + ErrorIfTenantTable(relationId, "ALTER TABLE SET SCHEMA"); + ErrorIfTenantSchema(get_namespace_oid(stmt->newschema, false), + "ALTER TABLE SET SCHEMA"); + DDLJob *ddlJob = palloc0(sizeof(DDLJob)); QualifyTreeNode((Node *) stmt); ObjectAddressSet(ddlJob->targetObjectAddress, RelationRelationId, relationId); diff --git a/src/include/distributed/commands.h b/src/include/distributed/commands.h index 3064f83cd..f5dc6e898 100644 --- a/src/include/distributed/commands.h +++ b/src/include/distributed/commands.h @@ -790,5 +790,6 @@ extern void ErrorIfIllegalPartitioningInTenantSchema(Oid parentRelationId, Oid partitionRelationId); extern void CreateTenantSchemaTable(Oid relationId); extern void ErrorIfTenantTable(Oid relationId, char *operationName); +extern void ErrorIfTenantSchema(Oid nspOid, char *operationName); #endif /*CITUS_COMMANDS_H */ diff --git a/src/test/regress/expected/schema_based_sharding.out b/src/test/regress/expected/schema_based_sharding.out index 7c17e5bb8..ba3018b15 100644 --- a/src/test/regress/expected/schema_based_sharding.out +++ b/src/test/regress/expected/schema_based_sharding.out @@ -89,6 +89,15 @@ ERROR: tenant_2.test_table is not allowed for alter_distributed_table because i -- verify we also don't allow colocate_with a tenant table SELECT alter_distributed_table('regular_schema.test_table', colocate_with => 'tenant_2.test_table'); ERROR: tenant_2.test_table is not allowed for colocate_with because it is a tenant table +-- verify we don't allow ALTER TABLE SET SCHEMA for tenant tables +ALTER TABLE tenant_2.test_table SET SCHEMA regular_schema; +ERROR: tenant_2.test_table is not allowed for ALTER TABLE SET SCHEMA because it is a tenant table +-- verify we don't allow ALTER TABLE SET SCHEMA for tenant schemas +ALTER TABLE regular_schema.test_table SET SCHEMA tenant_2; +ERROR: tenant_2 is not allowed for ALTER TABLE SET SCHEMA because it is a distributed schema +-- the same, from tenant schema to tenant schema +ALTER TABLE tenant_2.test_table SET SCHEMA tenant_3; +ERROR: tenant_2.test_table is not allowed for ALTER TABLE SET SCHEMA because it is a tenant table -- (on coordinator) verify that colocation id is set for empty tenants too SELECT colocationid > 0 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text IN ('tenant_1', 'tenant_3'); diff --git a/src/test/regress/sql/schema_based_sharding.sql b/src/test/regress/sql/schema_based_sharding.sql index 27741473b..309797a74 100644 --- a/src/test/regress/sql/schema_based_sharding.sql +++ b/src/test/regress/sql/schema_based_sharding.sql @@ -65,6 +65,12 @@ SELECT undistribute_table('tenant_2.test_table'); SELECT alter_distributed_table('tenant_2.test_table', colocate_with => 'none'); -- verify we also don't allow colocate_with a tenant table SELECT alter_distributed_table('regular_schema.test_table', colocate_with => 'tenant_2.test_table'); +-- verify we don't allow ALTER TABLE SET SCHEMA for tenant tables +ALTER TABLE tenant_2.test_table SET SCHEMA regular_schema; +-- verify we don't allow ALTER TABLE SET SCHEMA for tenant schemas +ALTER TABLE regular_schema.test_table SET SCHEMA tenant_2; +-- the same, from tenant schema to tenant schema +ALTER TABLE tenant_2.test_table SET SCHEMA tenant_3; -- (on coordinator) verify that colocation id is set for empty tenants too SELECT colocationid > 0 FROM pg_dist_tenant_schema From 6369645db4bd4d7cce3b000358d864c2a9056729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emel=20=C5=9Eim=C5=9Fek?= Date: Wed, 7 Jun 2023 12:14:34 +0300 Subject: [PATCH 076/118] Restore Test Coverage for Pushing Down Subqueries. (#6976) When we add the coordinator in metadata, reference tables gets replicated to coordinator. As a result we lose some test coverage since some queries start to run locally instead of getting pushed down. This PR adds new test cases involving distributed tables instead of reference tables for covering distributed execution in related cases. --- src/test/regress/expected/subquery_view.out | 40 +++++++++++++++++++++ src/test/regress/sql/subquery_view.sql | 18 ++++++++++ 2 files changed, 58 insertions(+) diff --git a/src/test/regress/expected/subquery_view.out b/src/test/regress/expected/subquery_view.out index 3de55b3aa..0fb4ec714 100644 --- a/src/test/regress/expected/subquery_view.out +++ b/src/test/regress/expected/subquery_view.out @@ -590,6 +590,30 @@ $Q$); Task Count: 1 (2 rows) +CREATE TABLE dist_table(text_col text, int_col int); +SELECT create_distributed_table('dist_table', 'text_col'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT public.coordinator_plan_with_subplans($Q$ +EXPLAIN (COSTS OFF) WITH cte AS ( + SELECT application_name AS text_col + FROM pg_stat_activity +) SELECT * FROM dist_table JOIN cte USING (text_col); +$Q$); + coordinator_plan_with_subplans +--------------------------------------------------------------------- + Custom Scan (Citus Adaptive) + -> Distributed Subplan XXX_1 + -> Function Scan on pg_stat_get_activity s + -> Distributed Subplan XXX_2 + -> Custom Scan (Citus Adaptive) + Task Count: 1 + Task Count: 4 +(7 rows) + CREATE OR REPLACE VIEW view_on_views AS SELECT pg_stat_activity.application_name, pg_locks.pid FROM pg_stat_activity, pg_locks; SELECT public.coordinator_plan_with_subplans($Q$ EXPLAIN (COSTS OFF) WITH cte AS ( @@ -603,6 +627,22 @@ $Q$); Task Count: 1 (2 rows) +SELECT public.coordinator_plan_with_subplans($Q$ +EXPLAIN (COSTS OFF) WITH cte AS ( + SELECT application_name AS text_col + FROM view_on_views +) SELECT * FROM dist_table JOIN cte USING (text_col); +$Q$); + coordinator_plan_with_subplans +--------------------------------------------------------------------- + Custom Scan (Citus Adaptive) + -> Distributed Subplan XXX_1 + -> Nested Loop + -> Function Scan on pg_stat_get_activity s + -> Function Scan on pg_lock_status l + Task Count: 4 +(6 rows) + SET client_min_messages TO WARNING; DROP SCHEMA subquery_view CASCADE; SET search_path TO public; diff --git a/src/test/regress/sql/subquery_view.sql b/src/test/regress/sql/subquery_view.sql index 23732d7e8..e5bc08c0f 100644 --- a/src/test/regress/sql/subquery_view.sql +++ b/src/test/regress/sql/subquery_view.sql @@ -434,6 +434,17 @@ EXPLAIN (COSTS OFF) WITH cte AS ( ) SELECT * FROM reference_table JOIN cte USING (text_col); $Q$); +CREATE TABLE dist_table(text_col text, int_col int); +SELECT create_distributed_table('dist_table', 'text_col'); + +SELECT public.coordinator_plan_with_subplans($Q$ +EXPLAIN (COSTS OFF) WITH cte AS ( + SELECT application_name AS text_col + FROM pg_stat_activity +) SELECT * FROM dist_table JOIN cte USING (text_col); +$Q$); + + CREATE OR REPLACE VIEW view_on_views AS SELECT pg_stat_activity.application_name, pg_locks.pid FROM pg_stat_activity, pg_locks; SELECT public.coordinator_plan_with_subplans($Q$ @@ -443,6 +454,13 @@ EXPLAIN (COSTS OFF) WITH cte AS ( ) SELECT * FROM reference_table JOIN cte USING (text_col); $Q$); +SELECT public.coordinator_plan_with_subplans($Q$ +EXPLAIN (COSTS OFF) WITH cte AS ( + SELECT application_name AS text_col + FROM view_on_views +) SELECT * FROM dist_table JOIN cte USING (text_col); +$Q$); + SET client_min_messages TO WARNING; DROP SCHEMA subquery_view CASCADE; SET search_path TO public; From b569d53a0ca4039c848cb7f646bb3f82879cd18a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20Ozan=20Akg=C3=BCl?= Date: Wed, 7 Jun 2023 13:30:50 +0300 Subject: [PATCH 077/118] Single shard misc udfs (#6956) This PR tests: - shards_colocated - citus_shard_cost_by_disk_size - citus_update_shard_statistics - citus_update_table_statistics --- .../expected/single_shard_table_udfs.out | 129 +++++++++++++++++- .../regress/sql/single_shard_table_udfs.sql | 71 +++++++++- 2 files changed, 190 insertions(+), 10 deletions(-) diff --git a/src/test/regress/expected/single_shard_table_udfs.out b/src/test/regress/expected/single_shard_table_udfs.out index 861ba1477..da264a1c9 100644 --- a/src/test/regress/expected/single_shard_table_udfs.out +++ b/src/test/regress/expected/single_shard_table_udfs.out @@ -36,6 +36,10 @@ CREATE OR REPLACE FUNCTION pg_catalog.is_citus_depended_object(oid,oid) RETURNS bool LANGUAGE C AS 'citus', $$is_citus_depended_object$$; +CREATE FUNCTION shards_colocated(bigint, bigint) +RETURNS bool +AS 'citus' +LANGUAGE C STRICT; -- test some other udf's with single shard tables CREATE TABLE null_dist_key_table(a int); SELECT create_distributed_table('null_dist_key_table', null, colocate_with=>'none', distribution_type=>null); @@ -528,25 +532,25 @@ SELECT create_distributed_table ('update_col_3', null, colocate_with:='none'); (1 row) --- make sure nodes are correct -SELECT c1.nodeport = c2.nodeport AS same_node +-- make sure nodes are correct and test shards_colocated UDF +SELECT c1.nodeport = c2.nodeport AS same_node, shards_colocated(c1.shardid, c2.shardid) FROM citus_shards c1, citus_shards c2, pg_dist_node p1, pg_dist_node p2 WHERE c1.table_name::text = 'update_col_1' AND c2.table_name::text = 'update_col_2' AND p1.nodeport = c1.nodeport AND p2.nodeport = c2.nodeport AND p1.noderole = 'primary' AND p2.noderole = 'primary'; - same_node + same_node | shards_colocated --------------------------------------------------------------------- - t + t | t (1 row) -SELECT c1.nodeport = c2.nodeport AS same_node +SELECT c1.nodeport = c2.nodeport AS same_node, shards_colocated(c1.shardid, c2.shardid) FROM citus_shards c1, citus_shards c2, pg_dist_node p1, pg_dist_node p2 WHERE c1.table_name::text = 'update_col_1' AND c2.table_name::text = 'update_col_3' AND p1.nodeport = c1.nodeport AND p2.nodeport = c2.nodeport AND p1.noderole = 'primary' AND p2.noderole = 'primary'; - same_node + same_node | shards_colocated --------------------------------------------------------------------- - f + f | f (1 row) -- and the update_col_1 and update_col_2 are colocated @@ -573,6 +577,17 @@ WHERE c1.table_name::text = 'update_col_1' AND c2.table_name::text = 'update_col f (1 row) +-- test shards_colocated UDF with shards in same node but different colocation groups +SELECT shards_colocated(c1.shardid, c2.shardid) +FROM citus_shards c1, citus_shards c2, pg_dist_node p1, pg_dist_node p2 +WHERE c1.table_name::text = 'update_col_1' AND c2.table_name::text = 'update_col_2' AND + p1.nodeport = c1.nodeport AND p2.nodeport = c2.nodeport AND + p1.noderole = 'primary' AND p2.noderole = 'primary'; + shards_colocated +--------------------------------------------------------------------- + f +(1 row) + -- re-colocate, the shards were already in the same node SELECT update_distributed_table_colocation('update_col_2', colocate_with:='update_col_1'); update_distributed_table_colocation @@ -1096,5 +1111,105 @@ SELECT create_distributed_table ('partcol_tbl', NULL, colocate_with:='none'); SELECT partition_column_id('partcol_tbl'::regclass); ERROR: table needs to be hash distributed +-- test citus_shard_cost_by_disk_size +CREATE TABLE size_tbl_dist (a INT, b TEXT); +SELECT create_distributed_table('size_tbl_dist', 'a', shard_count:=4, colocate_with:='none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE size_tbl_single (a INT, b TEXT); +SELECT create_distributed_table('size_tbl_single', NULL, colocate_with:='none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO size_tbl_dist SELECT 1, '1234567890' FROM generate_series(1, 10000); +INSERT INTO size_tbl_single SELECT 1, '1234567890' FROM generate_series(1, 10000); +SELECT citus_shard_cost_by_disk_size(c1.shardid) = citus_shard_cost_by_disk_size(c2.shardid) AS equal_cost +FROM citus_shards c1, citus_shards c2 +WHERE c1.table_name::TEXT = 'size_tbl_dist' AND c2.table_name::TEXT = 'size_tbl_single' +ORDER BY c1.shard_size DESC +LIMIT 1; + equal_cost +--------------------------------------------------------------------- + t +(1 row) + +-- test update statistics UDFs +CREATE TABLE update_tbl_stat (a INT, b TEXT); +SELECT create_distributed_table('update_tbl_stat', NULL, colocate_with:='none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT shardid AS update_tbl_stat_shard +FROM citus_shards +WHERE table_name::TEXT = 'update_tbl_stat' +LIMIT 1 \gset +SELECT shardlength > 0 FROM pg_dist_shard_placement WHERE shardid = :update_tbl_stat_shard LIMIT 1; + ?column? +--------------------------------------------------------------------- + f +(1 row) + +INSERT INTO update_tbl_stat SELECT 1, '1234567890' FROM generate_series(1, 10000); +SELECT shardlength > 0 FROM pg_dist_shard_placement WHERE shardid = :update_tbl_stat_shard LIMIT 1; + ?column? +--------------------------------------------------------------------- + f +(1 row) + +SELECT citus_update_table_statistics('update_tbl_stat'); + citus_update_table_statistics +--------------------------------------------------------------------- + +(1 row) + +SELECT shardlength > 0 FROM pg_dist_shard_placement WHERE shardid = :update_tbl_stat_shard LIMIT 1; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +CREATE TABLE update_shard_stat (a INT, b TEXT); +SELECT create_distributed_table('update_shard_stat', NULL, colocate_with:='none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT shardid AS update_shard_stat_shard +FROM citus_shards +WHERE table_name::TEXT = 'update_shard_stat' +LIMIT 1 \gset +SELECT shardlength > 0 FROM pg_dist_shard_placement WHERE shardid = :update_shard_stat_shard LIMIT 1; + ?column? +--------------------------------------------------------------------- + f +(1 row) + +INSERT INTO update_shard_stat SELECT 1, '1234567890' FROM generate_series(1, 10000); +SELECT shardlength > 0 FROM pg_dist_shard_placement WHERE shardid = :update_shard_stat_shard LIMIT 1; + ?column? +--------------------------------------------------------------------- + f +(1 row) + +SELECT 1 FROM citus_update_shard_statistics(:update_shard_stat_shard); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT shardlength > 0 FROM pg_dist_shard_placement WHERE shardid = :update_shard_stat_shard LIMIT 1; + ?column? +--------------------------------------------------------------------- + t +(1 row) + SET client_min_messages TO WARNING; DROP SCHEMA null_dist_key_udfs CASCADE; diff --git a/src/test/regress/sql/single_shard_table_udfs.sql b/src/test/regress/sql/single_shard_table_udfs.sql index 8d9057aca..68d04890f 100644 --- a/src/test/regress/sql/single_shard_table_udfs.sql +++ b/src/test/regress/sql/single_shard_table_udfs.sql @@ -39,6 +39,11 @@ RETURNS bool LANGUAGE C AS 'citus', $$is_citus_depended_object$$; +CREATE FUNCTION shards_colocated(bigint, bigint) +RETURNS bool +AS 'citus' +LANGUAGE C STRICT; + -- test some other udf's with single shard tables CREATE TABLE null_dist_key_table(a int); SELECT create_distributed_table('null_dist_key_table', null, colocate_with=>'none', distribution_type=>null); @@ -226,14 +231,14 @@ SELECT create_distributed_table ('update_col_2', null, colocate_with:='update_co -- with the new colocation id the new table will be in the other worker node SELECT create_distributed_table ('update_col_3', null, colocate_with:='none'); --- make sure nodes are correct -SELECT c1.nodeport = c2.nodeport AS same_node +-- make sure nodes are correct and test shards_colocated UDF +SELECT c1.nodeport = c2.nodeport AS same_node, shards_colocated(c1.shardid, c2.shardid) FROM citus_shards c1, citus_shards c2, pg_dist_node p1, pg_dist_node p2 WHERE c1.table_name::text = 'update_col_1' AND c2.table_name::text = 'update_col_2' AND p1.nodeport = c1.nodeport AND p2.nodeport = c2.nodeport AND p1.noderole = 'primary' AND p2.noderole = 'primary'; -SELECT c1.nodeport = c2.nodeport AS same_node +SELECT c1.nodeport = c2.nodeport AS same_node, shards_colocated(c1.shardid, c2.shardid) FROM citus_shards c1, citus_shards c2, pg_dist_node p1, pg_dist_node p2 WHERE c1.table_name::text = 'update_col_1' AND c2.table_name::text = 'update_col_3' AND p1.nodeport = c1.nodeport AND p2.nodeport = c2.nodeport AND @@ -251,6 +256,13 @@ SELECT c1.colocation_id = c2.colocation_id AS colocated FROM public.citus_tables c1, public.citus_tables c2 WHERE c1.table_name::text = 'update_col_1' AND c2.table_name::text = 'update_col_2'; +-- test shards_colocated UDF with shards in same node but different colocation groups +SELECT shards_colocated(c1.shardid, c2.shardid) +FROM citus_shards c1, citus_shards c2, pg_dist_node p1, pg_dist_node p2 +WHERE c1.table_name::text = 'update_col_1' AND c2.table_name::text = 'update_col_2' AND + p1.nodeport = c1.nodeport AND p2.nodeport = c2.nodeport AND + p1.noderole = 'primary' AND p2.noderole = 'primary'; + -- re-colocate, the shards were already in the same node SELECT update_distributed_table_colocation('update_col_2', colocate_with:='update_col_1'); @@ -523,5 +535,58 @@ CREATE TABLE partcol_tbl (a INT); SELECT create_distributed_table ('partcol_tbl', NULL, colocate_with:='none'); SELECT partition_column_id('partcol_tbl'::regclass); +-- test citus_shard_cost_by_disk_size +CREATE TABLE size_tbl_dist (a INT, b TEXT); +SELECT create_distributed_table('size_tbl_dist', 'a', shard_count:=4, colocate_with:='none'); + +CREATE TABLE size_tbl_single (a INT, b TEXT); +SELECT create_distributed_table('size_tbl_single', NULL, colocate_with:='none'); + +INSERT INTO size_tbl_dist SELECT 1, '1234567890' FROM generate_series(1, 10000); +INSERT INTO size_tbl_single SELECT 1, '1234567890' FROM generate_series(1, 10000); + +SELECT citus_shard_cost_by_disk_size(c1.shardid) = citus_shard_cost_by_disk_size(c2.shardid) AS equal_cost +FROM citus_shards c1, citus_shards c2 +WHERE c1.table_name::TEXT = 'size_tbl_dist' AND c2.table_name::TEXT = 'size_tbl_single' +ORDER BY c1.shard_size DESC +LIMIT 1; + +-- test update statistics UDFs +CREATE TABLE update_tbl_stat (a INT, b TEXT); +SELECT create_distributed_table('update_tbl_stat', NULL, colocate_with:='none'); + +SELECT shardid AS update_tbl_stat_shard +FROM citus_shards +WHERE table_name::TEXT = 'update_tbl_stat' +LIMIT 1 \gset + +SELECT shardlength > 0 FROM pg_dist_shard_placement WHERE shardid = :update_tbl_stat_shard LIMIT 1; + +INSERT INTO update_tbl_stat SELECT 1, '1234567890' FROM generate_series(1, 10000); + +SELECT shardlength > 0 FROM pg_dist_shard_placement WHERE shardid = :update_tbl_stat_shard LIMIT 1; + +SELECT citus_update_table_statistics('update_tbl_stat'); + +SELECT shardlength > 0 FROM pg_dist_shard_placement WHERE shardid = :update_tbl_stat_shard LIMIT 1; + +CREATE TABLE update_shard_stat (a INT, b TEXT); +SELECT create_distributed_table('update_shard_stat', NULL, colocate_with:='none'); + +SELECT shardid AS update_shard_stat_shard +FROM citus_shards +WHERE table_name::TEXT = 'update_shard_stat' +LIMIT 1 \gset + +SELECT shardlength > 0 FROM pg_dist_shard_placement WHERE shardid = :update_shard_stat_shard LIMIT 1; + +INSERT INTO update_shard_stat SELECT 1, '1234567890' FROM generate_series(1, 10000); + +SELECT shardlength > 0 FROM pg_dist_shard_placement WHERE shardid = :update_shard_stat_shard LIMIT 1; + +SELECT 1 FROM citus_update_shard_statistics(:update_shard_stat_shard); + +SELECT shardlength > 0 FROM pg_dist_shard_placement WHERE shardid = :update_shard_stat_shard LIMIT 1; + SET client_min_messages TO WARNING; DROP SCHEMA null_dist_key_udfs CASCADE; From fa8870217d34ec880737e4f17b44b1c77226f966 Mon Sep 17 00:00:00 2001 From: Onur Tirtir Date: Thu, 8 Jun 2023 10:57:23 +0300 Subject: [PATCH 078/118] Enable logical planner for single-shard tables (#6950) * Enable using logical planner for single-shard tables * Improve non-colocated table error in physical planner * Favor distributed tables over reference tables when chosing anchor shard --- .../distributed/planner/distributed_planner.c | 11 - .../planner/insert_select_planner.c | 6 +- .../planner/multi_logical_planner.c | 3 +- .../planner/multi_physical_planner.c | 57 +- .../planner/multi_router_planner.c | 21 - .../planner/query_colocation_checker.c | 5 +- .../distributed/multi_router_planner.h | 1 - src/test/regress/citus_tests/config.py | 32 +- .../expected/create_single_shard_table.out | 35 +- .../insert_select_single_shard_table.out | 273 ++- src/test/regress/expected/join_pushdown.out | 2 +- .../expected/multi_shard_update_delete.out | 2 +- .../expected/query_single_shard_table.out | 1877 +++++++++++++++-- .../regress/sql/create_single_shard_table.sql | 8 +- .../sql/insert_select_single_shard_table.sql | 35 +- .../regress/sql/query_single_shard_table.sql | 527 ++++- 16 files changed, 2505 insertions(+), 390 deletions(-) diff --git a/src/backend/distributed/planner/distributed_planner.c b/src/backend/distributed/planner/distributed_planner.c index 6c5d0f32a..e53259b77 100644 --- a/src/backend/distributed/planner/distributed_planner.c +++ b/src/backend/distributed/planner/distributed_planner.c @@ -1025,17 +1025,6 @@ CreateDistributedPlan(uint64 planId, bool allowRecursivePlanning, Query *origina { return distributedPlan; } - else if (ContainsSingleShardTable(originalQuery)) - { - /* - * We only support router queries if the query contains reference to - * a single-shard table. This temporary restriction will be removed - * once we support recursive planning for the queries that reference - * single-shard tables. - */ - WrapRouterErrorForSingleShardTable(distributedPlan->planningError); - RaiseDeferredError(distributedPlan->planningError, ERROR); - } else { RaiseDeferredError(distributedPlan->planningError, DEBUG2); diff --git a/src/backend/distributed/planner/insert_select_planner.c b/src/backend/distributed/planner/insert_select_planner.c index 4f24d396c..a44db5c28 100644 --- a/src/backend/distributed/planner/insert_select_planner.c +++ b/src/backend/distributed/planner/insert_select_planner.c @@ -1406,17 +1406,15 @@ CreateNonPushableInsertSelectPlan(uint64 planId, Query *parse, ParamListInfo bou IsSupportedRedistributionTarget(targetRelationId); /* - * Today it's not possible to generate a distributed plan for a SELECT + * It's not possible to generate a distributed plan for a SELECT * having more than one tasks if it references a single-shard table. - * This is because, we don't support queries beyond router planner - * if the query references a single-shard table. * * For this reason, right now we don't expect an INSERT .. SELECT * query to go through the repartitioned INSERT .. SELECT logic if the * SELECT query references a single-shard table. */ Assert(!repartitioned || - !GetRTEListPropertiesForQuery(selectQueryCopy)->hasSingleShardDistTable); + !ContainsSingleShardTable(selectQueryCopy)); distributedPlan->modifyQueryViaCoordinatorOrRepartition = insertSelectQuery; distributedPlan->selectPlanForModifyViaCoordinatorOrRepartition = selectPlan; diff --git a/src/backend/distributed/planner/multi_logical_planner.c b/src/backend/distributed/planner/multi_logical_planner.c index 7732b6c5e..fa9e5bb61 100644 --- a/src/backend/distributed/planner/multi_logical_planner.c +++ b/src/backend/distributed/planner/multi_logical_planner.c @@ -1028,7 +1028,8 @@ ErrorHintRequired(const char *errorHint, Query *queryTree) { continue; } - else if (IsCitusTableType(relationId, HASH_DISTRIBUTED)) + else if (IsCitusTableType(relationId, HASH_DISTRIBUTED) || + IsCitusTableType(relationId, SINGLE_SHARD_DISTRIBUTED)) { int colocationId = TableColocationId(relationId); colocationIdList = list_append_unique_int(colocationIdList, colocationId); diff --git a/src/backend/distributed/planner/multi_physical_planner.c b/src/backend/distributed/planner/multi_physical_planner.c index 8ca51a0a4..cef21d33e 100644 --- a/src/backend/distributed/planner/multi_physical_planner.c +++ b/src/backend/distributed/planner/multi_physical_planner.c @@ -2367,7 +2367,7 @@ ErrorIfUnsupportedShardDistribution(Query *query) ListCell *relationIdCell = NULL; uint32 relationIndex = 0; uint32 rangeDistributedRelationCount = 0; - uint32 hashDistributedRelationCount = 0; + uint32 hashDistOrSingleShardRelCount = 0; uint32 appendDistributedRelationCount = 0; foreach(relationIdCell, relationIdList) @@ -2379,9 +2379,10 @@ ErrorIfUnsupportedShardDistribution(Query *query) nonReferenceRelations = lappend_oid(nonReferenceRelations, relationId); } - else if (IsCitusTableType(relationId, HASH_DISTRIBUTED)) + else if (IsCitusTableType(relationId, HASH_DISTRIBUTED) || + IsCitusTableType(relationId, SINGLE_SHARD_DISTRIBUTED)) { - hashDistributedRelationCount++; + hashDistOrSingleShardRelCount++; nonReferenceRelations = lappend_oid(nonReferenceRelations, relationId); } @@ -2396,7 +2397,7 @@ ErrorIfUnsupportedShardDistribution(Query *query) } } - if ((rangeDistributedRelationCount > 0) && (hashDistributedRelationCount > 0)) + if ((rangeDistributedRelationCount > 0) && (hashDistOrSingleShardRelCount > 0)) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot push down this subquery"), @@ -2410,7 +2411,7 @@ ErrorIfUnsupportedShardDistribution(Query *query) errdetail("A query including both range and append " "partitioned relations are unsupported"))); } - else if ((appendDistributedRelationCount > 0) && (hashDistributedRelationCount > 0)) + else if ((appendDistributedRelationCount > 0) && (hashDistOrSingleShardRelCount > 0)) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot push down this subquery"), @@ -2439,8 +2440,9 @@ ErrorIfUnsupportedShardDistribution(Query *query) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot push down this subquery"), - errdetail("Shards of relations in subquery need to " - "have 1-to-1 shard partitioning"))); + errdetail("%s and %s are not colocated", + get_rel_name(firstTableRelationId), + get_rel_name(currentRelationId)))); } } } @@ -2813,15 +2815,15 @@ AnchorRangeTableId(List *rangeTableList) * have the most number of shards, we have a draw. */ List *baseTableIdList = BaseRangeTableIdList(rangeTableList); - List *anchorTableIdList = AnchorRangeTableIdList(rangeTableList, baseTableIdList); + List *anchorTableRTIList = AnchorRangeTableIdList(rangeTableList, baseTableIdList); ListCell *anchorTableIdCell = NULL; - int anchorTableIdCount = list_length(anchorTableIdList); + int anchorTableIdCount = list_length(anchorTableRTIList); Assert(anchorTableIdCount > 0); if (anchorTableIdCount == 1) { - anchorRangeTableId = (uint32) linitial_int(anchorTableIdList); + anchorRangeTableId = (uint32) linitial_int(anchorTableRTIList); return anchorRangeTableId; } @@ -2829,7 +2831,7 @@ AnchorRangeTableId(List *rangeTableList) * If more than one table has the most number of shards, we break the draw * by comparing table sizes and picking the table with the largest size. */ - foreach(anchorTableIdCell, anchorTableIdList) + foreach(anchorTableIdCell, anchorTableRTIList) { uint32 anchorTableId = (uint32) lfirst_int(anchorTableIdCell); RangeTblEntry *tableEntry = rt_fetch(anchorTableId, rangeTableList); @@ -2857,7 +2859,7 @@ AnchorRangeTableId(List *rangeTableList) if (anchorRangeTableId == 0) { /* all tables have the same shard count and size 0, pick the first */ - anchorRangeTableId = (uint32) linitial_int(anchorTableIdList); + anchorRangeTableId = (uint32) linitial_int(anchorTableRTIList); } return anchorRangeTableId; @@ -2898,7 +2900,7 @@ BaseRangeTableIdList(List *rangeTableList) static List * AnchorRangeTableIdList(List *rangeTableList, List *baseRangeTableIdList) { - List *anchorTableIdList = NIL; + List *anchorTableRTIList = NIL; uint32 maxShardCount = 0; ListCell *baseRangeTableIdCell = NULL; @@ -2908,25 +2910,46 @@ AnchorRangeTableIdList(List *rangeTableList, List *baseRangeTableIdList) return baseRangeTableIdList; } + uint32 referenceTableRTI = 0; + foreach(baseRangeTableIdCell, baseRangeTableIdList) { uint32 baseRangeTableId = (uint32) lfirst_int(baseRangeTableIdCell); RangeTblEntry *tableEntry = rt_fetch(baseRangeTableId, rangeTableList); - List *shardList = LoadShardList(tableEntry->relid); + + Oid citusTableId = tableEntry->relid; + if (IsCitusTableType(citusTableId, REFERENCE_TABLE)) + { + referenceTableRTI = baseRangeTableId; + continue; + } + + List *shardList = LoadShardList(citusTableId); uint32 shardCount = (uint32) list_length(shardList); if (shardCount > maxShardCount) { - anchorTableIdList = list_make1_int(baseRangeTableId); + anchorTableRTIList = list_make1_int(baseRangeTableId); maxShardCount = shardCount; } else if (shardCount == maxShardCount) { - anchorTableIdList = lappend_int(anchorTableIdList, baseRangeTableId); + anchorTableRTIList = lappend_int(anchorTableRTIList, baseRangeTableId); } } - return anchorTableIdList; + /* + * We favor distributed tables over reference tables as anchor tables. But + * in case we cannot find any distributed tables, we let reference table to be + * anchor table. For now, we cannot see a query that might require this, but we + * want to be backward compatiable. + */ + if (list_length(anchorTableRTIList) == 0) + { + return referenceTableRTI > 0 ? list_make1_int(referenceTableRTI) : NIL; + } + + return anchorTableRTIList; } diff --git a/src/backend/distributed/planner/multi_router_planner.c b/src/backend/distributed/planner/multi_router_planner.c index cc9d9732c..87ab1277f 100644 --- a/src/backend/distributed/planner/multi_router_planner.c +++ b/src/backend/distributed/planner/multi_router_planner.c @@ -258,22 +258,6 @@ CreateModifyPlan(Query *originalQuery, Query *query, } -/* - * WrapRouterErrorForSingleShardTable wraps given planning error with a - * generic error message if given query references a distributed table - * that doesn't have a distribution key. - */ -void -WrapRouterErrorForSingleShardTable(DeferredErrorMessage *planningError) -{ - planningError->detail = planningError->message; - planningError->message = pstrdup("queries that reference a distributed " - "table without a shard key can only " - "reference colocated distributed " - "tables or reference tables"); -} - - /* * CreateSingleTaskRouterSelectPlan creates a physical plan for given SELECT query. * The returned plan is a router task that returns query results from a single worker. @@ -1886,11 +1870,6 @@ RouterJob(Query *originalQuery, PlannerRestrictionContext *plannerRestrictionCon */ if (IsMergeQuery(originalQuery)) { - if (ContainsSingleShardTable(originalQuery)) - { - WrapRouterErrorForSingleShardTable(*planningError); - } - RaiseDeferredError(*planningError, ERROR); } else diff --git a/src/backend/distributed/planner/query_colocation_checker.c b/src/backend/distributed/planner/query_colocation_checker.c index c5de0ef9e..a6e64b9c1 100644 --- a/src/backend/distributed/planner/query_colocation_checker.c +++ b/src/backend/distributed/planner/query_colocation_checker.c @@ -168,11 +168,10 @@ AnchorRte(Query *subquery) { Oid relationId = currentRte->relid; - if (IsCitusTable(relationId) && !HasDistributionKey(relationId)) + if (!IsCitusTableType(relationId, DISTRIBUTED_TABLE)) { /* - * Non-distributed tables should not be the anchor rte since they - * don't have distribution key. + * We're not interested in non distributed relations. */ continue; } diff --git a/src/include/distributed/multi_router_planner.h b/src/include/distributed/multi_router_planner.h index 506e50135..a255fd520 100644 --- a/src/include/distributed/multi_router_planner.h +++ b/src/include/distributed/multi_router_planner.h @@ -36,7 +36,6 @@ extern DistributedPlan * CreateRouterPlan(Query *originalQuery, Query *query, extern DistributedPlan * CreateModifyPlan(Query *originalQuery, Query *query, PlannerRestrictionContext * plannerRestrictionContext); -extern void WrapRouterErrorForSingleShardTable(DeferredErrorMessage *planningError); extern DeferredErrorMessage * PlanRouterQuery(Query *originalQuery, PlannerRestrictionContext * plannerRestrictionContext, diff --git a/src/test/regress/citus_tests/config.py b/src/test/regress/citus_tests/config.py index a6499a42e..16b18d1e7 100644 --- a/src/test/regress/citus_tests/config.py +++ b/src/test/regress/citus_tests/config.py @@ -211,34 +211,14 @@ class AllSingleShardTableDefaultConfig(CitusDefaultClusterConfig): super().__init__(arguments) self.all_null_dist_key = True self.skip_tests += [ - # i) Skip the following tests because they require SQL support beyond - # router planner / supporting more DDL command types. - # - # group 1 - "dropped_columns_create_load", - "dropped_columns_1", - # group 2 - "distributed_planning_create_load", - "distributed_planning", - # group 4 - "views_create", - "views", - # group 5 - "intermediate_result_pruning_create", - "intermediate_result_pruning_queries_1", - "intermediate_result_pruning_queries_2", - # group 6 - "local_dist_join_load", - "local_dist_join", - "arbitrary_configs_recurring_outer_join", - # group 7 - "sequences_create", - "sequences", - # group 8 + # One of the distributed functions created in "function_create" + # requires setting a distribution column, which cannot be the + # case with single shard tables. "function_create", "functions", - # - # ii) Skip the following test as it requires support for create_distributed_function. + # In "nested_execution", one of the tests that query + # "dist_query_single_shard" table acts differently when the table + # has a single shard. This is explained with a comment in the test. "nested_execution", ] diff --git a/src/test/regress/expected/create_single_shard_table.out b/src/test/regress/expected/create_single_shard_table.out index 41a81346b..8d4756caf 100644 --- a/src/test/regress/expected/create_single_shard_table.out +++ b/src/test/regress/expected/create_single_shard_table.out @@ -909,15 +909,18 @@ ALTER TABLE "NULL_!_dist_key"."nullKeyTable.1!?!90123456789012345678901234567890 ERROR: referenced table "local_table_for_fkey" must be a distributed table or a reference table DETAIL: To enforce foreign keys, the referencing and referenced rows need to be stored on the same node. HINT: You could use SELECT create_reference_table('local_table_for_fkey') to replicate the referenced table to all nodes or consider dropping the foreign key --- Normally, we support foreign keys from Postgres tables to distributed --- tables assuming that the user will soon distribute the local table too --- anyway. However, this is not the case for single-shard tables before --- we improve SQL support. +-- foreign key from a local table ALTER TABLE local_table_for_fkey ADD CONSTRAINT fkey_from_dummy_local FOREIGN KEY (a) REFERENCES "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"(id); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. -CONTEXT: SQL statement "SELECT fk."a" FROM ONLY "create_single_shard_table"."local_table_for_fkey" fk LEFT OUTER JOIN "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234" pk ON ( pk."id" OPERATOR(pg_catalog.=) fk."a") WHERE pk."id" IS NULL AND (fk."a" IS NOT NULL)" +SELECT create_distributed_table('local_table_for_fkey', null, colocate_with=>'none'); +ERROR: cannot create foreign key constraint since relations are not colocated or not referencing a reference table +DETAIL: A distributed table can only have foreign keys if it is referencing another colocated hash distributed table or a reference table +SELECT create_distributed_table('local_table_for_fkey', null, colocate_with=>'"NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + -- foreign key to a citus local table, errors out CREATE TABLE citus_local_table_for_fkey (a INT PRIMARY KEY); SELECT citus_add_local_table_to_metadata('citus_local_table_for_fkey'); @@ -1128,7 +1131,7 @@ BEGIN; INSERT INTO referencing_table VALUES (1, 2); -- fails INSERT INTO referencing_table VALUES (2, 2); -ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730098" +ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730099" DETAIL: Key (a)=(2) is not present in table "referenced_table_xxxxxxx". CONTEXT: while executing command on localhost:xxxxx ROLLBACK; @@ -1174,7 +1177,7 @@ BEGIN; INSERT INTO referencing_table VALUES (1, 2); -- fails INSERT INTO referencing_table VALUES (2, 2); -ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730134" +ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730135" DETAIL: Key (a)=(2) is not present in table "referenced_table_xxxxxxx". CONTEXT: while executing command on localhost:xxxxx ROLLBACK; @@ -1292,8 +1295,8 @@ SELECT result, success FROM run_command_on_workers($$ $$); result | success --------------------------------------------------------------------- - ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730151" | f - ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730151" | f + ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730152" | f + ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730152" | f (2 rows) DROP TABLE referencing_table, referenced_table; @@ -1308,8 +1311,8 @@ SELECT create_distributed_table('self_fkey_test', NULL, distribution_type=>null) INSERT INTO self_fkey_test VALUES (1, 1); -- ok INSERT INTO self_fkey_test VALUES (2, 3); -- fails -ERROR: insert or update on table "self_fkey_test_1730152" violates foreign key constraint "self_fkey_test_b_fkey_1730152" -DETAIL: Key (b)=(3) is not present in table "self_fkey_test_1730152". +ERROR: insert or update on table "self_fkey_test_1730153" violates foreign key constraint "self_fkey_test_b_fkey_1730153" +DETAIL: Key (b)=(3) is not present in table "self_fkey_test_1730153". CONTEXT: while executing command on localhost:xxxxx -- similar foreign key tests but this time create the referencing table later on -- referencing table is a single-shard table @@ -1333,7 +1336,7 @@ BEGIN; INSERT INTO referencing_table VALUES (1, 2); -- fails INSERT INTO referencing_table VALUES (2, 2); -ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730154" +ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730155" DETAIL: Key (a)=(2) is not present in table "referenced_table_xxxxxxx". CONTEXT: while executing command on localhost:xxxxx ROLLBACK; @@ -1356,7 +1359,7 @@ BEGIN; INSERT INTO referencing_table VALUES (2, 1); -- fails INSERT INTO referencing_table VALUES (1, 2); -ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_b_fkey_1730156" +ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_b_fkey_1730157" DETAIL: Key (a, b)=(1, 2) is not present in table "referenced_table_xxxxxxx". CONTEXT: while executing command on localhost:xxxxx ROLLBACK; @@ -1463,7 +1466,7 @@ BEGIN; INSERT INTO referencing_table VALUES (1, 2); -- fails INSERT INTO referencing_table VALUES (2, 2); -ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730197" +ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730198" DETAIL: Key (a)=(2) is not present in table "referenced_table_xxxxxxx". CONTEXT: while executing command on localhost:xxxxx ROLLBACK; diff --git a/src/test/regress/expected/insert_select_single_shard_table.out b/src/test/regress/expected/insert_select_single_shard_table.out index d27bdcd73..f61d6b549 100644 --- a/src/test/regress/expected/insert_select_single_shard_table.out +++ b/src/test/regress/expected/insert_select_single_shard_table.out @@ -147,10 +147,20 @@ DEBUG: cannot perform distributed INSERT INTO ... SELECT because the partition DETAIL: The target table's partition column should correspond to a partition column in the subquery. DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator -INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 FULL JOIN matview USING (a); +INSERT INTO distributed_table_c1_t1 SELECT COALESCE(nullkey_c1_t1.a, 1), nullkey_c1_t1.b FROM nullkey_c1_t1 FULL JOIN matview USING (a); DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: Wrapping relation "matview" to a subquery +DEBUG: generating subplan XXX_1 for subquery SELECT a FROM insert_select_single_shard_table.matview WHERE true +DEBUG: recursively planning left side of the full join since the other side is a recurring rel +DEBUG: recursively planning distributed relation "nullkey_c1_t1" since it is part of a distributed join node that is outer joined with a recurring rel +DEBUG: Wrapping relation "nullkey_c1_t1" to a subquery +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_2 for subquery SELECT a, b FROM insert_select_single_shard_table.nullkey_c1_t1 WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT COALESCE(nullkey_c1_t1.a, 1) AS a, nullkey_c1_t1.b FROM ((SELECT nullkey_c1_t1_1.a, nullkey_c1_t1_1.b FROM (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) nullkey_c1_t1_1) nullkey_c1_t1 FULL JOIN (SELECT matview_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) matview_1) matview USING (a)) +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO distributed_table_c1_t1 SELECT * FROM nullkey_c1_t1 UNION SELECT * FROM nullkey_c1_t2; DEBUG: cannot perform distributed INSERT INTO ... SELECT because the partition columns in the source table and subquery do not match DETAIL: The target table's partition column should correspond to a partition column in the subquery. @@ -160,45 +170,65 @@ DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 LEFT JOIN nullkey_c2_t1 USING (a); DEBUG: cannot perform distributed INSERT INTO ... SELECT because the partition columns in the source table and subquery do not match DETAIL: The target table's partition column should correspond to a partition column in the subquery. -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: router planner does not support queries that reference non-colocated distributed tables +ERROR: cannot push down this subquery +DETAIL: nullkey_c1_t2 and nullkey_c2_t1 are not colocated INSERT INTO distributed_table_c1_t1 SELECT * FROM nullkey_c1_t1 UNION SELECT * FROM nullkey_c2_t1; DEBUG: cannot perform distributed INSERT INTO ... SELECT because the partition columns in the source table and subquery do not match DETAIL: The target table's partition column should correspond to a partition column in the subquery. -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables --- use a distributed table that is colocated with the target table +DEBUG: router planner does not support queries that reference non-colocated distributed tables +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_1 for subquery SELECT a, b FROM insert_select_single_shard_table.nullkey_c1_t1 +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_2 for subquery SELECT a, b FROM insert_select_single_shard_table.nullkey_c2_t1 +DEBUG: Creating router plan +DEBUG: generating subplan XXX_3 for subquery SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer) UNION SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer) +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT a, b FROM (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) citus_insert_select_subquery +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator +SET client_min_messages TO DEBUG1; +SET citus.enable_repartition_joins TO ON; +-- use a distributed table that is colocated with the target table, with repartition joins enabled INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a); DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO distributed_table_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a); DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO distributed_table_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (b); DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO distributed_table_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a) WHERE distributed_table_c1_t2.a = 1; DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables --- use a distributed table that is not colocated with the target table +DEBUG: Collecting INSERT ... SELECT results on coordinator +-- use a distributed table that is not colocated with the target table, with repartition joins enabled INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 JOIN distributed_table_c2_t1 USING (a); DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: Collecting INSERT ... SELECT results on coordinator +RESET citus.enable_repartition_joins; +SET client_min_messages TO DEBUG2; -- use a citus local table INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN citus_local_table USING (a); DEBUG: distributed INSERT ... SELECT cannot select from distributed tables and local tables at the same time -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: Wrapping relation "citus_local_table" to a subquery +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_1 for subquery SELECT a FROM insert_select_single_shard_table.citus_local_table WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM (insert_select_single_shard_table.nullkey_c1_t1 JOIN (SELECT citus_local_table_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) citus_local_table_1) citus_local_table USING (a)) +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator -- use a postgres local table INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 JOIN postgres_local_table USING (a); DEBUG: distributed INSERT ... SELECT cannot select from distributed tables and local tables at the same time -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: Wrapping relation "postgres_local_table" to a subquery +DEBUG: generating subplan XXX_1 for subquery SELECT a FROM insert_select_single_shard_table.postgres_local_table WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM (insert_select_single_shard_table.nullkey_c1_t2 JOIN (SELECT postgres_local_table_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) postgres_local_table_1) postgres_local_table USING (a)) +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator -- use append / range distributed tables INSERT INTO range_table SELECT * FROM nullkey_c1_t1; DEBUG: cannot perform distributed INSERT INTO ... SELECT because the partition columns in the source table and subquery do not match @@ -209,13 +239,13 @@ DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO append_table SELECT * FROM nullkey_c1_t1; DEBUG: cannot perform distributed INSERT INTO ... SELECT because the partition columns in the source table and subquery do not match DETAIL: The target table's partition column should correspond to a partition column in the subquery. -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: INSERT ... SELECT into an append-distributed table is not supported +DEBUG: INSERT ... SELECT into an append-distributed table is not supported +ERROR: INSERT ... SELECT into an append-distributed table is not supported SELECT avg(a), avg(b) FROM distributed_table_c1_t1 ORDER BY 1, 2; DEBUG: Router planner cannot handle multi-shard select queries avg | avg --------------------------------------------------------------------- - 4.2105263157894737 | 4.2105263157894737 + 4.3421052631578947 | 4.5277777777777778 (1 row) TRUNCATE distributed_table_c1_t1; @@ -246,8 +276,11 @@ DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO reference_table SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 LEFT JOIN reference_table USING (b) WHERE b IN (SELECT b FROM matview); DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: generating subplan XXX_1 for subquery SELECT b FROM insert_select_single_shard_table.matview +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM (insert_select_single_shard_table.nullkey_c1_t2 LEFT JOIN insert_select_single_shard_table.reference_table USING (b)) WHERE (nullkey_c1_t2.b OPERATOR(pg_catalog.=) ANY (SELECT intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(b integer))) +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator -- use a colocated single-shard table INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN nullkey_c1_t2 USING (b); DEBUG: only reference tables may be queried when targeting a reference table with distributed INSERT ... SELECT @@ -260,41 +293,52 @@ DEBUG: Collecting INSERT ... SELECT results on coordinator -- use a non-colocated single-shard table INSERT INTO reference_table SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 LEFT JOIN nullkey_c2_t1 USING (a); DEBUG: only reference tables may be queried when targeting a reference table with distributed INSERT ... SELECT -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: router planner does not support queries that reference non-colocated distributed tables +ERROR: cannot push down this subquery +DETAIL: nullkey_c1_t2 and nullkey_c2_t1 are not colocated -- use a distributed table +SET client_min_messages TO DEBUG1; +SET citus.enable_repartition_joins TO ON; INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a); DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO reference_table SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a); DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO reference_table SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (b); DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO reference_table SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a) WHERE distributed_table_c1_t2.a = 1; DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: Collecting INSERT ... SELECT results on coordinator +RESET citus.enable_repartition_joins; +SET client_min_messages TO DEBUG2; -- use a citus local table INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN citus_local_table USING (a); DEBUG: distributed INSERT ... SELECT cannot select from distributed tables and local tables at the same time -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: Wrapping relation "citus_local_table" to a subquery +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_1 for subquery SELECT a FROM insert_select_single_shard_table.citus_local_table WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM (insert_select_single_shard_table.nullkey_c1_t1 JOIN (SELECT citus_local_table_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) citus_local_table_1) citus_local_table USING (a)) +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator -- use a postgres local table INSERT INTO reference_table SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 JOIN postgres_local_table USING (a); DEBUG: distributed INSERT ... SELECT cannot select from distributed tables and local tables at the same time -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: Wrapping relation "postgres_local_table" to a subquery +DEBUG: generating subplan XXX_1 for subquery SELECT a FROM insert_select_single_shard_table.postgres_local_table WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM (insert_select_single_shard_table.nullkey_c1_t2 JOIN (SELECT postgres_local_table_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) postgres_local_table_1) postgres_local_table USING (a)) +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator SELECT avg(a), avg(b) FROM reference_table ORDER BY 1, 2; DEBUG: Distributed planning for a fast-path router query DEBUG: Creating router plan avg | avg --------------------------------------------------------------------- - 4.0428571428571429 | 4.0428571428571429 + 4.3063063063063063 | 4.3063063063063063 (1 row) TRUNCATE reference_table; @@ -320,26 +364,39 @@ DEBUG: distributed INSERT ... SELECT cannot insert into a local table that is a DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator -- use a distributed table +SET client_min_messages TO DEBUG1; +SET citus.enable_repartition_joins TO ON; INSERT INTO citus_local_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a); DEBUG: distributed INSERT ... SELECT cannot insert into a local table that is added to metadata -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: Collecting INSERT ... SELECT results on coordinator +RESET citus.enable_repartition_joins; +SET client_min_messages TO DEBUG2; -- use a citus local table INSERT INTO citus_local_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN citus_local_table USING (a); DEBUG: distributed INSERT ... SELECT cannot select from distributed tables and local tables at the same time -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: Wrapping relation "citus_local_table" to a subquery +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_1 for subquery SELECT a FROM insert_select_single_shard_table.citus_local_table WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM (insert_select_single_shard_table.nullkey_c1_t1 JOIN (SELECT citus_local_table_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) citus_local_table_1) citus_local_table USING (a)) +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator -- use a postgres local table INSERT INTO citus_local_table SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 JOIN postgres_local_table USING (a); DEBUG: distributed INSERT ... SELECT cannot select from distributed tables and local tables at the same time -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: Wrapping relation "postgres_local_table" to a subquery +DEBUG: generating subplan XXX_1 for subquery SELECT a FROM insert_select_single_shard_table.postgres_local_table WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM (insert_select_single_shard_table.nullkey_c1_t2 JOIN (SELECT postgres_local_table_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) postgres_local_table_1) postgres_local_table USING (a)) +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator SELECT avg(a), avg(b) FROM citus_local_table ORDER BY 1, 2; DEBUG: Distributed planning for a fast-path router query DEBUG: Creating router plan avg | avg --------------------------------------------------------------------- - 4.4333333333333333 | 4.4333333333333333 + 4.5270270270270270 | 4.5270270270270270 (1 row) TRUNCATE citus_local_table; @@ -358,8 +415,18 @@ DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO nullkey_c1_t1 SELECT postgres_local_table.a, postgres_local_table.b FROM postgres_local_table LEFT JOIN nullkey_c1_t1 USING (a); DEBUG: distributed INSERT ... SELECT cannot select from distributed tables and local tables at the same time -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: Wrapping relation "postgres_local_table" to a subquery +DEBUG: generating subplan XXX_1 for subquery SELECT a, b FROM insert_select_single_shard_table.postgres_local_table WHERE true +DEBUG: recursively planning right side of the left join since the outer side is a recurring rel +DEBUG: recursively planning distributed relation "nullkey_c1_t1" since it is part of a distributed join node that is outer joined with a recurring rel +DEBUG: Wrapping relation "nullkey_c1_t1" to a subquery +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_2 for subquery SELECT a FROM insert_select_single_shard_table.nullkey_c1_t1 WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT postgres_local_table.a, postgres_local_table.b FROM ((SELECT postgres_local_table_1.a, postgres_local_table_1.b FROM (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) postgres_local_table_1) postgres_local_table LEFT JOIN (SELECT nullkey_c1_t1_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) nullkey_c1_t1_1) nullkey_c1_t1 USING (a)) +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator -- use a citus local table INSERT INTO nullkey_c1_t1 SELECT citus_local_table.a, citus_local_table.b FROM citus_local_table; DEBUG: distributed INSERT ... SELECT cannot select from a local relation when inserting into a distributed table @@ -372,8 +439,14 @@ DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO nullkey_c1_t1 SELECT citus_local_table.a, citus_local_table.b FROM citus_local_table JOIN nullkey_c1_t1 USING (a); DEBUG: distributed INSERT ... SELECT cannot select from distributed tables and local tables at the same time -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: Wrapping relation "citus_local_table" to a subquery +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_1 for subquery SELECT a, b FROM insert_select_single_shard_table.citus_local_table WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT citus_local_table.a, citus_local_table.b FROM ((SELECT citus_local_table_1.a, citus_local_table_1.b FROM (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) citus_local_table_1) citus_local_table JOIN insert_select_single_shard_table.nullkey_c1_t1 USING (a)) +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator -- use a distributed table INSERT INTO nullkey_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM distributed_table_c1_t2; DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT @@ -383,10 +456,13 @@ INSERT INTO nullkey_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1 DEBUG: INSERT target relation and all source relations of the SELECT must be colocated in distributed INSERT ... SELECT DEBUG: Router planner cannot handle multi-shard select queries DEBUG: Collecting INSERT ... SELECT results on coordinator +SET client_min_messages TO DEBUG1; +SET citus.enable_repartition_joins TO ON; INSERT INTO nullkey_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM distributed_table_c1_t2 JOIN nullkey_c1_t1 USING (a); DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: Collecting INSERT ... SELECT results on coordinator +RESET citus.enable_repartition_joins; +SET client_min_messages TO DEBUG2; -- use a non-colocated single-shard table INSERT INTO nullkey_c2_t1 SELECT q.* FROM (SELECT reference_table.* FROM reference_table LEFT JOIN nullkey_c1_t1 USING (a)) q JOIN nullkey_c1_t2 USING (a); DEBUG: cannot perform a lateral outer join when a distributed subquery references a reference table @@ -402,8 +478,12 @@ DEBUG: Creating router plan DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO nullkey_c1_t1 SELECT q.* FROM (SELECT reference_table.* FROM reference_table JOIN nullkey_c1_t1 USING (a)) q JOIN matview USING (a); DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: Wrapping relation "matview" to a subquery +DEBUG: generating subplan XXX_1 for subquery SELECT a FROM insert_select_single_shard_table.matview WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT q.a, q.b FROM ((SELECT reference_table.a, reference_table.b FROM (insert_select_single_shard_table.reference_table JOIN insert_select_single_shard_table.nullkey_c1_t1 USING (a))) q JOIN (SELECT matview_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) matview_1) matview USING (a)) +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator -- use append / range distributed tables INSERT INTO nullkey_c1_t1 SELECT * FROM range_table; DEBUG: Router planner cannot handle multi-shard select queries @@ -416,9 +496,9 @@ DEBUG: Collecting INSERT ... SELECT results on coordinator SELECT avg(a), avg(b) FROM nullkey_c1_t1 ORDER BY 1, 2; DEBUG: Distributed planning for a fast-path router query DEBUG: Creating router plan - avg | avg + avg | avg --------------------------------------------------------------------- - 5.8611111111111111 | 13.9305555555555556 + 5.6971153846153846 | 8.4903846153846154 (1 row) SELECT avg(a), avg(b) FROM nullkey_c2_t1 ORDER BY 1, 2; @@ -426,7 +506,7 @@ DEBUG: Distributed planning for a fast-path router query DEBUG: Creating router plan avg | avg --------------------------------------------------------------------- - 3.8750000000000000 | 3.8750000000000000 + 3.9864864864864865 | 3.9864864864864865 (1 row) TRUNCATE nullkey_c1_t1, nullkey_c2_t1; @@ -448,8 +528,15 @@ WITH cte_1 AS ( INSERT INTO postgres_local_table SELECT cte_1.* FROM cte_1 LEFT JOIN nullkey_c1_t2 USING (a) WHERE nullkey_c1_t2.a IS NULL; DEBUG: Creating router plan INSERT INTO postgres_local_table SELECT * FROM nullkey_c1_t1 EXCEPT SELECT * FROM postgres_local_table; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: generating subplan XXX_1 for subquery SELECT a, b FROM insert_select_single_shard_table.postgres_local_table +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_2 for subquery SELECT a, b FROM insert_select_single_shard_table.nullkey_c1_t1 +DEBUG: Creating router plan +DEBUG: generating subplan XXX_3 for subquery SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer) EXCEPT SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer) +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT a, b FROM (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_3'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) citus_insert_select_subquery +DEBUG: Creating router plan SELECT avg(a), avg(b) FROM postgres_local_table ORDER BY 1, 2; avg | avg --------------------------------------------------------------------- @@ -459,6 +546,7 @@ SELECT avg(a), avg(b) FROM postgres_local_table ORDER BY 1, 2; TRUNCATE postgres_local_table; INSERT INTO postgres_local_table SELECT i, i FROM generate_series(5, 10) i; -- Try slightly more complex queries. +SET client_min_messages TO DEBUG1; WITH cte_1 AS ( SELECT nullkey_c1_t1.a, reference_table.b FROM nullkey_c1_t1 JOIN reference_table USING (a) ), @@ -470,8 +558,12 @@ SELECT cte_1.* FROM cte_1 JOIN cte_2 USING (a) JOIN distributed_table_c1_t2 USIN DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DEBUG: CTE cte_1 is going to be inlined via distributed planning DEBUG: CTE cte_2 is going to be inlined via distributed planning -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Wrapping relation "postgres_local_table" to a subquery +DEBUG: generating subplan XXX_1 for subquery SELECT b FROM insert_select_single_shard_table.postgres_local_table WHERE true +DEBUG: generating subplan XXX_2 for subquery SELECT nullkey_c1_t1.a, reference_table.b FROM (insert_select_single_shard_table.nullkey_c1_t1 JOIN insert_select_single_shard_table.reference_table USING (a)) +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT a, b FROM (SELECT cte_1.a, cte_1.b FROM (((SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) cte_1 JOIN (SELECT reference_table.a, postgres_local_table.b FROM ((SELECT NULL::integer AS a, postgres_local_table_1.b FROM (SELECT intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(b integer)) postgres_local_table_1) postgres_local_table LEFT JOIN insert_select_single_shard_table.reference_table USING (b))) cte_2 USING (a)) JOIN insert_select_single_shard_table.distributed_table_c1_t2 USING (a)) ORDER BY cte_1.a, cte_1.b) citus_insert_select_subquery +DEBUG: performing repartitioned INSERT ... SELECT +SET client_min_messages TO DEBUG2; WITH cte_1 AS ( SELECT nullkey_c1_t1.a, reference_table.b FROM nullkey_c1_t1 JOIN reference_table USING (a) ), @@ -521,8 +613,13 @@ CROSS JOIN ( SELECT b FROM nullkey_c2_t1 ORDER BY b LIMIT 1 ) t2; DEBUG: distributed INSERT ... SELECT cannot insert into a local table that is added to metadata -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: router planner does not support queries that reference non-colocated distributed tables +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_1 for subquery SELECT b FROM insert_select_single_shard_table.nullkey_c2_t1 ORDER BY b LIMIT 1 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT t1.a, t2.b FROM (insert_select_single_shard_table.nullkey_c1_t1 t1 CROSS JOIN (SELECT intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(b integer)) t2) +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO distributed_table_c1_t1 (a, b) SELECT t1.a, t2.b FROM reference_table t1 @@ -547,8 +644,12 @@ JOIN ( ) t2 ON t1.b = t2.b WHERE t2.rn > 2; DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: router planner does not support queries that reference non-colocated distributed tables +DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: generating subplan XXX_1 for subquery SELECT b, row_number() OVER (ORDER BY b DESC) AS rn FROM insert_select_single_shard_table.distributed_table_c2_t1 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT t1.a, t2.b FROM (insert_select_single_shard_table.nullkey_c1_t1 t1 JOIN (SELECT q.rn, q.b FROM (SELECT intermediate_result.b, intermediate_result.rn FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(b integer, rn bigint)) q) t2 ON ((t1.b OPERATOR(pg_catalog.=) t2.b))) WHERE (t2.rn OPERATOR(pg_catalog.>) 2) +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO distributed_table_c1_t1 (a, b) SELECT t1.a, t2.b FROM nullkey_c1_t1 t1 @@ -567,9 +668,6 @@ DEBUG: Collecting INSERT ... SELECT results on coordinator -- Temporaryly reduce the verbosity to avoid noise -- in the output of the next query. SET client_min_messages TO DEBUG1; --- MultiTaskRouterSelectQuerySupported() is unnecessarily restrictive --- about pushing down queries with DISTINCT ON clause even if the table --- doesn't have a shard key. See https://github.com/citusdata/citus/pull/6752. INSERT INTO nullkey_c1_t1 SELECT DISTINCT ON (a) a, b FROM nullkey_c1_t2; SET client_min_messages TO DEBUG2; -- Similarly, we could push down the following query as well. see @@ -597,8 +695,12 @@ WHERE t1.a NOT IN ( SELECT DISTINCT t2.a FROM distributed_table_c1_t2 AS t2 ); DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Router planner cannot handle multi-shard select queries +DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: generating subplan XXX_1 for subquery SELECT DISTINCT a FROM insert_select_single_shard_table.distributed_table_c1_t2 t2 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT a, b FROM insert_select_single_shard_table.nullkey_c1_t1 t1 WHERE (NOT (a OPERATOR(pg_catalog.=) ANY (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)))) +DEBUG: Creating router plan +DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO distributed_table_c1_t1 SELECT t1.a, t1.b FROM reference_table AS t1 @@ -664,23 +766,30 @@ INSERT INTO upsert_test_2 (key, value) VALUES (1, '5') ON CONFLICT(key) DEBUG: Creating router plan INSERT INTO upsert_test_1 (unique_col, other_col) VALUES (1, 1) ON CONFLICT (unique_col) DO UPDATE SET other_col = (SELECT count(*) from upsert_test_1); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: subqueries are not supported within INSERT queries +DEBUG: subqueries are not supported within INSERT queries +HINT: Try rewriting your queries with 'INSERT INTO ... SELECT' syntax. +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_1 for subquery SELECT count(*) AS count FROM insert_select_single_shard_table.upsert_test_1 +DEBUG: Plan XXX query after replacing subqueries and CTEs: INSERT INTO insert_select_single_shard_table.upsert_test_1 (unique_col, other_col) VALUES (1, 1) ON CONFLICT(unique_col) DO UPDATE SET other_col = (SELECT intermediate_result.count FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(count bigint)) +DEBUG: subqueries are not supported within INSERT queries +HINT: Try rewriting your queries with 'INSERT INTO ... SELECT' syntax. +ERROR: subqueries are not supported within INSERT queries HINT: Try rewriting your queries with 'INSERT INTO ... SELECT' syntax. INSERT INTO upsert_test_1 (unique_col, other_col) VALUES (1, 1) ON CONFLICT (unique_col) DO UPDATE SET other_col = random()::int; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: functions used in the DO UPDATE SET clause of INSERTs on distributed tables must be marked IMMUTABLE +DEBUG: functions used in the DO UPDATE SET clause of INSERTs on distributed tables must be marked IMMUTABLE +ERROR: functions used in the DO UPDATE SET clause of INSERTs on distributed tables must be marked IMMUTABLE INSERT INTO upsert_test_1 (unique_col, other_col) VALUES (1, 1) ON CONFLICT (unique_col) DO UPDATE SET other_col = 5 WHERE upsert_test_1.other_col = random()::int; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: functions used in the WHERE clause of the ON CONFLICT clause of INSERTs on distributed tables must be marked IMMUTABLE +DEBUG: functions used in the WHERE clause of the ON CONFLICT clause of INSERTs on distributed tables must be marked IMMUTABLE +ERROR: functions used in the WHERE clause of the ON CONFLICT clause of INSERTs on distributed tables must be marked IMMUTABLE INSERT INTO upsert_test_1 VALUES (3, 5, 7); DEBUG: Creating router plan INSERT INTO upsert_test_1 (unique_col, other_col) VALUES (1, 1) ON CONFLICT (unique_col) WHERE unique_col = random()::int DO UPDATE SET other_col = 5; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: functions used in the WHERE clause of the ON CONFLICT clause of INSERTs on distributed tables must be marked IMMUTABLE +DEBUG: functions used in the WHERE clause of the ON CONFLICT clause of INSERTs on distributed tables must be marked IMMUTABLE +ERROR: functions used in the WHERE clause of the ON CONFLICT clause of INSERTs on distributed tables must be marked IMMUTABLE CREATE TABLE upsert_test_3 (key_1 int, key_2 bigserial, value text DEFAULT 'default_value', PRIMARY KEY (key_1, key_2)); DEBUG: CREATE TABLE will create implicit sequence "upsert_test_3_key_2_seq" for serial column "upsert_test_3.key_2" DEBUG: CREATE TABLE / PRIMARY KEY will create implicit index "upsert_test_3_pkey" for table "upsert_test_3" diff --git a/src/test/regress/expected/join_pushdown.out b/src/test/regress/expected/join_pushdown.out index 02a16c195..004c007ef 100644 --- a/src/test/regress/expected/join_pushdown.out +++ b/src/test/regress/expected/join_pushdown.out @@ -152,7 +152,7 @@ ORDER BY 1; -- Full outer join with different distribution column types, should error out SELECT * FROM test_table_1 full join test_table_2 using(id); ERROR: cannot push down this subquery -DETAIL: Shards of relations in subquery need to have 1-to-1 shard partitioning +DETAIL: test_table_1 and test_table_2 are not colocated -- Test when the non-distributed column has the value of NULL INSERT INTO test_table_1 VALUES(7, NULL); INSERT INTO test_table_2 VALUES(7, NULL); diff --git a/src/test/regress/expected/multi_shard_update_delete.out b/src/test/regress/expected/multi_shard_update_delete.out index af8ddfb2d..a42f90475 100644 --- a/src/test/regress/expected/multi_shard_update_delete.out +++ b/src/test/regress/expected/multi_shard_update_delete.out @@ -725,7 +725,7 @@ SET value_2 = 5 FROM events_test_table_2 WHERE users_test_table.user_id = events_test_table_2.user_id; ERROR: cannot push down this subquery -DETAIL: Shards of relations in subquery need to have 1-to-1 shard partitioning +DETAIL: users_test_table and events_test_table_2 are not colocated -- Should error out due to multiple row return from subquery, but we can not get this information within -- subquery pushdown planner. This query will be sent to worker with recursive planner. \set VERBOSITY terse diff --git a/src/test/regress/expected/query_single_shard_table.out b/src/test/regress/expected/query_single_shard_table.out index 992b91f9f..68c178553 100644 --- a/src/test/regress/expected/query_single_shard_table.out +++ b/src/test/regress/expected/query_single_shard_table.out @@ -164,6 +164,69 @@ SELECT create_distributed_table('range_table', 'a', 'range'); CALL public.create_range_partitioned_shards('range_table', '{"0","25"}','{"24","49"}'); INSERT INTO range_table VALUES (0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 50); +\set users_table_data_file :abs_srcdir '/data/users_table.data' +\set events_table_data_file :abs_srcdir '/data/events_table.data' +CREATE TABLE users_table (user_id int, time timestamp, value_1 int, value_2 int, value_3 float, value_4 bigint); +SELECT create_distributed_table('users_table', null, colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +\set client_side_copy_command '\\copy users_table FROM ' :'users_table_data_file' ' WITH CSV;' +:client_side_copy_command +CREATE TABLE non_colocated_users_table (id int, value int); +SELECT create_distributed_table('non_colocated_users_table', null, colocate_with => 'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO non_colocated_users_table (id, value) VALUES(1, 2),(2, 3),(3,4); +CREATE TABLE colocated_events_table (user_id int, time timestamp, event_type int, value_2 int, value_3 float, value_4 bigint); +SELECT create_distributed_table('colocated_events_table', null, colocate_with=>'users_table'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +\set client_side_copy_command '\\copy colocated_events_table FROM ' :'events_table_data_file' ' WITH CSV;' +:client_side_copy_command +CREATE TABLE non_colocated_events_table (user_id int, time timestamp, event_type int, value_2 int, value_3 float, value_4 bigint); +SELECT create_distributed_table('non_colocated_events_table', null, colocate_with=>'non_colocated_users_table'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +\set client_side_copy_command '\\copy non_colocated_events_table FROM ' :'events_table_data_file' ' WITH CSV;' +:client_side_copy_command +CREATE TABLE users_table_local AS SELECT * FROM users_table; +CREATE TABLE colocated_users_table (id int, value int); +SELECT create_distributed_table('colocated_users_table', null, colocate_with => 'users_table'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO colocated_users_table (id, value) VALUES(1, 2),(2, 3),(3,4); +CREATE TABLE users_reference_table (like users_table including all); +SELECT create_reference_table('users_reference_table'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE events_reference_table (like colocated_events_table including all); +SELECT create_reference_table('events_reference_table'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +CREATE FUNCTION func() RETURNS TABLE (id int, value int) AS $$ + SELECT 1, 2 +$$ LANGUAGE SQL; SET client_min_messages to DEBUG2; -- simple insert INSERT INTO nullkey_c1_t1 VALUES (1,2), (2,2), (3,4); @@ -225,8 +288,9 @@ DEBUG: Creating router plan -- cartesian product with different table types -- with other table types SELECT COUNT(*) FROM distributed_table d1, nullkey_c1_t1; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: router planner does not support queries that reference non-colocated distributed tables +ERROR: cannot perform distributed planning on this query +DETAIL: Cartesian products are currently unsupported SELECT COUNT(*) FROM reference_table d1, nullkey_c1_t1; DEBUG: Creating router plan count @@ -235,11 +299,29 @@ DEBUG: Creating router plan (1 row) SELECT COUNT(*) FROM citus_local_table d1, nullkey_c1_t1; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: Wrapping relation "citus_local_table" "d1" to a subquery +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_1 for subquery SELECT NULL::integer AS "dummy-1" FROM query_single_shard_table.citus_local_table d1 WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT NULL::integer AS a, NULL::integer AS b FROM (SELECT intermediate_result."dummy-1" FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result("dummy-1" integer)) d1_1) d1, query_single_shard_table.nullkey_c1_t1 +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 121 +(1 row) + SELECT COUNT(*) FROM postgres_local_table d1, nullkey_c1_t1; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: Wrapping relation "postgres_local_table" "d1" to a subquery +DEBUG: generating subplan XXX_1 for subquery SELECT NULL::integer AS "dummy-1" FROM query_single_shard_table.postgres_local_table d1 WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT NULL::integer AS a, NULL::integer AS b FROM (SELECT intermediate_result."dummy-1" FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result("dummy-1" integer)) d1_1) d1, query_single_shard_table.nullkey_c1_t1 +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 66 +(1 row) + -- with a colocated single-shard table SELECT COUNT(*) FROM nullkey_c1_t1 d1, nullkey_c1_t2; DEBUG: Creating router plan @@ -250,8 +332,9 @@ DEBUG: Creating router plan -- with a non-colocated single-shard table SELECT COUNT(*) FROM nullkey_c1_t1 d1, nullkey_c2_t1; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: router planner does not support queries that reference non-colocated distributed tables +ERROR: cannot perform distributed planning on this query +DETAIL: Cartesian products are currently unsupported -- First, show that nullkey_c1_t1 and nullkey_c3_t1 are not colocated. SELECT (SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'query_single_shard_table.nullkey_c1_t1'::regclass) != @@ -273,9 +356,16 @@ DEBUG: Creating router plan (1 row) SET citus.enable_non_colocated_router_query_pushdown TO OFF; +SET citus.enable_repartition_joins TO ON; +SET client_min_messages TO DEBUG1; SELECT COUNT(*) FROM nullkey_c1_t1 JOIN nullkey_c3_t1 USING(a); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables + count +--------------------------------------------------------------------- + 11 +(1 row) + +SET client_min_messages TO DEBUG2; +SET citus.enable_repartition_joins TO OFF; RESET citus.enable_non_colocated_router_query_pushdown; -- colocated join between single-shard tables SELECT COUNT(*) FROM nullkey_c1_t1 JOIN nullkey_c1_t2 USING(a); @@ -350,52 +440,123 @@ DEBUG: Creating router plan (1 row) -- non-colocated inner joins between single-shard tables +SET client_min_messages to DEBUG1; +SET citus.enable_repartition_joins TO ON; SELECT * FROM nullkey_c1_t1 JOIN nullkey_c2_t1 USING(a) ORDER BY 1,2,3; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables + a | b | b +--------------------------------------------------------------------- + 1 | 1 | 0 + 1 | 2 | 0 + 2 | 2 | 2 + 2 | 2 | 2 + 2 | 2 | 5 + 2 | 2 | 5 + 3 | 3 | 3 + 3 | 4 | 3 + 4 | 4 | 3 + 4 | 4 | 4 + 5 | 5 | 2 + 5 | 5 | 5 + 6 | 6 | 6 + 7 | 7 | 7 +(14 rows) + +SELECT * FROM (SELECT * FROM nullkey_c1_t1) nullkey_c1_t1 JOIN nullkey_c2_t1 USING(a) ORDER BY 1,2,3; + a | b | b +--------------------------------------------------------------------- + 1 | 1 | 0 + 1 | 2 | 0 + 2 | 2 | 2 + 2 | 2 | 2 + 2 | 2 | 5 + 2 | 2 | 5 + 3 | 3 | 3 + 3 | 4 | 3 + 4 | 4 | 3 + 4 | 4 | 4 + 5 | 5 | 2 + 5 | 5 | 5 + 6 | 6 | 6 + 7 | 7 | 7 +(14 rows) + +SELECT * FROM nullkey_c2_t1 JOIN (SELECT * FROM nullkey_c1_t1) nullkey_c1_t1 USING(a) ORDER BY 1,2,3; + a | b | b +--------------------------------------------------------------------- + 1 | 0 | 1 + 1 | 0 | 2 + 2 | 2 | 2 + 2 | 2 | 2 + 2 | 5 | 2 + 2 | 5 | 2 + 3 | 3 | 3 + 3 | 3 | 4 + 4 | 3 | 4 + 4 | 4 | 4 + 5 | 2 | 5 + 5 | 5 | 5 + 6 | 6 | 6 + 7 | 7 | 7 +(14 rows) + SELECT COUNT(*) FROM nullkey_c1_t1 t1 JOIN LATERAL ( SELECT * FROM nullkey_c2_t2 t2 WHERE t2.b > t1.a ) q USING(a); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables + count +--------------------------------------------------------------------- + 2 +(1 row) + +SET citus.enable_repartition_joins TO OFF; +SET client_min_messages to DEBUG2; -- non-colocated outer joins between single-shard tables SELECT * FROM nullkey_c1_t1 LEFT JOIN nullkey_c2_t2 USING(a) ORDER BY 1,2,3 LIMIT 4; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: router planner does not support queries that reference non-colocated distributed tables +DEBUG: push down of limit count: 4 +ERROR: cannot push down this subquery +DETAIL: nullkey_c1_t1 and nullkey_c2_t2 are not colocated SELECT * FROM nullkey_c1_t1 FULL JOIN nullkey_c2_t2 USING(a) ORDER BY 1,2,3 LIMIT 4; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: router planner does not support queries that reference non-colocated distributed tables +DEBUG: push down of limit count: 4 +ERROR: cannot push down this subquery +DETAIL: nullkey_c1_t1 and nullkey_c2_t2 are not colocated SELECT * FROM nullkey_c1_t1 t1 LEFT JOIN LATERAL ( SELECT * FROM nullkey_c2_t2 t2 WHERE t2.b > t1.a ) q USING(a) ORDER BY 1,2,3 OFFSET 3 LIMIT 4; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: router planner does not support queries that reference non-colocated distributed tables +DEBUG: push down of limit count: 7 +ERROR: cannot push down this subquery +DETAIL: nullkey_c1_t1 and nullkey_c2_t2 are not colocated SELECT COUNT(*) FROM nullkey_c1_t1 t1 LEFT JOIN LATERAL ( SELECT * FROM nullkey_c2_t2 t2 WHERE t2.b > t1.a ) q USING(a); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: router planner does not support queries that reference non-colocated distributed tables +ERROR: cannot push down this subquery +DETAIL: nullkey_c1_t1 and nullkey_c2_t2 are not colocated SELECT COUNT(*) FROM nullkey_c1_t1 t1 WHERE EXISTS ( SELECT * FROM nullkey_c2_t2 t2 WHERE t2.b > t1.a ); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: router planner does not support queries that reference non-colocated distributed tables +ERROR: cannot push down this subquery +DETAIL: nullkey_c2_t2 and nullkey_c1_t1 are not colocated SELECT COUNT(*) FROM nullkey_c1_t1 t1 WHERE t1.b IN ( SELECT b+1 FROM nullkey_c2_t2 t2 WHERE t2.b = t1.a ); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: found no worker with all shard placements +DEBUG: found no worker with all shard placements +ERROR: cannot push down this subquery +DETAIL: nullkey_c2_t2 and nullkey_c1_t1 are not colocated SELECT COUNT(*) FROM nullkey_c1_t1 t1 WHERE t1.b NOT IN ( SELECT a FROM nullkey_c2_t2 t2 WHERE t2.b > t1.a ); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: found no worker with all shard placements +DEBUG: found no worker with all shard placements +ERROR: cannot push down this subquery +DETAIL: nullkey_c2_t2 and nullkey_c1_t1 are not colocated -- join with a reference table SELECT COUNT(*) FROM nullkey_c1_t1, reference_table WHERE nullkey_c1_t1.a = reference_table.a; DEBUG: Creating router plan @@ -414,28 +575,256 @@ DEBUG: Creating router plan (1 row) -- join with postgres / citus local tables -SELECT * FROM nullkey_c1_t1 JOIN postgres_local_table USING(a); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. -SELECT * FROM nullkey_c1_t1 JOIN citus_local_table USING(a); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +SELECT * FROM nullkey_c1_t1 JOIN postgres_local_table USING(a) ORDER BY 1,2,3; +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: Wrapping relation "postgres_local_table" to a subquery +DEBUG: generating subplan XXX_1 for subquery SELECT a, b FROM query_single_shard_table.postgres_local_table WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT nullkey_c1_t1.a, nullkey_c1_t1.b, postgres_local_table.b FROM (query_single_shard_table.nullkey_c1_t1 JOIN (SELECT postgres_local_table_1.a, postgres_local_table_1.b FROM (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) postgres_local_table_1) postgres_local_table USING (a)) ORDER BY nullkey_c1_t1.a, nullkey_c1_t1.b, postgres_local_table.b +DEBUG: Creating router plan + a | b | b +--------------------------------------------------------------------- + 5 | 5 | 5 + 6 | 6 | 6 + 7 | 7 | 7 + 8 | 8 | 8 +(4 rows) + +SELECT * FROM nullkey_c1_t1 JOIN citus_local_table USING(a) ORDER BY 1,2,3; +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: Wrapping relation "citus_local_table" to a subquery +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_1 for subquery SELECT a, b FROM query_single_shard_table.citus_local_table WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT nullkey_c1_t1.a, nullkey_c1_t1.b, citus_local_table.b FROM (query_single_shard_table.nullkey_c1_t1 JOIN (SELECT citus_local_table_1.a, citus_local_table_1.b FROM (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) citus_local_table_1) citus_local_table USING (a)) ORDER BY nullkey_c1_t1.a, nullkey_c1_t1.b, citus_local_table.b +DEBUG: Creating router plan + a | b | b +--------------------------------------------------------------------- + 1 | 1 | 1 + 1 | 2 | 1 + 2 | 2 | 2 + 2 | 2 | 2 + 3 | 3 | 3 + 3 | 4 | 3 + 4 | 4 | 4 + 5 | 5 | 5 + 6 | 6 | 6 + 7 | 7 | 7 + 8 | 8 | 8 +(11 rows) + +SET citus.local_table_join_policy TO 'prefer-distributed'; +SELECT * FROM nullkey_c1_t1 JOIN citus_local_table USING(a) ORDER BY 1,2,3; +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: Wrapping relation "nullkey_c1_t1" to a subquery +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_1 for subquery SELECT a, b FROM query_single_shard_table.nullkey_c1_t1 WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT nullkey_c1_t1.a, nullkey_c1_t1.b, citus_local_table.b FROM ((SELECT nullkey_c1_t1_1.a, nullkey_c1_t1_1.b FROM (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) nullkey_c1_t1_1) nullkey_c1_t1 JOIN query_single_shard_table.citus_local_table USING (a)) ORDER BY nullkey_c1_t1.a, nullkey_c1_t1.b, citus_local_table.b +DEBUG: Creating router plan + a | b | b +--------------------------------------------------------------------- + 1 | 1 | 1 + 1 | 2 | 1 + 2 | 2 | 2 + 2 | 2 | 2 + 3 | 3 | 3 + 3 | 4 | 3 + 4 | 4 | 4 + 5 | 5 | 5 + 6 | 6 | 6 + 7 | 7 | 7 + 8 | 8 | 8 +(11 rows) + +RESET citus.local_table_join_policy; -- join with a distributed table -SELECT * FROM distributed_table d1 JOIN nullkey_c1_t1 USING(a); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +SET citus.enable_repartition_joins TO ON; +SET client_min_messages TO DEBUG1; +SELECT * FROM distributed_table d1 JOIN nullkey_c1_t1 USING(a) ORDER BY 1,2,3; + a | b | b +--------------------------------------------------------------------- + 3 | 3 | 3 + 3 | 3 | 4 + 4 | 4 | 4 + 5 | 5 | 5 + 6 | 6 | 6 + 7 | 7 | 7 + 8 | 8 | 8 +(7 rows) + +SELECT * FROM (SELECT * FROM distributed_table) d1 JOIN nullkey_c1_t1 USING(a) ORDER BY 1,2,3; +DEBUG: generating subplan XXX_1 for subquery SELECT a, b FROM query_single_shard_table.distributed_table +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT d1.a, d1.b, nullkey_c1_t1.b FROM ((SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) d1 JOIN query_single_shard_table.nullkey_c1_t1 USING (a)) ORDER BY d1.a, d1.b, nullkey_c1_t1.b + a | b | b +--------------------------------------------------------------------- + 3 | 3 | 3 + 3 | 3 | 4 + 4 | 4 | 4 + 5 | 5 | 5 + 6 | 6 | 6 + 7 | 7 | 7 + 8 | 8 | 8 +(7 rows) + +SELECT * FROM nullkey_c1_t1 JOIN (SELECT * FROM distributed_table) d1 USING(a) ORDER BY 1,2,3; +DEBUG: generating subplan XXX_1 for subquery SELECT a, b FROM query_single_shard_table.distributed_table +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT nullkey_c1_t1.a, nullkey_c1_t1.b, d1.b FROM (query_single_shard_table.nullkey_c1_t1 JOIN (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) d1 USING (a)) ORDER BY nullkey_c1_t1.a, nullkey_c1_t1.b, d1.b + a | b | b +--------------------------------------------------------------------- + 3 | 3 | 3 + 3 | 4 | 3 + 4 | 4 | 4 + 5 | 5 | 5 + 6 | 6 | 6 + 7 | 7 | 7 + 8 | 8 | 8 +(7 rows) + +SELECT * FROM distributed_table d1 JOIN (SELECT * FROM nullkey_c1_t1) nullkey_c1_t1 USING(a) ORDER BY 1,2,3; +DEBUG: generating subplan XXX_1 for subquery SELECT a, b FROM query_single_shard_table.nullkey_c1_t1 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT d1.a, d1.b, nullkey_c1_t1.b FROM (query_single_shard_table.distributed_table d1 JOIN (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) nullkey_c1_t1 USING (a)) ORDER BY d1.a, d1.b, nullkey_c1_t1.b + a | b | b +--------------------------------------------------------------------- + 3 | 3 | 3 + 3 | 3 | 4 + 4 | 4 | 4 + 5 | 5 | 5 + 6 | 6 | 6 + 7 | 7 | 7 + 8 | 8 | 8 +(7 rows) + +SELECT * FROM (SELECT * FROM nullkey_c1_t1) nullkey_c1_t1 JOIN distributed_table d1 USING(a) ORDER BY 1,2,3; +DEBUG: generating subplan XXX_1 for subquery SELECT a, b FROM query_single_shard_table.nullkey_c1_t1 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT nullkey_c1_t1.a, nullkey_c1_t1.b, d1.b FROM ((SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) nullkey_c1_t1 JOIN query_single_shard_table.distributed_table d1 USING (a)) ORDER BY nullkey_c1_t1.a, nullkey_c1_t1.b, d1.b + a | b | b +--------------------------------------------------------------------- + 3 | 3 | 3 + 3 | 4 | 3 + 4 | 4 | 4 + 5 | 5 | 5 + 6 | 6 | 6 + 7 | 7 | 7 + 8 | 8 | 8 +(7 rows) + +-- test joins with non-colocated distributed tables, by using subqueries +SELECT * FROM nullkey_c1_t1 t1 JOIN (SELECT * FROM distributed_table) t2 USING (a) JOIN (SELECT * FROM nullkey_c1_t2) t3 USING (a) ORDER BY 1,2,3,4 LIMIT 1; +DEBUG: generating subplan XXX_1 for subquery SELECT a, b FROM query_single_shard_table.distributed_table +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT t1.a, t1.b, t2.b, t3.b FROM ((query_single_shard_table.nullkey_c1_t1 t1 JOIN (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) t2 USING (a)) JOIN (SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM query_single_shard_table.nullkey_c1_t2) t3 USING (a)) ORDER BY t1.a, t1.b, t2.b, t3.b LIMIT 1 + a | b | b | b +--------------------------------------------------------------------- + 3 | 3 | 3 | 3 +(1 row) + +SELECT * FROM (SELECT * FROM nullkey_c1_t1) t1 JOIN nullkey_c2_t1 t2 USING (a) JOIN (SELECT * FROM nullkey_c1_t2) t3 USING (a) ORDER BY 1,2,3,4 LIMIT 1; +DEBUG: push down of limit count: 1 + a | b | b | b +--------------------------------------------------------------------- + 1 | 1 | 0 | 3 +(1 row) + +SELECT * FROM distributed_table t1 JOIN (SELECT * FROM nullkey_c1_t1) t2 USING (a) JOIN (SELECT b as a FROM distributed_table) t3 USING (a) ORDER BY 1,2,3 LIMIT 1; +DEBUG: generating subplan XXX_1 for subquery SELECT a, b FROM query_single_shard_table.nullkey_c1_t1 +DEBUG: generating subplan XXX_2 for subquery SELECT b AS a FROM query_single_shard_table.distributed_table +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT t1.a, t1.b, t2.b FROM ((query_single_shard_table.distributed_table t1 JOIN (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) t2 USING (a)) JOIN (SELECT intermediate_result.a FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) t3 USING (a)) ORDER BY t1.a, t1.b, t2.b LIMIT 1 +DEBUG: push down of limit count: 1 + a | b | b +--------------------------------------------------------------------- + 3 | 3 | 3 +(1 row) + +SELECT * FROM (SELECT * FROM nullkey_c2_t1) t1 JOIN nullkey_c1_t1 t2 USING (a) JOIN (SELECT * FROM nullkey_c2_t1) t3 USING (a) ORDER BY 1,2,3,4 LIMIT 1; +DEBUG: push down of limit count: 1 + a | b | b | b +--------------------------------------------------------------------- + 1 | 0 | 1 | 0 +(1 row) + +SELECT * FROM nullkey_c1_t1 t1 JOIN (SELECT * FROM distributed_table) t2 USING (a) JOIN (SELECT * FROM distributed_table) t3 USING (a) ORDER BY 1,2,3,4 LIMIT 1; +DEBUG: push down of limit count: 1 + a | b | b | b +--------------------------------------------------------------------- + 3 | 3 | 3 | 3 +(1 row) + +SELECT * FROM (SELECT * FROM nullkey_c1_t1) t1 JOIN nullkey_c2_t1 t2 USING (a) JOIN (SELECT * FROM nullkey_c2_t1) t3 USING (a) ORDER BY 1,2,3,4 LIMIT 1; +DEBUG: push down of limit count: 1 + a | b | b | b +--------------------------------------------------------------------- + 1 | 1 | 0 | 0 +(1 row) + +SELECT * FROM distributed_table t1 JOIN (SELECT * FROM nullkey_c1_t1) t2 USING (a) JOIN (SELECT * FROM nullkey_c1_t1) t3 USING (a) ORDER BY 1,2,3,4 LIMIT 1; +DEBUG: generating subplan XXX_1 for subquery SELECT a, b FROM query_single_shard_table.nullkey_c1_t1 +DEBUG: generating subplan XXX_2 for subquery SELECT a, b FROM query_single_shard_table.nullkey_c1_t1 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT t1.a, t1.b, t2.b, t3.b FROM ((query_single_shard_table.distributed_table t1 JOIN (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) t2 USING (a)) JOIN (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) t3 USING (a)) ORDER BY t1.a, t1.b, t2.b, t3.b LIMIT 1 +DEBUG: push down of limit count: 1 + a | b | b | b +--------------------------------------------------------------------- + 3 | 3 | 3 | 3 +(1 row) + +SELECT * FROM (SELECT * FROM nullkey_c2_t1) t1 JOIN nullkey_c1_t1 t2 USING (a) JOIN (SELECT * FROM nullkey_c1_t1) t3 USING (a) ORDER BY 1,2,3,4 LIMIT 1; +DEBUG: push down of limit count: 1 + a | b | b | b +--------------------------------------------------------------------- + 1 | 0 | 1 | 1 +(1 row) + +SELECT * FROM nullkey_c1_t1 t1 JOIN (SELECT * FROM nullkey_c1_t1) t2 USING (a) JOIN distributed_table t3 USING (a) ORDER BY 1,2,3,4 LIMIT 1; +DEBUG: push down of limit count: 1 + a | b | b | b +--------------------------------------------------------------------- + 3 | 3 | 3 | 3 +(1 row) + +SELECT * FROM nullkey_c1_t1 t1 JOIN nullkey_c1_t1 t2 USING (a) JOIN nullkey_c2_t1 t3 USING (a) ORDER BY 1,2,3,4 LIMIT 1; +DEBUG: push down of limit count: 1 + a | b | b | b +--------------------------------------------------------------------- + 1 | 1 | 1 | 0 +(1 row) + +SELECT * FROM (SELECT * FROM distributed_table) t1 JOIN distributed_table t2 USING (a) JOIN (SELECT * FROM nullkey_c1_t1) t3 USING (a) ORDER BY 1,2,3,4 LIMIT 1; +DEBUG: push down of limit count: 1 + a | b | b | b +--------------------------------------------------------------------- + 3 | 3 | 3 | 3 +(1 row) + +SELECT * FROM (SELECT * FROM nullkey_c2_t1) t1 JOIN nullkey_c2_t1 t2 USING (a) JOIN (SELECT * FROM nullkey_c1_t1) t3 USING (a) ORDER BY 1,2,3,4 LIMIT 1; +DEBUG: push down of limit count: 1 + a | b | b | b +--------------------------------------------------------------------- + 1 | 0 | 0 | 1 +(1 row) + SELECT COUNT(*) FROM nullkey_c1_t1 t1 JOIN LATERAL ( SELECT * FROM distributed_table t2 WHERE t2.b > t1.a ) q USING(a); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables + count +--------------------------------------------------------------------- + 0 +(1 row) + +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +JOIN LATERAL ( + SELECT *, random() FROM distributed_table t2 WHERE t2.b > t1.a +) q USING(a); +ERROR: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns SELECT COUNT(*) FROM distributed_table t1 JOIN LATERAL ( SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a ) q USING(a); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables + count +--------------------------------------------------------------------- + 1 +(1 row) + +SET client_min_messages TO DEBUG2; +SET citus.enable_repartition_joins TO OFF; -- outer joins with different table types SELECT COUNT(*) FROM nullkey_c1_t1 LEFT JOIN reference_table USING(a); DEBUG: Creating router plan @@ -452,23 +841,101 @@ DEBUG: Creating router plan (1 row) SELECT COUNT(*) FROM nullkey_c1_t1 LEFT JOIN citus_local_table USING(a); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: Wrapping relation "citus_local_table" to a subquery +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_1 for subquery SELECT a FROM query_single_shard_table.citus_local_table WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (query_single_shard_table.nullkey_c1_t1 LEFT JOIN (SELECT citus_local_table_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) citus_local_table_1) citus_local_table USING (a)) +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 11 +(1 row) + SELECT COUNT(*) FROM citus_local_table LEFT JOIN nullkey_c1_t1 USING(a); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: Wrapping relation "citus_local_table" to a subquery +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_1 for subquery SELECT a FROM query_single_shard_table.citus_local_table WHERE true +DEBUG: recursively planning right side of the left join since the outer side is a recurring rel +DEBUG: recursively planning distributed relation "nullkey_c1_t1" since it is part of a distributed join node that is outer joined with a recurring rel +DEBUG: Wrapping relation "nullkey_c1_t1" to a subquery +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_2 for subquery SELECT a FROM query_single_shard_table.nullkey_c1_t1 WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((SELECT citus_local_table_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) citus_local_table_1) citus_local_table LEFT JOIN (SELECT nullkey_c1_t1_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) nullkey_c1_t1_1) nullkey_c1_t1 USING (a)) +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 14 +(1 row) + SELECT COUNT(*) FROM nullkey_c1_t1 LEFT JOIN postgres_local_table USING(a); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: Wrapping relation "postgres_local_table" to a subquery +DEBUG: generating subplan XXX_1 for subquery SELECT a FROM query_single_shard_table.postgres_local_table WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (query_single_shard_table.nullkey_c1_t1 LEFT JOIN (SELECT postgres_local_table_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) postgres_local_table_1) postgres_local_table USING (a)) +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 11 +(1 row) + SELECT COUNT(*) FROM postgres_local_table LEFT JOIN nullkey_c1_t1 USING(a); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: Wrapping relation "postgres_local_table" to a subquery +DEBUG: generating subplan XXX_1 for subquery SELECT a FROM query_single_shard_table.postgres_local_table WHERE true +DEBUG: recursively planning right side of the left join since the outer side is a recurring rel +DEBUG: recursively planning distributed relation "nullkey_c1_t1" since it is part of a distributed join node that is outer joined with a recurring rel +DEBUG: Wrapping relation "nullkey_c1_t1" to a subquery +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_2 for subquery SELECT a FROM query_single_shard_table.nullkey_c1_t1 WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((SELECT postgres_local_table_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) postgres_local_table_1) postgres_local_table LEFT JOIN (SELECT nullkey_c1_t1_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) nullkey_c1_t1_1) nullkey_c1_t1 USING (a)) +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 6 +(1 row) + SELECT COUNT(*) FROM nullkey_c1_t1 FULL JOIN citus_local_table USING(a); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: Wrapping relation "citus_local_table" to a subquery +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_1 for subquery SELECT a FROM query_single_shard_table.citus_local_table WHERE true +DEBUG: recursively planning left side of the full join since the other side is a recurring rel +DEBUG: recursively planning distributed relation "nullkey_c1_t1" since it is part of a distributed join node that is outer joined with a recurring rel +DEBUG: Wrapping relation "nullkey_c1_t1" to a subquery +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_2 for subquery SELECT a FROM query_single_shard_table.nullkey_c1_t1 WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((SELECT nullkey_c1_t1_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) nullkey_c1_t1_1) nullkey_c1_t1 FULL JOIN (SELECT citus_local_table_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) citus_local_table_1) citus_local_table USING (a)) +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 14 +(1 row) + SELECT COUNT(*) FROM nullkey_c1_t1 FULL JOIN postgres_local_table USING(a); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: Wrapping relation "postgres_local_table" to a subquery +DEBUG: generating subplan XXX_1 for subquery SELECT a FROM query_single_shard_table.postgres_local_table WHERE true +DEBUG: recursively planning left side of the full join since the other side is a recurring rel +DEBUG: recursively planning distributed relation "nullkey_c1_t1" since it is part of a distributed join node that is outer joined with a recurring rel +DEBUG: Wrapping relation "nullkey_c1_t1" to a subquery +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_2 for subquery SELECT a FROM query_single_shard_table.nullkey_c1_t1 WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((SELECT nullkey_c1_t1_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) nullkey_c1_t1_1) nullkey_c1_t1 FULL JOIN (SELECT postgres_local_table_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) postgres_local_table_1) postgres_local_table USING (a)) +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 13 +(1 row) + SELECT COUNT(*) FROM nullkey_c1_t1 FULL JOIN reference_table USING(a); DEBUG: Creating router plan count @@ -476,12 +943,22 @@ DEBUG: Creating router plan 12 (1 row) +SET citus.enable_repartition_joins TO ON; +SET client_min_messages TO DEBUG1; SELECT COUNT(*) FROM nullkey_c1_t1 JOIN append_table USING(a); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Router planner does not support append-partitioned tables. + count +--------------------------------------------------------------------- + 2 +(1 row) + SELECT COUNT(*) FROM nullkey_c1_t1 JOIN range_table USING(a); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables + count +--------------------------------------------------------------------- + 9 +(1 row) + +SET client_min_messages TO DEBUG2; +SET citus.enable_repartition_joins TO OFF; SET citus.enable_non_colocated_router_query_pushdown TO ON; SELECT COUNT(*) FROM nullkey_c1_t1 JOIN range_table USING(a) WHERE range_table.a = 20; DEBUG: Creating router plan @@ -491,9 +968,16 @@ DEBUG: Creating router plan (1 row) SET citus.enable_non_colocated_router_query_pushdown TO OFF; +SET citus.enable_repartition_joins TO ON; +SET client_min_messages TO DEBUG1; SELECT COUNT(*) FROM nullkey_c1_t1 JOIN range_table USING(a) WHERE range_table.a = 20; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables + count +--------------------------------------------------------------------- + 0 +(1 row) + +SET client_min_messages TO DEBUG2; +SET citus.enable_repartition_joins TO OFF; RESET citus.enable_non_colocated_router_query_pushdown; -- lateral / semi / anti joins with different table types -- with a reference table @@ -612,189 +1096,278 @@ SELECT COUNT(*) FROM nullkey_c1_t1 t1 LEFT JOIN LATERAL ( SELECT * FROM distributed_table t2 WHERE t2.b > t1.a ) q USING(a); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: router planner does not support queries that reference non-colocated distributed tables +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +ERROR: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns SELECT COUNT(*) FROM nullkey_c1_t1 t1 WHERE EXISTS ( SELECT * FROM distributed_table t2 WHERE t2.b > t1.a ); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: router planner does not support queries that reference non-colocated distributed tables +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +ERROR: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns SELECT COUNT(*) FROM nullkey_c1_t1 t1 WHERE NOT EXISTS ( SELECT * FROM distributed_table t2 WHERE t2.b > t1.a ); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: router planner does not support queries that reference non-colocated distributed tables +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +ERROR: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns SELECT COUNT(*) FROM nullkey_c1_t1 t1 WHERE t1.b IN ( SELECT b+1 FROM distributed_table t2 WHERE t2.b = t1.a ); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Router planner cannot handle multi-shard select queries +DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +ERROR: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns SELECT COUNT(*) FROM nullkey_c1_t1 t1 WHERE t1.b NOT IN ( SELECT a FROM distributed_table t2 WHERE t2.b > t1.a ); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Router planner cannot handle multi-shard select queries +DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +ERROR: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns SELECT COUNT(*) FROM distributed_table t1 LEFT JOIN LATERAL ( SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a ) q USING(a); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: router planner does not support queries that reference non-colocated distributed tables +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +ERROR: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns SELECT COUNT(*) FROM distributed_table t1 WHERE EXISTS ( SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a ); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: router planner does not support queries that reference non-colocated distributed tables +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +ERROR: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns SELECT COUNT(*) FROM distributed_table t1 WHERE t1.b IN ( SELECT b+1 FROM nullkey_c1_t1 t2 WHERE t2.b = t1.a ); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Router planner cannot handle multi-shard select queries +DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +ERROR: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns SELECT COUNT(*) FROM distributed_table t1 WHERE t1.b NOT IN ( SELECT a FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a ); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Router planner cannot handle multi-shard select queries +DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +ERROR: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns -- with postgres / citus local tables SELECT COUNT(*) FROM nullkey_c1_t1 t1 LEFT JOIN LATERAL ( SELECT * FROM citus_local_table t2 WHERE t2.b > t1.a ) q USING(a); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +ERROR: direct joins between distributed and local tables are not supported SELECT COUNT(*) FROM nullkey_c1_t1 t1 WHERE EXISTS ( SELECT * FROM citus_local_table t2 WHERE t2.b > t1.a ); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +ERROR: direct joins between distributed and local tables are not supported SELECT COUNT(*) FROM nullkey_c1_t1 t1 WHERE NOT EXISTS ( SELECT * FROM citus_local_table t2 WHERE t2.b > t1.a ); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +ERROR: direct joins between distributed and local tables are not supported SELECT COUNT(*) FROM nullkey_c1_t1 t1 WHERE t1.b IN ( SELECT b+1 FROM citus_local_table t2 WHERE t2.b = t1.a ); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: found no worker with all shard placements +DEBUG: found no worker with all shard placements +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +ERROR: direct joins between distributed and local tables are not supported SELECT COUNT(*) FROM nullkey_c1_t1 t1 WHERE t1.b NOT IN ( SELECT a FROM citus_local_table t2 WHERE t2.b > t1.a ); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: found no worker with all shard placements +DEBUG: found no worker with all shard placements +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +ERROR: direct joins between distributed and local tables are not supported SELECT COUNT(*) FROM nullkey_c1_t1 t1 JOIN LATERAL ( SELECT * FROM citus_local_table t2 WHERE t2.b > t1.a ) q USING(a); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +ERROR: direct joins between distributed and local tables are not supported +-- The following and a few other tests in this file unnecessarily go through +-- recursive planning. This is because we recursive plan distributed tables +-- when they are referred in the inner side of an outer join, if the outer +-- side is a recurring rel. In future, we can optimize that such that we +-- can skip recursively planning the single-shard table because such a join +-- wouldn't result in returning recurring tuples. +-- +-- And specifically for the tests that contains a sublink (as below), things +-- get even more interesting. We try to recursively plan the single-shard +-- table but we cannot do so due to the sublink. However, the final query +-- can go through router planner and hence is supported. SELECT COUNT(*) FROM citus_local_table t1 LEFT JOIN LATERAL ( SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a ) q USING(a); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: Wrapping relation "citus_local_table" "t1" to a subquery +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_1 for subquery SELECT a FROM query_single_shard_table.citus_local_table t1 WHERE true +DEBUG: recursively planning right side of the left join since the outer side is a recurring rel +DEBUG: recursively planning the distributed subquery since it is part of a distributed join node that is outer joined with a recurring rel +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((SELECT t1_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) t1_1) t1 LEFT JOIN LATERAL (SELECT t2.a, t2.b FROM query_single_shard_table.nullkey_c1_t1 t2 WHERE (t2.b OPERATOR(pg_catalog.>) t1.a)) q USING (a)) +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 11 +(1 row) + SELECT COUNT(*) FROM postgres_local_table t1 LEFT JOIN LATERAL ( SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a ) q USING(a); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: Wrapping relation "postgres_local_table" "t1" to a subquery +DEBUG: generating subplan XXX_1 for subquery SELECT a FROM query_single_shard_table.postgres_local_table t1 WHERE true +DEBUG: recursively planning right side of the left join since the outer side is a recurring rel +DEBUG: recursively planning the distributed subquery since it is part of a distributed join node that is outer joined with a recurring rel +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((SELECT t1_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) t1_1) t1 LEFT JOIN LATERAL (SELECT t2.a, t2.b FROM query_single_shard_table.nullkey_c1_t1 t2 WHERE (t2.b OPERATOR(pg_catalog.>) t1.a)) q USING (a)) +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 6 +(1 row) + SELECT COUNT(*) FROM citus_local_table t1 WHERE EXISTS ( SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a ); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +ERROR: direct joins between distributed and local tables are not supported SELECT COUNT(*) FROM citus_local_table t1 WHERE t1.b IN ( SELECT b+1 FROM nullkey_c1_t1 t2 WHERE t2.b = t1.a ); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: found no worker with all shard placements +DEBUG: found no worker with all shard placements +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +ERROR: direct joins between distributed and local tables are not supported SELECT COUNT(*) FROM citus_local_table t1 WHERE t1.b NOT IN ( SELECT a FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a ); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: found no worker with all shard placements +DEBUG: found no worker with all shard placements +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +ERROR: direct joins between distributed and local tables are not supported SELECT COUNT(*) FROM citus_local_table t1 JOIN LATERAL ( SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a ) q USING(a); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: Wrapping relation "citus_local_table" "t1" to a subquery +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_1 for subquery SELECT a FROM query_single_shard_table.citus_local_table t1 WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((SELECT t1_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) t1_1) t1 JOIN LATERAL (SELECT t2.a, t2.b FROM query_single_shard_table.nullkey_c1_t1 t2 WHERE (t2.b OPERATOR(pg_catalog.>) t1.a)) q USING (a)) +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 2 +(1 row) + SELECT COUNT(*) FROM nullkey_c1_t1 t1 LEFT JOIN LATERAL ( SELECT * FROM postgres_local_table t2 WHERE t2.b > t1.a ) q USING(a); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +ERROR: direct joins between distributed and local tables are not supported +HINT: Use CTE's or subqueries to select from local tables and use them in joins SELECT COUNT(*) FROM nullkey_c1_t1 t1 WHERE EXISTS ( SELECT * FROM postgres_local_table t2 WHERE t2.b > t1.a ); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +ERROR: direct joins between distributed and local tables are not supported +HINT: Use CTE's or subqueries to select from local tables and use them in joins SELECT COUNT(*) FROM nullkey_c1_t1 t1 WHERE NOT EXISTS ( SELECT * FROM postgres_local_table t2 WHERE t2.b > t1.a ); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +ERROR: direct joins between distributed and local tables are not supported +HINT: Use CTE's or subqueries to select from local tables and use them in joins SELECT COUNT(*) FROM nullkey_c1_t1 t1 WHERE t1.b IN ( SELECT b+1 FROM postgres_local_table t2 WHERE t2.b = t1.a ); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: found no worker with all shard placements +DEBUG: found no worker with all shard placements +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +ERROR: direct joins between distributed and local tables are not supported +HINT: Use CTE's or subqueries to select from local tables and use them in joins SELECT COUNT(*) FROM nullkey_c1_t1 t1 WHERE t1.b NOT IN ( SELECT a FROM postgres_local_table t2 WHERE t2.b > t1.a ); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: found no worker with all shard placements +DEBUG: found no worker with all shard placements +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +ERROR: direct joins between distributed and local tables are not supported +HINT: Use CTE's or subqueries to select from local tables and use them in joins SELECT COUNT(*) FROM nullkey_c1_t1 t1 JOIN LATERAL ( SELECT * FROM postgres_local_table t2 WHERE t2.b > t1.a ) q USING(a); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +ERROR: direct joins between distributed and local tables are not supported +HINT: Use CTE's or subqueries to select from local tables and use them in joins SELECT COUNT(*) FROM postgres_local_table t1 WHERE EXISTS ( SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a ); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +ERROR: direct joins between distributed and local tables are not supported +HINT: Use CTE's or subqueries to select from local tables and use them in joins SELECT COUNT(*) FROM postgres_local_table t1 WHERE t1.b IN ( SELECT b+1 FROM nullkey_c1_t1 t2 WHERE t2.b = t1.a ); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: found no worker with all shard placements +DEBUG: found no worker with all shard placements +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +ERROR: direct joins between distributed and local tables are not supported +HINT: Use CTE's or subqueries to select from local tables and use them in joins SELECT COUNT(*) FROM postgres_local_table t1 WHERE t1.b NOT IN ( SELECT a FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a ); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: found no worker with all shard placements +DEBUG: found no worker with all shard placements +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +ERROR: direct joins between distributed and local tables are not supported +HINT: Use CTE's or subqueries to select from local tables and use them in joins SELECT COUNT(*) FROM postgres_local_table t1 JOIN LATERAL ( SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a ) q USING(a); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: Wrapping relation "postgres_local_table" "t1" to a subquery +DEBUG: generating subplan XXX_1 for subquery SELECT a FROM query_single_shard_table.postgres_local_table t1 WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM ((SELECT t1_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) t1_1) t1 JOIN LATERAL (SELECT t2.a, t2.b FROM query_single_shard_table.nullkey_c1_t1 t2 WHERE (t2.b OPERATOR(pg_catalog.>) t1.a)) q USING (a)) +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 0 +(1 row) + -- insert .. select -- between two colocated single-shard tables -- The target list of "distributed statement"s that we send to workers @@ -890,8 +1463,18 @@ WHERE cte_nullkey_c1_t1.a > 3 AND cte_distributed_table.a < 5; DEBUG: CTE cte_nullkey_c1_t1 is going to be inlined via distributed planning DEBUG: CTE cte_postgres_local_table is going to be inlined via distributed planning DEBUG: CTE cte_distributed_table is going to be inlined via distributed planning -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Local tables cannot be used in distributed queries. +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: generating subplan XXX_1 for subquery SELECT a, b FROM query_single_shard_table.postgres_local_table +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_2 for subquery SELECT a, b FROM query_single_shard_table.nullkey_c1_t1 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT distributed_table.a, distributed_table.b FROM query_single_shard_table.distributed_table) cte_distributed_table, (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) cte_nullkey_c1_t1, (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) cte_postgres_local_table WHERE ((cte_nullkey_c1_t1.a OPERATOR(pg_catalog.>) 3) AND (cte_distributed_table.a OPERATOR(pg_catalog.<) 5)) +DEBUG: Router planner cannot handle multi-shard select queries + count +--------------------------------------------------------------------- + 78144 +(1 row) + -- test recursive ctes WITH level_0 AS ( WITH level_1 AS ( @@ -927,8 +1510,15 @@ WITH level_0 AS ( SELECT COUNT(*) FROM level_0; DEBUG: CTE level_0 is going to be inlined via distributed planning DEBUG: CTE level_1 is going to be inlined via distributed planning -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: router planner does not support queries that reference non-colocated distributed tables +DEBUG: router planner does not support queries that reference non-colocated distributed tables +DEBUG: generating subplan XXX_1 for CTE level_0: WITH level_1 AS (WITH RECURSIVE level_2_recursive(x) AS (VALUES (1) UNION ALL SELECT (nullkey_c1_t1.a OPERATOR(pg_catalog.+) 1) FROM (query_single_shard_table.nullkey_c1_t1 JOIN level_2_recursive level_2_recursive_1 ON ((nullkey_c1_t1.a OPERATOR(pg_catalog.=) level_2_recursive_1.x))) WHERE (nullkey_c1_t1.a OPERATOR(pg_catalog.<) 100)) SELECT level_2_recursive.x, distributed_table.a, distributed_table.b FROM (level_2_recursive JOIN query_single_shard_table.distributed_table ON ((level_2_recursive.x OPERATOR(pg_catalog.=) distributed_table.a)))) SELECT x, a, b FROM level_1 +DEBUG: CTE level_1 is going to be inlined via distributed planning +DEBUG: router planner does not support queries that reference non-colocated distributed tables +DEBUG: router planner does not support queries that reference non-colocated distributed tables +DEBUG: generating subplan XXX_1 for CTE level_1: WITH RECURSIVE level_2_recursive(x) AS (VALUES (1) UNION ALL SELECT (nullkey_c1_t1.a OPERATOR(pg_catalog.+) 1) FROM (query_single_shard_table.nullkey_c1_t1 JOIN level_2_recursive level_2_recursive_1 ON ((nullkey_c1_t1.a OPERATOR(pg_catalog.=) level_2_recursive_1.x))) WHERE (nullkey_c1_t1.a OPERATOR(pg_catalog.<) 100)) SELECT level_2_recursive.x, distributed_table.a, distributed_table.b FROM (level_2_recursive JOIN query_single_shard_table.distributed_table ON ((level_2_recursive.x OPERATOR(pg_catalog.=) distributed_table.a))) +DEBUG: router planner does not support queries that reference non-colocated distributed tables +ERROR: recursive CTEs are not supported in distributed queries -- grouping set SELECT id, substring(title, 2, 1) AS subtitle, count(*) @@ -965,6 +1555,101 @@ DEBUG: Creating router plan ausable | 42 (12 rows) +-- test having clause +SELECT COUNT(*), b FROM nullkey_c1_t1 GROUP BY 2 +HAVING (SELECT COUNT(*) FROM nullkey_c1_t2) > 0 +ORDER BY 1,2; +DEBUG: Creating router plan + count | b +--------------------------------------------------------------------- + 2 | 9 + 2 | 10 + 3 | 0 + 4 | 1 + 4 | 8 + 6 | 6 + 6 | 7 + 8 | 3 + 8 | 4 + 8 | 5 + 9 | 2 +(11 rows) + +SELECT COUNT(*), b FROM nullkey_c1_t1 GROUP BY 2 +HAVING (SELECT COUNT(*) FROM nullkey_c2_t1) > 0 +ORDER BY 1,2; +DEBUG: found no worker with all shard placements +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_1 for subquery SELECT count(*) AS count FROM query_single_shard_table.nullkey_c2_t1 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count, b FROM query_single_shard_table.nullkey_c1_t1 GROUP BY b HAVING ((SELECT intermediate_result.count FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(count bigint)) OPERATOR(pg_catalog.>) 0) ORDER BY (count(*)), b +DEBUG: Creating router plan + count | b +--------------------------------------------------------------------- + 2 | 9 + 2 | 10 + 3 | 0 + 4 | 1 + 4 | 8 + 6 | 6 + 6 | 7 + 8 | 3 + 8 | 4 + 8 | 5 + 9 | 2 +(11 rows) + +SELECT COUNT(*), b FROM nullkey_c1_t1 GROUP BY 2 +HAVING (SELECT COUNT(*) FROM distributed_table) > 0 +ORDER BY 1,2; +DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: generating subplan XXX_1 for subquery SELECT count(*) AS count FROM query_single_shard_table.distributed_table +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count, b FROM query_single_shard_table.nullkey_c1_t1 GROUP BY b HAVING ((SELECT intermediate_result.count FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(count bigint)) OPERATOR(pg_catalog.>) 0) ORDER BY (count(*)), b +DEBUG: Creating router plan + count | b +--------------------------------------------------------------------- + 2 | 9 + 2 | 10 + 3 | 0 + 4 | 1 + 4 | 8 + 6 | 6 + 6 | 7 + 8 | 3 + 8 | 4 + 8 | 5 + 9 | 2 +(11 rows) + +SELECT COUNT(*), b FROM nullkey_c1_t1 t4 GROUP BY 2 +HAVING ( + SELECT COUNT(*) FROM nullkey_c1_t1 t1 JOIN (SELECT * FROM nullkey_c1_t2) t2 USING (a) JOIN (SELECT * FROM nullkey_c1_t2) t3 USING (a) + WHERE t2.b > t4.b +) > 5 +ORDER BY 1,2; +DEBUG: Creating router plan + count | b +--------------------------------------------------------------------- + 3 | 0 + 4 | 1 + 6 | 6 + 8 | 3 + 8 | 4 + 8 | 5 + 9 | 2 +(7 rows) + +SELECT COUNT(*), b FROM distributed_table t4 GROUP BY 2 +HAVING ( + SELECT COUNT(*) FROM nullkey_c1_t1 t1 JOIN (SELECT * FROM distributed_table) t2 USING (a) JOIN (SELECT * FROM nullkey_c1_t2) t3 USING (a) + WHERE t2.b > t4.b +) > 5 +ORDER BY 1,2; +DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: generating subplan XXX_1 for subquery SELECT a, b FROM query_single_shard_table.distributed_table +ERROR: Subqueries in HAVING cannot refer to outer query -- test prepared statements -- prepare queries can be router plannable PREPARE author_1_articles as @@ -1118,8 +1803,7 @@ EXECUTE author_articles_update(NULL); SET client_min_messages TO DEBUG1; INSERT INTO bigserial_test (x, y) SELECT x, y FROM bigserial_test; DEBUG: volatile functions are not allowed in distributed INSERT ... SELECT queries -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: Sequences cannot be used in router queries +DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO bigserial_test (x, y) SELECT a, a FROM reference_table; DEBUG: volatile functions are not allowed in distributed INSERT ... SELECT queries DEBUG: Collecting INSERT ... SELECT results on coordinator @@ -1411,8 +2095,7 @@ DEBUG: distributed INSERT ... SELECT can only select from distributed tables DEBUG: Collecting INSERT ... SELECT results on coordinator -- non-immutable function INSERT INTO modify_fast_path (key, value_1) VALUES (2,1) RETURNING value_1, random() * key; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: non-IMMUTABLE functions are not allowed in the RETURNING clause +ERROR: non-IMMUTABLE functions are not allowed in the RETURNING clause SET client_min_messages TO DEBUG2; -- update / delete UPDATE nullkey_c1_t1 SET a = 1 WHERE b = 5; @@ -1422,102 +2105,154 @@ UPDATE nullkey_c1_t1 SET a = 1 WHERE a = 5; DEBUG: Distributed planning for a fast-path router query DEBUG: Creating router plan UPDATE nullkey_c1_t1 SET a = random(); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: functions used in UPDATE queries on distributed tables must not be VOLATILE +DEBUG: functions used in UPDATE queries on distributed tables must not be VOLATILE +ERROR: functions used in UPDATE queries on distributed tables must not be VOLATILE UPDATE nullkey_c1_t1 SET a = 1 WHERE a = random(); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: functions used in the WHERE/ON/WHEN clause of modification queries on distributed tables must not be VOLATILE +DEBUG: functions used in the WHERE/ON/WHEN clause of modification queries on distributed tables must not be VOLATILE +ERROR: functions used in the WHERE/ON/WHEN clause of modification queries on distributed tables must not be VOLATILE DELETE FROM nullkey_c1_t1 WHERE b = 5; DEBUG: Distributed planning for a fast-path router query DEBUG: Creating router plan DELETE FROM nullkey_c1_t1 WHERE a = random(); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: functions used in the WHERE/ON/WHEN clause of modification queries on distributed tables must not be VOLATILE +DEBUG: functions used in the WHERE/ON/WHEN clause of modification queries on distributed tables must not be VOLATILE +ERROR: functions used in the WHERE/ON/WHEN clause of modification queries on distributed tables must not be VOLATILE -- simple update queries between different table types / colocated tables UPDATE nullkey_c1_t1 SET b = 5 FROM nullkey_c1_t2 WHERE nullkey_c1_t1.b = nullkey_c1_t2.b; DEBUG: Creating router plan UPDATE nullkey_c1_t1 SET b = 5 FROM nullkey_c2_t1 WHERE nullkey_c1_t1.b = nullkey_c2_t1.b; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: found no worker with all shard placements +DEBUG: found no worker with all shard placements +ERROR: found no worker with all shard placements UPDATE nullkey_c1_t1 SET b = 5 FROM reference_table WHERE nullkey_c1_t1.b = reference_table.b; DEBUG: Creating router plan UPDATE nullkey_c1_t1 SET b = 5 FROM distributed_table WHERE nullkey_c1_t1.b = distributed_table.b; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +ERROR: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns UPDATE nullkey_c1_t1 SET b = 5 FROM distributed_table WHERE nullkey_c1_t1.b = distributed_table.a; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +ERROR: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns UPDATE nullkey_c1_t1 SET b = 5 FROM citus_local_table WHERE nullkey_c1_t1.b = citus_local_table.b; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: local table citus_local_table cannot be joined with these distributed tables +DEBUG: local table citus_local_table cannot be joined with these distributed tables +DEBUG: Wrapping relation "citus_local_table" to a subquery +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_1 for subquery SELECT b FROM query_single_shard_table.citus_local_table WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: UPDATE query_single_shard_table.nullkey_c1_t1 SET b = 5 FROM (SELECT NULL::integer AS a, citus_local_table_1.b FROM (SELECT intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(b integer)) citus_local_table_1) citus_local_table WHERE (nullkey_c1_t1.b OPERATOR(pg_catalog.=) citus_local_table.b) +DEBUG: Creating router plan UPDATE nullkey_c1_t1 SET b = 5 FROM postgres_local_table WHERE nullkey_c1_t1.b = postgres_local_table.b; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: relation postgres_local_table is not distributed +DEBUG: relation postgres_local_table is not distributed +DEBUG: Wrapping relation "postgres_local_table" to a subquery +DEBUG: generating subplan XXX_1 for subquery SELECT b FROM query_single_shard_table.postgres_local_table WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: UPDATE query_single_shard_table.nullkey_c1_t1 SET b = 5 FROM (SELECT NULL::integer AS a, postgres_local_table_1.b FROM (SELECT intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(b integer)) postgres_local_table_1) postgres_local_table WHERE (nullkey_c1_t1.b OPERATOR(pg_catalog.=) postgres_local_table.b) +DEBUG: Creating router plan UPDATE reference_table SET b = 5 FROM nullkey_c1_t1 WHERE nullkey_c1_t1.b = reference_table.b; ERROR: cannot perform select on a distributed table and modify a reference table UPDATE distributed_table SET b = 5 FROM nullkey_c1_t1 WHERE nullkey_c1_t1.b = distributed_table.b; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +ERROR: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns UPDATE distributed_table SET b = 5 FROM nullkey_c1_t1 WHERE nullkey_c1_t1.b = distributed_table.a; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +ERROR: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns UPDATE citus_local_table SET b = 5 FROM nullkey_c1_t1 WHERE nullkey_c1_t1.b = citus_local_table.b; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: local table citus_local_table cannot be joined with these distributed tables +DEBUG: local table citus_local_table cannot be joined with these distributed tables +DEBUG: Wrapping relation "nullkey_c1_t1" to a subquery +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_1 for subquery SELECT b FROM query_single_shard_table.nullkey_c1_t1 WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: UPDATE query_single_shard_table.citus_local_table SET b = 5 FROM (SELECT NULL::integer AS a, nullkey_c1_t1_1.b FROM (SELECT intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(b integer)) nullkey_c1_t1_1) nullkey_c1_t1 WHERE (nullkey_c1_t1.b OPERATOR(pg_catalog.=) citus_local_table.b) +DEBUG: Creating router plan UPDATE postgres_local_table SET b = 5 FROM nullkey_c1_t1 WHERE nullkey_c1_t1.b = postgres_local_table.b; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: relation postgres_local_table is not distributed +DEBUG: relation postgres_local_table is not distributed +DEBUG: Wrapping relation "nullkey_c1_t1" to a subquery +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_1 for subquery SELECT b FROM query_single_shard_table.nullkey_c1_t1 WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: UPDATE query_single_shard_table.postgres_local_table SET b = 5 FROM (SELECT NULL::integer AS a, nullkey_c1_t1_1.b FROM (SELECT intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(b integer)) nullkey_c1_t1_1) nullkey_c1_t1 WHERE (nullkey_c1_t1.b OPERATOR(pg_catalog.=) postgres_local_table.b) +DEBUG: Creating router plan -- simple delete queries between different table types / colocated tables DELETE FROM nullkey_c1_t1 USING nullkey_c1_t2 WHERE nullkey_c1_t1.b = nullkey_c1_t2.b; DEBUG: Creating router plan DELETE FROM nullkey_c1_t1 USING nullkey_c2_t1 WHERE nullkey_c1_t1.b = nullkey_c2_t1.b; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: found no worker with all shard placements +DEBUG: found no worker with all shard placements +ERROR: found no worker with all shard placements DELETE FROM nullkey_c1_t1 USING reference_table WHERE nullkey_c1_t1.b = reference_table.b; DEBUG: Creating router plan DELETE FROM nullkey_c1_t1 USING distributed_table WHERE nullkey_c1_t1.b = distributed_table.b; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +ERROR: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DELETE FROM nullkey_c1_t1 USING distributed_table WHERE nullkey_c1_t1.b = distributed_table.a; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +ERROR: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DELETE FROM nullkey_c1_t1 USING citus_local_table WHERE nullkey_c1_t1.b = citus_local_table.b; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: local table citus_local_table cannot be joined with these distributed tables +DEBUG: local table citus_local_table cannot be joined with these distributed tables +DEBUG: Wrapping relation "citus_local_table" to a subquery +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_1 for subquery SELECT b FROM query_single_shard_table.citus_local_table WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: DELETE FROM query_single_shard_table.nullkey_c1_t1 USING (SELECT NULL::integer AS a, citus_local_table_1.b FROM (SELECT intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(b integer)) citus_local_table_1) citus_local_table WHERE (nullkey_c1_t1.b OPERATOR(pg_catalog.=) citus_local_table.b) +DEBUG: Creating router plan DELETE FROM nullkey_c1_t1 USING postgres_local_table WHERE nullkey_c1_t1.b = postgres_local_table.b; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: relation postgres_local_table is not distributed +DEBUG: relation postgres_local_table is not distributed +DEBUG: Wrapping relation "postgres_local_table" to a subquery +DEBUG: generating subplan XXX_1 for subquery SELECT b FROM query_single_shard_table.postgres_local_table WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: DELETE FROM query_single_shard_table.nullkey_c1_t1 USING (SELECT NULL::integer AS a, postgres_local_table_1.b FROM (SELECT intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(b integer)) postgres_local_table_1) postgres_local_table WHERE (nullkey_c1_t1.b OPERATOR(pg_catalog.=) postgres_local_table.b) +DEBUG: Creating router plan DELETE FROM reference_table USING nullkey_c1_t1 WHERE nullkey_c1_t1.b = reference_table.b; ERROR: cannot perform select on a distributed table and modify a reference table DELETE FROM distributed_table USING nullkey_c1_t1 WHERE nullkey_c1_t1.b = distributed_table.b; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +ERROR: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DELETE FROM distributed_table USING nullkey_c1_t1 WHERE nullkey_c1_t1.b = distributed_table.a; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +ERROR: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns DELETE FROM citus_local_table USING nullkey_c1_t1 WHERE nullkey_c1_t1.b = citus_local_table.b; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: local table citus_local_table cannot be joined with these distributed tables +DEBUG: local table citus_local_table cannot be joined with these distributed tables +DEBUG: Wrapping relation "nullkey_c1_t1" to a subquery +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_1 for subquery SELECT b FROM query_single_shard_table.nullkey_c1_t1 WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: DELETE FROM query_single_shard_table.citus_local_table USING (SELECT NULL::integer AS a, nullkey_c1_t1_1.b FROM (SELECT intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(b integer)) nullkey_c1_t1_1) nullkey_c1_t1 WHERE (nullkey_c1_t1.b OPERATOR(pg_catalog.=) citus_local_table.b) +DEBUG: Creating router plan DELETE FROM postgres_local_table USING nullkey_c1_t1 WHERE nullkey_c1_t1.b = postgres_local_table.b; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: relation postgres_local_table is not distributed +DEBUG: relation postgres_local_table is not distributed +DEBUG: Wrapping relation "nullkey_c1_t1" to a subquery +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_1 for subquery SELECT b FROM query_single_shard_table.nullkey_c1_t1 WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: DELETE FROM query_single_shard_table.postgres_local_table USING (SELECT NULL::integer AS a, nullkey_c1_t1_1.b FROM (SELECT intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(b integer)) nullkey_c1_t1_1) nullkey_c1_t1 WHERE (nullkey_c1_t1.b OPERATOR(pg_catalog.=) postgres_local_table.b) +DEBUG: Creating router plan -- slightly more complex update queries UPDATE nullkey_c1_t1 SET b = 5 WHERE nullkey_c1_t1.b IN (SELECT b FROM distributed_table); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: generating subplan XXX_1 for subquery SELECT b FROM query_single_shard_table.distributed_table +DEBUG: Plan XXX query after replacing subqueries and CTEs: UPDATE query_single_shard_table.nullkey_c1_t1 SET b = 5 WHERE (b OPERATOR(pg_catalog.=) ANY (SELECT intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(b integer))) +DEBUG: Creating router plan WITH cte AS materialized( SELECT * FROM distributed_table ) UPDATE nullkey_c1_t1 SET b = 5 FROM cte WHERE nullkey_c1_t1.b = cte.a; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +DEBUG: generating subplan XXX_1 for CTE cte: SELECT a, b FROM query_single_shard_table.distributed_table +DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: Plan XXX query after replacing subqueries and CTEs: UPDATE query_single_shard_table.nullkey_c1_t1 SET b = 5 FROM (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) cte WHERE (nullkey_c1_t1.b OPERATOR(pg_catalog.=) cte.a) +DEBUG: Creating router plan WITH cte AS ( SELECT reference_table.a AS a, 1 AS b FROM distributed_table RIGHT JOIN reference_table USING (a) ) UPDATE nullkey_c1_t1 SET b = 5 WHERE nullkey_c1_t1.b IN (SELECT b FROM cte); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +DEBUG: generating subplan XXX_1 for CTE cte: SELECT reference_table.a, 1 AS b FROM (query_single_shard_table.distributed_table RIGHT JOIN query_single_shard_table.reference_table USING (a)) +DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: recursively planning left side of the right join since the outer side is a recurring rel +DEBUG: recursively planning distributed relation "distributed_table" since it is part of a distributed join node that is outer joined with a recurring rel +DEBUG: Wrapping relation "distributed_table" to a subquery +DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: generating subplan XXX_1 for subquery SELECT a FROM query_single_shard_table.distributed_table WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT reference_table.a, 1 AS b FROM ((SELECT distributed_table_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) distributed_table_1) distributed_table RIGHT JOIN query_single_shard_table.reference_table USING (a)) +DEBUG: Creating router plan +DEBUG: Plan XXX query after replacing subqueries and CTEs: UPDATE query_single_shard_table.nullkey_c1_t1 SET b = 5 WHERE (b OPERATOR(pg_catalog.=) ANY (SELECT cte.b FROM (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) cte)) +DEBUG: Creating router plan UPDATE nullkey_c1_t1 SET b = 5 FROM reference_table WHERE EXISTS ( SELECT 1 FROM reference_table LEFT JOIN nullkey_c1_t1 USING (a) WHERE nullkey_c1_t1.b IS NULL ); @@ -1597,21 +2332,37 @@ DEBUG: Creating router plan EXECUTE prepared_zero_shard_update(7); -- slightly more complex delete queries DELETE FROM nullkey_c1_t1 WHERE nullkey_c1_t1.b IN (SELECT b FROM distributed_table); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: generating subplan XXX_1 for subquery SELECT b FROM query_single_shard_table.distributed_table +DEBUG: Plan XXX query after replacing subqueries and CTEs: DELETE FROM query_single_shard_table.nullkey_c1_t1 WHERE (b OPERATOR(pg_catalog.=) ANY (SELECT intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(b integer))) +DEBUG: Creating router plan WITH cte AS materialized( SELECT * FROM distributed_table ) DELETE FROM nullkey_c1_t1 USING cte WHERE nullkey_c1_t1.b = cte.a; -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +DEBUG: generating subplan XXX_1 for CTE cte: SELECT a, b FROM query_single_shard_table.distributed_table +DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: Plan XXX query after replacing subqueries and CTEs: DELETE FROM query_single_shard_table.nullkey_c1_t1 USING (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) cte WHERE (nullkey_c1_t1.b OPERATOR(pg_catalog.=) cte.a) +DEBUG: Creating router plan WITH cte AS ( SELECT reference_table.a AS a, 1 AS b FROM distributed_table RIGHT JOIN reference_table USING (a) ) DELETE FROM nullkey_c1_t1 WHERE nullkey_c1_t1.b IN (SELECT b FROM cte); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +DEBUG: complex joins are only supported when all distributed tables are co-located and joined on their distribution columns +DEBUG: generating subplan XXX_1 for CTE cte: SELECT reference_table.a, 1 AS b FROM (query_single_shard_table.distributed_table RIGHT JOIN query_single_shard_table.reference_table USING (a)) +DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: recursively planning left side of the right join since the outer side is a recurring rel +DEBUG: recursively planning distributed relation "distributed_table" since it is part of a distributed join node that is outer joined with a recurring rel +DEBUG: Wrapping relation "distributed_table" to a subquery +DEBUG: Router planner cannot handle multi-shard select queries +DEBUG: generating subplan XXX_1 for subquery SELECT a FROM query_single_shard_table.distributed_table WHERE true +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT reference_table.a, 1 AS b FROM ((SELECT distributed_table_1.a, NULL::integer AS b FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) distributed_table_1) distributed_table RIGHT JOIN query_single_shard_table.reference_table USING (a)) +DEBUG: Creating router plan +DEBUG: Plan XXX query after replacing subqueries and CTEs: DELETE FROM query_single_shard_table.nullkey_c1_t1 WHERE (b OPERATOR(pg_catalog.=) ANY (SELECT cte.b FROM (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) cte)) +DEBUG: Creating router plan DELETE FROM nullkey_c1_t1 USING reference_table WHERE EXISTS ( SELECT 1 FROM reference_table LEFT JOIN nullkey_c1_t1 USING (a) WHERE nullkey_c1_t1.b IS NULL ); @@ -1713,8 +2464,16 @@ WITH cte AS ( DELETE FROM reference_table WHERE a = 1 RETURNING * ) SELECT * FROM nullkey_c1_t1 WHERE a IN (SELECT a FROM cte); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: cannot router plan modification of a non-distributed table +DEBUG: cannot router plan modification of a non-distributed table +DEBUG: generating subplan XXX_1 for CTE cte: DELETE FROM query_single_shard_table.reference_table WHERE (a OPERATOR(pg_catalog.=) 1) RETURNING a, b +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT a, b FROM query_single_shard_table.nullkey_c1_t1 WHERE (a OPERATOR(pg_catalog.=) ANY (SELECT cte.a FROM (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) cte)) +DEBUG: Creating router plan + a | b +--------------------------------------------------------------------- +(0 rows) + WITH cte AS ( DELETE FROM nullkey_c1_t1 WHERE a = 1 RETURNING * ) @@ -1728,14 +2487,30 @@ WITH cte AS ( DELETE FROM nullkey_c1_t1 WHERE a = 1 RETURNING * ) SELECT * FROM nullkey_c2_t1 WHERE a IN (SELECT a FROM cte); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: router planner does not support queries that reference non-colocated distributed tables +DEBUG: generating subplan XXX_1 for CTE cte: DELETE FROM query_single_shard_table.nullkey_c1_t1 WHERE (a OPERATOR(pg_catalog.=) 1) RETURNING a, b +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT a, b FROM query_single_shard_table.nullkey_c2_t1 WHERE (a OPERATOR(pg_catalog.=) ANY (SELECT cte.a FROM (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) cte)) +DEBUG: Creating router plan + a | b +--------------------------------------------------------------------- +(0 rows) + WITH cte AS ( DELETE FROM nullkey_c1_t1 WHERE a = 1 RETURNING * ) SELECT * FROM distributed_table WHERE a IN (SELECT a FROM cte); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: router planner does not support queries that reference non-colocated distributed tables +DEBUG: generating subplan XXX_1 for CTE cte: DELETE FROM query_single_shard_table.nullkey_c1_t1 WHERE (a OPERATOR(pg_catalog.=) 1) RETURNING a, b +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT a, b FROM query_single_shard_table.distributed_table WHERE (a OPERATOR(pg_catalog.=) ANY (SELECT cte.a FROM (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) cte)) +DEBUG: Router planner cannot handle multi-shard select queries + a | b +--------------------------------------------------------------------- +(0 rows) + -- Below two queries fail very late when -- citus.enable_non_colocated_router_query_pushdown is set to on. SET citus.enable_non_colocated_router_query_pushdown TO ON; @@ -1760,14 +2535,32 @@ WITH cte AS ( DELETE FROM distributed_table WHERE a = 1 RETURNING * ) SELECT * FROM nullkey_c1_t1 WHERE a IN (SELECT a FROM cte); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: router planner does not support queries that reference non-colocated distributed tables +DEBUG: generating subplan XXX_1 for CTE cte: DELETE FROM query_single_shard_table.distributed_table WHERE (a OPERATOR(pg_catalog.=) 1) RETURNING a, b +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: query has a single distribution column value: 1 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT a, b FROM query_single_shard_table.nullkey_c1_t1 WHERE (a OPERATOR(pg_catalog.=) ANY (SELECT cte.a FROM (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) cte)) +DEBUG: Creating router plan + a | b +--------------------------------------------------------------------- +(0 rows) + WITH cte AS ( DELETE FROM distributed_table WHERE a = 1 RETURNING * ) SELECT * FROM nullkey_c1_t1 WHERE b IN (SELECT b FROM cte); -ERROR: queries that reference a distributed table without a shard key can only reference colocated distributed tables or reference tables -DETAIL: router planner does not support queries that reference non-colocated distributed tables +DEBUG: router planner does not support queries that reference non-colocated distributed tables +DEBUG: generating subplan XXX_1 for CTE cte: DELETE FROM query_single_shard_table.distributed_table WHERE (a OPERATOR(pg_catalog.=) 1) RETURNING a, b +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: query has a single distribution column value: 1 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT a, b FROM query_single_shard_table.nullkey_c1_t1 WHERE (b OPERATOR(pg_catalog.=) ANY (SELECT cte.b FROM (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) cte)) +DEBUG: Creating router plan + a | b +--------------------------------------------------------------------- +(0 rows) + RESET citus.enable_non_colocated_router_query_pushdown; WITH cte AS ( UPDATE modify_fast_path SET value_1 = value_1 + 1 WHERE key = 1 RETURNING * @@ -1870,5 +2663,703 @@ DEBUG: Creating router plan 3 | 1 (10 rows) +-- more tests with ctes and subqueries +-- CTEs are recursively planned, and subquery foo is also recursively planned. +-- Then the final plan becomes a router plan. +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( + SELECT * FROM users_table_local + ), + dist_cte AS MATERIALIZED ( + SELECT user_id FROM colocated_events_table + ) + SELECT dist_cte.user_id FROM local_cte JOIN dist_cte ON dist_cte.user_id=local_cte.user_id +) +SELECT count(*) +FROM cte, + ( + SELECT DISTINCT users_table.user_id + FROM users_table, colocated_events_table + WHERE users_table.user_id = colocated_events_table.user_id AND event_type IN (1,2,3,4) + ORDER BY 1 DESC LIMIT 5 + ) AS foo +WHERE foo.user_id = cte.user_id; +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: generating subplan XXX_1 for CTE cte: WITH local_cte AS MATERIALIZED (SELECT users_table_local.user_id, users_table_local."time", users_table_local.value_1, users_table_local.value_2, users_table_local.value_3, users_table_local.value_4 FROM query_single_shard_table.users_table_local), dist_cte AS MATERIALIZED (SELECT colocated_events_table.user_id FROM query_single_shard_table.colocated_events_table) SELECT dist_cte.user_id FROM (local_cte JOIN dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: generating subplan XXX_1 for CTE local_cte: SELECT user_id, "time", value_1, value_2, value_3, value_4 FROM query_single_shard_table.users_table_local +DEBUG: generating subplan XXX_2 for CTE dist_cte: SELECT user_id FROM query_single_shard_table.colocated_events_table +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT dist_cte.user_id FROM ((SELECT intermediate_result.user_id, intermediate_result."time", intermediate_result.value_1, intermediate_result.value_2, intermediate_result.value_3, intermediate_result.value_4 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, "time" timestamp without time zone, value_1 integer, value_2 integer, value_3 double precision, value_4 bigint)) local_cte JOIN (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) +DEBUG: Creating router plan +DEBUG: Creating router plan +DEBUG: generating subplan XXX_2 for subquery SELECT DISTINCT users_table.user_id FROM query_single_shard_table.users_table, query_single_shard_table.colocated_events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) colocated_events_table.user_id) AND (colocated_events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4]))) ORDER BY users_table.user_id DESC LIMIT 5 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte, (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) foo WHERE (foo.user_id OPERATOR(pg_catalog.=) cte.user_id) +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 1644 +(1 row) + +-- CTEs are colocated, route entire query. +WITH cte1 AS ( + SELECT * FROM users_table WHERE user_id = 1 +), cte2 AS ( + SELECT * FROM colocated_events_table WHERE user_id = 1 +) +SELECT cte1.user_id, cte1.value_1, cte2.user_id, cte2.event_type +FROM cte1, cte2 +ORDER BY cte1.user_id, cte1.value_1, cte2.user_id, cte2.event_type +LIMIT 5; +DEBUG: CTE cte1 is going to be inlined via distributed planning +DEBUG: CTE cte2 is going to be inlined via distributed planning +DEBUG: Creating router plan + user_id | value_1 | user_id | event_type +--------------------------------------------------------------------- + 1 | 1 | 1 | 0 + 1 | 1 | 1 | 0 + 1 | 1 | 1 | 1 + 1 | 1 | 1 | 1 + 1 | 1 | 1 | 2 +(5 rows) + +-- CTEs aren't colocated, CTEs become intermediate results. +WITH cte1 AS MATERIALIZED ( + SELECT * FROM users_table WHERE user_id = 1 +), cte2 AS MATERIALIZED ( + SELECT * FROM non_colocated_events_table WHERE user_id = 6 +) +SELECT cte1.user_id, cte1.value_1, cte2.user_id, cte2.user_id +FROM cte1, cte2 +ORDER BY cte1.user_id, cte1.value_1, cte2.user_id, cte2.event_type +LIMIT 5; +DEBUG: router planner does not support queries that reference non-colocated distributed tables +DEBUG: generating subplan XXX_1 for CTE cte1: SELECT user_id, "time", value_1, value_2, value_3, value_4 FROM query_single_shard_table.users_table WHERE (user_id OPERATOR(pg_catalog.=) 1) +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_2 for CTE cte2: SELECT user_id, "time", event_type, value_2, value_3, value_4 FROM query_single_shard_table.non_colocated_events_table WHERE (user_id OPERATOR(pg_catalog.=) 6) +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT cte1.user_id, cte1.value_1, cte2.user_id, cte2.user_id FROM (SELECT intermediate_result.user_id, intermediate_result."time", intermediate_result.value_1, intermediate_result.value_2, intermediate_result.value_3, intermediate_result.value_4 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, "time" timestamp without time zone, value_1 integer, value_2 integer, value_3 double precision, value_4 bigint)) cte1, (SELECT intermediate_result.user_id, intermediate_result."time", intermediate_result.event_type, intermediate_result.value_2, intermediate_result.value_3, intermediate_result.value_4 FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, "time" timestamp without time zone, event_type integer, value_2 integer, value_3 double precision, value_4 bigint)) cte2 ORDER BY cte1.user_id, cte1.value_1, cte2.user_id, cte2.event_type LIMIT 5 +DEBUG: Creating router plan + user_id | value_1 | user_id | user_id +--------------------------------------------------------------------- + 1 | 1 | 6 | 6 + 1 | 1 | 6 | 6 + 1 | 1 | 6 | 6 + 1 | 1 | 6 | 6 + 1 | 1 | 6 | 6 +(5 rows) + +-- users_table & colocated_users_table are colocated, route entire query. +WITH cte1 AS ( + SELECT * FROM users_table WHERE user_id = 1 +) +UPDATE colocated_users_table dt SET value = cte1.value_1 +FROM cte1 WHERE cte1.user_id = dt.id AND dt.id = 1; +DEBUG: Creating router plan +-- users_table & non_colocated_users_table are not colocated, cte is recursive planned. +WITH cte1 AS ( + SELECT * FROM users_table WHERE user_id = 1 +) +UPDATE non_colocated_users_table dt SET value = cte1.value_1 +FROM cte1 WHERE cte1.user_id = dt.id AND dt.id = 1; +DEBUG: found no worker with all shard placements +DEBUG: generating subplan XXX_1 for CTE cte1: SELECT user_id, "time", value_1, value_2, value_3, value_4 FROM query_single_shard_table.users_table WHERE (user_id OPERATOR(pg_catalog.=) 1) +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: Plan XXX query after replacing subqueries and CTEs: UPDATE query_single_shard_table.non_colocated_users_table dt SET value = cte1.value_1 FROM (SELECT intermediate_result.user_id, intermediate_result."time", intermediate_result.value_1, intermediate_result.value_2, intermediate_result.value_3, intermediate_result.value_4 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, "time" timestamp without time zone, value_1 integer, value_2 integer, value_3 double precision, value_4 bigint)) cte1 WHERE ((cte1.user_id OPERATOR(pg_catalog.=) dt.id) AND (dt.id OPERATOR(pg_catalog.=) 1)) +DEBUG: Creating router plan +-- All relations are not colocated, CTEs become intermediate results. +WITH cte1 AS MATERIALIZED ( + SELECT * FROM users_table WHERE user_id = 1 +), cte2 AS MATERIALIZED ( + SELECT * FROM non_colocated_events_table WHERE user_id = 6 +) +UPDATE non_colocated_users_table dt SET value = cte1.value_1 + cte2.event_type +FROM cte1, cte2 WHERE cte1.user_id = dt.id AND dt.id = 1; +DEBUG: found no worker with all shard placements +DEBUG: generating subplan XXX_1 for CTE cte1: SELECT user_id, "time", value_1, value_2, value_3, value_4 FROM query_single_shard_table.users_table WHERE (user_id OPERATOR(pg_catalog.=) 1) +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_2 for CTE cte2: SELECT user_id, "time", event_type, value_2, value_3, value_4 FROM query_single_shard_table.non_colocated_events_table WHERE (user_id OPERATOR(pg_catalog.=) 6) +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: Plan XXX query after replacing subqueries and CTEs: UPDATE query_single_shard_table.non_colocated_users_table dt SET value = (cte1.value_1 OPERATOR(pg_catalog.+) cte2.event_type) FROM (SELECT intermediate_result.user_id, intermediate_result."time", intermediate_result.value_1, intermediate_result.value_2, intermediate_result.value_3, intermediate_result.value_4 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, "time" timestamp without time zone, value_1 integer, value_2 integer, value_3 double precision, value_4 bigint)) cte1, (SELECT intermediate_result.user_id, intermediate_result."time", intermediate_result.event_type, intermediate_result.value_2, intermediate_result.value_3, intermediate_result.value_4 FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, "time" timestamp without time zone, event_type integer, value_2 integer, value_3 double precision, value_4 bigint)) cte2 WHERE ((cte1.user_id OPERATOR(pg_catalog.=) dt.id) AND (dt.id OPERATOR(pg_catalog.=) 1)) +DEBUG: Creating router plan +-- Volatile function calls should not be routed. +WITH cte1 AS MATERIALIZED (SELECT id, value FROM func()) +UPDATE colocated_users_table dt SET value = cte1.value +FROM cte1 WHERE dt.id = 1; +DEBUG: Router planner doesn't support VOLATILE functions in common table expressions. +DEBUG: generating subplan XXX_1 for CTE cte1: SELECT id, value FROM query_single_shard_table.func() func(id, value) +DEBUG: Plan XXX query after replacing subqueries and CTEs: UPDATE query_single_shard_table.colocated_users_table dt SET value = cte1.value FROM (SELECT intermediate_result.id, intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(id integer, value integer)) cte1 WHERE (dt.id OPERATOR(pg_catalog.=) 1) +DEBUG: Creating router plan +-- CTEs are recursively planned, and subquery foo is also recursively planned. +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( + SELECT * FROM users_table_local + ), + dist_cte AS MATERIALIZED ( + SELECT user_id FROM colocated_events_table + ) + SELECT dist_cte.user_id FROM local_cte JOIN dist_cte ON dist_cte.user_id=local_cte.user_id +) +SELECT count(*) +FROM + cte, + ( + SELECT DISTINCT users_table.user_id + FROM users_table, colocated_events_table + WHERE users_table.user_id = colocated_events_table.user_id AND event_type IN (1,2,3,4) + ORDER BY 1 DESC LIMIT 5 + ) AS foo, colocated_events_table +WHERE foo.user_id = cte.user_id AND colocated_events_table.user_id = cte.user_id; +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: generating subplan XXX_1 for CTE cte: WITH local_cte AS MATERIALIZED (SELECT users_table_local.user_id, users_table_local."time", users_table_local.value_1, users_table_local.value_2, users_table_local.value_3, users_table_local.value_4 FROM query_single_shard_table.users_table_local), dist_cte AS MATERIALIZED (SELECT colocated_events_table.user_id FROM query_single_shard_table.colocated_events_table) SELECT dist_cte.user_id FROM (local_cte JOIN dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: generating subplan XXX_1 for CTE local_cte: SELECT user_id, "time", value_1, value_2, value_3, value_4 FROM query_single_shard_table.users_table_local +DEBUG: generating subplan XXX_2 for CTE dist_cte: SELECT user_id FROM query_single_shard_table.colocated_events_table +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT dist_cte.user_id FROM ((SELECT intermediate_result.user_id, intermediate_result."time", intermediate_result.value_1, intermediate_result.value_2, intermediate_result.value_3, intermediate_result.value_4 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, "time" timestamp without time zone, value_1 integer, value_2 integer, value_3 double precision, value_4 bigint)) local_cte JOIN (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) +DEBUG: Creating router plan +DEBUG: Creating router plan +DEBUG: generating subplan XXX_2 for subquery SELECT DISTINCT users_table.user_id FROM query_single_shard_table.users_table, query_single_shard_table.colocated_events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) colocated_events_table.user_id) AND (colocated_events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4]))) ORDER BY users_table.user_id DESC LIMIT 5 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte, (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) foo, query_single_shard_table.colocated_events_table WHERE ((foo.user_id OPERATOR(pg_catalog.=) cte.user_id) AND (colocated_events_table.user_id OPERATOR(pg_catalog.=) cte.user_id)) +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 30608 +(1 row) + +-- CTEs are replaced and subquery in WHERE is also replaced. +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( + SELECT * FROM users_table_local + ), + dist_cte AS MATERIALIZED ( + SELECT user_id FROM colocated_events_table + ) + SELECT dist_cte.user_id FROM local_cte JOIN dist_cte ON dist_cte.user_id=local_cte.user_id +) +SELECT DISTINCT cte.user_id +FROM users_table, cte +WHERE users_table.user_id = cte.user_id AND + users_table.user_id IN ( + SELECT DISTINCT value_2 FROM users_table WHERE value_1 >= 1 AND value_1 <= 20 ORDER BY 1 LIMIT 5 + ) +ORDER BY 1 DESC; +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: generating subplan XXX_1 for CTE cte: WITH local_cte AS MATERIALIZED (SELECT users_table_local.user_id, users_table_local."time", users_table_local.value_1, users_table_local.value_2, users_table_local.value_3, users_table_local.value_4 FROM query_single_shard_table.users_table_local), dist_cte AS MATERIALIZED (SELECT colocated_events_table.user_id FROM query_single_shard_table.colocated_events_table) SELECT dist_cte.user_id FROM (local_cte JOIN dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: generating subplan XXX_1 for CTE local_cte: SELECT user_id, "time", value_1, value_2, value_3, value_4 FROM query_single_shard_table.users_table_local +DEBUG: generating subplan XXX_2 for CTE dist_cte: SELECT user_id FROM query_single_shard_table.colocated_events_table +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT dist_cte.user_id FROM ((SELECT intermediate_result.user_id, intermediate_result."time", intermediate_result.value_1, intermediate_result.value_2, intermediate_result.value_3, intermediate_result.value_4 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, "time" timestamp without time zone, value_1 integer, value_2 integer, value_3 double precision, value_4 bigint)) local_cte JOIN (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) +DEBUG: Creating router plan +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_2 for subquery SELECT DISTINCT value_2 FROM query_single_shard_table.users_table WHERE ((value_1 OPERATOR(pg_catalog.>=) 1) AND (value_1 OPERATOR(pg_catalog.<=) 20)) ORDER BY value_2 LIMIT 5 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT DISTINCT cte.user_id FROM query_single_shard_table.users_table, (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte WHERE ((users_table.user_id OPERATOR(pg_catalog.=) cte.user_id) AND (users_table.user_id OPERATOR(pg_catalog.=) ANY (SELECT intermediate_result.value_2 FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(value_2 integer)))) ORDER BY cte.user_id DESC +DEBUG: Creating router plan + user_id +--------------------------------------------------------------------- + 4 + 3 + 2 + 1 +(4 rows) + +-- Subquery in WHERE clause is planned recursively due to the recurring table +-- in FROM clause. +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( + SELECT * FROM users_table_local + ), + dist_cte AS MATERIALIZED ( + SELECT user_id FROM colocated_events_table + ) + SELECT dist_cte.user_id FROM local_cte JOIN dist_cte ON dist_cte.user_id=local_cte.user_id +) +SELECT DISTINCT cte.user_id +FROM cte +WHERE cte.user_id IN (SELECT DISTINCT user_id FROM users_table WHERE value_1 >= 1 AND value_1 <= 20) +ORDER BY 1 DESC; +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: generating subplan XXX_1 for CTE cte: WITH local_cte AS MATERIALIZED (SELECT users_table_local.user_id, users_table_local."time", users_table_local.value_1, users_table_local.value_2, users_table_local.value_3, users_table_local.value_4 FROM query_single_shard_table.users_table_local), dist_cte AS MATERIALIZED (SELECT colocated_events_table.user_id FROM query_single_shard_table.colocated_events_table) SELECT dist_cte.user_id FROM (local_cte JOIN dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: generating subplan XXX_1 for CTE local_cte: SELECT user_id, "time", value_1, value_2, value_3, value_4 FROM query_single_shard_table.users_table_local +DEBUG: generating subplan XXX_2 for CTE dist_cte: SELECT user_id FROM query_single_shard_table.colocated_events_table +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT dist_cte.user_id FROM ((SELECT intermediate_result.user_id, intermediate_result."time", intermediate_result.value_1, intermediate_result.value_2, intermediate_result.value_3, intermediate_result.value_4 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, "time" timestamp without time zone, value_1 integer, value_2 integer, value_3 double precision, value_4 bigint)) local_cte JOIN (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) +DEBUG: Creating router plan +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_2 for subquery SELECT DISTINCT user_id FROM query_single_shard_table.users_table WHERE ((value_1 OPERATOR(pg_catalog.>=) 1) AND (value_1 OPERATOR(pg_catalog.<=) 20)) +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT DISTINCT user_id FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte WHERE (user_id OPERATOR(pg_catalog.=) ANY (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer))) ORDER BY user_id DESC +DEBUG: Creating router plan + user_id +--------------------------------------------------------------------- + 6 + 5 + 4 + 3 + 2 + 1 +(6 rows) + +-- CTEs inside a subquery and the final query becomes a router +-- query. +SELECT + user_id +FROM + ( + WITH cte AS MATERIALIZED ( + SELECT DISTINCT users_table.user_id + FROM users_table, colocated_events_table + WHERE users_table.user_id = colocated_events_table.user_id AND + event_type IN (1,2,3,4) + ) + SELECT * FROM cte ORDER BY 1 DESC + ) AS foo +ORDER BY 1 DESC; +DEBUG: Creating router plan + user_id +--------------------------------------------------------------------- + 6 + 5 + 4 + 3 + 2 + 1 +(6 rows) + +-- CTEs inside a deeper subquery and also the subquery that contains the CTE are +-- recursively planned. +SELECT DISTINCT bar.user_id +FROM + ( + WITH cte AS MATERIALIZED ( + SELECT DISTINCT users_table.user_id + FROM users_table, colocated_events_table + WHERE users_table.user_id = colocated_events_table.user_id AND event_type IN (1,2,3,4) + ) + SELECT * FROM cte ORDER BY 1 DESC + ) AS foo, + ( + SELECT users_table.user_id, some_events.event_type + FROM + users_table, + ( + WITH cte AS MATERIALIZED ( + SELECT event_type, users_table.user_id + FROM users_table, colocated_events_table + WHERE users_table.user_id = colocated_events_table.user_id AND value_1 IN (1,2) + ) SELECT * FROM cte ORDER BY 1 DESC + ) AS some_events + WHERE users_table.user_id = some_events.user_id AND event_type IN (1,2,3,4) + ORDER BY 2,1 LIMIT 2 + ) AS bar +WHERE foo.user_id = bar.user_id +ORDER BY 1 DESC LIMIT 5; +DEBUG: Creating router plan + user_id +--------------------------------------------------------------------- + 1 +(1 row) + +-- Recursively plan subqueries inside the CTEs that contains LIMIT and OFFSET. +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( + SELECT * FROM users_table_local + ), + dist_cte AS MATERIALIZED ( + SELECT + user_id + FROM + colocated_events_table, + (SELECT DISTINCT value_2 FROM users_table OFFSET 0) as foo + WHERE + colocated_events_table.user_id = foo.value_2 AND + colocated_events_table.user_id IN (SELECT DISTINCT value_1 FROM users_table ORDER BY 1 LIMIT 3) + ) + SELECT dist_cte.user_id FROM local_cte JOIN dist_cte ON dist_cte.user_id=local_cte.user_id +) +SELECT count(*) +FROM + cte, + ( + SELECT DISTINCT users_table.user_id + FROM users_table, colocated_events_table + WHERE users_table.user_id = colocated_events_table.user_id AND event_type IN (1,2,3,4) + ORDER BY 1 DESC LIMIT 5 + ) AS foo +WHERE foo.user_id = cte.user_id; +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: generating subplan XXX_1 for CTE cte: WITH local_cte AS MATERIALIZED (SELECT users_table_local.user_id, users_table_local."time", users_table_local.value_1, users_table_local.value_2, users_table_local.value_3, users_table_local.value_4 FROM query_single_shard_table.users_table_local), dist_cte AS MATERIALIZED (SELECT colocated_events_table.user_id FROM query_single_shard_table.colocated_events_table, (SELECT DISTINCT users_table.value_2 FROM query_single_shard_table.users_table OFFSET 0) foo WHERE ((colocated_events_table.user_id OPERATOR(pg_catalog.=) foo.value_2) AND (colocated_events_table.user_id OPERATOR(pg_catalog.=) ANY (SELECT DISTINCT users_table.value_1 FROM query_single_shard_table.users_table ORDER BY users_table.value_1 LIMIT 3)))) SELECT dist_cte.user_id FROM (local_cte JOIN dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) +DEBUG: Local tables cannot be used in distributed queries. +DEBUG: generating subplan XXX_1 for CTE local_cte: SELECT user_id, "time", value_1, value_2, value_3, value_4 FROM query_single_shard_table.users_table_local +DEBUG: generating subplan XXX_2 for CTE dist_cte: SELECT colocated_events_table.user_id FROM query_single_shard_table.colocated_events_table, (SELECT DISTINCT users_table.value_2 FROM query_single_shard_table.users_table OFFSET 0) foo WHERE ((colocated_events_table.user_id OPERATOR(pg_catalog.=) foo.value_2) AND (colocated_events_table.user_id OPERATOR(pg_catalog.=) ANY (SELECT DISTINCT users_table.value_1 FROM query_single_shard_table.users_table ORDER BY users_table.value_1 LIMIT 3))) +DEBUG: Creating router plan +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT dist_cte.user_id FROM ((SELECT intermediate_result.user_id, intermediate_result."time", intermediate_result.value_1, intermediate_result.value_2, intermediate_result.value_3, intermediate_result.value_4 FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer, "time" timestamp without time zone, value_1 integer, value_2 integer, value_3 double precision, value_4 bigint)) local_cte JOIN (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) dist_cte ON ((dist_cte.user_id OPERATOR(pg_catalog.=) local_cte.user_id))) +DEBUG: Creating router plan +DEBUG: Creating router plan +DEBUG: generating subplan XXX_2 for subquery SELECT DISTINCT users_table.user_id FROM query_single_shard_table.users_table, query_single_shard_table.colocated_events_table WHERE ((users_table.user_id OPERATOR(pg_catalog.=) colocated_events_table.user_id) AND (colocated_events_table.event_type OPERATOR(pg_catalog.=) ANY (ARRAY[1, 2, 3, 4]))) ORDER BY users_table.user_id DESC LIMIT 5 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte, (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) foo WHERE (foo.user_id OPERATOR(pg_catalog.=) cte.user_id) +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 432 +(1 row) + +-- more tests with sublinks and subqueries in targetlist +SELECT event_type, (SELECT e.value_2 FROM users_reference_table WHERE user_id = 1 AND value_1 = 1), (SELECT e.value_2) +FROM non_colocated_events_table e +ORDER BY 1,2 LIMIT 1; +DEBUG: Creating router plan + event_type | value_2 | value_2 +--------------------------------------------------------------------- + 0 | | 0 +(1 row) + +SELECT event_type, (SELECT time FROM users_table WHERE user_id = e.user_id ORDER BY time LIMIT 1) +FROM non_colocated_events_table e +ORDER BY 1,2 LIMIT 1; +DEBUG: found no worker with all shard placements +DEBUG: push down of limit count: 1 +ERROR: cannot push down this subquery +DETAIL: users_table and non_colocated_events_table are not colocated +SELECT event_type, (SELECT max(time) FROM users_table WHERE user_id = e.value_2) +FROM non_colocated_events_table e +ORDER BY 1,2 LIMIT 1; +DEBUG: found no worker with all shard placements +DEBUG: push down of limit count: 1 +ERROR: cannot push down this subquery +DETAIL: users_table and non_colocated_events_table are not colocated +SELECT event_type, (SELECT max(time) FROM users_table) +FROM non_colocated_events_table e +ORDER BY 1,2 LIMIT 1; +DEBUG: found no worker with all shard placements +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_1 for subquery SELECT max("time") AS max FROM query_single_shard_table.users_table +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT event_type, (SELECT intermediate_result.max FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(max timestamp without time zone)) AS max FROM query_single_shard_table.non_colocated_events_table e ORDER BY event_type, (SELECT intermediate_result.max FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(max timestamp without time zone)) LIMIT 1 +DEBUG: Creating router plan + event_type | max +--------------------------------------------------------------------- + 0 | Thu Nov 23 17:30:34.635085 2017 +(1 row) + +WITH cte_1 AS (SELECT max(time) FROM users_table) +SELECT event_type, (SELECT * FROM cte_1) +FROM non_colocated_events_table e +ORDER BY 1,2 LIMIT 1; +DEBUG: CTE cte_1 is going to be inlined via distributed planning +DEBUG: router planner does not support queries that reference non-colocated distributed tables +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_1 for subquery SELECT max("time") AS max FROM query_single_shard_table.users_table +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT event_type, (SELECT cte_1.max FROM (SELECT intermediate_result.max FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(max timestamp without time zone)) cte_1) AS max FROM query_single_shard_table.non_colocated_events_table e ORDER BY event_type, (SELECT cte_1.max FROM (SELECT intermediate_result.max FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(max timestamp without time zone)) cte_1) LIMIT 1 +DEBUG: Creating router plan + event_type | max +--------------------------------------------------------------------- + 0 | Thu Nov 23 17:30:34.635085 2017 +(1 row) + +WITH cte_1 AS (SELECT max(time) FROM users_table) +SELECT event_type, (SELECT * FROM cte_1 LIMIT 1) +FROM non_colocated_events_table e +ORDER BY 1,2 LIMIT 1; +DEBUG: CTE cte_1 is going to be inlined via distributed planning +DEBUG: router planner does not support queries that reference non-colocated distributed tables +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_1 for subquery SELECT max("time") AS max FROM query_single_shard_table.users_table +DEBUG: Creating router plan +DEBUG: generating subplan XXX_2 for subquery SELECT max FROM (SELECT intermediate_result.max FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(max timestamp without time zone)) cte_1 LIMIT 1 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT event_type, (SELECT intermediate_result.max FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(max timestamp without time zone)) AS max FROM query_single_shard_table.non_colocated_events_table e ORDER BY event_type, (SELECT intermediate_result.max FROM read_intermediate_result('XXX_2'::text, 'binary'::citus_copy_format) intermediate_result(max timestamp without time zone)) LIMIT 1 +DEBUG: Creating router plan + event_type | max +--------------------------------------------------------------------- + 0 | Thu Nov 23 17:30:34.635085 2017 +(1 row) + +WITH cte_1 AS (SELECT max(time) m FROM users_table) +SELECT count(*), (SELECT * FROM cte_1 c1 join cte_1 c2 using (m)) +FROM non_colocated_events_table e +GROUP BY 2 +ORDER BY 1,2 LIMIT 1; +DEBUG: router planner does not support queries that reference non-colocated distributed tables +DEBUG: generating subplan XXX_1 for CTE cte_1: SELECT max("time") AS m FROM query_single_shard_table.users_table +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count, (SELECT c1.m FROM ((SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m timestamp without time zone)) c1 JOIN (SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m timestamp without time zone)) c2 USING (m))) AS m FROM query_single_shard_table.non_colocated_events_table e GROUP BY (SELECT c1.m FROM ((SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m timestamp without time zone)) c1 JOIN (SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m timestamp without time zone)) c2 USING (m))) ORDER BY (count(*)), (SELECT c1.m FROM ((SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m timestamp without time zone)) c1 JOIN (SELECT intermediate_result.m FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(m timestamp without time zone)) c2 USING (m))) LIMIT 1 +DEBUG: Creating router plan + count | m +--------------------------------------------------------------------- + 101 | Thu Nov 23 17:30:34.635085 2017 +(1 row) + +WITH cte_1 AS (SELECT min(user_id) u, max(time) m FROM users_table) +SELECT count(*), (SELECT max(time) FROM users_table WHERE user_id = cte_1.u GROUP BY user_id) +FROM cte_1 +GROUP BY 2 +ORDER BY 1,2 LIMIT 1; +DEBUG: CTE cte_1 is going to be inlined via distributed planning +DEBUG: Creating router plan + count | max +--------------------------------------------------------------------- + 1 | Thu Nov 23 17:30:34.635085 2017 +(1 row) + +SELECT sum(e.user_id) + (SELECT max(value_3) FROM users_table WHERE user_id = e.user_id GROUP BY user_id) +FROM non_colocated_events_table e +GROUP BY e.user_id +ORDER BY 1 LIMIT 3; +DEBUG: found no worker with all shard placements +ERROR: cannot push down subquery on the target list +DETAIL: Subqueries in the SELECT part of the query can only be pushed down if they happen before aggregates and window functions +SELECT e.user_id, sum((SELECT any_value(value_3) FROM users_reference_table WHERE user_id = e.user_id GROUP BY user_id)) OVER (PARTITION BY e.user_id) +FROM non_colocated_events_table e +ORDER BY 1, 2 LIMIT 3; +DEBUG: Creating router plan + user_id | sum +--------------------------------------------------------------------- + 1 | + 1 | + 1 | +(3 rows) + +SELECT (SELECT (SELECT e.user_id + user_id) FROM users_table WHERE user_id = e.user_id GROUP BY user_id) +FROM non_colocated_events_table e +GROUP BY 1 +ORDER BY 1 LIMIT 3; +DEBUG: found no worker with all shard placements +DEBUG: push down of limit count: 3 +ERROR: cannot push down this subquery +DETAIL: users_table and non_colocated_events_table are not colocated +SELECT (SELECT (SELECT e.user_id + user_id) FROM users_reference_table WHERE user_id = e.user_id GROUP BY user_id) +FROM non_colocated_events_table e +GROUP BY 1 +ORDER BY 1 LIMIT 3; +DEBUG: Creating router plan + ?column? +--------------------------------------------------------------------- + +(1 row) + +WITH cte_1 AS (SELECT user_id FROM users_table ORDER BY 1 LIMIT 1) +SELECT (SELECT (SELECT e.user_id + user_id) FROM cte_1 WHERE user_id = e.user_id GROUP BY user_id) +FROM non_colocated_events_table e +GROUP BY 1 +ORDER BY 1 LIMIT 3; +DEBUG: CTE cte_1 is going to be inlined via distributed planning +DEBUG: router planner does not support queries that reference non-colocated distributed tables +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_1 for subquery SELECT user_id FROM query_single_shard_table.users_table ORDER BY user_id LIMIT 1 +DEBUG: skipping recursive planning for the subquery since it contains references to outer queries +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT (SELECT (SELECT (e.user_id OPERATOR(pg_catalog.+) cte_1.user_id)) FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte_1 WHERE (cte_1.user_id OPERATOR(pg_catalog.=) e.user_id) GROUP BY cte_1.user_id) FROM query_single_shard_table.non_colocated_events_table e GROUP BY (SELECT (SELECT (e.user_id OPERATOR(pg_catalog.+) cte_1.user_id)) FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte_1 WHERE (cte_1.user_id OPERATOR(pg_catalog.=) e.user_id) GROUP BY cte_1.user_id) ORDER BY (SELECT (SELECT (e.user_id OPERATOR(pg_catalog.+) cte_1.user_id)) FROM (SELECT intermediate_result.user_id FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(user_id integer)) cte_1 WHERE (cte_1.user_id OPERATOR(pg_catalog.=) e.user_id) GROUP BY cte_1.user_id) LIMIT 3 +DEBUG: Creating router plan + ?column? +--------------------------------------------------------------------- + 2 + +(2 rows) + +SELECT (SELECT (SELECT e.user_id + user_id) FROM (SELECT 1 AS user_id) s WHERE user_id = e.user_id GROUP BY user_id) +FROM non_colocated_events_table e +GROUP BY 1 +ORDER BY 1 LIMIT 3; +DEBUG: Creating router plan + ?column? +--------------------------------------------------------------------- + 2 + +(2 rows) + +CREATE TEMP VIEW view_1 AS (SELECT user_id, value_2 FROM users_table WHERE user_id = 1 AND value_1 = 1 ORDER BY 1,2); +WARNING: "view view_1" has dependency on unsupported object "schema pg_temp_xxx" +DETAIL: "view view_1" will be created only locally +SELECT (SELECT value_2 FROM view_1 WHERE user_id = e.user_id GROUP BY value_2) +FROM non_colocated_events_table e +GROUP BY 1 +ORDER BY 1 LIMIT 3; +DEBUG: found no worker with all shard placements +DEBUG: push down of limit count: 3 +ERROR: cannot push down this subquery +DETAIL: users_table and non_colocated_events_table are not colocated +SELECT + user_id, count(*) +FROM + non_colocated_events_table e1 +GROUP BY user_id + HAVING + count(*) > (SELECT count(*) FROM (SELECT + (SELECT sum(user_id) FROM users_table WHERE user_id = u1.user_id GROUP BY user_id) + FROM users_table u1 + GROUP BY user_id) as foo) ORDER BY 1 DESC; +DEBUG: found no worker with all shard placements +DEBUG: Creating router plan +DEBUG: generating subplan XXX_1 for subquery SELECT count(*) AS count FROM (SELECT (SELECT sum(users_table.user_id) AS sum FROM query_single_shard_table.users_table WHERE (users_table.user_id OPERATOR(pg_catalog.=) u1.user_id) GROUP BY users_table.user_id) AS sum FROM query_single_shard_table.users_table u1 GROUP BY u1.user_id) foo +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT user_id, count(*) AS count FROM query_single_shard_table.non_colocated_events_table e1 GROUP BY user_id HAVING (count(*) OPERATOR(pg_catalog.>) (SELECT intermediate_result.count FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(count bigint))) ORDER BY user_id DESC +DEBUG: Creating router plan + user_id | count +--------------------------------------------------------------------- + 6 | 10 + 5 | 14 + 4 | 17 + 3 | 21 + 2 | 24 + 1 | 15 +(6 rows) + +SELECT count(*) FROM (SELECT + (SELECT user_id FROM users_table WHERE user_id = u1.user_id FOR UPDATE) +FROM users_table u1 +GROUP BY user_id) as foo; +DEBUG: Creating router plan + count +--------------------------------------------------------------------- + 6 +(1 row) + +-- test single hash repartition join +SET citus.log_multi_join_order TO ON; +SET client_min_messages TO DEBUG1; +SET citus.enable_repartition_joins TO ON; +SET citus.enable_single_hash_repartition_joins TO ON; +SELECT count(*) FROM nullkey_c1_t1 JOIN distributed_table USING(a); +LOG: join order: [ "nullkey_c1_t1" ][ single hash partition join "distributed_table" ] + count +--------------------------------------------------------------------- + 0 +(1 row) + +select count(*) from nullkey_c1_t1 JOIN nullkey_c2_t2 USING(a); +LOG: join order: [ "nullkey_c1_t1" ][ dual partition join "nullkey_c2_t2" ] + count +--------------------------------------------------------------------- + 0 +(1 row) + +RESET citus.log_multi_join_order; +SET client_min_messages TO DEBUG2; +RESET citus.enable_repartition_joins; +RESET citus.enable_single_hash_repartition_joins; +SET client_min_messages TO DEBUG1; +SET citus.enable_repartition_joins TO ON; +SET citus.log_multi_join_order TO ON; +SELECT count(*), avg(avgsub.a) +FROM ( + SELECT table_0.a + FROM reference_table AS table_0 + INNER JOIN nullkey_c1_t1 AS table_1 USING (a) + INNER JOIN reference_table AS table_2 USING (a) + INNER JOIN nullkey_c2_t1 AS table_3 USING (a) + ORDER BY a LIMIT 7 +) AS avgsub; +LOG: join order: [ "nullkey_c1_t1" ][ reference join "reference_table" ][ reference join "reference_table" ][ dual partition join "nullkey_c2_t1" ] +DEBUG: push down of limit count: 7 +DEBUG: generating subplan XXX_1 for subquery SELECT table_0.a FROM (((query_single_shard_table.reference_table table_0 JOIN query_single_shard_table.nullkey_c1_t1 table_1 USING (a)) JOIN query_single_shard_table.reference_table table_2 USING (a)) JOIN query_single_shard_table.nullkey_c2_t1 table_3 USING (a)) ORDER BY table_0.a LIMIT 7 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count, avg(a) AS avg FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) avgsub + count | avg +--------------------------------------------------------------------- + 0 | +(1 row) + +SET citus.enable_single_hash_repartition_joins TO ON; +-- We prefer dual-hash repartition join over single-hash repartition join +-- even if citus.enable_single_hash_repartition_joins is set to ON. This +-- happens because single shard tables don't have a shard key. +SELECT count(*), avg(avgsub.a) +FROM ( + SELECT table_0.a + FROM reference_table AS table_0 + INNER JOIN nullkey_c1_t1 AS table_1 USING (a) + INNER JOIN reference_table AS table_2 USING (a) + INNER JOIN nullkey_c2_t1 AS table_3 USING (a) + ORDER BY a LIMIT 7 +) AS avgsub; +LOG: join order: [ "nullkey_c1_t1" ][ reference join "reference_table" ][ reference join "reference_table" ][ dual partition join "nullkey_c2_t1" ] +DEBUG: push down of limit count: 7 +DEBUG: generating subplan XXX_1 for subquery SELECT table_0.a FROM (((query_single_shard_table.reference_table table_0 JOIN query_single_shard_table.nullkey_c1_t1 table_1 USING (a)) JOIN query_single_shard_table.reference_table table_2 USING (a)) JOIN query_single_shard_table.nullkey_c2_t1 table_3 USING (a)) ORDER BY table_0.a LIMIT 7 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count, avg(a) AS avg FROM (SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) avgsub + count | avg +--------------------------------------------------------------------- + 0 | +(1 row) + +RESET citus.enable_single_hash_repartition_joins; +SET client_min_messages TO DEBUG2; +RESET citus.enable_repartition_joins; +RESET citus.log_multi_join_order; +SELECT count(*), avg(avgsub.a) +FROM ( + SELECT table_0.a + FROM nullkey_c1_t1 AS table_0 + RIGHT JOIN ( + SELECT table_2.a FROM ( + SELECT table_3.a FROM nullkey_c2_t1 AS table_3 + ORDER BY a LIMIT 0 + ) AS table_2 + INNER JOIN nullkey_c2_t1 AS table_4 USING (a) + WHERE table_4.a < 8 + ) AS table_1 USING (a) +) AS avgsub; +DEBUG: router planner does not support queries that reference non-colocated distributed tables +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: generating subplan XXX_1 for subquery SELECT a FROM query_single_shard_table.nullkey_c2_t1 table_3 ORDER BY a LIMIT 0 +DEBUG: Plan XXX query after replacing subqueries and CTEs: SELECT count(*) AS count, avg(a) AS avg FROM (SELECT table_0.a FROM (query_single_shard_table.nullkey_c1_t1 table_0 RIGHT JOIN (SELECT table_2.a FROM ((SELECT intermediate_result.a FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(a integer)) table_2 JOIN query_single_shard_table.nullkey_c2_t1 table_4 USING (a)) WHERE (table_4.a OPERATOR(pg_catalog.<) 8)) table_1 USING (a))) avgsub +DEBUG: router planner does not support queries that reference non-colocated distributed tables +ERROR: cannot perform a lateral outer join when a distributed subquery references complex subqueries, CTEs or local tables +-- test nested exec +CREATE FUNCTION dist_query_single_shard(p_key int) +RETURNS bigint +LANGUAGE plpgsql AS $$ +DECLARE + result bigint; +BEGIN + SELECT count(*) INTO result FROM query_single_shard_table.nullkey_c1_t1 WHERE a = p_key; + RETURN result; +END; +$$; +DEBUG: switching to sequential query execution mode +DETAIL: A command for a distributed function is run. To make sure subsequent commands see the function correctly we need to make sure to use only one connection for all future commands +CREATE FUNCTION ref_query() +RETURNS bigint +LANGUAGE plpgsql AS $$ +DECLARE + result bigint; +BEGIN + SELECT count(*) INTO result FROM query_single_shard_table.reference_table; + RETURN result; +END; +$$; +DEBUG: switching to sequential query execution mode +DETAIL: A command for a distributed function is run. To make sure subsequent commands see the function correctly we need to make sure to use only one connection for all future commands +SELECT dist_query_single_shard(count(*)::int) FROM nullkey_c1_t1; +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +ERROR: cannot execute a distributed query from a query on a shard +DETAIL: Executing a distributed query in a function call that may be pushed to a remote node can lead to incorrect results. +HINT: Avoid nesting of distributed queries or use alter user current_user set citus.allow_nested_distributed_execution to on to allow it with possible incorrectness. +CONTEXT: SQL statement "SELECT count(*) FROM query_single_shard_table.nullkey_c1_t1 WHERE a = p_key" +PL/pgSQL function query_single_shard_table.dist_query_single_shard(integer) line XX at SQL statement +while executing command on localhost:xxxxx +SELECT ref_query()+count(*) FROM nullkey_c1_t1; +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +ERROR: cannot execute a distributed query from a query on a shard +DETAIL: Executing a distributed query in a function call that may be pushed to a remote node can lead to incorrect results. +HINT: Avoid nesting of distributed queries or use alter user current_user set citus.allow_nested_distributed_execution to on to allow it with possible incorrectness. +CONTEXT: SQL statement "SELECT count(*) FROM query_single_shard_table.reference_table" +PL/pgSQL function query_single_shard_table.ref_query() line XX at SQL statement +while executing command on localhost:xxxxx SET client_min_messages TO ERROR; DROP SCHEMA query_single_shard_table CASCADE; diff --git a/src/test/regress/sql/create_single_shard_table.sql b/src/test/regress/sql/create_single_shard_table.sql index 55f390921..0830aa7f8 100644 --- a/src/test/regress/sql/create_single_shard_table.sql +++ b/src/test/regress/sql/create_single_shard_table.sql @@ -627,13 +627,13 @@ CREATE TABLE local_table_for_fkey (a INT PRIMARY KEY); ALTER TABLE "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" ADD CONSTRAINT fkey_to_dummy_local FOREIGN KEY (id) REFERENCES local_table_for_fkey(a); --- Normally, we support foreign keys from Postgres tables to distributed --- tables assuming that the user will soon distribute the local table too --- anyway. However, this is not the case for single-shard tables before --- we improve SQL support. +-- foreign key from a local table ALTER TABLE local_table_for_fkey ADD CONSTRAINT fkey_from_dummy_local FOREIGN KEY (a) REFERENCES "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"(id); +SELECT create_distributed_table('local_table_for_fkey', null, colocate_with=>'none'); +SELECT create_distributed_table('local_table_for_fkey', null, colocate_with=>'"NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789"'); + -- foreign key to a citus local table, errors out CREATE TABLE citus_local_table_for_fkey (a INT PRIMARY KEY); SELECT citus_add_local_table_to_metadata('citus_local_table_for_fkey'); diff --git a/src/test/regress/sql/insert_select_single_shard_table.sql b/src/test/regress/sql/insert_select_single_shard_table.sql index 3ea036772..4d1e1a73c 100644 --- a/src/test/regress/sql/insert_select_single_shard_table.sql +++ b/src/test/regress/sql/insert_select_single_shard_table.sql @@ -98,22 +98,28 @@ INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM -- use a colocated single-shard table INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN nullkey_c1_t2 USING (b); INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 FULL JOIN nullkey_c1_t2 USING (a); -INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 FULL JOIN matview USING (a); +INSERT INTO distributed_table_c1_t1 SELECT COALESCE(nullkey_c1_t1.a, 1), nullkey_c1_t1.b FROM nullkey_c1_t1 FULL JOIN matview USING (a); INSERT INTO distributed_table_c1_t1 SELECT * FROM nullkey_c1_t1 UNION SELECT * FROM nullkey_c1_t2; -- use a non-colocated single-shard table INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 LEFT JOIN nullkey_c2_t1 USING (a); INSERT INTO distributed_table_c1_t1 SELECT * FROM nullkey_c1_t1 UNION SELECT * FROM nullkey_c2_t1; --- use a distributed table that is colocated with the target table +SET client_min_messages TO DEBUG1; +SET citus.enable_repartition_joins TO ON; + +-- use a distributed table that is colocated with the target table, with repartition joins enabled INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a); INSERT INTO distributed_table_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a); INSERT INTO distributed_table_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (b); INSERT INTO distributed_table_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a) WHERE distributed_table_c1_t2.a = 1; --- use a distributed table that is not colocated with the target table +-- use a distributed table that is not colocated with the target table, with repartition joins enabled INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 JOIN distributed_table_c2_t1 USING (a); +RESET citus.enable_repartition_joins; +SET client_min_messages TO DEBUG2; + -- use a citus local table INSERT INTO distributed_table_c1_t1 SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN citus_local_table USING (a); @@ -148,11 +154,18 @@ INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey INSERT INTO reference_table SELECT nullkey_c1_t2.a, nullkey_c1_t2.b FROM nullkey_c1_t2 LEFT JOIN nullkey_c2_t1 USING (a); -- use a distributed table + +SET client_min_messages TO DEBUG1; +SET citus.enable_repartition_joins TO ON; + INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a); INSERT INTO reference_table SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a); INSERT INTO reference_table SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (b); INSERT INTO reference_table SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a) WHERE distributed_table_c1_t2.a = 1; +RESET citus.enable_repartition_joins; +SET client_min_messages TO DEBUG2; + -- use a citus local table INSERT INTO reference_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN citus_local_table USING (a); @@ -176,7 +189,11 @@ INSERT INTO citus_local_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullk INSERT INTO citus_local_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN nullkey_c1_t2 USING (b); -- use a distributed table +SET client_min_messages TO DEBUG1; +SET citus.enable_repartition_joins TO ON; INSERT INTO citus_local_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN distributed_table_c1_t2 USING (a); +RESET citus.enable_repartition_joins; +SET client_min_messages TO DEBUG2; -- use a citus local table INSERT INTO citus_local_table SELECT nullkey_c1_t1.a, nullkey_c1_t1.b FROM nullkey_c1_t1 JOIN citus_local_table USING (a); @@ -204,7 +221,12 @@ INSERT INTO nullkey_c1_t1 SELECT citus_local_table.a, citus_local_table.b FROM c -- use a distributed table INSERT INTO nullkey_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM distributed_table_c1_t2; INSERT INTO nullkey_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM distributed_table_c1_t2 JOIN reference_table USING (a); + +SET client_min_messages TO DEBUG1; +SET citus.enable_repartition_joins TO ON; INSERT INTO nullkey_c1_t1 SELECT distributed_table_c1_t2.a, distributed_table_c1_t2.b FROM distributed_table_c1_t2 JOIN nullkey_c1_t1 USING (a); +RESET citus.enable_repartition_joins; +SET client_min_messages TO DEBUG2; -- use a non-colocated single-shard table INSERT INTO nullkey_c2_t1 SELECT q.* FROM (SELECT reference_table.* FROM reference_table LEFT JOIN nullkey_c1_t1 USING (a)) q JOIN nullkey_c1_t2 USING (a); @@ -244,6 +266,8 @@ INSERT INTO postgres_local_table SELECT i, i FROM generate_series(5, 10) i; -- Try slightly more complex queries. +SET client_min_messages TO DEBUG1; + WITH cte_1 AS ( SELECT nullkey_c1_t1.a, reference_table.b FROM nullkey_c1_t1 JOIN reference_table USING (a) ), @@ -253,6 +277,8 @@ cte_2 AS ( INSERT INTO distributed_table_c1_t1 SELECT cte_1.* FROM cte_1 JOIN cte_2 USING (a) JOIN distributed_table_c1_t2 USING (a) ORDER BY 1,2; +SET client_min_messages TO DEBUG2; + WITH cte_1 AS ( SELECT nullkey_c1_t1.a, reference_table.b FROM nullkey_c1_t1 JOIN reference_table USING (a) ), @@ -326,9 +352,6 @@ WHERE t2.sum_val > 2; -- in the output of the next query. SET client_min_messages TO DEBUG1; --- MultiTaskRouterSelectQuerySupported() is unnecessarily restrictive --- about pushing down queries with DISTINCT ON clause even if the table --- doesn't have a shard key. See https://github.com/citusdata/citus/pull/6752. INSERT INTO nullkey_c1_t1 SELECT DISTINCT ON (a) a, b FROM nullkey_c1_t2; SET client_min_messages TO DEBUG2; diff --git a/src/test/regress/sql/query_single_shard_table.sql b/src/test/regress/sql/query_single_shard_table.sql index f1a04c9e3..b6002f8b1 100644 --- a/src/test/regress/sql/query_single_shard_table.sql +++ b/src/test/regress/sql/query_single_shard_table.sql @@ -109,6 +109,44 @@ SELECT create_distributed_table('range_table', 'a', 'range'); CALL public.create_range_partitioned_shards('range_table', '{"0","25"}','{"24","49"}'); INSERT INTO range_table VALUES (0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 50); +\set users_table_data_file :abs_srcdir '/data/users_table.data' +\set events_table_data_file :abs_srcdir '/data/events_table.data' + +CREATE TABLE users_table (user_id int, time timestamp, value_1 int, value_2 int, value_3 float, value_4 bigint); +SELECT create_distributed_table('users_table', null, colocate_with=>'none'); +\set client_side_copy_command '\\copy users_table FROM ' :'users_table_data_file' ' WITH CSV;' +:client_side_copy_command + +CREATE TABLE non_colocated_users_table (id int, value int); +SELECT create_distributed_table('non_colocated_users_table', null, colocate_with => 'none'); +INSERT INTO non_colocated_users_table (id, value) VALUES(1, 2),(2, 3),(3,4); + +CREATE TABLE colocated_events_table (user_id int, time timestamp, event_type int, value_2 int, value_3 float, value_4 bigint); +SELECT create_distributed_table('colocated_events_table', null, colocate_with=>'users_table'); +\set client_side_copy_command '\\copy colocated_events_table FROM ' :'events_table_data_file' ' WITH CSV;' +:client_side_copy_command + +CREATE TABLE non_colocated_events_table (user_id int, time timestamp, event_type int, value_2 int, value_3 float, value_4 bigint); +SELECT create_distributed_table('non_colocated_events_table', null, colocate_with=>'non_colocated_users_table'); +\set client_side_copy_command '\\copy non_colocated_events_table FROM ' :'events_table_data_file' ' WITH CSV;' +:client_side_copy_command + +CREATE TABLE users_table_local AS SELECT * FROM users_table; + +CREATE TABLE colocated_users_table (id int, value int); +SELECT create_distributed_table('colocated_users_table', null, colocate_with => 'users_table'); +INSERT INTO colocated_users_table (id, value) VALUES(1, 2),(2, 3),(3,4); + +CREATE TABLE users_reference_table (like users_table including all); +SELECT create_reference_table('users_reference_table'); + +CREATE TABLE events_reference_table (like colocated_events_table including all); +SELECT create_reference_table('events_reference_table'); + +CREATE FUNCTION func() RETURNS TABLE (id int, value int) AS $$ + SELECT 1, 2 +$$ LANGUAGE SQL; + SET client_min_messages to DEBUG2; -- simple insert @@ -155,9 +193,13 @@ SET citus.enable_non_colocated_router_query_pushdown TO ON; SELECT COUNT(*) FROM nullkey_c1_t1 JOIN nullkey_c3_t1 USING(a); SET citus.enable_non_colocated_router_query_pushdown TO OFF; +SET citus.enable_repartition_joins TO ON; +SET client_min_messages TO DEBUG1; SELECT COUNT(*) FROM nullkey_c1_t1 JOIN nullkey_c3_t1 USING(a); +SET client_min_messages TO DEBUG2; +SET citus.enable_repartition_joins TO OFF; RESET citus.enable_non_colocated_router_query_pushdown; -- colocated join between single-shard tables @@ -191,13 +233,23 @@ WHERE t1.b NOT IN ( ); -- non-colocated inner joins between single-shard tables + +SET client_min_messages to DEBUG1; +SET citus.enable_repartition_joins TO ON; + SELECT * FROM nullkey_c1_t1 JOIN nullkey_c2_t1 USING(a) ORDER BY 1,2,3; +SELECT * FROM (SELECT * FROM nullkey_c1_t1) nullkey_c1_t1 JOIN nullkey_c2_t1 USING(a) ORDER BY 1,2,3; +SELECT * FROM nullkey_c2_t1 JOIN (SELECT * FROM nullkey_c1_t1) nullkey_c1_t1 USING(a) ORDER BY 1,2,3; + SELECT COUNT(*) FROM nullkey_c1_t1 t1 JOIN LATERAL ( SELECT * FROM nullkey_c2_t2 t2 WHERE t2.b > t1.a ) q USING(a); +SET citus.enable_repartition_joins TO OFF; +SET client_min_messages to DEBUG2; + -- non-colocated outer joins between single-shard tables SELECT * FROM nullkey_c1_t1 LEFT JOIN nullkey_c2_t2 USING(a) ORDER BY 1,2,3 LIMIT 4; SELECT * FROM nullkey_c1_t1 FULL JOIN nullkey_c2_t2 USING(a) ORDER BY 1,2,3 LIMIT 4; @@ -234,22 +286,57 @@ WITH cte_1 AS SELECT COUNT(*) FROM cte_1; -- join with postgres / citus local tables -SELECT * FROM nullkey_c1_t1 JOIN postgres_local_table USING(a); -SELECT * FROM nullkey_c1_t1 JOIN citus_local_table USING(a); +SELECT * FROM nullkey_c1_t1 JOIN postgres_local_table USING(a) ORDER BY 1,2,3; +SELECT * FROM nullkey_c1_t1 JOIN citus_local_table USING(a) ORDER BY 1,2,3; + +SET citus.local_table_join_policy TO 'prefer-distributed'; +SELECT * FROM nullkey_c1_t1 JOIN citus_local_table USING(a) ORDER BY 1,2,3; +RESET citus.local_table_join_policy; -- join with a distributed table -SELECT * FROM distributed_table d1 JOIN nullkey_c1_t1 USING(a); + +SET citus.enable_repartition_joins TO ON; +SET client_min_messages TO DEBUG1; + +SELECT * FROM distributed_table d1 JOIN nullkey_c1_t1 USING(a) ORDER BY 1,2,3; + +SELECT * FROM (SELECT * FROM distributed_table) d1 JOIN nullkey_c1_t1 USING(a) ORDER BY 1,2,3; +SELECT * FROM nullkey_c1_t1 JOIN (SELECT * FROM distributed_table) d1 USING(a) ORDER BY 1,2,3; +SELECT * FROM distributed_table d1 JOIN (SELECT * FROM nullkey_c1_t1) nullkey_c1_t1 USING(a) ORDER BY 1,2,3; +SELECT * FROM (SELECT * FROM nullkey_c1_t1) nullkey_c1_t1 JOIN distributed_table d1 USING(a) ORDER BY 1,2,3; + +-- test joins with non-colocated distributed tables, by using subqueries +SELECT * FROM nullkey_c1_t1 t1 JOIN (SELECT * FROM distributed_table) t2 USING (a) JOIN (SELECT * FROM nullkey_c1_t2) t3 USING (a) ORDER BY 1,2,3,4 LIMIT 1; +SELECT * FROM (SELECT * FROM nullkey_c1_t1) t1 JOIN nullkey_c2_t1 t2 USING (a) JOIN (SELECT * FROM nullkey_c1_t2) t3 USING (a) ORDER BY 1,2,3,4 LIMIT 1; +SELECT * FROM distributed_table t1 JOIN (SELECT * FROM nullkey_c1_t1) t2 USING (a) JOIN (SELECT b as a FROM distributed_table) t3 USING (a) ORDER BY 1,2,3 LIMIT 1; +SELECT * FROM (SELECT * FROM nullkey_c2_t1) t1 JOIN nullkey_c1_t1 t2 USING (a) JOIN (SELECT * FROM nullkey_c2_t1) t3 USING (a) ORDER BY 1,2,3,4 LIMIT 1; +SELECT * FROM nullkey_c1_t1 t1 JOIN (SELECT * FROM distributed_table) t2 USING (a) JOIN (SELECT * FROM distributed_table) t3 USING (a) ORDER BY 1,2,3,4 LIMIT 1; +SELECT * FROM (SELECT * FROM nullkey_c1_t1) t1 JOIN nullkey_c2_t1 t2 USING (a) JOIN (SELECT * FROM nullkey_c2_t1) t3 USING (a) ORDER BY 1,2,3,4 LIMIT 1; +SELECT * FROM distributed_table t1 JOIN (SELECT * FROM nullkey_c1_t1) t2 USING (a) JOIN (SELECT * FROM nullkey_c1_t1) t3 USING (a) ORDER BY 1,2,3,4 LIMIT 1; +SELECT * FROM (SELECT * FROM nullkey_c2_t1) t1 JOIN nullkey_c1_t1 t2 USING (a) JOIN (SELECT * FROM nullkey_c1_t1) t3 USING (a) ORDER BY 1,2,3,4 LIMIT 1; +SELECT * FROM nullkey_c1_t1 t1 JOIN (SELECT * FROM nullkey_c1_t1) t2 USING (a) JOIN distributed_table t3 USING (a) ORDER BY 1,2,3,4 LIMIT 1; +SELECT * FROM nullkey_c1_t1 t1 JOIN nullkey_c1_t1 t2 USING (a) JOIN nullkey_c2_t1 t3 USING (a) ORDER BY 1,2,3,4 LIMIT 1; +SELECT * FROM (SELECT * FROM distributed_table) t1 JOIN distributed_table t2 USING (a) JOIN (SELECT * FROM nullkey_c1_t1) t3 USING (a) ORDER BY 1,2,3,4 LIMIT 1; +SELECT * FROM (SELECT * FROM nullkey_c2_t1) t1 JOIN nullkey_c2_t1 t2 USING (a) JOIN (SELECT * FROM nullkey_c1_t1) t3 USING (a) ORDER BY 1,2,3,4 LIMIT 1; SELECT COUNT(*) FROM nullkey_c1_t1 t1 JOIN LATERAL ( SELECT * FROM distributed_table t2 WHERE t2.b > t1.a ) q USING(a); +SELECT COUNT(*) FROM nullkey_c1_t1 t1 +JOIN LATERAL ( + SELECT *, random() FROM distributed_table t2 WHERE t2.b > t1.a +) q USING(a); + SELECT COUNT(*) FROM distributed_table t1 JOIN LATERAL ( SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a ) q USING(a); +SET client_min_messages TO DEBUG2; +SET citus.enable_repartition_joins TO OFF; + -- outer joins with different table types SELECT COUNT(*) FROM nullkey_c1_t1 LEFT JOIN reference_table USING(a); SELECT COUNT(*) FROM reference_table LEFT JOIN nullkey_c1_t1 USING(a); @@ -264,17 +351,27 @@ SELECT COUNT(*) FROM nullkey_c1_t1 FULL JOIN citus_local_table USING(a); SELECT COUNT(*) FROM nullkey_c1_t1 FULL JOIN postgres_local_table USING(a); SELECT COUNT(*) FROM nullkey_c1_t1 FULL JOIN reference_table USING(a); +SET citus.enable_repartition_joins TO ON; +SET client_min_messages TO DEBUG1; + SELECT COUNT(*) FROM nullkey_c1_t1 JOIN append_table USING(a); SELECT COUNT(*) FROM nullkey_c1_t1 JOIN range_table USING(a); +SET client_min_messages TO DEBUG2; +SET citus.enable_repartition_joins TO OFF; + SET citus.enable_non_colocated_router_query_pushdown TO ON; SELECT COUNT(*) FROM nullkey_c1_t1 JOIN range_table USING(a) WHERE range_table.a = 20; SET citus.enable_non_colocated_router_query_pushdown TO OFF; +SET citus.enable_repartition_joins TO ON; +SET client_min_messages TO DEBUG1; SELECT COUNT(*) FROM nullkey_c1_t1 JOIN range_table USING(a) WHERE range_table.a = 20; +SET client_min_messages TO DEBUG2; +SET citus.enable_repartition_joins TO OFF; RESET citus.enable_non_colocated_router_query_pushdown; -- lateral / semi / anti joins with different table types @@ -412,6 +509,17 @@ JOIN LATERAL ( SELECT * FROM citus_local_table t2 WHERE t2.b > t1.a ) q USING(a); +-- The following and a few other tests in this file unnecessarily go through +-- recursive planning. This is because we recursive plan distributed tables +-- when they are referred in the inner side of an outer join, if the outer +-- side is a recurring rel. In future, we can optimize that such that we +-- can skip recursively planning the single-shard table because such a join +-- wouldn't result in returning recurring tuples. +-- +-- And specifically for the tests that contains a sublink (as below), things +-- get even more interesting. We try to recursively plan the single-shard +-- table but we cannot do so due to the sublink. However, the final query +-- can go through router planner and hence is supported. SELECT COUNT(*) FROM citus_local_table t1 LEFT JOIN LATERAL ( SELECT * FROM nullkey_c1_t1 t2 WHERE t2.b > t1.a @@ -578,6 +686,33 @@ SELECT a.title AS name, (SELECT a2.id FROM articles_hash a2 WHERE a.id = a2.id AS special_price FROM articles_hash a ORDER BY 1,2; +-- test having clause +SELECT COUNT(*), b FROM nullkey_c1_t1 GROUP BY 2 +HAVING (SELECT COUNT(*) FROM nullkey_c1_t2) > 0 +ORDER BY 1,2; + +SELECT COUNT(*), b FROM nullkey_c1_t1 GROUP BY 2 +HAVING (SELECT COUNT(*) FROM nullkey_c2_t1) > 0 +ORDER BY 1,2; + +SELECT COUNT(*), b FROM nullkey_c1_t1 GROUP BY 2 +HAVING (SELECT COUNT(*) FROM distributed_table) > 0 +ORDER BY 1,2; + +SELECT COUNT(*), b FROM nullkey_c1_t1 t4 GROUP BY 2 +HAVING ( + SELECT COUNT(*) FROM nullkey_c1_t1 t1 JOIN (SELECT * FROM nullkey_c1_t2) t2 USING (a) JOIN (SELECT * FROM nullkey_c1_t2) t3 USING (a) + WHERE t2.b > t4.b +) > 5 +ORDER BY 1,2; + +SELECT COUNT(*), b FROM distributed_table t4 GROUP BY 2 +HAVING ( + SELECT COUNT(*) FROM nullkey_c1_t1 t1 JOIN (SELECT * FROM distributed_table) t2 USING (a) JOIN (SELECT * FROM nullkey_c1_t2) t3 USING (a) + WHERE t2.b > t4.b +) > 5 +ORDER BY 1,2; + -- test prepared statements -- prepare queries can be router plannable @@ -1170,5 +1305,391 @@ ORDER BY rnk DESC, 1 DESC LIMIT 10; +-- more tests with ctes and subqueries + +-- CTEs are recursively planned, and subquery foo is also recursively planned. +-- Then the final plan becomes a router plan. +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( + SELECT * FROM users_table_local + ), + dist_cte AS MATERIALIZED ( + SELECT user_id FROM colocated_events_table + ) + SELECT dist_cte.user_id FROM local_cte JOIN dist_cte ON dist_cte.user_id=local_cte.user_id +) +SELECT count(*) +FROM cte, + ( + SELECT DISTINCT users_table.user_id + FROM users_table, colocated_events_table + WHERE users_table.user_id = colocated_events_table.user_id AND event_type IN (1,2,3,4) + ORDER BY 1 DESC LIMIT 5 + ) AS foo +WHERE foo.user_id = cte.user_id; + +-- CTEs are colocated, route entire query. +WITH cte1 AS ( + SELECT * FROM users_table WHERE user_id = 1 +), cte2 AS ( + SELECT * FROM colocated_events_table WHERE user_id = 1 +) +SELECT cte1.user_id, cte1.value_1, cte2.user_id, cte2.event_type +FROM cte1, cte2 +ORDER BY cte1.user_id, cte1.value_1, cte2.user_id, cte2.event_type +LIMIT 5; + +-- CTEs aren't colocated, CTEs become intermediate results. +WITH cte1 AS MATERIALIZED ( + SELECT * FROM users_table WHERE user_id = 1 +), cte2 AS MATERIALIZED ( + SELECT * FROM non_colocated_events_table WHERE user_id = 6 +) +SELECT cte1.user_id, cte1.value_1, cte2.user_id, cte2.user_id +FROM cte1, cte2 +ORDER BY cte1.user_id, cte1.value_1, cte2.user_id, cte2.event_type +LIMIT 5; + +-- users_table & colocated_users_table are colocated, route entire query. +WITH cte1 AS ( + SELECT * FROM users_table WHERE user_id = 1 +) +UPDATE colocated_users_table dt SET value = cte1.value_1 +FROM cte1 WHERE cte1.user_id = dt.id AND dt.id = 1; + +-- users_table & non_colocated_users_table are not colocated, cte is recursive planned. +WITH cte1 AS ( + SELECT * FROM users_table WHERE user_id = 1 +) +UPDATE non_colocated_users_table dt SET value = cte1.value_1 +FROM cte1 WHERE cte1.user_id = dt.id AND dt.id = 1; + +-- All relations are not colocated, CTEs become intermediate results. +WITH cte1 AS MATERIALIZED ( + SELECT * FROM users_table WHERE user_id = 1 +), cte2 AS MATERIALIZED ( + SELECT * FROM non_colocated_events_table WHERE user_id = 6 +) +UPDATE non_colocated_users_table dt SET value = cte1.value_1 + cte2.event_type +FROM cte1, cte2 WHERE cte1.user_id = dt.id AND dt.id = 1; + +-- Volatile function calls should not be routed. +WITH cte1 AS MATERIALIZED (SELECT id, value FROM func()) +UPDATE colocated_users_table dt SET value = cte1.value +FROM cte1 WHERE dt.id = 1; + +-- CTEs are recursively planned, and subquery foo is also recursively planned. +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( + SELECT * FROM users_table_local + ), + dist_cte AS MATERIALIZED ( + SELECT user_id FROM colocated_events_table + ) + SELECT dist_cte.user_id FROM local_cte JOIN dist_cte ON dist_cte.user_id=local_cte.user_id +) +SELECT count(*) +FROM + cte, + ( + SELECT DISTINCT users_table.user_id + FROM users_table, colocated_events_table + WHERE users_table.user_id = colocated_events_table.user_id AND event_type IN (1,2,3,4) + ORDER BY 1 DESC LIMIT 5 + ) AS foo, colocated_events_table +WHERE foo.user_id = cte.user_id AND colocated_events_table.user_id = cte.user_id; + +-- CTEs are replaced and subquery in WHERE is also replaced. +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( + SELECT * FROM users_table_local + ), + dist_cte AS MATERIALIZED ( + SELECT user_id FROM colocated_events_table + ) + SELECT dist_cte.user_id FROM local_cte JOIN dist_cte ON dist_cte.user_id=local_cte.user_id +) +SELECT DISTINCT cte.user_id +FROM users_table, cte +WHERE users_table.user_id = cte.user_id AND + users_table.user_id IN ( + SELECT DISTINCT value_2 FROM users_table WHERE value_1 >= 1 AND value_1 <= 20 ORDER BY 1 LIMIT 5 + ) +ORDER BY 1 DESC; + +-- Subquery in WHERE clause is planned recursively due to the recurring table +-- in FROM clause. +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( + SELECT * FROM users_table_local + ), + dist_cte AS MATERIALIZED ( + SELECT user_id FROM colocated_events_table + ) + SELECT dist_cte.user_id FROM local_cte JOIN dist_cte ON dist_cte.user_id=local_cte.user_id +) +SELECT DISTINCT cte.user_id +FROM cte +WHERE cte.user_id IN (SELECT DISTINCT user_id FROM users_table WHERE value_1 >= 1 AND value_1 <= 20) +ORDER BY 1 DESC; + +-- CTEs inside a subquery and the final query becomes a router +-- query. +SELECT + user_id +FROM + ( + WITH cte AS MATERIALIZED ( + SELECT DISTINCT users_table.user_id + FROM users_table, colocated_events_table + WHERE users_table.user_id = colocated_events_table.user_id AND + event_type IN (1,2,3,4) + ) + SELECT * FROM cte ORDER BY 1 DESC + ) AS foo +ORDER BY 1 DESC; + +-- CTEs inside a deeper subquery and also the subquery that contains the CTE are +-- recursively planned. +SELECT DISTINCT bar.user_id +FROM + ( + WITH cte AS MATERIALIZED ( + SELECT DISTINCT users_table.user_id + FROM users_table, colocated_events_table + WHERE users_table.user_id = colocated_events_table.user_id AND event_type IN (1,2,3,4) + ) + SELECT * FROM cte ORDER BY 1 DESC + ) AS foo, + ( + SELECT users_table.user_id, some_events.event_type + FROM + users_table, + ( + WITH cte AS MATERIALIZED ( + SELECT event_type, users_table.user_id + FROM users_table, colocated_events_table + WHERE users_table.user_id = colocated_events_table.user_id AND value_1 IN (1,2) + ) SELECT * FROM cte ORDER BY 1 DESC + ) AS some_events + WHERE users_table.user_id = some_events.user_id AND event_type IN (1,2,3,4) + ORDER BY 2,1 LIMIT 2 + ) AS bar +WHERE foo.user_id = bar.user_id +ORDER BY 1 DESC LIMIT 5; + +-- Recursively plan subqueries inside the CTEs that contains LIMIT and OFFSET. +WITH cte AS MATERIALIZED ( + WITH local_cte AS MATERIALIZED ( + SELECT * FROM users_table_local + ), + dist_cte AS MATERIALIZED ( + SELECT + user_id + FROM + colocated_events_table, + (SELECT DISTINCT value_2 FROM users_table OFFSET 0) as foo + WHERE + colocated_events_table.user_id = foo.value_2 AND + colocated_events_table.user_id IN (SELECT DISTINCT value_1 FROM users_table ORDER BY 1 LIMIT 3) + ) + SELECT dist_cte.user_id FROM local_cte JOIN dist_cte ON dist_cte.user_id=local_cte.user_id +) +SELECT count(*) +FROM + cte, + ( + SELECT DISTINCT users_table.user_id + FROM users_table, colocated_events_table + WHERE users_table.user_id = colocated_events_table.user_id AND event_type IN (1,2,3,4) + ORDER BY 1 DESC LIMIT 5 + ) AS foo +WHERE foo.user_id = cte.user_id; + +-- more tests with sublinks and subqueries in targetlist + +SELECT event_type, (SELECT e.value_2 FROM users_reference_table WHERE user_id = 1 AND value_1 = 1), (SELECT e.value_2) +FROM non_colocated_events_table e +ORDER BY 1,2 LIMIT 1; + +SELECT event_type, (SELECT time FROM users_table WHERE user_id = e.user_id ORDER BY time LIMIT 1) +FROM non_colocated_events_table e +ORDER BY 1,2 LIMIT 1; + +SELECT event_type, (SELECT max(time) FROM users_table WHERE user_id = e.value_2) +FROM non_colocated_events_table e +ORDER BY 1,2 LIMIT 1; + +SELECT event_type, (SELECT max(time) FROM users_table) +FROM non_colocated_events_table e +ORDER BY 1,2 LIMIT 1; + +WITH cte_1 AS (SELECT max(time) FROM users_table) +SELECT event_type, (SELECT * FROM cte_1) +FROM non_colocated_events_table e +ORDER BY 1,2 LIMIT 1; + +WITH cte_1 AS (SELECT max(time) FROM users_table) +SELECT event_type, (SELECT * FROM cte_1 LIMIT 1) +FROM non_colocated_events_table e +ORDER BY 1,2 LIMIT 1; + +WITH cte_1 AS (SELECT max(time) m FROM users_table) +SELECT count(*), (SELECT * FROM cte_1 c1 join cte_1 c2 using (m)) +FROM non_colocated_events_table e +GROUP BY 2 +ORDER BY 1,2 LIMIT 1; + +WITH cte_1 AS (SELECT min(user_id) u, max(time) m FROM users_table) +SELECT count(*), (SELECT max(time) FROM users_table WHERE user_id = cte_1.u GROUP BY user_id) +FROM cte_1 +GROUP BY 2 +ORDER BY 1,2 LIMIT 1; + +SELECT sum(e.user_id) + (SELECT max(value_3) FROM users_table WHERE user_id = e.user_id GROUP BY user_id) +FROM non_colocated_events_table e +GROUP BY e.user_id +ORDER BY 1 LIMIT 3; + +SELECT e.user_id, sum((SELECT any_value(value_3) FROM users_reference_table WHERE user_id = e.user_id GROUP BY user_id)) OVER (PARTITION BY e.user_id) +FROM non_colocated_events_table e +ORDER BY 1, 2 LIMIT 3; + +SELECT (SELECT (SELECT e.user_id + user_id) FROM users_table WHERE user_id = e.user_id GROUP BY user_id) +FROM non_colocated_events_table e +GROUP BY 1 +ORDER BY 1 LIMIT 3; + +SELECT (SELECT (SELECT e.user_id + user_id) FROM users_reference_table WHERE user_id = e.user_id GROUP BY user_id) +FROM non_colocated_events_table e +GROUP BY 1 +ORDER BY 1 LIMIT 3; + +WITH cte_1 AS (SELECT user_id FROM users_table ORDER BY 1 LIMIT 1) +SELECT (SELECT (SELECT e.user_id + user_id) FROM cte_1 WHERE user_id = e.user_id GROUP BY user_id) +FROM non_colocated_events_table e +GROUP BY 1 +ORDER BY 1 LIMIT 3; + +SELECT (SELECT (SELECT e.user_id + user_id) FROM (SELECT 1 AS user_id) s WHERE user_id = e.user_id GROUP BY user_id) +FROM non_colocated_events_table e +GROUP BY 1 +ORDER BY 1 LIMIT 3; + +CREATE TEMP VIEW view_1 AS (SELECT user_id, value_2 FROM users_table WHERE user_id = 1 AND value_1 = 1 ORDER BY 1,2); + +SELECT (SELECT value_2 FROM view_1 WHERE user_id = e.user_id GROUP BY value_2) +FROM non_colocated_events_table e +GROUP BY 1 +ORDER BY 1 LIMIT 3; + +SELECT + user_id, count(*) +FROM + non_colocated_events_table e1 +GROUP BY user_id + HAVING + count(*) > (SELECT count(*) FROM (SELECT + (SELECT sum(user_id) FROM users_table WHERE user_id = u1.user_id GROUP BY user_id) + FROM users_table u1 + GROUP BY user_id) as foo) ORDER BY 1 DESC; + +SELECT count(*) FROM (SELECT + (SELECT user_id FROM users_table WHERE user_id = u1.user_id FOR UPDATE) +FROM users_table u1 +GROUP BY user_id) as foo; + +-- test single hash repartition join + +SET citus.log_multi_join_order TO ON; +SET client_min_messages TO DEBUG1; +SET citus.enable_repartition_joins TO ON; +SET citus.enable_single_hash_repartition_joins TO ON; + +SELECT count(*) FROM nullkey_c1_t1 JOIN distributed_table USING(a); +select count(*) from nullkey_c1_t1 JOIN nullkey_c2_t2 USING(a); + +RESET citus.log_multi_join_order; +SET client_min_messages TO DEBUG2; +RESET citus.enable_repartition_joins; +RESET citus.enable_single_hash_repartition_joins; + +SET client_min_messages TO DEBUG1; +SET citus.enable_repartition_joins TO ON; +SET citus.log_multi_join_order TO ON; + +SELECT count(*), avg(avgsub.a) +FROM ( + SELECT table_0.a + FROM reference_table AS table_0 + INNER JOIN nullkey_c1_t1 AS table_1 USING (a) + INNER JOIN reference_table AS table_2 USING (a) + INNER JOIN nullkey_c2_t1 AS table_3 USING (a) + ORDER BY a LIMIT 7 +) AS avgsub; + +SET citus.enable_single_hash_repartition_joins TO ON; + +-- We prefer dual-hash repartition join over single-hash repartition join +-- even if citus.enable_single_hash_repartition_joins is set to ON. This +-- happens because single shard tables don't have a shard key. + +SELECT count(*), avg(avgsub.a) +FROM ( + SELECT table_0.a + FROM reference_table AS table_0 + INNER JOIN nullkey_c1_t1 AS table_1 USING (a) + INNER JOIN reference_table AS table_2 USING (a) + INNER JOIN nullkey_c2_t1 AS table_3 USING (a) + ORDER BY a LIMIT 7 +) AS avgsub; + +RESET citus.enable_single_hash_repartition_joins; + +SET client_min_messages TO DEBUG2; +RESET citus.enable_repartition_joins; +RESET citus.log_multi_join_order; + +SELECT count(*), avg(avgsub.a) +FROM ( + SELECT table_0.a + FROM nullkey_c1_t1 AS table_0 + RIGHT JOIN ( + SELECT table_2.a FROM ( + SELECT table_3.a FROM nullkey_c2_t1 AS table_3 + ORDER BY a LIMIT 0 + ) AS table_2 + INNER JOIN nullkey_c2_t1 AS table_4 USING (a) + WHERE table_4.a < 8 + ) AS table_1 USING (a) +) AS avgsub; + +-- test nested exec + +CREATE FUNCTION dist_query_single_shard(p_key int) +RETURNS bigint +LANGUAGE plpgsql AS $$ +DECLARE + result bigint; +BEGIN + SELECT count(*) INTO result FROM query_single_shard_table.nullkey_c1_t1 WHERE a = p_key; + RETURN result; +END; +$$; + +CREATE FUNCTION ref_query() +RETURNS bigint +LANGUAGE plpgsql AS $$ +DECLARE + result bigint; +BEGIN + SELECT count(*) INTO result FROM query_single_shard_table.reference_table; + RETURN result; +END; +$$; + +SELECT dist_query_single_shard(count(*)::int) FROM nullkey_c1_t1; +SELECT ref_query()+count(*) FROM nullkey_c1_t1; + SET client_min_messages TO ERROR; DROP SCHEMA query_single_shard_table CASCADE; From 8b2024b730238589f46451c60270cc5fe8ecb646 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emel=20=C5=9Eim=C5=9Fek?= Date: Fri, 9 Jun 2023 14:13:13 +0300 Subject: [PATCH 079/118] When Creating a FOREIGN KEY without a name, schema qualify referenced table name in deparser. (#6986) DESCRIPTION: Fixes a bug which causes an error when creating a FOREIGN KEY constraint without a name if the referenced table is schema qualified. In deparsing the `ALTER TABLE s1.t1 ADD FOREIGN KEY (key) REFERENCES s2.t2; `, command back from its cooked form, we should schema qualify the REFERENCED table. Fixes #6982. --- .../deparser/deparse_table_stmts.c | 4 ++- ...ter_table_add_foreign_key_without_name.out | 26 ++++++++++++++++--- ...ter_table_add_foreign_key_without_name.sql | 21 ++++++++++++--- 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/backend/distributed/deparser/deparse_table_stmts.c b/src/backend/distributed/deparser/deparse_table_stmts.c index c12e5401a..9a8cdedef 100644 --- a/src/backend/distributed/deparser/deparse_table_stmts.c +++ b/src/backend/distributed/deparser/deparse_table_stmts.c @@ -279,7 +279,9 @@ AppendAlterTableCmdAddConstraint(StringInfo buf, Constraint *constraint, appendStringInfoString(buf, " REFERENCES"); - appendStringInfo(buf, " %s", quote_identifier(constraint->pktable->relname)); + appendStringInfo(buf, " %s", quote_qualified_identifier( + constraint->pktable->schemaname, + constraint->pktable->relname)); if (list_length(constraint->pk_attrs) > 0) { diff --git a/src/test/regress/expected/multi_alter_table_add_foreign_key_without_name.out b/src/test/regress/expected/multi_alter_table_add_foreign_key_without_name.out index 58571cc34..9af806685 100644 --- a/src/test/regress/expected/multi_alter_table_add_foreign_key_without_name.out +++ b/src/test/regress/expected/multi_alter_table_add_foreign_key_without_name.out @@ -156,7 +156,7 @@ SELECT con.conname FROM pg_catalog.pg_constraint con INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace - WHERE rel.relname = 'referencing_table'; + WHERE rel.relname = 'referencing_table' ORDER BY con.conname ASC; conname --------------------------------------------------------------------- referencing_table_ref_id_fkey @@ -167,7 +167,7 @@ SELECT con.conname FROM pg_catalog.pg_constraint con INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace - WHERE rel.relname LIKE 'referencing_table%'; + WHERE rel.relname LIKE 'referencing_table%' ORDER BY con.conname ASC; conname --------------------------------------------------------------------- referencing_table_ref_id_fkey @@ -184,7 +184,7 @@ SELECT con.conname FROM pg_catalog.pg_constraint con INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace - WHERE rel.relname = 'referencing_table'; + WHERE rel.relname = 'referencing_table' ORDER BY con.conname ASC; conname --------------------------------------------------------------------- referencing_table_ref_id_fkey @@ -195,7 +195,7 @@ SELECT con.conname FROM pg_catalog.pg_constraint con INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace - WHERE rel.relname LIKE 'referencing_table%'; + WHERE rel.relname LIKE 'referencing_table%' ORDER BY con.conname ASC; conname --------------------------------------------------------------------- referencing_table_ref_id_fkey @@ -560,3 +560,21 @@ drop cascades to table at_add_fk.referenced_table drop cascades to table at_add_fk.referencing_table drop cascades to table at_add_fk.reference_table drop cascades to table at_add_fk.reference_table_1770052 +-- test ADD FOREIGN KEY when REFERENCED table is in another schema. +CREATE SCHEMA schema_1; +CREATE TABLE schema_1.referenced_table(a int PRIMARY KEY, b int); +SELECT create_reference_table('schema_1.referenced_table'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +CREATE SCHEMA schema_2; +CREATE TABLE schema_2.referencing_table (a int PRIMARY KEY, b int, c text); +ALTER TABLE schema_2.referencing_table ADD FOREIGN KEY (b) REFERENCES schema_1.referenced_table(a); +DROP SCHEMA schema_1, schema_2 CASCADE; +NOTICE: drop cascades to 4 other objects +DETAIL: drop cascades to table schema_2.referencing_table_1770054 +drop cascades to table schema_2.referencing_table +drop cascades to table schema_1.referenced_table +drop cascades to table schema_1.referenced_table_1770053 diff --git a/src/test/regress/sql/multi_alter_table_add_foreign_key_without_name.sql b/src/test/regress/sql/multi_alter_table_add_foreign_key_without_name.sql index 03df65709..f7ffd66a0 100644 --- a/src/test/regress/sql/multi_alter_table_add_foreign_key_without_name.sql +++ b/src/test/regress/sql/multi_alter_table_add_foreign_key_without_name.sql @@ -89,14 +89,14 @@ SELECT con.conname FROM pg_catalog.pg_constraint con INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace - WHERE rel.relname = 'referencing_table'; + WHERE rel.relname = 'referencing_table' ORDER BY con.conname ASC; \c - - :public_worker_1_host :worker_1_port SELECT con.conname FROM pg_catalog.pg_constraint con INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace - WHERE rel.relname LIKE 'referencing_table%'; + WHERE rel.relname LIKE 'referencing_table%' ORDER BY con.conname ASC; \c - - :master_host :master_port SET SEARCH_PATH = at_add_fk; @@ -109,14 +109,14 @@ SELECT con.conname FROM pg_catalog.pg_constraint con INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace - WHERE rel.relname = 'referencing_table'; + WHERE rel.relname = 'referencing_table' ORDER BY con.conname ASC; \c - - :public_worker_1_host :worker_1_port SELECT con.conname FROM pg_catalog.pg_constraint con INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace - WHERE rel.relname LIKE 'referencing_table%'; + WHERE rel.relname LIKE 'referencing_table%' ORDER BY con.conname ASC; \c - - :master_host :master_port SET SEARCH_PATH = at_add_fk; @@ -376,3 +376,16 @@ DROP TABLE citus_local_table CASCADE; RESET SEARCH_PATH; RESET client_min_messages; DROP SCHEMA at_add_fk CASCADE; + +-- test ADD FOREIGN KEY when REFERENCED table is in another schema. +CREATE SCHEMA schema_1; + +CREATE TABLE schema_1.referenced_table(a int PRIMARY KEY, b int); +SELECT create_reference_table('schema_1.referenced_table'); + +CREATE SCHEMA schema_2; + +CREATE TABLE schema_2.referencing_table (a int PRIMARY KEY, b int, c text); +ALTER TABLE schema_2.referencing_table ADD FOREIGN KEY (b) REFERENCES schema_1.referenced_table(a); + +DROP SCHEMA schema_1, schema_2 CASCADE; From 2ba3bffe1e33a95ab6d1061e7442328b58911cfe Mon Sep 17 00:00:00 2001 From: Naisila Puka <37271756+naisila@users.noreply.github.com> Date: Fri, 9 Jun 2023 14:36:43 +0300 Subject: [PATCH 080/118] Random warning fixes (#6974) Citus build with PG16 fails because of the following warnings: - using char* instead of Datum - using pointer instead of oid - candidate function for format attribute - remove old definition from PG11 compatibility https://github.com/citusdata/citus/commit/62bf571cedcfdf4451614b8520448d105df07045 This commit fixes the above. --- src/backend/columnar/columnar_reader.c | 2 +- src/backend/distributed/metadata/node_metadata.c | 8 ++++---- .../distributed/test/foreign_key_relationship_query.c | 4 ++-- src/backend/distributed/utils/citus_safe_lib.c | 4 +--- src/include/distributed/citus_safe_lib.h | 3 ++- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/backend/columnar/columnar_reader.c b/src/backend/columnar/columnar_reader.c index dec19929b..7917a446a 100644 --- a/src/backend/columnar/columnar_reader.c +++ b/src/backend/columnar/columnar_reader.c @@ -1557,7 +1557,7 @@ DeserializeDatumArray(StringInfo datumBuffer, bool *existsArray, uint32 datumCou datumTypeLength); currentDatumDataOffset = att_addlength_datum(currentDatumDataOffset, datumTypeLength, - currentDatumDataPointer); + datumArray[datumIndex]); currentDatumDataOffset = att_align_nominal(currentDatumDataOffset, datumTypeAlign); diff --git a/src/backend/distributed/metadata/node_metadata.c b/src/backend/distributed/metadata/node_metadata.c index 8586ac934..60a5ab92b 100644 --- a/src/backend/distributed/metadata/node_metadata.c +++ b/src/backend/distributed/metadata/node_metadata.c @@ -2871,15 +2871,15 @@ TupleToWorkerNode(TupleDesc tupleDescriptor, HeapTuple heapTuple) */ heap_deform_tuple(heapTuple, tupleDescriptor, datumArray, isNullArray); - char *nodeName = DatumGetCString(datumArray[Anum_pg_dist_node_nodename - 1]); - char *nodeRack = DatumGetCString(datumArray[Anum_pg_dist_node_noderack - 1]); + char *nodeName = TextDatumGetCString(datumArray[Anum_pg_dist_node_nodename - 1]); + char *nodeRack = TextDatumGetCString(datumArray[Anum_pg_dist_node_noderack - 1]); WorkerNode *workerNode = (WorkerNode *) palloc0(sizeof(WorkerNode)); workerNode->nodeId = DatumGetUInt32(datumArray[Anum_pg_dist_node_nodeid - 1]); workerNode->workerPort = DatumGetUInt32(datumArray[Anum_pg_dist_node_nodeport - 1]); workerNode->groupId = DatumGetInt32(datumArray[Anum_pg_dist_node_groupid - 1]); - strlcpy(workerNode->workerName, TextDatumGetCString(nodeName), WORKER_LENGTH); - strlcpy(workerNode->workerRack, TextDatumGetCString(nodeRack), WORKER_LENGTH); + strlcpy(workerNode->workerName, nodeName, WORKER_LENGTH); + strlcpy(workerNode->workerRack, nodeRack, WORKER_LENGTH); workerNode->hasMetadata = DatumGetBool(datumArray[Anum_pg_dist_node_hasmetadata - 1]); workerNode->metadataSynced = DatumGetBool(datumArray[Anum_pg_dist_node_metadatasynced - 1]); diff --git a/src/backend/distributed/test/foreign_key_relationship_query.c b/src/backend/distributed/test/foreign_key_relationship_query.c index e83e95bac..545c2e970 100644 --- a/src/backend/distributed/test/foreign_key_relationship_query.c +++ b/src/backend/distributed/test/foreign_key_relationship_query.c @@ -119,7 +119,7 @@ get_referencing_relation_id_list(PG_FUNCTION_ARGS) wrapper->listCell = lnext(wrapper->list, wrapper->listCell); - SRF_RETURN_NEXT(functionContext, PointerGetDatum(refId)); + SRF_RETURN_NEXT(functionContext, ObjectIdGetDatum(refId)); } else { @@ -178,7 +178,7 @@ get_referenced_relation_id_list(PG_FUNCTION_ARGS) wrapper->listCell = lnext(wrapper->list, wrapper->listCell); - SRF_RETURN_NEXT(functionContext, PointerGetDatum(refId)); + SRF_RETURN_NEXT(functionContext, ObjectIdGetDatum(refId)); } else { diff --git a/src/backend/distributed/utils/citus_safe_lib.c b/src/backend/distributed/utils/citus_safe_lib.c index 0b830fafc..82fa8f6f2 100644 --- a/src/backend/distributed/utils/citus_safe_lib.c +++ b/src/backend/distributed/utils/citus_safe_lib.c @@ -23,8 +23,6 @@ #include "distributed/citus_safe_lib.h" #include "lib/stringinfo.h" -#define citus_vsnprintf pg_vsnprintf - /* * ereport_constraint_handler is a constraint handler that calls ereport. A @@ -338,7 +336,7 @@ SafeSnprintf(char *restrict buffer, rsize_t bufsz, const char *restrict format, va_list args; va_start(args, format); - size_t result = citus_vsnprintf(buffer, bufsz, format, args); + int result = pg_vsnprintf(buffer, bufsz, format, args); va_end(args); return result; } diff --git a/src/include/distributed/citus_safe_lib.h b/src/include/distributed/citus_safe_lib.h index e039072a7..62142df0a 100644 --- a/src/include/distributed/citus_safe_lib.h +++ b/src/include/distributed/citus_safe_lib.h @@ -25,7 +25,8 @@ extern void SafeQsort(void *ptr, rsize_t count, rsize_t size, int (*comp)(const void *, const void *)); void * SafeBsearch(const void *key, const void *ptr, rsize_t count, rsize_t size, int (*comp)(const void *, const void *)); -int SafeSnprintf(char *str, rsize_t count, const char *fmt, ...); +int SafeSnprintf(char *str, rsize_t count, const char *fmt, ...) pg_attribute_printf(3, + 0); #define memset_struct_0(variable) memset(&variable, 0, sizeof(variable)) From 3acadd73219ae886bfbccabb4374221922eeb7a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20Ozan=20Akg=C3=BCl?= Date: Fri, 9 Jun 2023 15:06:46 +0300 Subject: [PATCH 081/118] Citus Clock tests with Single Shard Tables (#6938) This PR tests Citus clock with single shard tables. --- .../expected/single_shard_table_udfs.out | 91 +++++++++++++++++++ .../regress/sql/single_shard_table_udfs.sql | 66 ++++++++++++++ 2 files changed, 157 insertions(+) diff --git a/src/test/regress/expected/single_shard_table_udfs.out b/src/test/regress/expected/single_shard_table_udfs.out index da264a1c9..78b4ee25a 100644 --- a/src/test/regress/expected/single_shard_table_udfs.out +++ b/src/test/regress/expected/single_shard_table_udfs.out @@ -1211,5 +1211,96 @@ SELECT shardlength > 0 FROM pg_dist_shard_placement WHERE shardid = :update_shar t (1 row) +-- test citus clock +SET citus.enable_cluster_clock TO ON; +CREATE TABLE clock_single(a INT); +SELECT create_distributed_table('clock_single', NULL, colocate_with:='none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_get_node_clock() AS nc1 \gset +SELECT citus_get_node_clock() AS nc2 \gset +SELECT citus_get_node_clock() AS nc3 \gset +SELECT citus_is_clock_after(:'nc2', :'nc1'); + citus_is_clock_after +--------------------------------------------------------------------- + t +(1 row) + +SELECT citus_is_clock_after(:'nc3', :'nc2'); + citus_is_clock_after +--------------------------------------------------------------------- + t +(1 row) + +BEGIN; +SELECT citus_get_node_clock() AS nc4 \gset +COPY clock_single FROM STDIN; +SELECT citus_get_node_clock() AS nc5 \gset +END; +SELECT citus_is_clock_after(:'nc4', :'nc3'); + citus_is_clock_after +--------------------------------------------------------------------- + t +(1 row) + +SELECT citus_is_clock_after(:'nc5', :'nc4'); + citus_is_clock_after +--------------------------------------------------------------------- + t +(1 row) + +BEGIN; +SELECT citus_get_transaction_clock(); + citus_get_transaction_clock +--------------------------------------------------------------------- + (xxxxxxxxxxxxx,x) +(1 row) + +END; +-- Transaction with single shard table access +SELECT nodeport AS clock_shard_nodeport FROM citus_shards +WHERE table_name::text = 'clock_single' AND nodeport IN (:worker_1_port, :worker_2_port) \gset +BEGIN; +COPY clock_single FROM STDIN; +SELECT get_current_transaction_id() \gset tid +SET client_min_messages TO DEBUG1; +-- Capture the transaction timestamp +SELECT citus_get_transaction_clock() as txnclock \gset +DEBUG: node xxxx transaction clock xxxxxx +DEBUG: node xxxx transaction clock xxxxxx +DEBUG: final global transaction clock xxxxxx +COMMIT; +-- Check to see if the clock is persisted in the sequence. +SELECT result as logseq from run_command_on_workers($$SELECT last_value FROM pg_dist_clock_logical_seq$$) +WHERE nodeport = :clock_shard_nodeport \gset +SELECT cluster_clock_logical(:'txnclock') as txnlog \gset +SELECT :logseq = :txnlog; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +BEGIN; +COPY clock_single FROM STDIN; +SELECT get_current_transaction_id() \gset tid +SET client_min_messages TO DEBUG1; +-- Capture the transaction timestamp +SELECT citus_get_transaction_clock() as txnclock \gset +DEBUG: node xxxx transaction clock xxxxxx +DEBUG: node xxxx transaction clock xxxxxx +DEBUG: final global transaction clock xxxxxx +ROLLBACK; +SELECT result as logseq from run_command_on_workers($$SELECT last_value FROM pg_dist_clock_logical_seq$$) +WHERE nodeport = :clock_shard_nodeport \gset +SELECT cluster_clock_logical(:'txnclock') as txnlog \gset +SELECT :logseq = :txnlog; + ?column? +--------------------------------------------------------------------- + t +(1 row) + SET client_min_messages TO WARNING; DROP SCHEMA null_dist_key_udfs CASCADE; diff --git a/src/test/regress/sql/single_shard_table_udfs.sql b/src/test/regress/sql/single_shard_table_udfs.sql index 68d04890f..aac01f085 100644 --- a/src/test/regress/sql/single_shard_table_udfs.sql +++ b/src/test/regress/sql/single_shard_table_udfs.sql @@ -588,5 +588,71 @@ SELECT 1 FROM citus_update_shard_statistics(:update_shard_stat_shard); SELECT shardlength > 0 FROM pg_dist_shard_placement WHERE shardid = :update_shard_stat_shard LIMIT 1; +-- test citus clock +SET citus.enable_cluster_clock TO ON; + +CREATE TABLE clock_single(a INT); +SELECT create_distributed_table('clock_single', NULL, colocate_with:='none'); + +SELECT citus_get_node_clock() AS nc1 \gset +SELECT citus_get_node_clock() AS nc2 \gset +SELECT citus_get_node_clock() AS nc3 \gset + +SELECT citus_is_clock_after(:'nc2', :'nc1'); +SELECT citus_is_clock_after(:'nc3', :'nc2'); + +BEGIN; +SELECT citus_get_node_clock() AS nc4 \gset +COPY clock_single FROM STDIN; +1 +2 +\. +SELECT citus_get_node_clock() AS nc5 \gset +END; + +SELECT citus_is_clock_after(:'nc4', :'nc3'); +SELECT citus_is_clock_after(:'nc5', :'nc4'); + +BEGIN; +SELECT citus_get_transaction_clock(); +END; + +-- Transaction with single shard table access +SELECT nodeport AS clock_shard_nodeport FROM citus_shards +WHERE table_name::text = 'clock_single' AND nodeport IN (:worker_1_port, :worker_2_port) \gset + +BEGIN; +COPY clock_single FROM STDIN; +1 +2 +\. +SELECT get_current_transaction_id() \gset tid +SET client_min_messages TO DEBUG1; +-- Capture the transaction timestamp +SELECT citus_get_transaction_clock() as txnclock \gset +COMMIT; + +-- Check to see if the clock is persisted in the sequence. +SELECT result as logseq from run_command_on_workers($$SELECT last_value FROM pg_dist_clock_logical_seq$$) +WHERE nodeport = :clock_shard_nodeport \gset +SELECT cluster_clock_logical(:'txnclock') as txnlog \gset +SELECT :logseq = :txnlog; + +BEGIN; +COPY clock_single FROM STDIN; +1 +2 +\. +SELECT get_current_transaction_id() \gset tid +SET client_min_messages TO DEBUG1; +-- Capture the transaction timestamp +SELECT citus_get_transaction_clock() as txnclock \gset +ROLLBACK; + +SELECT result as logseq from run_command_on_workers($$SELECT last_value FROM pg_dist_clock_logical_seq$$) +WHERE nodeport = :clock_shard_nodeport \gset +SELECT cluster_clock_logical(:'txnclock') as txnlog \gset +SELECT :logseq = :txnlog; + SET client_min_messages TO WARNING; DROP SCHEMA null_dist_key_udfs CASCADE; From e6ac9f2a680126a9d2784b3396e6cf3ef0070dd9 Mon Sep 17 00:00:00 2001 From: Ahmet Gedemenli Date: Fri, 9 Jun 2023 15:32:18 +0300 Subject: [PATCH 082/118] Propagate ALTER SCHEMA .. OWNER TO .. (#6987) Propagate `ALTER SCHEMA .. OWNER TO ..` commands to workers --- .../commands/distribute_object_ops.c | 14 ++++++++++ src/backend/distributed/commands/schema.c | 14 ++++++++++ .../deparser/deparse_schema_stmts.c | 26 +++++++++++++++++++ src/include/distributed/commands.h | 2 ++ src/include/distributed/deparser.h | 1 + .../regress/expected/multi_schema_support.out | 25 ++++++++++++++++++ .../expected/schema_based_sharding.out | 24 ++++++++++++----- src/test/regress/sql/multi_schema_support.sql | 19 ++++++++++++++ .../regress/sql/schema_based_sharding.sql | 14 +++++++--- 9 files changed, 130 insertions(+), 9 deletions(-) diff --git a/src/backend/distributed/commands/distribute_object_ops.c b/src/backend/distributed/commands/distribute_object_ops.c index ce8d16275..b417e416e 100644 --- a/src/backend/distributed/commands/distribute_object_ops.c +++ b/src/backend/distributed/commands/distribute_object_ops.c @@ -1024,6 +1024,15 @@ static DistributeObjectOps Routine_Rename = { .address = RenameFunctionStmtObjectAddress, .markDistributed = false, }; +static DistributeObjectOps Schema_AlterOwner = { + .deparse = DeparseAlterSchemaOwnerStmt, + .qualify = NULL, + .preprocess = PreprocessAlterDistributedObjectStmt, + .operationType = DIST_OPS_ALTER, + .postprocess = NULL, + .address = AlterSchemaOwnerStmtObjectAddress, + .markDistributed = false, +}; static DistributeObjectOps Schema_Drop = { .deparse = DeparseDropSchemaStmt, .qualify = NULL, @@ -1457,6 +1466,11 @@ GetDistributeObjectOps(Node *node) return &Routine_AlterOwner; } + case OBJECT_SCHEMA: + { + return &Schema_AlterOwner; + } + case OBJECT_STATISTIC_EXT: { return &Statistics_AlterOwner; diff --git a/src/backend/distributed/commands/schema.c b/src/backend/distributed/commands/schema.c index f1d1472c5..c0affce58 100644 --- a/src/backend/distributed/commands/schema.c +++ b/src/backend/distributed/commands/schema.c @@ -250,6 +250,20 @@ CreateSchemaStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess) } +/* + * AlterSchemaOwnerStmtObjectAddress returns the ObjectAddress of the schema that is + * the object of the AlterOwnerStmt. Errors if missing_ok is false. + */ +List * +AlterSchemaOwnerStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess) +{ + AlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node); + Assert(stmt->objectType == OBJECT_SCHEMA); + + return GetObjectAddressBySchemaName(strVal(stmt->object), missing_ok); +} + + /* * AlterSchemaRenameStmtObjectAddress returns the ObjectAddress of the schema that is * the object of the RenameStmt. Errors if missing_ok is false. diff --git a/src/backend/distributed/deparser/deparse_schema_stmts.c b/src/backend/distributed/deparser/deparse_schema_stmts.c index 471025b23..cf8bf3418 100644 --- a/src/backend/distributed/deparser/deparse_schema_stmts.c +++ b/src/backend/distributed/deparser/deparse_schema_stmts.c @@ -24,6 +24,7 @@ static void AppendDropSchemaStmt(StringInfo buf, DropStmt *stmt); static void AppendGrantOnSchemaStmt(StringInfo buf, GrantStmt *stmt); static void AppendGrantOnSchemaSchemas(StringInfo buf, GrantStmt *stmt); static void AppendAlterSchemaRenameStmt(StringInfo buf, RenameStmt *stmt); +static void AppendAlterSchemaOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt); char * DeparseCreateSchemaStmt(Node *node) @@ -68,6 +69,31 @@ DeparseGrantOnSchemaStmt(Node *node) } +char * +DeparseAlterSchemaOwnerStmt(Node *node) +{ + AlterOwnerStmt *stmt = castNode(AlterOwnerStmt, node); + + StringInfoData str = { 0 }; + initStringInfo(&str); + + AppendAlterSchemaOwnerStmt(&str, stmt); + + return str.data; +} + + +static void +AppendAlterSchemaOwnerStmt(StringInfo buf, AlterOwnerStmt *stmt) +{ + Assert(stmt->objectType == OBJECT_SCHEMA); + + appendStringInfo(buf, "ALTER SCHEMA %s OWNER TO %s;", + quote_identifier(strVal(stmt->object)), + RoleSpecString(stmt->newowner, true)); +} + + char * DeparseAlterSchemaRenameStmt(Node *node) { diff --git a/src/include/distributed/commands.h b/src/include/distributed/commands.h index f5dc6e898..542e85326 100644 --- a/src/include/distributed/commands.h +++ b/src/include/distributed/commands.h @@ -469,6 +469,8 @@ extern List * PreprocessGrantOnSchemaStmt(Node *node, const char *queryString, ProcessUtilityContext processUtilityContext); extern List * CreateSchemaStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess); +extern List * AlterSchemaOwnerStmtObjectAddress(Node *node, bool missing_ok, + bool isPostprocess); extern List * AlterSchemaRenameStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess); diff --git a/src/include/distributed/deparser.h b/src/include/distributed/deparser.h index 0d4f605d8..7390b486c 100644 --- a/src/include/distributed/deparser.h +++ b/src/include/distributed/deparser.h @@ -105,6 +105,7 @@ extern char * DeparseCreateSchemaStmt(Node *node); extern char * DeparseDropSchemaStmt(Node *node); extern char * DeparseGrantOnSchemaStmt(Node *stmt); extern char * DeparseAlterSchemaRenameStmt(Node *stmt); +extern char * DeparseAlterSchemaOwnerStmt(Node *node); extern void AppendGrantPrivileges(StringInfo buf, GrantStmt *stmt); extern void AppendGrantGrantees(StringInfo buf, GrantStmt *stmt); diff --git a/src/test/regress/expected/multi_schema_support.out b/src/test/regress/expected/multi_schema_support.out index 9128491c9..26bc5c47a 100644 --- a/src/test/regress/expected/multi_schema_support.out +++ b/src/test/regress/expected/multi_schema_support.out @@ -1333,6 +1333,31 @@ SELECT * FROM "Citus'Teen123"."TeeNTabLE.1!?!" ORDER BY id; 4 | 4 | (4 rows) +-- test alter owner propagation +CREATE ROLE test_non_super_user; +ALTER ROLE test_non_super_user NOSUPERUSER; +SELECT pg_get_userbyid(nspowner) AS schema_owner + FROM pg_namespace + WHERE nspname = 'bar'; + schema_owner +--------------------------------------------------------------------- + postgres +(1 row) + +ALTER SCHEMA bar OWNER TO test_non_super_user; +select result from run_command_on_workers ($$ + SELECT pg_get_userbyid(nspowner) AS schema_owner + FROM pg_namespace + WHERE nspname = 'bar' +$$); + result +--------------------------------------------------------------------- + test_non_super_user + test_non_super_user +(2 rows) + +ALTER SCHEMA bar OWNER TO postgres; +DROP ROLE test_non_super_user; -- test error INSERT INTO bar.test VALUES (3,3), (4,4), (5,5), (6,6), (7,7), (8,8), (9,9); BEGIN; diff --git a/src/test/regress/expected/schema_based_sharding.out b/src/test/regress/expected/schema_based_sharding.out index ba3018b15..1180fe573 100644 --- a/src/test/regress/expected/schema_based_sharding.out +++ b/src/test/regress/expected/schema_based_sharding.out @@ -725,14 +725,26 @@ SET citus.enable_schema_based_sharding TO OFF; DROP SCHEMA tenant_1 CASCADE; CREATE ROLE test_non_super_user; ALTER ROLE test_non_super_user NOSUPERUSER; +ALTER SCHEMA tenant_2 OWNER TO non_existing_role; +ERROR: role "non_existing_role" does not exist ALTER SCHEMA tenant_2 OWNER TO test_non_super_user; --- XXX: ALTER SCHEMA .. OWNER TO .. is not propagated to workers, --- see https://github.com/citusdata/citus/issues/4812. -SELECT result FROM run_command_on_workers($$ALTER SCHEMA tenant_2 OWNER TO test_non_super_user$$); - result +SELECT pg_get_userbyid(nspowner) AS schema_owner + FROM pg_namespace + WHERE nspname = 'tenant_2'; + schema_owner --------------------------------------------------------------------- - ALTER SCHEMA - ALTER SCHEMA + test_non_super_user +(1 row) + +select result from run_command_on_workers ($$ + SELECT pg_get_userbyid(nspowner) AS schema_owner + FROM pg_namespace + WHERE nspname = 'tenant_2' +$$); + result +--------------------------------------------------------------------- + test_non_super_user + test_non_super_user (2 rows) DROP OWNED BY test_non_super_user CASCADE; diff --git a/src/test/regress/sql/multi_schema_support.sql b/src/test/regress/sql/multi_schema_support.sql index 944667c2a..2d3b780fd 100644 --- a/src/test/regress/sql/multi_schema_support.sql +++ b/src/test/regress/sql/multi_schema_support.sql @@ -949,6 +949,25 @@ SELECT COUNT(*) FROM bar.test; ALTER SCHEMA "CiTuS.TeeN" RENAME TO "Citus'Teen123"; SELECT * FROM "Citus'Teen123"."TeeNTabLE.1!?!" ORDER BY id; +-- test alter owner propagation +CREATE ROLE test_non_super_user; +ALTER ROLE test_non_super_user NOSUPERUSER; + +SELECT pg_get_userbyid(nspowner) AS schema_owner + FROM pg_namespace + WHERE nspname = 'bar'; + +ALTER SCHEMA bar OWNER TO test_non_super_user; + +select result from run_command_on_workers ($$ + SELECT pg_get_userbyid(nspowner) AS schema_owner + FROM pg_namespace + WHERE nspname = 'bar' +$$); + +ALTER SCHEMA bar OWNER TO postgres; +DROP ROLE test_non_super_user; + -- test error INSERT INTO bar.test VALUES (3,3), (4,4), (5,5), (6,6), (7,7), (8,8), (9,9); diff --git a/src/test/regress/sql/schema_based_sharding.sql b/src/test/regress/sql/schema_based_sharding.sql index 309797a74..4dda95dcd 100644 --- a/src/test/regress/sql/schema_based_sharding.sql +++ b/src/test/regress/sql/schema_based_sharding.sql @@ -505,10 +505,18 @@ DROP SCHEMA tenant_1 CASCADE; CREATE ROLE test_non_super_user; ALTER ROLE test_non_super_user NOSUPERUSER; +ALTER SCHEMA tenant_2 OWNER TO non_existing_role; ALTER SCHEMA tenant_2 OWNER TO test_non_super_user; --- XXX: ALTER SCHEMA .. OWNER TO .. is not propagated to workers, --- see https://github.com/citusdata/citus/issues/4812. -SELECT result FROM run_command_on_workers($$ALTER SCHEMA tenant_2 OWNER TO test_non_super_user$$); + +SELECT pg_get_userbyid(nspowner) AS schema_owner + FROM pg_namespace + WHERE nspname = 'tenant_2'; + +select result from run_command_on_workers ($$ + SELECT pg_get_userbyid(nspowner) AS schema_owner + FROM pg_namespace + WHERE nspname = 'tenant_2' +$$); DROP OWNED BY test_non_super_user CASCADE; From 50e6c505346cb79243bf69145162da897da6c193 Mon Sep 17 00:00:00 2001 From: Naisila Puka <37271756+naisila@users.noreply.github.com> Date: Fri, 9 Jun 2023 15:59:30 +0300 Subject: [PATCH 083/118] Remove flaky rebalance plan from test (#6990) Looks like sometimes shards are a slightly different size than we expect, 16k vs 8k, resulting in a different rebalance plan. --- .../expected/background_rebalance_parallel.out | 11 ----------- .../regress/sql/background_rebalance_parallel.sql | 4 ---- 2 files changed, 15 deletions(-) diff --git a/src/test/regress/expected/background_rebalance_parallel.out b/src/test/regress/expected/background_rebalance_parallel.out index dbdd963a9..cc3470de9 100644 --- a/src/test/regress/expected/background_rebalance_parallel.out +++ b/src/test/regress/expected/background_rebalance_parallel.out @@ -466,17 +466,6 @@ SELECT citus_rebalance_start AS job_id from citus_rebalance_start() \gset -- see dependent tasks to understand which tasks remain runnable because of -- citus.max_background_task_executors_per_node -- and which tasks are actually blocked from colocation group dependencies -SELECT (SELECT T.command FROM pg_dist_background_task T WHERE T.task_id = D.task_id), - (SELECT T.command depends_on_command FROM pg_dist_background_task T WHERE T.task_id = D.depends_on) -FROM pg_dist_background_task_depend D WHERE job_id in (:job_id) ORDER BY 1, 2 ASC; - command | depends_on_command ---------------------------------------------------------------------- - SELECT pg_catalog.citus_move_shard_placement(85674026,50,57,'auto') | SELECT pg_catalog.citus_move_shard_placement(85674025,50,56,'auto') - SELECT pg_catalog.citus_move_shard_placement(85674032,50,57,'auto') | SELECT pg_catalog.citus_move_shard_placement(85674031,50,56,'auto') - SELECT pg_catalog.citus_move_shard_placement(85674038,50,57,'auto') | SELECT pg_catalog.citus_move_shard_placement(85674037,50,56,'auto') - SELECT pg_catalog.citus_move_shard_placement(85674044,50,57,'auto') | SELECT pg_catalog.citus_move_shard_placement(85674043,50,56,'auto') -(4 rows) - SELECT task_id, depends_on FROM pg_dist_background_task_depend WHERE job_id in (:job_id) diff --git a/src/test/regress/sql/background_rebalance_parallel.sql b/src/test/regress/sql/background_rebalance_parallel.sql index 2eb952b67..8f8ed4d74 100644 --- a/src/test/regress/sql/background_rebalance_parallel.sql +++ b/src/test/regress/sql/background_rebalance_parallel.sql @@ -204,10 +204,6 @@ SELECT citus_rebalance_start AS job_id from citus_rebalance_start() \gset -- see dependent tasks to understand which tasks remain runnable because of -- citus.max_background_task_executors_per_node -- and which tasks are actually blocked from colocation group dependencies -SELECT (SELECT T.command FROM pg_dist_background_task T WHERE T.task_id = D.task_id), - (SELECT T.command depends_on_command FROM pg_dist_background_task T WHERE T.task_id = D.depends_on) -FROM pg_dist_background_task_depend D WHERE job_id in (:job_id) ORDER BY 1, 2 ASC; - SELECT task_id, depends_on FROM pg_dist_background_task_depend WHERE job_id in (:job_id) From 2f13b37ce463e3f37add15926f5f2d067b1d6c11 Mon Sep 17 00:00:00 2001 From: Ahmet Gedemenli Date: Fri, 9 Jun 2023 17:03:58 +0300 Subject: [PATCH 084/118] Fix flaky multi_schema_support (#6991) Dropping a leftover table, delete some unnecessary command, add some ORDER BY to avoid flakiness in `multi_schema_support` --- src/test/regress/expected/multi_schema_support.out | 10 +++++----- src/test/regress/sql/multi_schema_support.sql | 11 +++++------ 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/test/regress/expected/multi_schema_support.out b/src/test/regress/expected/multi_schema_support.out index 26bc5c47a..9adb0c211 100644 --- a/src/test/regress/expected/multi_schema_support.out +++ b/src/test/regress/expected/multi_schema_support.out @@ -868,7 +868,7 @@ SELECT create_distributed_table('old_schema.table_set_schema', 'id'); CREATE SCHEMA new_schema; SELECT objid::oid::regnamespace as "Distributed Schemas" FROM pg_catalog.pg_dist_object - WHERE objid::oid::regnamespace IN ('old_schema', 'new_schema'); + WHERE objid::oid::regnamespace IN ('old_schema', 'new_schema') ORDER BY objid::oid::regnamespace; Distributed Schemas --------------------------------------------------------------------- old_schema @@ -890,7 +890,7 @@ SELECT table_schema AS "Shards' Schema" ALTER TABLE old_schema.table_set_schema SET SCHEMA new_schema; SELECT objid::oid::regnamespace as "Distributed Schemas" FROM pg_catalog.pg_dist_object - WHERE objid::oid::regnamespace IN ('old_schema', 'new_schema'); + WHERE objid::oid::regnamespace IN ('old_schema', 'new_schema') ORDER BY objid::oid::regnamespace; Distributed Schemas --------------------------------------------------------------------- old_schema @@ -994,7 +994,7 @@ SELECT create_distributed_table('table_set_schema', 'id'); CREATE SCHEMA new_schema; SELECT objid::oid::regnamespace as "Distributed Schemas" FROM pg_catalog.pg_dist_object - WHERE objid::oid::regnamespace IN ('old_schema', 'new_schema'); + WHERE objid::oid::regnamespace IN ('old_schema', 'new_schema') ORDER BY objid::oid::regnamespace; Distributed Schemas --------------------------------------------------------------------- old_schema @@ -1018,7 +1018,7 @@ SET search_path TO old_schema; ALTER TABLE table_set_schema SET SCHEMA new_schema; SELECT objid::oid::regnamespace as "Distributed Schemas" FROM pg_catalog.pg_dist_object - WHERE objid::oid::regnamespace IN ('old_schema', 'new_schema'); + WHERE objid::oid::regnamespace IN ('old_schema', 'new_schema') ORDER BY objid::oid::regnamespace; Distributed Schemas --------------------------------------------------------------------- old_schema @@ -1160,7 +1160,6 @@ NOTICE: schema "schema_with_user" does not exist, skipping CONTEXT: SQL statement "SELECT citus_drop_all_shards(v_obj.objid, v_obj.schema_name, v_obj.object_name, drop_shards_metadata_only := false)" PL/pgSQL function citus_drop_trigger() line XX at PERFORM DROP USER "test-user"; -DROP FUNCTION run_command_on_coordinator_and_workers(p_sql text); -- test run_command_on_* UDFs with schema CREATE SCHEMA run_test_schema; CREATE TABLE run_test_schema.test_table(id int); @@ -1399,3 +1398,4 @@ SELECT pg_identify_object_as_address(classid, objid, objsubid) FROM pg_catalog.p --------------------------------------------------------------------- (0 rows) +DROP TABLE public.nation_local CASCADE; diff --git a/src/test/regress/sql/multi_schema_support.sql b/src/test/regress/sql/multi_schema_support.sql index 2d3b780fd..1116cabac 100644 --- a/src/test/regress/sql/multi_schema_support.sql +++ b/src/test/regress/sql/multi_schema_support.sql @@ -671,7 +671,7 @@ CREATE SCHEMA new_schema; SELECT objid::oid::regnamespace as "Distributed Schemas" FROM pg_catalog.pg_dist_object - WHERE objid::oid::regnamespace IN ('old_schema', 'new_schema'); + WHERE objid::oid::regnamespace IN ('old_schema', 'new_schema') ORDER BY objid::oid::regnamespace; \c - - - :worker_1_port SELECT table_schema AS "Shards' Schema" FROM information_schema.tables @@ -684,7 +684,7 @@ ALTER TABLE old_schema.table_set_schema SET SCHEMA new_schema; SELECT objid::oid::regnamespace as "Distributed Schemas" FROM pg_catalog.pg_dist_object - WHERE objid::oid::regnamespace IN ('old_schema', 'new_schema'); + WHERE objid::oid::regnamespace IN ('old_schema', 'new_schema') ORDER BY objid::oid::regnamespace; \c - - - :worker_1_port SELECT table_schema AS "Shards' Schema" FROM information_schema.tables @@ -742,7 +742,7 @@ CREATE SCHEMA new_schema; SELECT objid::oid::regnamespace as "Distributed Schemas" FROM pg_catalog.pg_dist_object - WHERE objid::oid::regnamespace IN ('old_schema', 'new_schema'); + WHERE objid::oid::regnamespace IN ('old_schema', 'new_schema') ORDER BY objid::oid::regnamespace; \c - - - :worker_1_port SELECT table_schema AS "Shards' Schema", COUNT(*) AS "Counts" FROM information_schema.tables @@ -756,7 +756,7 @@ ALTER TABLE table_set_schema SET SCHEMA new_schema; SELECT objid::oid::regnamespace as "Distributed Schemas" FROM pg_catalog.pg_dist_object - WHERE objid::oid::regnamespace IN ('old_schema', 'new_schema'); + WHERE objid::oid::regnamespace IN ('old_schema', 'new_schema') ORDER BY objid::oid::regnamespace; \c - - - :worker_1_port SELECT table_schema AS "Shards' Schema", COUNT(*) AS "Counts" FROM information_schema.tables @@ -844,8 +844,6 @@ SET citus.next_shard_id TO 1197000; DROP OWNED BY "test-user" CASCADE; DROP USER "test-user"; -DROP FUNCTION run_command_on_coordinator_and_workers(p_sql text); - -- test run_command_on_* UDFs with schema CREATE SCHEMA run_test_schema; CREATE TABLE run_test_schema.test_table(id int); @@ -991,3 +989,4 @@ DROP SCHEMA run_test_schema, test_schema_support_join_1, test_schema_support_joi -- verify that the dropped schema is removed from worker's pg_dist_object SELECT pg_identify_object_as_address(classid, objid, objsubid) FROM pg_catalog.pg_dist_object WHERE classid=2615 and objid IN (select oid from pg_namespace where nspname='run_test_schema'); +DROP TABLE public.nation_local CASCADE; From 2c509b712a577a64291ede8f6af229fc2223d550 Mon Sep 17 00:00:00 2001 From: Gokhan Gulbiz Date: Sun, 11 Jun 2023 12:17:31 +0300 Subject: [PATCH 085/118] Tenant monitoring performance improvements (#6868) - [x] Use spinlock instead of lwlock per tenant [b437aa9](https://github.com/citusdata/citus/pull/6868/commits/b437aa9e52ea312df58d3bb5c0d324afb29c2a71) - [x] Use hashtable to store tenant stats [ccd464b](https://github.com/citusdata/citus/pull/6868/commits/ccd464ba0483de4a2af162c22609125c913ac93b) - [x] Introduce a new GUC for specifying the sampling rate of new tenant entries in the tenant monitor. [a8d3805](https://github.com/citusdata/citus/pull/6868/commits/a8d3805bd6b9ee9fc27906d8bed7287a00b45565) Below are the pgbench metrics with select-only workloads from my local machine. Here is the [script](https://gist.github.com/gokhangulbiz/7a2308470597dc06734ff7c08f87c656) I used for benchmarking. | | Connection Count | Initial Implementation (TPS) | On/Off Diff | Final Implementation -Run#1 (TPS) | On/Off Diff | Final Implementation -Run#2 (TPS) | On/Off Diff | Final Implementation -Run#3 (TPS) | On/Off Diff | Avg On/Off Diff | | --- | ---------------- | ---------------------------- | ----------- | ---------------------------------- | ----------- | ---------------------------------- | ----------- | ---------------------------------- | ----------- | --------------- | | On | 32 | 37488.69839 | \-17% | 42859.94402 | \-5% | 43379.63121 | \-2% | 42636.2264 | \-7% | \-5% | | Off | 32 | 43909.83121 | | 45139.63151 | | 44188.77425 | | 45451.9548 | | | | On | 300 | 30463.03538 | \-15% | 33265.19957 | \-7% | 34685.87233 | \-2% | 34682.5214 | \-1% | \-3% | | Off | 300 | 35105.73594 | | 35637.45423 | | 35331.33447 | | 35113.3214 | | | --- .../distributed/operations/shard_rebalancer.c | 2 +- src/backend/distributed/shared_library_init.c | 12 +- .../distributed/utils/citus_stat_tenants.c | 228 ++++++++++++------ .../distributed/utils/citus_stat_tenants.h | 32 ++- .../regress/expected/citus_stat_tenants.out | 30 ++- src/test/regress/sql/citus_stat_tenants.sql | 8 +- 6 files changed, 207 insertions(+), 105 deletions(-) diff --git a/src/backend/distributed/operations/shard_rebalancer.c b/src/backend/distributed/operations/shard_rebalancer.c index bc5b0af74..5750b9e8d 100644 --- a/src/backend/distributed/operations/shard_rebalancer.c +++ b/src/backend/distributed/operations/shard_rebalancer.c @@ -2017,7 +2017,7 @@ GenerateTaskMoveDependencyList(PlacementUpdateEvent *move, int64 colocationId, * overlaps with the current move's target node. * The earlier/first move might make space for the later/second move. * So we could run out of disk space (or at least overload the node) - * if we move the second shard to it before the first one is moved away.  + * if we move the second shard to it before the first one is moved away. */ ShardMoveSourceNodeHashEntry *shardMoveSourceNodeHashEntry = hash_search( shardMoveDependencies.nodeDependencies, &move->targetNode->nodeId, HASH_FIND, diff --git a/src/backend/distributed/shared_library_init.c b/src/backend/distributed/shared_library_init.c index 8f6485c25..0ed4c17c6 100644 --- a/src/backend/distributed/shared_library_init.c +++ b/src/backend/distributed/shared_library_init.c @@ -2416,7 +2416,6 @@ RegisterCitusConfigVariables(void) PGC_POSTMASTER, GUC_STANDARD, NULL, NULL, NULL); - DefineCustomEnumVariable( "citus.stat_tenants_log_level", gettext_noop("Sets the level of citus_stat_tenants log messages"), @@ -2438,6 +2437,17 @@ RegisterCitusConfigVariables(void) GUC_STANDARD, NULL, NULL, NULL); + + DefineCustomIntVariable( + "citus.stat_tenants_sample_rate_for_new_tenants", + gettext_noop("Sampling rate for new tenants in citus_stat_tenants."), + NULL, + &StatTenantsSampleRateForNewTenants, + 100, 1, 100, + PGC_USERSET, + GUC_STANDARD, + NULL, NULL, NULL); + DefineCustomEnumVariable( "citus.stat_tenants_track", gettext_noop("Enables/Disables the stats collection for citus_stat_tenants."), diff --git a/src/backend/distributed/utils/citus_stat_tenants.c b/src/backend/distributed/utils/citus_stat_tenants.c index 0d2d0754d..68ec3dfa4 100644 --- a/src/backend/distributed/utils/citus_stat_tenants.c +++ b/src/backend/distributed/utils/citus_stat_tenants.c @@ -11,6 +11,7 @@ #include "postgres.h" #include "unistd.h" +#include "access/hash.h" #include "distributed/citus_safe_lib.h" #include "distributed/colocation_utils.h" #include "distributed/distributed_planner.h" @@ -50,7 +51,6 @@ static clock_t QueryEndClock = { 0 }; static const char *SharedMemoryNameForMultiTenantMonitor = "Shared memory for multi tenant monitor"; -static char *TenantTrancheName = "Tenant Tranche"; static char *MonitorTrancheName = "Multi Tenant Monitor Tranche"; static shmem_startup_hook_type prev_shmem_startup_hook = NULL; @@ -60,12 +60,14 @@ static void UpdatePeriodsIfNecessary(TenantStats *tenantStats, TimestampTz query static void ReduceScoreIfNecessary(TenantStats *tenantStats, TimestampTz queryTime); static void EvictTenantsIfNecessary(TimestampTz queryTime); static void RecordTenantStats(TenantStats *tenantStats, TimestampTz queryTime); -static void CreateMultiTenantMonitor(void); static MultiTenantMonitor * CreateSharedMemoryForMultiTenantMonitor(void); static MultiTenantMonitor * GetMultiTenantMonitor(void); static void MultiTenantMonitorSMInit(void); -static int CreateTenantStats(MultiTenantMonitor *monitor, TimestampTz queryTime); -static int FindTenantStats(MultiTenantMonitor *monitor); +static TenantStats * CreateTenantStats(MultiTenantMonitor *monitor, TimestampTz + queryTime); +static void FillTenantStatsHashKey(TenantStatsHashKey *key, char *tenantAttribute, uint32 + colocationGroupId); +static TenantStats * FindTenantStats(MultiTenantMonitor *monitor); static size_t MultiTenantMonitorshmemSize(void); static char * ExtractTopComment(const char *inputString); static char * EscapeCommentChars(const char *str); @@ -75,7 +77,7 @@ int StatTenantsLogLevel = CITUS_LOG_LEVEL_OFF; int StatTenantsPeriod = (time_t) 60; int StatTenantsLimit = 100; int StatTenantsTrack = STAT_TENANTS_TRACK_NONE; - +int StatTenantsSampleRateForNewTenants = 100; PG_FUNCTION_INFO_V1(citus_stat_tenants_local); PG_FUNCTION_INFO_V1(citus_stat_tenants_local_reset); @@ -113,21 +115,36 @@ citus_stat_tenants_local(PG_FUNCTION_ARGS) LWLockAcquire(&monitor->lock, LW_EXCLUSIVE); int numberOfRowsToReturn = 0; + int tenantStatsCount = hash_get_num_entries(monitor->tenants); if (returnAllTenants) { - numberOfRowsToReturn = monitor->tenantCount; + numberOfRowsToReturn = tenantStatsCount; } else { - numberOfRowsToReturn = Min(monitor->tenantCount, StatTenantsLimit); + numberOfRowsToReturn = Min(tenantStatsCount, + StatTenantsLimit); } - for (int tenantIndex = 0; tenantIndex < monitor->tenantCount; tenantIndex++) + /* Allocate an array to hold the tenants. */ + TenantStats **stats = palloc(tenantStatsCount * + sizeof(TenantStats *)); + + HASH_SEQ_STATUS hash_seq; + TenantStats *stat; + + /* Get all the tenants from the hash table. */ + int j = 0; + hash_seq_init(&hash_seq, monitor->tenants); + while ((stat = hash_seq_search(&hash_seq)) != NULL) { - UpdatePeriodsIfNecessary(&monitor->tenants[tenantIndex], monitoringTime); - ReduceScoreIfNecessary(&monitor->tenants[tenantIndex], monitoringTime); + stats[j++] = stat; + UpdatePeriodsIfNecessary(stat, monitoringTime); + ReduceScoreIfNecessary(stat, monitoringTime); } - SafeQsort(monitor->tenants, monitor->tenantCount, sizeof(TenantStats), + + /* Sort the tenants by their score. */ + SafeQsort(stats, j, sizeof(TenantStats *), CompareTenantScore); for (int i = 0; i < numberOfRowsToReturn; i++) @@ -135,10 +152,10 @@ citus_stat_tenants_local(PG_FUNCTION_ARGS) memset(values, 0, sizeof(values)); memset(isNulls, false, sizeof(isNulls)); - TenantStats *tenantStats = &monitor->tenants[i]; + TenantStats *tenantStats = stats[i]; - values[0] = Int32GetDatum(tenantStats->colocationGroupId); - values[1] = PointerGetDatum(cstring_to_text(tenantStats->tenantAttribute)); + values[0] = Int32GetDatum(tenantStats->key.colocationGroupId); + values[1] = PointerGetDatum(cstring_to_text(tenantStats->key.tenantAttribute)); values[2] = Int32GetDatum(tenantStats->readsInThisPeriod); values[3] = Int32GetDatum(tenantStats->readsInLastPeriod); values[4] = Int32GetDatum(tenantStats->readsInThisPeriod + @@ -152,6 +169,8 @@ citus_stat_tenants_local(PG_FUNCTION_ARGS) tuplestore_putvalues(tupleStore, tupleDescriptor, values, isNulls); } + pfree(stats); + LWLockRelease(&monitor->lock); PG_RETURN_VOID(); @@ -166,7 +185,25 @@ Datum citus_stat_tenants_local_reset(PG_FUNCTION_ARGS) { MultiTenantMonitor *monitor = GetMultiTenantMonitor(); - monitor->tenantCount = 0; + + /* if monitor is not created yet, there is nothing to reset */ + if (monitor == NULL) + { + PG_RETURN_VOID(); + } + + HASH_SEQ_STATUS hash_seq; + TenantStats *stats; + + LWLockAcquire(&monitor->lock, LW_EXCLUSIVE); + + hash_seq_init(&hash_seq, monitor->tenants); + while ((stats = hash_seq_search(&hash_seq)) != NULL) + { + hash_search(monitor->tenants, &stats->key, HASH_REMOVE, NULL); + } + + LWLockRelease(&monitor->lock); PG_RETURN_VOID(); } @@ -226,6 +263,24 @@ AttributeTask(char *tenantId, int colocationId, CmdType commandType) return; } + TenantStatsHashKey key = { 0 }; + FillTenantStatsHashKey(&key, tenantId, colocationId); + + MultiTenantMonitor *monitor = GetMultiTenantMonitor(); + bool found = false; + hash_search(monitor->tenants, &key, HASH_FIND, &found); + + /* If the tenant is not found in the hash table, we will track the query with a probability of StatTenantsSampleRateForNewTenants. */ + if (!found) + { + int randomValue = rand() % 100; + bool shouldTrackQuery = randomValue < StatTenantsSampleRateForNewTenants; + if (!shouldTrackQuery) + { + return; + } + } + AttributeToColocationGroupId = colocationId; strncpy_s(AttributeToTenant, MAX_TENANT_ATTRIBUTE_LENGTH, tenantId, MAX_TENANT_ATTRIBUTE_LENGTH - 1); @@ -295,14 +350,14 @@ CitusAttributeToEnd(QueryDesc *queryDesc) static int CompareTenantScore(const void *leftElement, const void *rightElement) { - const TenantStats *leftTenant = (const TenantStats *) leftElement; - const TenantStats *rightTenant = (const TenantStats *) rightElement; + double l_usage = (*(TenantStats *const *) leftElement)->score; + double r_usage = (*(TenantStats *const *) rightElement)->score; - if (leftTenant->score > rightTenant->score) + if (l_usage > r_usage) { return -1; } - else if (leftTenant->score < rightTenant->score) + else if (l_usage < r_usage) { return 1; } @@ -353,45 +408,43 @@ AttributeMetricsIfApplicable() */ LWLockAcquire(&monitor->lock, LW_SHARED); - int currentTenantIndex = FindTenantStats(monitor); + TenantStats *tenantStats = FindTenantStats(monitor); - if (currentTenantIndex != -1) + if (tenantStats != NULL) { - TenantStats *tenantStats = &monitor->tenants[currentTenantIndex]; - LWLockAcquire(&tenantStats->lock, LW_EXCLUSIVE); + SpinLockAcquire(&tenantStats->lock); UpdatePeriodsIfNecessary(tenantStats, queryTime); ReduceScoreIfNecessary(tenantStats, queryTime); RecordTenantStats(tenantStats, queryTime); - LWLockRelease(&tenantStats->lock); + SpinLockRelease(&tenantStats->lock); } else { LWLockRelease(&monitor->lock); LWLockAcquire(&monitor->lock, LW_EXCLUSIVE); - currentTenantIndex = FindTenantStats(monitor); + tenantStats = FindTenantStats(monitor); - if (currentTenantIndex == -1) + if (tenantStats == NULL) { - currentTenantIndex = CreateTenantStats(monitor, queryTime); + tenantStats = CreateTenantStats(monitor, queryTime); } LWLockRelease(&monitor->lock); LWLockAcquire(&monitor->lock, LW_SHARED); - currentTenantIndex = FindTenantStats(monitor); - if (currentTenantIndex != -1) + tenantStats = FindTenantStats(monitor); + if (tenantStats != NULL) { - TenantStats *tenantStats = &monitor->tenants[currentTenantIndex]; - LWLockAcquire(&tenantStats->lock, LW_EXCLUSIVE); + SpinLockAcquire(&tenantStats->lock); UpdatePeriodsIfNecessary(tenantStats, queryTime); ReduceScoreIfNecessary(tenantStats, queryTime); RecordTenantStats(tenantStats, queryTime); - LWLockRelease(&tenantStats->lock); + SpinLockRelease(&tenantStats->lock); } } LWLockRelease(&monitor->lock); @@ -507,15 +560,29 @@ EvictTenantsIfNecessary(TimestampTz queryTime) * * Every time tenant count hits StatTenantsLimit * 3, we reduce it back to StatTenantsLimit * 2. */ - if (monitor->tenantCount >= StatTenantsLimit * 3) + long tenantStatsCount = hash_get_num_entries(monitor->tenants); + if (tenantStatsCount >= StatTenantsLimit * 3) { - for (int tenantIndex = 0; tenantIndex < monitor->tenantCount; tenantIndex++) + HASH_SEQ_STATUS hash_seq; + TenantStats *stat; + TenantStats **stats = palloc(tenantStatsCount * + sizeof(TenantStats *)); + + int i = 0; + hash_seq_init(&hash_seq, monitor->tenants); + while ((stat = hash_seq_search(&hash_seq)) != NULL) { - ReduceScoreIfNecessary(&monitor->tenants[tenantIndex], queryTime); + stats[i++] = stat; } - SafeQsort(monitor->tenants, monitor->tenantCount, sizeof(TenantStats), - CompareTenantScore); - monitor->tenantCount = StatTenantsLimit * 2; + + SafeQsort(stats, i, sizeof(TenantStats *), CompareTenantScore); + + for (i = StatTenantsLimit * 2; i < tenantStatsCount; i++) + { + hash_search(monitor->tenants, &stats[i]->key, HASH_REMOVE, NULL); + } + + pfree(stats); } } @@ -553,17 +620,6 @@ RecordTenantStats(TenantStats *tenantStats, TimestampTz queryTime) } -/* - * CreateMultiTenantMonitor creates the data structure for multi tenant monitor. - */ -static void -CreateMultiTenantMonitor() -{ - MultiTenantMonitor *monitor = CreateSharedMemoryForMultiTenantMonitor(); - monitor->tenantCount = 0; -} - - /* * CreateSharedMemoryForMultiTenantMonitor creates a dynamic shared memory segment for multi tenant monitor. */ @@ -586,6 +642,17 @@ CreateSharedMemoryForMultiTenantMonitor() monitor->namedLockTranche.trancheName); LWLockInitialize(&monitor->lock, monitor->namedLockTranche.trancheId); + HASHCTL info; + + memset(&info, 0, sizeof(info)); + info.keysize = sizeof(TenantStatsHashKey); + info.entrysize = sizeof(TenantStats); + + monitor->tenants = ShmemInitHash("citus_stats_tenants hash", + StatTenantsLimit * 3, StatTenantsLimit * 3, + &info, HASH_ELEM | + HASH_SHARED_MEM | HASH_BLOBS); + return monitor; } @@ -629,7 +696,7 @@ InitializeMultiTenantMonitorSMHandleManagement() static void MultiTenantMonitorSMInit() { - CreateMultiTenantMonitor(); + CreateSharedMemoryForMultiTenantMonitor(); if (prev_shmem_startup_hook != NULL) { @@ -643,7 +710,7 @@ MultiTenantMonitorSMInit() * * Calling this function should be protected by the monitor->lock in LW_EXCLUSIVE mode. */ -static int +static TenantStats * CreateTenantStats(MultiTenantMonitor *monitor, TimestampTz queryTime) { /* @@ -652,45 +719,50 @@ CreateTenantStats(MultiTenantMonitor *monitor, TimestampTz queryTime) */ EvictTenantsIfNecessary(queryTime); - int tenantIndex = monitor->tenantCount; + TenantStatsHashKey key = { 0 }; + FillTenantStatsHashKey(&key, AttributeToTenant, AttributeToColocationGroupId); - memset(&monitor->tenants[tenantIndex], 0, sizeof(monitor->tenants[tenantIndex])); + TenantStats *stats = (TenantStats *) hash_search(monitor->tenants, &key, + HASH_ENTER, NULL); - strcpy_s(monitor->tenants[tenantIndex].tenantAttribute, - sizeof(monitor->tenants[tenantIndex].tenantAttribute), AttributeToTenant); - monitor->tenants[tenantIndex].colocationGroupId = AttributeToColocationGroupId; + stats->writesInLastPeriod = 0; + stats->writesInThisPeriod = 0; + stats->readsInLastPeriod = 0; + stats->readsInThisPeriod = 0; + stats->cpuUsageInLastPeriod = 0; + stats->cpuUsageInThisPeriod = 0; + stats->score = 0; + stats->lastScoreReduction = 0; - monitor->tenants[tenantIndex].namedLockTranche.trancheId = LWLockNewTrancheId(); - monitor->tenants[tenantIndex].namedLockTranche.trancheName = TenantTrancheName; + SpinLockInit(&stats->lock); - LWLockRegisterTranche(monitor->tenants[tenantIndex].namedLockTranche.trancheId, - monitor->tenants[tenantIndex].namedLockTranche.trancheName); - LWLockInitialize(&monitor->tenants[tenantIndex].lock, - monitor->tenants[tenantIndex].namedLockTranche.trancheId); - - monitor->tenantCount++; - - return tenantIndex; + return stats; } /* - * FindTenantStats finds the index for the current tenant's statistics. + * FindTenantStats finds the current tenant's statistics. */ -static int +static TenantStats * FindTenantStats(MultiTenantMonitor *monitor) { - for (int i = 0; i < monitor->tenantCount; i++) - { - TenantStats *tenantStats = &monitor->tenants[i]; - if (strcmp(tenantStats->tenantAttribute, AttributeToTenant) == 0 && - tenantStats->colocationGroupId == AttributeToColocationGroupId) - { - return i; - } - } + TenantStatsHashKey key = { 0 }; + FillTenantStatsHashKey(&key, AttributeToTenant, AttributeToColocationGroupId); - return -1; + TenantStats *stats = (TenantStats *) hash_search(monitor->tenants, &key, + HASH_FIND, NULL); + + return stats; +} + + +static void +FillTenantStatsHashKey(TenantStatsHashKey *key, char *tenantAttribute, uint32 + colocationGroupId) +{ + memset(key->tenantAttribute, 0, MAX_TENANT_ATTRIBUTE_LENGTH); + strlcpy(key->tenantAttribute, tenantAttribute, MAX_TENANT_ATTRIBUTE_LENGTH); + key->colocationGroupId = colocationGroupId; } diff --git a/src/include/distributed/utils/citus_stat_tenants.h b/src/include/distributed/utils/citus_stat_tenants.h index aa413c29d..8dac393a8 100644 --- a/src/include/distributed/utils/citus_stat_tenants.h +++ b/src/include/distributed/utils/citus_stat_tenants.h @@ -11,24 +11,33 @@ #ifndef CITUS_ATTRIBUTE_H #define CITUS_ATTRIBUTE_H +#include "distributed/hash_helpers.h" #include "executor/execdesc.h" #include "executor/executor.h" #include "storage/lwlock.h" #include "utils/datetime.h" +#include "utils/hsearch.h" #define MAX_TENANT_ATTRIBUTE_LENGTH 100 +/* + * Hashtable key that defines the identity of a hashtable entry. + * The key is the attribute value, e.g distribution column and the colocation group id of the tenant. + */ +typedef struct TenantStatsHashKey +{ + char tenantAttribute[MAX_TENANT_ATTRIBUTE_LENGTH]; + int colocationGroupId; +} TenantStatsHashKey; +assert_valid_hash_key2(TenantStatsHashKey, tenantAttribute, colocationGroupId); + /* * TenantStats is the struct that keeps statistics about one tenant. */ typedef struct TenantStats { - /* - * The attribute value, e.g distribution column, and colocation group id - * of the tenant. - */ - char tenantAttribute[MAX_TENANT_ATTRIBUTE_LENGTH]; - int colocationGroupId; + TenantStatsHashKey key; /* hash key of entry - MUST BE FIRST */ + /* * Number of SELECT queries this tenant ran in this and last periods. @@ -70,8 +79,7 @@ typedef struct TenantStats /* * Locks needed to update this tenant's statistics. */ - NamedLWLockTranche namedLockTranche; - LWLock lock; + slock_t lock; } TenantStats; /* @@ -89,12 +97,9 @@ typedef struct MultiTenantMonitor LWLock lock; /* - * tenantCount is the number of items in the tenants array. - * The total length of tenants array is set up at CreateSharedMemoryForMultiTenantMonitor - * and is 3 * citus.stat_tenants_limit + * The max length of tenants hashtable is 3 * citus.stat_tenants_limit */ - int tenantCount; - TenantStats tenants[FLEXIBLE_ARRAY_MEMBER]; + HTAB *tenants; } MultiTenantMonitor; typedef enum @@ -116,5 +121,6 @@ extern int StatTenantsLogLevel; extern int StatTenantsPeriod; extern int StatTenantsLimit; extern int StatTenantsTrack; +extern int StatTenantsSampleRateForNewTenants; #endif /*CITUS_ATTRIBUTE_H */ diff --git a/src/test/regress/expected/citus_stat_tenants.out b/src/test/regress/expected/citus_stat_tenants.out index 102f5e9a3..3b3c728f8 100644 --- a/src/test/regress/expected/citus_stat_tenants.out +++ b/src/test/regress/expected/citus_stat_tenants.out @@ -137,6 +137,18 @@ SELECT count(*)>=0 FROM dist_tbl WHERE a = 2; t (1 row) +SELECT count(*)>=0 FROM dist_tbl WHERE a = 2; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SELECT count(*)>=0 FROM dist_tbl WHERE a = 3; + ?column? +--------------------------------------------------------------------- + t +(1 row) + SELECT count(*)>=0 FROM dist_tbl WHERE a = 3; ?column? --------------------------------------------------------------------- @@ -158,8 +170,8 @@ SELECT count(*)>=0 FROM dist_tbl_text WHERE a = 'abcd'; SELECT tenant_attribute, query_count_in_this_period, score FROM citus_stat_tenants(true) WHERE nodeid = :worker_2_nodeid ORDER BY score DESC, tenant_attribute; tenant_attribute | query_count_in_this_period | score --------------------------------------------------------------------- - 2 | 1 | 1000000000 - 3 | 1 | 1000000000 + 2 | 2 | 2000000000 + 3 | 2 | 2000000000 4 | 1 | 1000000000 abcd | 1 | 1000000000 (4 rows) @@ -192,8 +204,8 @@ SELECT tenant_attribute, query_count_in_this_period, score FROM citus_stat_tenan tenant_attribute | query_count_in_this_period | score --------------------------------------------------------------------- abcd | 3 | 3000000000 - 2 | 1 | 1000000000 - 3 | 1 | 1000000000 + 2 | 2 | 2000000000 + 3 | 2 | 2000000000 4 | 1 | 1000000000 bcde | 1 | 1000000000 cdef | 1 | 1000000000 @@ -222,8 +234,8 @@ SELECT tenant_attribute, query_count_in_this_period, score FROM citus_stat_tenan --------------------------------------------------------------------- abcd | 3 | 3000000000 bcde | 3 | 3000000000 - 2 | 1 | 1000000000 - 3 | 1 | 1000000000 + 2 | 2 | 2000000000 + 3 | 2 | 2000000000 defg | 1 | 1000000000 (5 rows) @@ -789,7 +801,7 @@ SELECT select_from_dist_tbl_text(U&'\0061\0308bc'); t (1 row) -SELECT tenant_attribute, query_count_in_this_period FROM citus_stat_tenants; +SELECT tenant_attribute, query_count_in_this_period FROM citus_stat_tenants ORDER BY tenant_attribute; tenant_attribute | query_count_in_this_period --------------------------------------------------------------------- /b*c/de | 2 @@ -815,7 +827,7 @@ CALL citus_stat_tenants.select_from_dist_tbl_text_proc(U&'\0061\0308bc'); CALL citus_stat_tenants.select_from_dist_tbl_text_proc(U&'\0061\0308bc'); CALL citus_stat_tenants.select_from_dist_tbl_text_proc(U&'\0061\0308bc'); CALL citus_stat_tenants.select_from_dist_tbl_text_proc(NULL); -SELECT tenant_attribute, query_count_in_this_period FROM citus_stat_tenants; +SELECT tenant_attribute, query_count_in_this_period FROM citus_stat_tenants ORDER BY tenant_attribute; tenant_attribute | query_count_in_this_period --------------------------------------------------------------------- /b*c/de | 8 @@ -862,7 +874,7 @@ SELECT count(*)>=0 FROM select_from_dist_tbl_text_view WHERE a = U&'\0061\0308bc t (1 row) -SELECT tenant_attribute, query_count_in_this_period FROM citus_stat_tenants; +SELECT tenant_attribute, query_count_in_this_period FROM citus_stat_tenants ORDER BY tenant_attribute; tenant_attribute | query_count_in_this_period --------------------------------------------------------------------- /b*c/de | 11 diff --git a/src/test/regress/sql/citus_stat_tenants.sql b/src/test/regress/sql/citus_stat_tenants.sql index 61d9f19b3..efe77ec84 100644 --- a/src/test/regress/sql/citus_stat_tenants.sql +++ b/src/test/regress/sql/citus_stat_tenants.sql @@ -61,6 +61,8 @@ SELECT tenant_attribute, query_count_in_this_period FROM citus_stat_tenants(true SELECT nodeid AS worker_2_nodeid FROM pg_dist_node WHERE nodeport = :worker_2_port \gset SELECT count(*)>=0 FROM dist_tbl WHERE a = 2; +SELECT count(*)>=0 FROM dist_tbl WHERE a = 2; +SELECT count(*)>=0 FROM dist_tbl WHERE a = 3; SELECT count(*)>=0 FROM dist_tbl WHERE a = 3; SELECT count(*)>=0 FROM dist_tbl WHERE a = 4; SELECT count(*)>=0 FROM dist_tbl_text WHERE a = 'abcd'; @@ -272,7 +274,7 @@ SELECT select_from_dist_tbl_text('/b*c/de'); SELECT select_from_dist_tbl_text(U&'\0061\0308bc'); SELECT select_from_dist_tbl_text(U&'\0061\0308bc'); -SELECT tenant_attribute, query_count_in_this_period FROM citus_stat_tenants; +SELECT tenant_attribute, query_count_in_this_period FROM citus_stat_tenants ORDER BY tenant_attribute; CREATE OR REPLACE PROCEDURE select_from_dist_tbl_text_proc( p_keyword text @@ -295,7 +297,7 @@ CALL citus_stat_tenants.select_from_dist_tbl_text_proc(U&'\0061\0308bc'); CALL citus_stat_tenants.select_from_dist_tbl_text_proc(U&'\0061\0308bc'); CALL citus_stat_tenants.select_from_dist_tbl_text_proc(NULL); -SELECT tenant_attribute, query_count_in_this_period FROM citus_stat_tenants; +SELECT tenant_attribute, query_count_in_this_period FROM citus_stat_tenants ORDER BY tenant_attribute; CREATE OR REPLACE VIEW select_from_dist_tbl_text_view @@ -309,7 +311,7 @@ SELECT count(*)>=0 FROM select_from_dist_tbl_text_view WHERE a = U&'\0061\0308bc SELECT count(*)>=0 FROM select_from_dist_tbl_text_view WHERE a = U&'\0061\0308bc'; SELECT count(*)>=0 FROM select_from_dist_tbl_text_view WHERE a = U&'\0061\0308bc'; -SELECT tenant_attribute, query_count_in_this_period FROM citus_stat_tenants; +SELECT tenant_attribute, query_count_in_this_period FROM citus_stat_tenants ORDER BY tenant_attribute; SET client_min_messages TO ERROR; DROP SCHEMA citus_stat_tenants CASCADE; From e37ee16d594fba390b74301dd1bc811212c5d6b2 Mon Sep 17 00:00:00 2001 From: Jelte Fennema Date: Mon, 12 Jun 2023 10:42:26 +0200 Subject: [PATCH 086/118] Add a section on backporting to CONTRIBUTING.md (#6995) Backporting changes is pretty easy, but the steps are not obvious if you're new to the project. --- CONTRIBUTING.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cff39bf51..010842a5f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -219,6 +219,18 @@ style `#include` statements like this: Any other SQL you can put directly in the main sql file, e.g. `src/backend/distributed/sql/citus--8.3-1--9.0-1.sql`. +### Backporting a commit to a release branch + +1. Check out the release branch that you want to backport to `git checkout release-11.3` +2. Make sure you have the latest changes `git pull` +3. Create a new release branch with a unique name `git checkout release-11.3-` +4. Cherry-pick the commit that you want to backport `git cherry-pick -x ` (the `-x` is important) +5. Push the branch `git push` +6. Wait for tests to pass +7. If the cherry-pick required non-trivial merge conflicts, create a PR and ask + for a review. +8. After the tests pass on CI, fast-forward the release branch `git push origin release-11.3-:release-11.3` + ### Running tests See [`src/test/regress/README.md`](https://github.com/citusdata/citus/blob/master/src/test/regress/README.md) From 213d363bc3c3a3544aea366c385c1ef4c49140dd Mon Sep 17 00:00:00 2001 From: aykut-bozkurt <51649454+aykut-bozkurt@users.noreply.github.com> Date: Mon, 12 Jun 2023 18:41:31 +0300 Subject: [PATCH 087/118] Add citus_schema_distribute/undistribute udfs to convert a schema into a tenant schema / back to a regular schema (#6933) * Currently we do not allow any Citus tables other than Citus local tables inside a regular schema before executing `citus_schema_distribute`. * `citus_schema_undistribute` expects only single shard distributed tables inside a tenant schema. DESCRIPTION: Adds the udf `citus_schema_distribute` to convert a regular schema into a tenant schema. DESCRIPTION: Adds the udf `citus_schema_undistribute` to convert a tenant schema back to a regular schema. --------- Co-authored-by: Onur Tirtir --- .../distributed/commands/alter_table.c | 31 + .../commands/create_distributed_table.c | 10 +- .../commands/schema_based_sharding.c | 579 ++++++++- src/backend/distributed/commands/table.c | 14 +- src/backend/distributed/metadata/dependency.c | 41 + .../distributed/metadata/metadata_utility.c | 15 + .../distributed/sql/citus--11.3-1--12.0-1.sql | 4 + .../sql/downgrades/citus--12.0-1--11.3-1.sql | 3 + .../udfs/citus_schema_distribute/12.0-1.sql | 6 + .../udfs/citus_schema_distribute/latest.sql | 6 + .../udfs/citus_schema_undistribute/12.0-1.sql | 6 + .../udfs/citus_schema_undistribute/latest.sql | 6 + src/include/distributed/commands.h | 6 + src/include/distributed/metadata/distobject.h | 1 + src/include/distributed/metadata_utility.h | 2 + .../citus_schema_distribute_undistribute.out | 923 ++++++++++++++ ...n_citus_schema_distribute_undistribute.out | 1136 +++++++++++++++++ src/test/regress/expected/multi_extension.out | 4 +- .../expected/schema_based_sharding.out | 44 +- .../expected/upgrade_list_citus_objects.out | 4 +- src/test/regress/isolation_schedule | 1 + src/test/regress/multi_1_schedule | 1 + ..._citus_schema_distribute_undistribute.spec | 171 +++ .../citus_schema_distribute_undistribute.sql | 437 +++++++ 24 files changed, 3375 insertions(+), 76 deletions(-) create mode 100644 src/backend/distributed/sql/udfs/citus_schema_distribute/12.0-1.sql create mode 100644 src/backend/distributed/sql/udfs/citus_schema_distribute/latest.sql create mode 100644 src/backend/distributed/sql/udfs/citus_schema_undistribute/12.0-1.sql create mode 100644 src/backend/distributed/sql/udfs/citus_schema_undistribute/latest.sql create mode 100644 src/test/regress/expected/citus_schema_distribute_undistribute.out create mode 100644 src/test/regress/expected/isolation_citus_schema_distribute_undistribute.out create mode 100644 src/test/regress/spec/isolation_citus_schema_distribute_undistribute.spec create mode 100644 src/test/regress/sql/citus_schema_distribute_undistribute.sql diff --git a/src/backend/distributed/commands/alter_table.c b/src/backend/distributed/commands/alter_table.c index 40324cfcb..86dbe48e9 100644 --- a/src/backend/distributed/commands/alter_table.c +++ b/src/backend/distributed/commands/alter_table.c @@ -361,6 +361,37 @@ worker_change_sequence_dependency(PG_FUNCTION_ARGS) } +/* + * UndistributeTables undistributes given relations. It first collects all foreign keys + * to recreate them after the undistribution. Then, drops the foreign keys and + * undistributes the relations. Finally, it recreates foreign keys. + */ +void +UndistributeTables(List *relationIdList) +{ + /* + * Collect foreign keys for recreation and then drop fkeys and undistribute + * tables. + */ + List *originalForeignKeyRecreationCommands = NIL; + Oid relationId = InvalidOid; + foreach_oid(relationId, relationIdList) + { + List *fkeyCommandsForRelation = + GetFKeyCreationCommandsRelationInvolvedWithTableType(relationId, + INCLUDE_ALL_TABLE_TYPES); + originalForeignKeyRecreationCommands = list_concat( + originalForeignKeyRecreationCommands, fkeyCommandsForRelation); + DropFKeysAndUndistributeTable(relationId); + } + + /* We can skip foreign key validations as we are sure about them at start */ + bool skip_validation = true; + ExecuteForeignKeyCreateCommandList(originalForeignKeyRecreationCommands, + skip_validation); +} + + /* * UndistributeTable undistributes the given table. It uses ConvertTable function to * create a new local table and move everything to that table. diff --git a/src/backend/distributed/commands/create_distributed_table.c b/src/backend/distributed/commands/create_distributed_table.c index 71fe0b9e3..a1ae2d9ea 100644 --- a/src/backend/distributed/commands/create_distributed_table.c +++ b/src/backend/distributed/commands/create_distributed_table.c @@ -159,10 +159,6 @@ static void EnsureCitusTableCanBeCreated(Oid relationOid); static void PropagatePrerequisiteObjectsForDistributedTable(Oid relationId); static void EnsureDistributedSequencesHaveOneType(Oid relationId, List *seqInfoList); -static List * GetFKeyCreationCommandsRelationInvolvedWithTableType(Oid relationId, - int tableTypeFlag); -static Oid DropFKeysAndUndistributeTable(Oid relationId); -static void DropFKeysRelationInvolvedWithTableType(Oid relationId, int tableTypeFlag); static void CopyLocalDataIntoShards(Oid relationId); static List * TupleDescColumnNameList(TupleDesc tupleDescriptor); @@ -1580,7 +1576,7 @@ EnsureDistributedSequencesHaveOneType(Oid relationId, List *seqInfoList) * commands to recreate the foreign keys that relation with relationId is involved * with given table type. */ -static List * +List * GetFKeyCreationCommandsRelationInvolvedWithTableType(Oid relationId, int tableTypeFlag) { int referencingFKeysFlag = INCLUDE_REFERENCING_CONSTRAINTS | @@ -1606,7 +1602,7 @@ GetFKeyCreationCommandsRelationInvolvedWithTableType(Oid relationId, int tableTy * Also note that callers are responsible for storing & recreating foreign * keys to be dropped if needed. */ -static Oid +Oid DropFKeysAndUndistributeTable(Oid relationId) { DropFKeysRelationInvolvedWithTableType(relationId, INCLUDE_ALL_TABLE_TYPES); @@ -1639,7 +1635,7 @@ DropFKeysAndUndistributeTable(Oid relationId) * DropFKeysRelationInvolvedWithTableType drops foreign keys that relation * with relationId is involved with given table type. */ -static void +void DropFKeysRelationInvolvedWithTableType(Oid relationId, int tableTypeFlag) { int referencingFKeysFlag = INCLUDE_REFERENCING_CONSTRAINTS | diff --git a/src/backend/distributed/commands/schema_based_sharding.c b/src/backend/distributed/commands/schema_based_sharding.c index 77daaa629..91bae0943 100644 --- a/src/backend/distributed/commands/schema_based_sharding.c +++ b/src/backend/distributed/commands/schema_based_sharding.c @@ -8,28 +8,43 @@ #include "postgres.h" #include "miscadmin.h" + +#include "access/genam.h" +#include "catalog/catalog.h" #include "catalog/pg_namespace_d.h" #include "commands/extension.h" #include "distributed/argutils.h" #include "distributed/backend_data.h" #include "distributed/colocation_utils.h" #include "distributed/commands.h" +#include "distributed/listutils.h" #include "distributed/metadata_sync.h" +#include "distributed/metadata/distobject.h" #include "distributed/multi_partitioning_utils.h" #include "distributed/tenant_schema_metadata.h" -#include "distributed/metadata/distobject.h" +#include "distributed/worker_shard_visibility.h" #include "utils/builtins.h" +#include "utils/fmgroids.h" #include "utils/lsyscache.h" #include "utils/syscache.h" -PG_FUNCTION_INFO_V1(citus_internal_unregister_tenant_schema_globally); - +static void UnregisterTenantSchemaGlobally(Oid schemaId, char *schemaName); +static List * SchemaGetNonShardTableIdList(Oid schemaId); +static void EnsureSchemaCanBeDistributed(Oid schemaId, List *schemaTableIdList); +static void EnsureTenantSchemaNameAllowed(Oid schemaId); +static void EnsureTableKindSupportedForTenantSchema(Oid relationId); +static void EnsureFKeysForTenantTable(Oid relationId); +static void EnsureSchemaExist(Oid schemaId); /* controlled via citus.enable_schema_based_sharding GUC */ bool EnableSchemaBasedSharding = false; +PG_FUNCTION_INFO_V1(citus_internal_unregister_tenant_schema_globally); +PG_FUNCTION_INFO_V1(citus_schema_distribute); +PG_FUNCTION_INFO_V1(citus_schema_undistribute); + /* * ShouldUseSchemaBasedSharding returns true if schema given name should be * used as a tenant schema. @@ -108,6 +123,112 @@ ShouldCreateTenantSchemaTable(Oid relationId) } +/* + * EnsureTableKindSupportedForTenantSchema ensures that given table's kind is + * supported by a tenant schema. + */ +static void +EnsureTableKindSupportedForTenantSchema(Oid relationId) +{ + if (IsForeignTable(relationId)) + { + ereport(ERROR, (errmsg("cannot create a foreign table in a distributed " + "schema"))); + } + + if (PartitionTable(relationId)) + { + ErrorIfIllegalPartitioningInTenantSchema(PartitionParentOid(relationId), + relationId); + } + + if (PartitionedTable(relationId)) + { + List *partitionList = PartitionList(relationId); + + Oid partitionRelationId = InvalidOid; + foreach_oid(partitionRelationId, partitionList) + { + ErrorIfIllegalPartitioningInTenantSchema(relationId, partitionRelationId); + } + } + + if (IsChildTable(relationId) || IsParentTable(relationId)) + { + ereport(ERROR, (errmsg("tables in a distributed schema cannot inherit or " + "be inherited"))); + } +} + + +/* + * EnsureFKeysForTenantTable ensures that all referencing and referenced foreign + * keys are allowed for given table. + */ +static void +EnsureFKeysForTenantTable(Oid relationId) +{ + Oid tenantSchemaId = get_rel_namespace(relationId); + int fKeyReferencingFlags = INCLUDE_REFERENCING_CONSTRAINTS | INCLUDE_ALL_TABLE_TYPES; + List *referencingForeignKeys = GetForeignKeyOids(relationId, fKeyReferencingFlags); + Oid foreignKeyId = InvalidOid; + foreach_oid(foreignKeyId, referencingForeignKeys) + { + Oid referencingTableId = GetReferencingTableId(foreignKeyId); + Oid referencedTableId = GetReferencedTableId(foreignKeyId); + Oid referencedTableSchemaId = get_rel_namespace(referencedTableId); + + /* We allow foreign keys to a table in the same schema */ + if (tenantSchemaId == referencedTableSchemaId) + { + continue; + } + + /* + * Allow foreign keys to the other schema only if the referenced table is + * a reference table. + */ + if (!IsCitusTable(referencedTableId) || + !IsCitusTableType(referencedTableId, REFERENCE_TABLE)) + { + ereport(ERROR, (errmsg("foreign keys from distributed schemas can only " + "point to the same distributed schema or reference " + "tables in regular schemas"), + errdetail("\"%s\" references \"%s\" via foreign key " + "constraint \"%s\"", + generate_qualified_relation_name( + referencingTableId), + generate_qualified_relation_name(referencedTableId), + get_constraint_name(foreignKeyId)))); + } + } + + int fKeyReferencedFlags = INCLUDE_REFERENCED_CONSTRAINTS | INCLUDE_ALL_TABLE_TYPES; + List *referencedForeignKeys = GetForeignKeyOids(relationId, fKeyReferencedFlags); + foreach_oid(foreignKeyId, referencedForeignKeys) + { + Oid referencingTableId = GetReferencingTableId(foreignKeyId); + Oid referencedTableId = GetReferencedTableId(foreignKeyId); + Oid referencingTableSchemaId = get_rel_namespace(referencingTableId); + + /* We allow foreign keys from a table in the same schema */ + if (tenantSchemaId == referencingTableSchemaId) + { + continue; + } + + /* Not allow any foreign keys from the other schema */ + ereport(ERROR, (errmsg("cannot create foreign keys to tables in a distributed " + "schema from another schema"), + errdetail("\"%s\" references \"%s\" via foreign key " + "constraint \"%s\"", + generate_qualified_relation_name(referencingTableId), + generate_qualified_relation_name(referencedTableId), + get_constraint_name(foreignKeyId)))); + } +} + + /* * CreateTenantSchemaTable creates a tenant table with given relationId. * @@ -130,20 +251,12 @@ CreateTenantSchemaTable(Oid relationId) * prefer to throw an error with a more meaningful message, rather * than saying "operation is not allowed on this node". */ - ereport(ERROR, (errmsg("cannot create a tenant table from a worker node"), + ereport(ERROR, (errmsg("cannot create tables in a distributed schema from " + "a worker node"), errhint("Connect to the coordinator node and try again."))); } - if (IsForeignTable(relationId)) - { - /* throw an error that is nicer than the one CreateSingleShardTable() would throw */ - ereport(ERROR, (errmsg("cannot create a tenant table from a foreign table"))); - } - else if (PartitionTable(relationId)) - { - ErrorIfIllegalPartitioningInTenantSchema(PartitionParentOid(relationId), - relationId); - } + EnsureTableKindSupportedForTenantSchema(relationId); /* * We don't expect this to happen because ShouldCreateTenantSchemaTable() @@ -153,7 +266,7 @@ CreateTenantSchemaTable(Oid relationId) uint32 colocationId = SchemaIdGetTenantColocationId(schemaId); if (colocationId == INVALID_COLOCATION_ID) { - ereport(ERROR, (errmsg("schema \"%s\" is not a tenant schema", + ereport(ERROR, (errmsg("schema \"%s\" is not distributed", get_namespace_name(schemaId)))); } @@ -169,29 +282,16 @@ CreateTenantSchemaTable(Oid relationId) * ErrorIfIllegalPartitioningInTenantSchema throws an error if the * partitioning relationship between the parent and the child is illegal * because they are in different schemas while one of them is a tenant table. + * + * This function assumes that either the parent or the child are in a tenant + * schema. */ void ErrorIfIllegalPartitioningInTenantSchema(Oid parentRelationId, Oid partitionRelationId) { - Oid partitionSchemaId = get_rel_namespace(partitionRelationId); - Oid parentSchemaId = get_rel_namespace(parentRelationId); - - bool partitionIsTenantTable = IsTenantSchema(partitionSchemaId); - bool parentIsTenantTable = IsTenantSchema(parentSchemaId); - - bool illegalPartitioning = false; - if (partitionIsTenantTable != parentIsTenantTable) + if (get_rel_namespace(partitionRelationId) != get_rel_namespace(parentRelationId)) { - illegalPartitioning = true; - } - else if (partitionIsTenantTable && parentIsTenantTable) - { - illegalPartitioning = (parentSchemaId != partitionSchemaId); - } - - if (illegalPartitioning) - { - ereport(ERROR, (errmsg("partitioning with tenant tables is not " + ereport(ERROR, (errmsg("partitioning within a distributed schema is not " "supported when the parent and the child " "are in different schemas"))); } @@ -199,9 +299,236 @@ ErrorIfIllegalPartitioningInTenantSchema(Oid parentRelationId, Oid partitionRela /* - * citus_internal_unregister_tenant_schema_globally removes given schema from - * the tenant schema metadata table, deletes the colocation group of the schema - * and sends the command to do the same on the workers. + * CreateTenantSchemaColocationId returns new colocation id for a tenant schema. + */ +uint32 +CreateTenantSchemaColocationId(void) +{ + int shardCount = 1; + int replicationFactor = 1; + Oid distributionColumnType = InvalidOid; + Oid distributionColumnCollation = InvalidOid; + uint32 schemaColocationId = CreateColocationGroup( + shardCount, replicationFactor, distributionColumnType, + distributionColumnCollation); + return schemaColocationId; +} + + +/* + * SchemaGetNonShardTableIdList returns all nonshard relation ids + * inside given schema. + */ +static List * +SchemaGetNonShardTableIdList(Oid schemaId) +{ + List *relationIdList = NIL; + + /* scan all relations in pg_class and return all tables inside given schema */ + Relation relationRelation = relation_open(RelationRelationId, AccessShareLock); + + ScanKeyData scanKey[1] = { 0 }; + ScanKeyInit(&scanKey[0], Anum_pg_class_relnamespace, BTEqualStrategyNumber, + F_OIDEQ, ObjectIdGetDatum(schemaId)); + SysScanDesc scanDescriptor = systable_beginscan(relationRelation, ClassNameNspIndexId, + true, NULL, 1, scanKey); + + HeapTuple heapTuple = NULL; + while (HeapTupleIsValid(heapTuple = systable_getnext(scanDescriptor))) + { + Form_pg_class relationForm = (Form_pg_class) GETSTRUCT(heapTuple); + char *relationName = NameStr(relationForm->relname); + Oid relationId = get_relname_relid(relationName, schemaId); + + if (!OidIsValid(relationId)) + { + ereport(ERROR, errmsg("table %s is dropped by a concurrent operation", + relationName)); + } + + /* skip shards */ + if (RelationIsAKnownShard(relationId)) + { + continue; + } + + if (RegularTable(relationId) || PartitionTable(relationId) || + IsForeignTable(relationId)) + { + relationIdList = lappend_oid(relationIdList, relationId); + } + } + + systable_endscan(scanDescriptor); + relation_close(relationRelation, AccessShareLock); + + return relationIdList; +} + + +/* + * EnsureSchemaCanBeDistributed ensures the schema can be distributed. + * Caller should take required the lock on relations and the schema. + * + * It checks: + * - Schema name is in the allowed-list, + * - Schema does not depend on an extension (created by extension), + * - No extension depends on the schema (CREATE EXTENSION SCHEMA ), + * - Current user should be the owner of tables under the schema, + * - Table kinds are supported, + * - Referencing and referenced foreign keys for the tables under the schema are + * supported, + * - Tables under the schema are not owned by an extension, + * - Only Citus local and Postgres local tables exist under the schema. + */ +static void +EnsureSchemaCanBeDistributed(Oid schemaId, List *schemaTableIdList) +{ + /* Ensure schema name is allowed */ + EnsureTenantSchemaNameAllowed(schemaId); + + /* Any schema owned by extension is not allowed */ + char *schemaName = get_namespace_name(schemaId); + ObjectAddress *schemaAddress = palloc0(sizeof(ObjectAddress)); + ObjectAddressSet(*schemaAddress, NamespaceRelationId, schemaId); + if (IsAnyObjectAddressOwnedByExtension(list_make1(schemaAddress), NULL)) + { + ereport(ERROR, (errmsg("schema %s, which is owned by an extension, cannot " + "be distributed", schemaName))); + } + + /* Extension schemas are not allowed */ + ObjectAddress *extensionAddress = FirstExtensionWithSchema(schemaId); + if (extensionAddress) + { + char *extensionName = get_extension_name(extensionAddress->objectId); + ereport(ERROR, (errmsg("schema %s cannot be distributed since it is the schema " + "of extension %s", schemaName, extensionName))); + } + + Oid relationId = InvalidOid; + foreach_oid(relationId, schemaTableIdList) + { + /* Ensure table owner */ + EnsureTableOwner(relationId); + + /* Check relation kind */ + EnsureTableKindSupportedForTenantSchema(relationId); + + /* Check foreign keys */ + EnsureFKeysForTenantTable(relationId); + + /* Check table not owned by an extension */ + ObjectAddress *tableAddress = palloc0(sizeof(ObjectAddress)); + ObjectAddressSet(*tableAddress, RelationRelationId, relationId); + if (IsAnyObjectAddressOwnedByExtension(list_make1(tableAddress), NULL)) + { + char *tableName = get_namespace_name(schemaId); + ereport(ERROR, (errmsg("schema cannot be distributed since it has " + "table %s which is owned by an extension", + tableName))); + } + + /* Postgres local tables are allowed */ + if (!IsCitusTable(relationId)) + { + continue; + } + + /* Only Citus local tables, amongst Citus table types, are allowed */ + if (!IsCitusTableType(relationId, CITUS_LOCAL_TABLE)) + { + ereport(ERROR, (errmsg("schema already has distributed tables"), + errhint("Undistribute distributed tables under " + "the schema before distributing the schema."))); + } + } +} + + +/* + * EnsureTenantSchemaNameAllowed ensures if given schema is applicable for registering + * as a tenant schema. + */ +static void +EnsureTenantSchemaNameAllowed(Oid schemaId) +{ + char *schemaName = get_namespace_name(schemaId); + + /* public schema is not allowed */ + if (strcmp(schemaName, "public") == 0) + { + ereport(ERROR, (errmsg("public schema cannot be distributed"))); + } + + /* information_schema schema is not allowed */ + if (strcmp(schemaName, "information_schema") == 0) + { + ereport(ERROR, (errmsg("information_schema schema cannot be distributed"))); + } + + /* pg_temp_xx and pg_toast_temp_xx schemas are not allowed */ + if (isAnyTempNamespace(schemaId)) + { + ereport(ERROR, (errmsg("temporary schema cannot be distributed"))); + } + + /* pg_catalog schema is not allowed */ + if (IsCatalogNamespace(schemaId)) + { + ereport(ERROR, (errmsg("pg_catalog schema cannot be distributed"))); + } + + /* pg_toast schema is not allowed */ + if (IsToastNamespace(schemaId)) + { + ereport(ERROR, (errmsg("pg_toast schema cannot be distributed"))); + } +} + + +/* + * EnsureSchemaExist ensures that schema exists. Caller is responsible to take + * the required lock on the schema. + */ +static void +EnsureSchemaExist(Oid schemaId) +{ + if (!SearchSysCacheExists1(NAMESPACEOID, ObjectIdGetDatum(schemaId))) + { + ereport(ERROR, (errcode(ERRCODE_UNDEFINED_SCHEMA), + errmsg("schema with OID %u does not exist", schemaId))); + } +} + + +/* + * UnregisterTenantSchemaGlobally removes given schema from the tenant schema + * metadata table, deletes the colocation group of the schema and sends the + * command to do the same on the workers. + */ +static void +UnregisterTenantSchemaGlobally(Oid schemaId, char *schemaName) +{ + uint32 tenantSchemaColocationId = SchemaIdGetTenantColocationId(schemaId); + + DeleteTenantSchemaLocally(schemaId); + if (EnableMetadataSync) + { + SendCommandToWorkersWithMetadata(TenantSchemaDeleteCommand(schemaName)); + } + + DeleteColocationGroup(tenantSchemaColocationId); +} + + +/* + * citus_internal_unregister_tenant_schema_globally, called by Citus drop hook, + * unregisters the schema when a tenant schema is dropped. + * + * NOTE: We need to pass schema_name as an argument. We cannot use schema id + * to obtain schema name since the schema would have already been dropped when this + * udf is called by the drop hook. */ Datum citus_internal_unregister_tenant_schema_globally(PG_FUNCTION_ARGS) @@ -227,18 +554,185 @@ citus_internal_unregister_tenant_schema_globally(PG_FUNCTION_ARGS) if (HeapTupleIsValid(namespaceTuple)) { ReleaseSysCache(namespaceTuple); - ereport(ERROR, (errmsg("schema is expected to be already dropped " "because this function is only expected to " "be called from Citus drop hook"))); } + UnregisterTenantSchemaGlobally(schemaId, schemaNameStr); + PG_RETURN_VOID(); +} - uint32 tenantSchemaColocationId = SchemaIdGetTenantColocationId(schemaId); - DeleteTenantSchemaLocally(schemaId); - SendCommandToWorkersWithMetadata(TenantSchemaDeleteCommand(schemaNameStr)); +/* + * citus_schema_distribute gets a regular schema name, then converts it to a tenant + * schema. + */ +Datum +citus_schema_distribute(PG_FUNCTION_ARGS) +{ + CheckCitusVersion(ERROR); + EnsureCoordinator(); - DeleteColocationGroup(tenantSchemaColocationId); + Oid schemaId = PG_GETARG_OID(0); + EnsureSchemaExist(schemaId); + EnsureSchemaOwner(schemaId); + + /* Prevent concurrent table creation under the schema */ + LockDatabaseObject(NamespaceRelationId, schemaId, 0, AccessExclusiveLock); + + /* + * We should ensure the existence of the schema after taking the lock since + * the schema could have been dropped before we acquired the lock. + */ + EnsureSchemaExist(schemaId); + EnsureSchemaOwner(schemaId); + + /* Return if the schema is already a tenant schema */ + char *schemaName = get_namespace_name(schemaId); + if (IsTenantSchema(schemaId)) + { + ereport(NOTICE, (errmsg("schema %s is already distributed", schemaName))); + PG_RETURN_VOID(); + } + + /* Take lock on the relations and filter out partition tables */ + List *tableIdListInSchema = SchemaGetNonShardTableIdList(schemaId); + List *tableIdListToConvert = NIL; + Oid relationId = InvalidOid; + foreach_oid(relationId, tableIdListInSchema) + { + /* prevent concurrent drop of the relation */ + LockRelationOid(relationId, AccessShareLock); + EnsureRelationExists(relationId); + + /* + * Skip partitions as they would be distributed by the parent table. + * + * We should filter out partitions here before distributing the schema. + * Otherwise, converted partitioned table would change oid of partitions and its + * partition tables would fail with oid not exist. + */ + if (PartitionTable(relationId)) + { + continue; + } + + tableIdListToConvert = lappend_oid(tableIdListToConvert, relationId); + } + + /* Makes sure the schema can be distributed. */ + EnsureSchemaCanBeDistributed(schemaId, tableIdListInSchema); + + ereport(NOTICE, (errmsg("distributing the schema %s", schemaName))); + + /* Create colocation id and then single shard tables with the colocation id */ + uint32 colocationId = CreateTenantSchemaColocationId(); + ColocationParam colocationParam = { + .colocationParamType = COLOCATE_WITH_COLOCATION_ID, + .colocationId = colocationId, + }; + + /* + * Collect foreign keys for recreation and then drop fkeys and create single shard + * tables. + */ + List *originalForeignKeyRecreationCommands = NIL; + foreach_oid(relationId, tableIdListToConvert) + { + List *fkeyCommandsForRelation = + GetFKeyCreationCommandsRelationInvolvedWithTableType(relationId, + INCLUDE_ALL_TABLE_TYPES); + originalForeignKeyRecreationCommands = list_concat( + originalForeignKeyRecreationCommands, fkeyCommandsForRelation); + + DropFKeysRelationInvolvedWithTableType(relationId, INCLUDE_ALL_TABLE_TYPES); + CreateSingleShardTable(relationId, colocationParam); + } + + /* We can skip foreign key validations as we are sure about them at start */ + bool skip_validation = true; + ExecuteForeignKeyCreateCommandList(originalForeignKeyRecreationCommands, + skip_validation); + + /* Register the schema locally and sync it to workers */ + InsertTenantSchemaLocally(schemaId, colocationId); + char *registerSchemaCommand = TenantSchemaInsertCommand(schemaId, colocationId); + if (EnableMetadataSync) + { + SendCommandToWorkersWithMetadata(registerSchemaCommand); + } + + PG_RETURN_VOID(); +} + + +/* + * citus_schema_undistribute gets a tenant schema name, then converts it to a regular + * schema by undistributing all tables under it. + */ +Datum +citus_schema_undistribute(PG_FUNCTION_ARGS) +{ + CheckCitusVersion(ERROR); + EnsureCoordinator(); + + Oid schemaId = PG_GETARG_OID(0); + EnsureSchemaExist(schemaId); + EnsureSchemaOwner(schemaId); + + /* Prevent concurrent table creation under the schema */ + LockDatabaseObject(NamespaceRelationId, schemaId, 0, AccessExclusiveLock); + + /* + * We should ensure the existence of the schema after taking the lock since + * the schema could have been dropped before we acquired the lock. + */ + EnsureSchemaExist(schemaId); + EnsureSchemaOwner(schemaId); + + /* The schema should be a tenant schema */ + char *schemaName = get_namespace_name(schemaId); + if (!IsTenantSchema(schemaId)) + { + ereport(ERROR, (errmsg("schema %s is not distributed", schemaName))); + } + + ereport(NOTICE, (errmsg("undistributing schema %s", schemaName))); + + /* Take lock on the relations and filter out partition tables */ + List *tableIdListInSchema = SchemaGetNonShardTableIdList(schemaId); + List *tableIdListToConvert = NIL; + Oid relationId = InvalidOid; + foreach_oid(relationId, tableIdListInSchema) + { + /* prevent concurrent drop of the relation */ + LockRelationOid(relationId, AccessShareLock); + EnsureRelationExists(relationId); + + /* + * Skip partitions as they would be undistributed by the parent table. + * + * We should filter out partitions here before undistributing the schema. + * Otherwise, converted partitioned table would change oid of partitions and its + * partition tables would fail with oid not exist. + */ + if (PartitionTable(relationId)) + { + continue; + } + + tableIdListToConvert = lappend_oid(tableIdListToConvert, relationId); + + /* Only single shard tables are expected during the undistribution of the schema */ + Assert(IsCitusTableType(relationId, SINGLE_SHARD_DISTRIBUTED)); + } + + /* + * First, we need to delete schema metadata and sync it to workers. Otherwise, + * we would get error from `ErrorIfTenantTable` while undistributing the tables. + */ + UnregisterTenantSchemaGlobally(schemaId, schemaName); + UndistributeTables(tableIdListToConvert); PG_RETURN_VOID(); } @@ -253,7 +747,8 @@ ErrorIfTenantTable(Oid relationId, char *operationName) { if (IsTenantSchema(get_rel_namespace(relationId))) { - ereport(ERROR, (errmsg("%s is not allowed for %s because it is a tenant table", + ereport(ERROR, (errmsg("%s is not allowed for %s because it belongs to " + "a distributed schema", generate_qualified_relation_name(relationId), operationName))); } diff --git a/src/backend/distributed/commands/table.c b/src/backend/distributed/commands/table.c index df0aa757b..aa4570f5e 100644 --- a/src/backend/distributed/commands/table.c +++ b/src/backend/distributed/commands/table.c @@ -414,7 +414,11 @@ PostprocessCreateTableStmtPartitionOf(CreateStmt *createStatement, const } } - ErrorIfIllegalPartitioningInTenantSchema(PartitionParentOid(relationId), relationId); + if (IsTenantSchema(get_rel_namespace(parentRelationId)) || + IsTenantSchema(get_rel_namespace(relationId))) + { + ErrorIfIllegalPartitioningInTenantSchema(parentRelationId, relationId); + } /* * If a partition is being created and if its parent is a distributed @@ -496,8 +500,12 @@ PreprocessAlterTableStmtAttachPartition(AlterTableStmt *alterTableStatement, return NIL; } - ErrorIfIllegalPartitioningInTenantSchema(parentRelationId, - partitionRelationId); + if (IsTenantSchema(get_rel_namespace(parentRelationId)) || + IsTenantSchema(get_rel_namespace(partitionRelationId))) + { + ErrorIfIllegalPartitioningInTenantSchema(parentRelationId, + partitionRelationId); + } if (!IsCitusTable(parentRelationId)) { diff --git a/src/backend/distributed/metadata/dependency.c b/src/backend/distributed/metadata/dependency.c index a58e57be7..adc3fc1ab 100644 --- a/src/backend/distributed/metadata/dependency.c +++ b/src/backend/distributed/metadata/dependency.c @@ -1192,6 +1192,47 @@ IsAnyObjectAddressOwnedByExtension(const List *targets, } +/* + * FirstExtensionWithSchema returns the first extension address whose schema is the same + * as given schema. If no extension depends on the schema, it returns NULL. + * i.e. decide if given schema is an extension schema as in + * `CREATE EXTENSION [WITH] SCHEMA ;` + */ +ObjectAddress * +FirstExtensionWithSchema(Oid schemaId) +{ + ObjectAddress *extensionAddress = NULL; + + Relation relation = table_open(ExtensionRelationId, AccessShareLock); + + ScanKeyData entry[1]; + ScanKeyInit(&entry[0], Anum_pg_extension_extnamespace, BTEqualStrategyNumber, + F_INT4EQ, schemaId); + + SysScanDesc scan = systable_beginscan(relation, InvalidOid, false, NULL, 1, entry); + HeapTuple extensionTuple = systable_getnext(scan); + if (HeapTupleIsValid(extensionTuple)) + { + int extensionIdIndex = Anum_pg_extension_oid; + TupleDesc tupleDescriptor = RelationGetDescr(relation); + bool isNull = false; + Datum extensionIdDatum = heap_getattr(extensionTuple, extensionIdIndex, + tupleDescriptor, &isNull); + Oid extensionId = DatumGetObjectId(extensionIdDatum); + + extensionAddress = palloc0(sizeof(ObjectAddress)); + extensionAddress->objectId = extensionId; + extensionAddress->classId = ExtensionRelationId; + extensionAddress->objectSubId = 0; + } + + systable_endscan(scan); + table_close(relation, AccessShareLock); + + return extensionAddress; +} + + /* * IsObjectAddressOwnedByCitus returns true if the given object address * is owned by the citus or citus_columnar extensions. diff --git a/src/backend/distributed/metadata/metadata_utility.c b/src/backend/distributed/metadata/metadata_utility.c index 00aceaf04..7585769ad 100644 --- a/src/backend/distributed/metadata/metadata_utility.c +++ b/src/backend/distributed/metadata/metadata_utility.c @@ -2256,6 +2256,21 @@ EnsureTableOwner(Oid relationId) } +/* + * Check that the current user has owner rights to schemaId, error out if + * not. Superusers are regarded as owners. + */ +void +EnsureSchemaOwner(Oid schemaId) +{ + if (!pg_namespace_ownercheck(schemaId, GetUserId())) + { + aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SCHEMA, + get_namespace_name(schemaId)); + } +} + + /* * Check that the current user has owner rights to functionId, error out if * not. Superusers are regarded as owners. Functions and procedures are diff --git a/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql b/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql index d241fd235..6125f0d10 100644 --- a/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql +++ b/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql @@ -26,3 +26,7 @@ GRANT SELECT ON pg_catalog.pg_dist_tenant_schema TO public; #include "udfs/citus_tables/12.0-1.sql" #include "udfs/citus_shards/12.0-1.sql" + +-- udfs to convert a regular/tenant schema to a tenant/regular schema +#include "udfs/citus_schema_distribute/12.0-1.sql" +#include "udfs/citus_schema_undistribute/12.0-1.sql" diff --git a/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql b/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql index 1cddc488c..ef668c97f 100644 --- a/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql +++ b/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql @@ -30,6 +30,9 @@ BEGIN END; $$ LANGUAGE plpgsql; +DROP FUNCTION pg_catalog.citus_schema_distribute(regnamespace); +DROP FUNCTION pg_catalog.citus_schema_undistribute(regnamespace); + DROP FUNCTION pg_catalog.citus_internal_add_tenant_schema(Oid, int); #include "../udfs/citus_prepare_pg_upgrade/11.2-1.sql" diff --git a/src/backend/distributed/sql/udfs/citus_schema_distribute/12.0-1.sql b/src/backend/distributed/sql/udfs/citus_schema_distribute/12.0-1.sql new file mode 100644 index 000000000..7ca91fbbd --- /dev/null +++ b/src/backend/distributed/sql/udfs/citus_schema_distribute/12.0-1.sql @@ -0,0 +1,6 @@ +CREATE OR REPLACE FUNCTION pg_catalog.citus_schema_distribute(schemaname regnamespace) + RETURNS void + LANGUAGE C STRICT + AS 'MODULE_PATHNAME', $$citus_schema_distribute$$; +COMMENT ON FUNCTION pg_catalog.citus_schema_distribute(schemaname regnamespace) + IS 'distributes a schema, allowing it to move between nodes'; diff --git a/src/backend/distributed/sql/udfs/citus_schema_distribute/latest.sql b/src/backend/distributed/sql/udfs/citus_schema_distribute/latest.sql new file mode 100644 index 000000000..7ca91fbbd --- /dev/null +++ b/src/backend/distributed/sql/udfs/citus_schema_distribute/latest.sql @@ -0,0 +1,6 @@ +CREATE OR REPLACE FUNCTION pg_catalog.citus_schema_distribute(schemaname regnamespace) + RETURNS void + LANGUAGE C STRICT + AS 'MODULE_PATHNAME', $$citus_schema_distribute$$; +COMMENT ON FUNCTION pg_catalog.citus_schema_distribute(schemaname regnamespace) + IS 'distributes a schema, allowing it to move between nodes'; diff --git a/src/backend/distributed/sql/udfs/citus_schema_undistribute/12.0-1.sql b/src/backend/distributed/sql/udfs/citus_schema_undistribute/12.0-1.sql new file mode 100644 index 000000000..881dd3f5a --- /dev/null +++ b/src/backend/distributed/sql/udfs/citus_schema_undistribute/12.0-1.sql @@ -0,0 +1,6 @@ +CREATE OR REPLACE FUNCTION pg_catalog.citus_schema_undistribute(schemaname regnamespace) + RETURNS void + LANGUAGE C STRICT + AS 'MODULE_PATHNAME', $$citus_schema_undistribute$$; +COMMENT ON FUNCTION pg_catalog.citus_schema_undistribute(schemaname regnamespace) + IS 'reverts schema distribution, moving it back to the coordinator'; diff --git a/src/backend/distributed/sql/udfs/citus_schema_undistribute/latest.sql b/src/backend/distributed/sql/udfs/citus_schema_undistribute/latest.sql new file mode 100644 index 000000000..881dd3f5a --- /dev/null +++ b/src/backend/distributed/sql/udfs/citus_schema_undistribute/latest.sql @@ -0,0 +1,6 @@ +CREATE OR REPLACE FUNCTION pg_catalog.citus_schema_undistribute(schemaname regnamespace) + RETURNS void + LANGUAGE C STRICT + AS 'MODULE_PATHNAME', $$citus_schema_undistribute$$; +COMMENT ON FUNCTION pg_catalog.citus_schema_undistribute(schemaname regnamespace) + IS 'reverts schema distribution, moving it back to the coordinator'; diff --git a/src/include/distributed/commands.h b/src/include/distributed/commands.h index 542e85326..9a1c22c64 100644 --- a/src/include/distributed/commands.h +++ b/src/include/distributed/commands.h @@ -15,6 +15,7 @@ #include "postgres.h" +#include "distributed/metadata_utility.h" #include "utils/rel.h" #include "nodes/parsenodes.h" #include "tcop/dest.h" @@ -273,6 +274,10 @@ extern List * GetForeignConstraintToReferenceTablesCommands(Oid relationId); extern List * GetForeignConstraintToDistributedTablesCommands(Oid relationId); extern List * GetForeignConstraintFromDistributedTablesCommands(Oid relationId); extern List * GetForeignConstraintCommandsInternal(Oid relationId, int flags); +extern Oid DropFKeysAndUndistributeTable(Oid relationId); +extern void DropFKeysRelationInvolvedWithTableType(Oid relationId, int tableTypeFlag); +extern List * GetFKeyCreationCommandsRelationInvolvedWithTableType(Oid relationId, + int tableTypeFlag); extern bool AnyForeignKeyDependsOnIndex(Oid indexId); extern bool HasForeignKeyWithLocalTable(Oid relationId); extern bool HasForeignKeyToReferenceTable(Oid relationOid); @@ -793,5 +798,6 @@ extern void ErrorIfIllegalPartitioningInTenantSchema(Oid parentRelationId, extern void CreateTenantSchemaTable(Oid relationId); extern void ErrorIfTenantTable(Oid relationId, char *operationName); extern void ErrorIfTenantSchema(Oid nspOid, char *operationName); +extern uint32 CreateTenantSchemaColocationId(void); #endif /*CITUS_COMMANDS_H */ diff --git a/src/include/distributed/metadata/distobject.h b/src/include/distributed/metadata/distobject.h index 91f4fb630..de56c0e1f 100644 --- a/src/include/distributed/metadata/distobject.h +++ b/src/include/distributed/metadata/distobject.h @@ -30,6 +30,7 @@ extern bool IsTableOwnedByExtension(Oid relationId); extern bool ObjectAddressDependsOnExtension(const ObjectAddress *target); extern bool IsAnyObjectAddressOwnedByExtension(const List *targets, ObjectAddress *extensionAddress); +extern ObjectAddress * FirstExtensionWithSchema(Oid schemaId); extern bool IsObjectAddressOwnedByCitus(const ObjectAddress *objectAddress); extern ObjectAddress PgGetObjectAddress(char *ttype, ArrayType *namearr, ArrayType *argsarr); diff --git a/src/include/distributed/metadata_utility.h b/src/include/distributed/metadata_utility.h index 70fa32324..abed55e48 100644 --- a/src/include/distributed/metadata_utility.h +++ b/src/include/distributed/metadata_utility.h @@ -364,6 +364,7 @@ extern void CreateDistributedTable(Oid relationId, char *distributionColumnName, extern void CreateReferenceTable(Oid relationId); extern void CreateTruncateTrigger(Oid relationId); extern TableConversionReturn * UndistributeTable(TableConversionParameters *params); +extern void UndistributeTables(List *relationIdList); extern void EnsureAllObjectDependenciesExistOnAllNodes(const List *targets); extern DeferredErrorMessage * DeferErrorIfCircularDependencyExists(const @@ -383,6 +384,7 @@ extern void EnsureTableOwner(Oid relationId); extern void EnsureHashDistributedTable(Oid relationId); extern void EnsureHashOrSingleShardDistributedTable(Oid relationId); extern void EnsureFunctionOwner(Oid functionId); +extern void EnsureSchemaOwner(Oid schemaId); extern void EnsureSuperUser(void); extern void ErrorIfTableIsACatalogTable(Relation relation); extern void EnsureTableNotDistributed(Oid relationId); diff --git a/src/test/regress/expected/citus_schema_distribute_undistribute.out b/src/test/regress/expected/citus_schema_distribute_undistribute.out new file mode 100644 index 000000000..ffdc20154 --- /dev/null +++ b/src/test/regress/expected/citus_schema_distribute_undistribute.out @@ -0,0 +1,923 @@ +SET citus.next_shard_id TO 1730000; +SET citus.shard_replication_factor TO 1; +SET client_min_messages TO WARNING; +SET citus.enable_schema_based_sharding TO off; +SELECT 1 FROM citus_add_node('localhost', :master_port, groupid => 0); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +CREATE USER tenantuser superuser; +SET role tenantuser; +-- check invalid input +SELECT citus_schema_distribute(1); +ERROR: schema with OID 1 does not exist +SELECT citus_schema_undistribute(1); +ERROR: schema with OID 1 does not exist +-- noop +SELECT citus_schema_distribute(null); + citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_schema_undistribute(null); + citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +-- public and some others cannot be distributed as a tenant schema, but check what happens +-- if we try to call citus_schema_undistribute() for such a schema. +SELECT citus_schema_undistribute('public'); +ERROR: schema public is not distributed +-- create non-tenant schema +CREATE SCHEMA citus_schema_distribute_undistribute; +-- create tenant schema +CREATE SCHEMA tenant1; +CREATE TABLE tenant1.table1(id int PRIMARY KEY, name text); +INSERT INTO tenant1.table1 SELECT i, 'asd'::text FROM generate_series(1,20) i; +CREATE TABLE tenant1.table2(id int REFERENCES tenant1.table1(id), num bigint UNIQUE); +INSERT INTO tenant1.table2 SELECT i, i FROM generate_series(1,20) i; +CREATE TABLE citus_schema_distribute_undistribute.ref(id int PRIMARY KEY); +SELECT create_reference_table('citus_schema_distribute_undistribute.ref'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO citus_schema_distribute_undistribute.ref SELECT i FROM generate_series(1,100) i; +-- autoconverted to Citus local table due to foreign key to reference table +CREATE TABLE tenant1.table3(id int REFERENCES citus_schema_distribute_undistribute.ref(id)); +INSERT INTO tenant1.table3 SELECT i FROM generate_series(1,100) i; +-- Citus local table with autoconverted=false +CREATE TABLE tenant1.table3x(id int PRIMARY KEY REFERENCES citus_schema_distribute_undistribute.ref(id)); +SELECT citus_add_local_table_to_metadata('tenant1.table3x'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO tenant1.table3x SELECT i FROM generate_series(1,100) i; +-- foreign key to another local table in the same schema +CREATE TABLE tenant1.table3y(id int PRIMARY KEY REFERENCES tenant1.table3x(id)); +SELECT citus_add_local_table_to_metadata('tenant1.table3y'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO tenant1.table3y SELECT i FROM generate_series(1,100) i; +-- table with composite type +CREATE TYPE tenant1.catname AS ENUM ('baby', 'teen', 'mid'); +CREATE TYPE tenant1.agecat AS (below_age int, name tenant1.catname); +CREATE TABLE tenant1.table4(id int, age tenant1.agecat); +-- create autoconverted partitioned table +CREATE TABLE tenant1.partitioned_table(id int REFERENCES citus_schema_distribute_undistribute.ref(id)) PARTITION BY RANGE(id); +CREATE TABLE tenant1.partition1 PARTITION OF tenant1.partitioned_table FOR VALUES FROM (1) TO (11); +CREATE TABLE tenant1.partition2 PARTITION OF tenant1.partitioned_table FOR VALUES FROM (11) TO (21); +INSERT INTO tenant1.partitioned_table SELECT i FROM generate_series(1,20) i; +-- create view +CREATE VIEW tenant1.view1 AS SELECT * FROM tenant1.table1 JOIN tenant1.table2 USING(id); +WARNING: "view tenant1.view1" has dependency to "table tenant1.table2" that is not in Citus' metadata +DETAIL: "view tenant1.view1" will be created only locally +HINT: Distribute "table tenant1.table2" first to distribute "view tenant1.view1" +-- create view in regular schema +CREATE VIEW citus_schema_distribute_undistribute.view2 AS SELECT * FROM tenant1.view1; +WARNING: "view citus_schema_distribute_undistribute.view2" has dependency to "table tenant1.table2" that is not in Citus' metadata +DETAIL: "view citus_schema_distribute_undistribute.view2" will be created only locally +HINT: Distribute "table tenant1.table2" first to distribute "view citus_schema_distribute_undistribute.view2" +-- create materialized view +CREATE MATERIALIZED VIEW tenant1.view2 AS SELECT * FROM tenant1.table1; +-- create collation +CREATE COLLATION citus_schema_distribute_undistribute.german_phonebook (provider = icu, locale = 'de-u-co-phonebk'); +-- create type +CREATE TYPE citus_schema_distribute_undistribute.pair_type AS (a int, b int); +-- Create function +CREATE FUNCTION citus_schema_distribute_undistribute.one_as_result() RETURNS INT LANGUAGE SQL AS +$$ + SELECT 1; +$$; +-- create text search dictionary +CREATE TEXT SEARCH DICTIONARY citus_schema_distribute_undistribute.my_german_dict ( + template = snowball, + language = german, + stopwords = german +); +-- create text search config +CREATE TEXT SEARCH CONFIGURATION citus_schema_distribute_undistribute.my_ts_config ( parser = default ); +ALTER TEXT SEARCH CONFIGURATION citus_schema_distribute_undistribute.my_ts_config ALTER MAPPING FOR asciiword WITH citus_schema_distribute_undistribute.my_german_dict; +-- create sequence +CREATE SEQUENCE citus_schema_distribute_undistribute.seq; +-- create complex table +CREATE TABLE tenant1.complextable (id int PRIMARY KEY default nextval('citus_schema_distribute_undistribute.seq'), col int default (citus_schema_distribute_undistribute.one_as_result()), myserial serial, phone text COLLATE citus_schema_distribute_undistribute.german_phonebook, initials citus_schema_distribute_undistribute.pair_type); +CREATE SEQUENCE tenant1.seq_owned OWNED BY tenant1.complextable.id; +-- not allowed from workers +SELECT run_command_on_workers($$SELECT citus_schema_distribute('tenant1');$$); + run_command_on_workers +--------------------------------------------------------------------- + (localhost,57637,f,"ERROR: operation is not allowed on this node") + (localhost,57638,f,"ERROR: operation is not allowed on this node") +(2 rows) + +SELECT run_command_on_workers($$SELECT citus_schema_undistribute('tenant1');$$); + run_command_on_workers +--------------------------------------------------------------------- + (localhost,57637,f,"ERROR: operation is not allowed on this node") + (localhost,57638,f,"ERROR: operation is not allowed on this node") +(2 rows) + +-- inherited table not allowed +CREATE TABLE citus_schema_distribute_undistribute.cities ( + name text, + population real, + elevation int +); +CREATE TABLE citus_schema_distribute_undistribute.capitals ( + state char(2) UNIQUE NOT NULL +) INHERITS (citus_schema_distribute_undistribute.cities); +-- temporarily move "cities" into tenant1 (not a tenant schema yet) to test citus_schema_distribute() with a table that is inherited +ALTER TABLE citus_schema_distribute_undistribute.cities SET SCHEMA tenant1; +SELECT citus_schema_distribute('tenant1'); +ERROR: tables in a distributed schema cannot inherit or be inherited +ALTER TABLE tenant1.cities SET SCHEMA citus_schema_distribute_undistribute; +-- temporarily move "capitals" into tenant1 (not a tenant schema yet) to test citus_schema_distribute() with a table that inherits +ALTER TABLE citus_schema_distribute_undistribute.capitals SET SCHEMA tenant1; +SELECT citus_schema_distribute('tenant1'); +ERROR: tables in a distributed schema cannot inherit or be inherited +ALTER TABLE tenant1.capitals SET SCHEMA citus_schema_distribute_undistribute; +SELECT citus_schema_distribute('tenant1'); + citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_schema_undistribute('tenant1'); + citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE citus_schema_distribute_undistribute.illegal_partitioned_table(id int) PARTITION BY RANGE(id); +CREATE TABLE citus_schema_distribute_undistribute.illegal_partition1 PARTITION OF citus_schema_distribute_undistribute.illegal_partitioned_table FOR VALUES FROM (1) TO (11); +-- temporarily move "illegal_partitioned_table" into tenant1 (not a tenant schema yet) to test citus_schema_distribute() with a partition table whose parent is created in another schema +ALTER TABLE citus_schema_distribute_undistribute.illegal_partitioned_table SET SCHEMA tenant1; +SELECT citus_schema_distribute('tenant1'); +ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas +ALTER TABLE tenant1.illegal_partitioned_table SET SCHEMA citus_schema_distribute_undistribute; +-- temporarily move "illegal_partition1" into tenant1 (not a tenant schema yet) to test citus_schema_distribute() with a parent table whose partition is created in another schema +ALTER TABLE citus_schema_distribute_undistribute.illegal_partition1 SET SCHEMA tenant1; +SELECT citus_schema_distribute('tenant1'); +ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas +ALTER TABLE tenant1.illegal_partition1 SET SCHEMA citus_schema_distribute_undistribute; +SELECT citus_schema_distribute('tenant1'); + citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_schema_undistribute('tenant1'); + citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +-- foreign key to a local table in another schema is not allowed +CREATE TABLE citus_schema_distribute_undistribute.tbl1(id int PRIMARY KEY); +CREATE TABLE tenant1.table3z(id int PRIMARY KEY REFERENCES citus_schema_distribute_undistribute.tbl1(id)); +SELECT citus_schema_distribute('tenant1'); +ERROR: foreign keys from distributed schemas can only point to the same distributed schema or reference tables in regular schemas +DETAIL: "tenant1.table3z" references "citus_schema_distribute_undistribute.tbl1" via foreign key constraint "table3z_id_fkey" +SELECT create_reference_table('citus_schema_distribute_undistribute.tbl1'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_schema_distribute('tenant1'); + citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_schema_undistribute('tenant1'); + citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +-- foreign key to a distributed table in another schema is not allowed +CREATE TABLE citus_schema_distribute_undistribute.tbl2(id int PRIMARY KEY); +SELECT create_distributed_table('citus_schema_distribute_undistribute.tbl2','id'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE tenant1.table3w(id int PRIMARY KEY REFERENCES citus_schema_distribute_undistribute.tbl2(id)); +SELECT citus_schema_distribute('tenant1'); +ERROR: foreign keys from distributed schemas can only point to the same distributed schema or reference tables in regular schemas +DETAIL: "tenant1.table3w" references "citus_schema_distribute_undistribute.tbl2" via foreign key constraint "table3w_id_fkey" +DROP TABLE citus_schema_distribute_undistribute.tbl2 CASCADE; +SELECT citus_schema_distribute('tenant1'); + citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_schema_undistribute('tenant1'); + citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +-- foreign key from a local table in another schema is not allowed +CREATE TABLE tenant1.table3q(id int PRIMARY KEY); +CREATE TABLE citus_schema_distribute_undistribute.tbl3(id int PRIMARY KEY REFERENCES tenant1.table3q(id)); +SELECT citus_schema_distribute('tenant1'); +ERROR: cannot create foreign keys to tables in a distributed schema from another schema +DETAIL: "citus_schema_distribute_undistribute.tbl3" references "tenant1.table3q" via foreign key constraint "tbl3_id_fkey" +DROP TABLE citus_schema_distribute_undistribute.tbl3 CASCADE; +SELECT citus_schema_distribute('tenant1'); + citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_schema_undistribute('tenant1'); + citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +-- foreign key from a reference table in another schema is not allowed +CREATE TABLE tenant1.table3t(id int PRIMARY KEY); +CREATE TABLE citus_schema_distribute_undistribute.tbl4(id int PRIMARY KEY REFERENCES tenant1.table3t(id)); +SELECT create_reference_table('citus_schema_distribute_undistribute.tbl4'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_schema_distribute('tenant1'); +ERROR: cannot create foreign keys to tables in a distributed schema from another schema +DETAIL: "citus_schema_distribute_undistribute.tbl4" references "tenant1.table3t" via foreign key constraint "tbl4_id_fkey" +DROP TABLE citus_schema_distribute_undistribute.tbl4 CASCADE; +SELECT citus_schema_distribute('tenant1'); + citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_schema_undistribute('tenant1'); + citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +-- only allowed for schema owner or superuser +CREATE USER dummyregular; +SET role dummyregular; +SELECT citus_schema_distribute('tenant1'); +ERROR: must be owner of schema tenant1 +SELECT citus_schema_undistribute('tenant1'); +ERROR: must be owner of schema tenant1 +-- assign all tables to dummyregular except table5 +SET role tenantuser; +SELECT result FROM run_command_on_all_nodes($$ REASSIGN OWNED BY tenantuser TO dummyregular; $$); + result +--------------------------------------------------------------------- + REASSIGN OWNED + REASSIGN OWNED + REASSIGN OWNED +(3 rows) + +CREATE TABLE tenant1.table5(id int); +-- table owner check fails the distribution +SET role dummyregular; +SELECT citus_schema_distribute('tenant1'); +ERROR: must be owner of table table5 +-- alter table owner, then redistribute +SET role tenantuser; +ALTER TABLE tenant1.table5 OWNER TO dummyregular; +SET role dummyregular; +SELECT citus_schema_distribute('tenant1'); + citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +-- show the schema is a tenant schema now +SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset +-- below query verifies the same colocationid in pg_dist_tenant_schema, pg_dist_colocation and all entries in pg_dist_partition at the same time +SELECT '$$' || + ' SELECT colocationid = ' || :tenant1_colocid || + ' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' || + ' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''tenant1%'')' || + '$$' +AS verify_tenant_query \gset +SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*) AS total FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1%' $$); + result +--------------------------------------------------------------------- + 15 + 15 + 15 +(3 rows) + +SELECT result FROM run_command_on_all_nodes(:verify_tenant_query); + result +--------------------------------------------------------------------- + t + t + t +(3 rows) + +SELECT citus_schema_undistribute('tenant1'); + citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +-- show the schema is a regular schema +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$); + result +--------------------------------------------------------------------- + + + +(3 rows) + +-- below query verifies the tenant colocationid is removed from both pg_dist_colocation and all entries in pg_dist_partition at the same time +SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$); + result +--------------------------------------------------------------------- + t + t + t +(3 rows) + +SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$); + result +--------------------------------------------------------------------- + + + +(3 rows) + +RESET role; +SELECT result FROM run_command_on_all_nodes($$ REASSIGN OWNED BY dummyregular TO tenantuser; $$); + result +--------------------------------------------------------------------- + REASSIGN OWNED + REASSIGN OWNED + REASSIGN OWNED +(3 rows) + +DROP USER dummyregular; +CREATE USER dummysuper superuser; +SET role dummysuper; +SELECT citus_schema_distribute('tenant1'); + citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +-- show the schema is a tenant schema now +SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset +SELECT '$$' || + ' SELECT colocationid = ' || :tenant1_colocid || + ' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' || + ' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''tenant1%'')' || + '$$' +AS verify_tenant_query \gset +SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*) AS total FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1%' $$); + result +--------------------------------------------------------------------- + 15 + 15 + 15 +(3 rows) + +SELECT result FROM run_command_on_all_nodes(:verify_tenant_query); + result +--------------------------------------------------------------------- + t + t + t +(3 rows) + +SELECT citus_schema_undistribute('tenant1'); + citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +-- show the schema is a regular schema +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$); + result +--------------------------------------------------------------------- + + + +(3 rows) + +SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$); + result +--------------------------------------------------------------------- + t + t + t +(3 rows) + +SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$); + result +--------------------------------------------------------------------- + + + +(3 rows) + +RESET role; +DROP USER dummysuper; +-- foreign table +CREATE TABLE tenant1.foreign_table_test (id integer NOT NULL, data text, a bigserial); +INSERT INTO tenant1.foreign_table_test SELECT i FROM generate_series(1,100) i; +CREATE EXTENSION postgres_fdw; +CREATE SERVER foreign_server + FOREIGN DATA WRAPPER postgres_fdw + OPTIONS (host 'localhost', port :'master_port', dbname 'regression'); +CREATE USER MAPPING FOR CURRENT_USER + SERVER foreign_server + OPTIONS (user 'postgres'); +CREATE FOREIGN TABLE tenant1.foreign_table ( + id integer NOT NULL, + data text, + a bigserial +) + SERVER foreign_server + OPTIONS (schema_name 'tenant1', table_name 'foreign_table_test'); +-- foreign table not allowed +SELECT citus_schema_distribute('tenant1'); +ERROR: cannot create a foreign table in a distributed schema +ALTER FOREIGN TABLE tenant1.foreign_table SET SCHEMA citus_schema_distribute_undistribute; +SELECT citus_schema_distribute('tenant1'); + citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_schema_undistribute('tenant1'); + citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +-- already have distributed table error +CREATE TABLE tenant1.dist(id int); +SELECT create_distributed_table('tenant1.dist', 'id'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_schema_distribute('tenant1'); +ERROR: schema already has distributed tables +HINT: Undistribute distributed tables under the schema before distributing the schema. +SELECT undistribute_table('tenant1.dist'); + undistribute_table +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_schema_distribute('tenant1'); + citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_schema_undistribute('tenant1'); + citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE tenant1.ref2(id int); +SELECT create_reference_table('tenant1.ref2'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_schema_distribute('tenant1'); +ERROR: schema already has distributed tables +HINT: Undistribute distributed tables under the schema before distributing the schema. +SELECT undistribute_table('tenant1.ref2'); + undistribute_table +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_schema_distribute('tenant1'); + citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_schema_undistribute('tenant1'); + citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +BEGIN; +SELECT citus_schema_distribute('tenant1'); + citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +ROLLBACK; +-- show the schema is a regular schema +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$); + result +--------------------------------------------------------------------- + + + +(3 rows) + +SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$); + result +--------------------------------------------------------------------- + t + t + t +(3 rows) + +SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$); + result +--------------------------------------------------------------------- + + + +(3 rows) + +-- errors not a tenant schema +SELECT citus_schema_undistribute('tenant1'); +ERROR: schema tenant1 is not distributed +-- make it a tenant schema +BEGIN; +SELECT citus_schema_distribute('tenant1'); + citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +COMMIT; +-- show the schema is a tenant schema now +SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset +SELECT '$$' || + ' SELECT colocationid = ' || :tenant1_colocid || + ' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' || + ' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''tenant1%'')' || + '$$' +AS verify_tenant_query \gset +SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*) AS total FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1%' $$); + result +--------------------------------------------------------------------- + 18 + 18 + 18 +(3 rows) + +SELECT result FROM run_command_on_all_nodes(:verify_tenant_query); + result +--------------------------------------------------------------------- + t + t + t +(3 rows) + +-- already a tenant schema notice +SET client_min_messages TO NOTICE; +SELECT citus_schema_distribute('tenant1'); +NOTICE: schema tenant1 is already distributed + citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +SET client_min_messages TO WARNING; +-- convert back to a regular schema +SELECT citus_schema_undistribute('tenant1'); + citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +-- show the schema is a regular schema now +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$); + result +--------------------------------------------------------------------- + + + +(3 rows) + +SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$); + result +--------------------------------------------------------------------- + t + t + t +(3 rows) + +SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$); + result +--------------------------------------------------------------------- + + + +(3 rows) + +-- tables still have valid data +SELECT COUNT(*) FROM tenant1.partitioned_table; + count +--------------------------------------------------------------------- + 20 +(1 row) + +SELECT COUNT(*) FROM citus_schema_distribute_undistribute.foreign_table; + count +--------------------------------------------------------------------- + 100 +(1 row) + +SELECT COUNT(*) FROM tenant1.table3; + count +--------------------------------------------------------------------- + 100 +(1 row) + +TRUNCATE citus_schema_distribute_undistribute.ref CASCADE; +SELECT COUNT(*) FROM tenant1.table3; + count +--------------------------------------------------------------------- + 0 +(1 row) + +-- disallowed namespaces +SELECT citus_schema_distribute('public'); +ERROR: public schema cannot be distributed +SELECT citus_schema_distribute('pg_catalog'); +ERROR: pg_catalog schema cannot be distributed +SELECT citus_schema_distribute('pg_toast'); +ERROR: pg_toast schema cannot be distributed +CREATE TEMP TABLE xx(id int); -- create a temp table in case we do not have any pg_temp_xx schema yet +SELECT nspname AS temp_schema_name FROM pg_namespace WHERE nspname LIKE 'pg_temp%' LIMIT 1 \gset +SELECT nspname AS temp_toast_schema_name FROM pg_namespace WHERE nspname LIKE 'pg_toast_temp%' LIMIT 1 \gset +SELECT citus_schema_distribute(:'temp_schema_name'); +ERROR: temporary schema cannot be distributed +SELECT citus_schema_distribute(:'temp_toast_schema_name'); +ERROR: temporary schema cannot be distributed +SELECT citus_schema_distribute('citus'); +ERROR: schema citus, which is owned by an extension, cannot be distributed +CREATE SCHEMA extensionschema; +CREATE EXTENSION citext SCHEMA extensionschema; +SELECT citus_schema_distribute('extensionschema'); +ERROR: schema extensionschema cannot be distributed since it is the schema of extension citext +DROP SCHEMA extensionschema CASCADE; +ALTER EXTENSION citus ADD TABLE tenant1.table1; +SELECT citus_schema_distribute('tenant1'); +ERROR: schema cannot be distributed since it has table tenant1 which is owned by an extension +ALTER EXTENSION citus DROP TABLE tenant1.table1; +-- weird schema and table names +CREATE SCHEMA "CiTuS.TeeN"; +CREATE TABLE "CiTuS.TeeN"."TeeNTabLE.1!?!"(id int PRIMARY KEY, name text); +INSERT INTO "CiTuS.TeeN"."TeeNTabLE.1!?!" SELECT i, 'asd'::text FROM generate_series(1,20) i; +SELECT citus_schema_distribute('"CiTuS.TeeN"'); + citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +-- show the schema is a tenant schema now +SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset +SELECT '$$' || + ' SELECT colocationid = ' || :tenant1_colocid || + ' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' || + ' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''"CiTuS.TeeN"%'')' || + '$$' +AS verify_tenant_query \gset +SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*) AS total FROM pg_dist_partition WHERE logicalrelid::text LIKE '"CiTuS.TeeN"%' $$); + result +--------------------------------------------------------------------- + 1 + 1 + 1 +(3 rows) + +SELECT result FROM run_command_on_all_nodes(:verify_tenant_query); + result +--------------------------------------------------------------------- + t + t + t +(3 rows) + +SELECT citus_schema_undistribute('"CiTuS.TeeN"'); + citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +-- show the schema is a regular schema +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$); + result +--------------------------------------------------------------------- + + + +(3 rows) + +SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$); + result +--------------------------------------------------------------------- + t + t + t +(3 rows) + +SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$); + result +--------------------------------------------------------------------- + + + +(3 rows) + +-- try setting the schema again after adding a distributed table into the schema. It should complain about distributed table. +CREATE TABLE tenant1.new_dist(id int); +SELECT create_distributed_table('tenant1.new_dist', 'id'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_schema_distribute('tenant1'); +ERROR: schema already has distributed tables +HINT: Undistribute distributed tables under the schema before distributing the schema. +SELECT undistribute_table('tenant1.new_dist'); + undistribute_table +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_schema_distribute('tenant1'); + citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_schema_undistribute('tenant1'); + citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +-- try setting the schema again after adding a single shard table into the schema. It should complain about distributed table. +CREATE TABLE tenant1.single_shard_t(id int); +SELECT create_distributed_table('tenant1.single_shard_t', NULL); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_schema_distribute('tenant1'); +ERROR: schema already has distributed tables +HINT: Undistribute distributed tables under the schema before distributing the schema. +SELECT undistribute_table('tenant1.single_shard_t'); + undistribute_table +--------------------------------------------------------------------- + +(1 row) + +-- try setting the schema again. It should succeed now. +SELECT citus_schema_distribute('tenant1'); + citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +-- show the schema is a tenant schema now +SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset +SELECT '$$' || + ' SELECT colocationid = ' || :tenant1_colocid || + ' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' || + ' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''tenant1%'')' || + '$$' +AS verify_tenant_query \gset +SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*) AS total FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1%' $$); + result +--------------------------------------------------------------------- + 20 + 20 + 20 +(3 rows) + +SELECT result FROM run_command_on_all_nodes(:verify_tenant_query); + result +--------------------------------------------------------------------- + t + t + t +(3 rows) + +SELECT citus_schema_undistribute('tenant1'); + citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +-- show the schema is a regular schema now +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$); + result +--------------------------------------------------------------------- + + + +(3 rows) + +SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$); + result +--------------------------------------------------------------------- + t + t + t +(3 rows) + +SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$); + result +--------------------------------------------------------------------- + + + +(3 rows) + +-- create an empty tenant schema to verify colocation id is removed successfully after we undistribute it +CREATE SCHEMA empty_tenant; +SELECT citus_schema_distribute('empty_tenant'); + citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +-- show the schema is a tenant schema now +SELECT colocationid AS empty_tenant_colocid FROM pg_dist_tenant_schema schemaid \gset +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace FROM pg_dist_colocation JOIN pg_dist_tenant_schema USING(colocationid) $$); + result +--------------------------------------------------------------------- + empty_tenant + empty_tenant + empty_tenant +(3 rows) + +SELECT citus_schema_undistribute('empty_tenant'); + citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +-- show the schema is a regular schema now +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$); + result +--------------------------------------------------------------------- + + + +(3 rows) + +SELECT '$$' || 'SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = ' || :empty_tenant_colocid || '$$' +AS verify_empty_tenant_query \gset +SELECT result FROM run_command_on_all_nodes(:verify_empty_tenant_query); + result +--------------------------------------------------------------------- + t + t + t +(3 rows) + +-- cleanup +DROP SCHEMA "CiTuS.TeeN" CASCADE; +DROP SCHEMA tenant1 CASCADE; +DROP SCHEMA empty_tenant CASCADE; +DROP EXTENSION postgres_fdw CASCADE; +DROP SCHEMA citus_schema_distribute_undistribute CASCADE; +DROP USER tenantuser; +SELECT citus_remove_node('localhost', :master_port); + citus_remove_node +--------------------------------------------------------------------- + +(1 row) + diff --git a/src/test/regress/expected/isolation_citus_schema_distribute_undistribute.out b/src/test/regress/expected/isolation_citus_schema_distribute_undistribute.out new file mode 100644 index 000000000..4fd00cc00 --- /dev/null +++ b/src/test/regress/expected/isolation_citus_schema_distribute_undistribute.out @@ -0,0 +1,1136 @@ +Parsed test spec with 2 sessions + +starting permutation: s1-begin s1-schema-distribute s2-drop-schema s1-commit s1-verify-distributed-schema +citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + +step s1-schema-distribute: + SELECT citus_schema_distribute('tenant1'); + +citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +step s2-drop-schema: + DROP SCHEMA tenant1 CASCADE; + +step s1-commit: + COMMIT; + +step s2-drop-schema: <... completed> +step s1-verify-distributed-schema: + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + +logicalrelid|partmethod|partkey|is_correct_colocid|repmodel|autoconverted +--------------------------------------------------------------------- +(0 rows) + +citus_remove_node +--------------------------------------------------------------------- + +(1 row) + + +starting permutation: s1-schema-distribute s1-begin s1-schema-undistribute s2-drop-schema s1-commit s1-verify-distributed-schema +citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +step s1-schema-distribute: + SELECT citus_schema_distribute('tenant1'); + +citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + +step s1-schema-undistribute: + SELECT citus_schema_undistribute('tenant1'); + +citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +step s2-drop-schema: + DROP SCHEMA tenant1 CASCADE; + +step s1-commit: + COMMIT; + +step s2-drop-schema: <... completed> +step s1-verify-distributed-schema: + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + +logicalrelid|partmethod|partkey|is_correct_colocid|repmodel|autoconverted +--------------------------------------------------------------------- +(0 rows) + +citus_remove_node +--------------------------------------------------------------------- + +(1 row) + + +starting permutation: s1-begin s1-schema-distribute s2-rename-schema s1-commit s1-verify-distributed-schema +citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + +step s1-schema-distribute: + SELECT citus_schema_distribute('tenant1'); + +citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +step s2-rename-schema: + ALTER SCHEMA tenant1 RENAME TO tenant2; + +step s1-commit: + COMMIT; + +step s1-verify-distributed-schema: + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + +logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted +--------------------------------------------------------------------- +tenant2.table1|n | |t |s |f +tenant2.table2|n | |t |s |f +tenant2.table3|n | |t |s |f +(3 rows) + +citus_remove_node +--------------------------------------------------------------------- + +(1 row) + + +starting permutation: s1-schema-distribute s1-begin s1-schema-undistribute s2-rename-schema s1-commit s1-verify-distributed-schema +citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +step s1-schema-distribute: + SELECT citus_schema_distribute('tenant1'); + +citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + +step s1-schema-undistribute: + SELECT citus_schema_undistribute('tenant1'); + +citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +step s2-rename-schema: + ALTER SCHEMA tenant1 RENAME TO tenant2; + +step s1-commit: + COMMIT; + +step s1-verify-distributed-schema: + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + +logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted +--------------------------------------------------------------------- +tenant2.table3|n | | |s |t +(1 row) + +citus_remove_node +--------------------------------------------------------------------- + +(1 row) + + +starting permutation: s1-begin s1-schema-distribute s2-add-table s1-commit s1-verify-distributed-schema +citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + +step s1-schema-distribute: + SELECT citus_schema_distribute('tenant1'); + +citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +step s2-add-table: + CREATE TABLE tenant1.table4(id int PRIMARY KEY, name text, col bigint); + +step s1-commit: + COMMIT; + +step s2-add-table: <... completed> +step s1-verify-distributed-schema: + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + +logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted +--------------------------------------------------------------------- +tenant1.table1|n | |t |s |f +tenant1.table2|n | |t |s |f +tenant1.table3|n | |t |s |f +tenant1.table4|n | |t |s |f +(4 rows) + +citus_remove_node +--------------------------------------------------------------------- + +(1 row) + + +starting permutation: s1-schema-distribute s1-begin s1-schema-undistribute s2-add-table s1-commit s1-verify-distributed-schema +citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +step s1-schema-distribute: + SELECT citus_schema_distribute('tenant1'); + +citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + +step s1-schema-undistribute: + SELECT citus_schema_undistribute('tenant1'); + +citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +step s2-add-table: + CREATE TABLE tenant1.table4(id int PRIMARY KEY, name text, col bigint); + +step s1-commit: + COMMIT; + +step s2-add-table: <... completed> +step s1-verify-distributed-schema: + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + +logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted +--------------------------------------------------------------------- +tenant1.table3|n | | |s |t +(1 row) + +citus_remove_node +--------------------------------------------------------------------- + +(1 row) + + +starting permutation: s1-begin s1-schema-distribute s2-drop-table s1-commit s1-verify-distributed-schema +citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + +step s1-schema-distribute: + SELECT citus_schema_distribute('tenant1'); + +citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +step s2-drop-table: + DROP TABLE tenant1.table3; + +step s1-commit: + COMMIT; + +step s2-drop-table: <... completed> +step s1-verify-distributed-schema: + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + +logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted +--------------------------------------------------------------------- +tenant1.table1|n | |t |s |f +tenant1.table2|n | |t |s |f +(2 rows) + +citus_remove_node +--------------------------------------------------------------------- + +(1 row) + + +starting permutation: s1-schema-distribute s1-begin s1-schema-undistribute s2-drop-table s1-commit s1-verify-distributed-schema +citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +step s1-schema-distribute: + SELECT citus_schema_distribute('tenant1'); + +citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + +step s1-schema-undistribute: + SELECT citus_schema_undistribute('tenant1'); + +citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +step s2-drop-table: + DROP TABLE tenant1.table3; + +step s1-commit: + COMMIT; + +step s2-drop-table: <... completed> +step s1-verify-distributed-schema: + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + +logicalrelid|partmethod|partkey|is_correct_colocid|repmodel|autoconverted +--------------------------------------------------------------------- +(0 rows) + +citus_remove_node +--------------------------------------------------------------------- + +(1 row) + + +starting permutation: s1-begin s1-schema-distribute s2-alter-col-type s1-commit s1-verify-distributed-schema +citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + +step s1-schema-distribute: + SELECT citus_schema_distribute('tenant1'); + +citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +step s2-alter-col-type: + ALTER TABLE tenant1.table3 ALTER COLUMN col1 TYPE text; + +step s1-commit: + COMMIT; + +step s2-alter-col-type: <... completed> +step s1-verify-distributed-schema: + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + +logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted +--------------------------------------------------------------------- +tenant1.table1|n | |t |s |f +tenant1.table2|n | |t |s |f +tenant1.table3|n | |t |s |f +(3 rows) + +citus_remove_node +--------------------------------------------------------------------- + +(1 row) + + +starting permutation: s1-schema-distribute s1-begin s1-schema-undistribute s2-alter-col-type s1-commit s1-verify-distributed-schema +citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +step s1-schema-distribute: + SELECT citus_schema_distribute('tenant1'); + +citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + +step s1-schema-undistribute: + SELECT citus_schema_undistribute('tenant1'); + +citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +step s2-alter-col-type: + ALTER TABLE tenant1.table3 ALTER COLUMN col1 TYPE text; + +step s1-commit: + COMMIT; + +step s2-alter-col-type: <... completed> +step s1-verify-distributed-schema: + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + +logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted +--------------------------------------------------------------------- +tenant1.table3|n | | |s |t +(1 row) + +citus_remove_node +--------------------------------------------------------------------- + +(1 row) + + +starting permutation: s1-begin s1-schema-distribute s2-add-foreign-key s1-commit s1-verify-distributed-schema +citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + +step s1-schema-distribute: + SELECT citus_schema_distribute('tenant1'); + +citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +step s2-add-foreign-key: + ALTER TABLE tenant1.table3 ADD CONSTRAINT table3_fk1 FOREIGN KEY (id) REFERENCES tenant1.table2 (id); + +step s1-commit: + COMMIT; + +step s2-add-foreign-key: <... completed> +step s1-verify-distributed-schema: + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + +logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted +--------------------------------------------------------------------- +tenant1.table1|n | |t |s |f +tenant1.table2|n | |t |s |f +tenant1.table3|n | |t |s |f +(3 rows) + +citus_remove_node +--------------------------------------------------------------------- + +(1 row) + + +starting permutation: s1-schema-distribute s1-begin s1-schema-undistribute s2-add-foreign-key s1-commit s1-verify-distributed-schema +citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +step s1-schema-distribute: + SELECT citus_schema_distribute('tenant1'); + +citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + +step s1-schema-undistribute: + SELECT citus_schema_undistribute('tenant1'); + +citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +step s2-add-foreign-key: + ALTER TABLE tenant1.table3 ADD CONSTRAINT table3_fk1 FOREIGN KEY (id) REFERENCES tenant1.table2 (id); + +step s1-commit: + COMMIT; + +step s2-add-foreign-key: <... completed> +step s1-verify-distributed-schema: + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + +logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted +--------------------------------------------------------------------- +tenant1.table3|n | | |s |t +tenant1.table2|n | | |s |t +(2 rows) + +citus_remove_node +--------------------------------------------------------------------- + +(1 row) + + +starting permutation: s1-begin s1-schema-distribute s2-drop-foreign-key s1-commit s1-verify-distributed-schema +citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + +step s1-schema-distribute: + SELECT citus_schema_distribute('tenant1'); + +citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +step s2-drop-foreign-key: + ALTER TABLE tenant1.table3 DROP CONSTRAINT table3_col_fkey; + +step s1-commit: + COMMIT; + +step s2-drop-foreign-key: <... completed> +step s1-verify-distributed-schema: + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + +logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted +--------------------------------------------------------------------- +tenant1.table1|n | |t |s |f +tenant1.table2|n | |t |s |f +tenant1.table3|n | |t |s |f +(3 rows) + +citus_remove_node +--------------------------------------------------------------------- + +(1 row) + + +starting permutation: s1-schema-distribute s1-begin s1-schema-undistribute s2-drop-foreign-key s1-commit s1-verify-distributed-schema +citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +step s1-schema-distribute: + SELECT citus_schema_distribute('tenant1'); + +citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + +step s1-schema-undistribute: + SELECT citus_schema_undistribute('tenant1'); + +citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +step s2-drop-foreign-key: + ALTER TABLE tenant1.table3 DROP CONSTRAINT table3_col_fkey; + +step s1-commit: + COMMIT; + +step s2-drop-foreign-key: <... completed> +step s1-verify-distributed-schema: + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + +logicalrelid|partmethod|partkey|is_correct_colocid|repmodel|autoconverted +--------------------------------------------------------------------- +(0 rows) + +citus_remove_node +--------------------------------------------------------------------- + +(1 row) + + +starting permutation: s1-begin s1-schema-distribute s2-create-unique-index s1-commit s1-verify-distributed-schema +citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + +step s1-schema-distribute: + SELECT citus_schema_distribute('tenant1'); + +citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +step s2-create-unique-index: + CREATE UNIQUE INDEX idx_2 ON tenant1.table3 (col); + +step s1-commit: + COMMIT; + +step s2-create-unique-index: <... completed> +step s1-verify-distributed-schema: + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + +logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted +--------------------------------------------------------------------- +tenant1.table1|n | |t |s |f +tenant1.table2|n | |t |s |f +tenant1.table3|n | |t |s |f +(3 rows) + +citus_remove_node +--------------------------------------------------------------------- + +(1 row) + + +starting permutation: s1-schema-distribute s1-begin s1-schema-undistribute s2-create-unique-index s1-commit s1-verify-distributed-schema +citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +step s1-schema-distribute: + SELECT citus_schema_distribute('tenant1'); + +citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + +step s1-schema-undistribute: + SELECT citus_schema_undistribute('tenant1'); + +citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +step s2-create-unique-index: + CREATE UNIQUE INDEX idx_2 ON tenant1.table3 (col); + +step s1-commit: + COMMIT; + +step s2-create-unique-index: <... completed> +step s1-verify-distributed-schema: + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + +logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted +--------------------------------------------------------------------- +tenant1.table3|n | | |s |t +(1 row) + +citus_remove_node +--------------------------------------------------------------------- + +(1 row) + + +starting permutation: s1-begin s1-schema-distribute s2-create-unique-index-concurrently s1-commit s1-verify-distributed-schema +citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + +step s1-schema-distribute: + SELECT citus_schema_distribute('tenant1'); + +citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +step s2-create-unique-index-concurrently: + CREATE UNIQUE INDEX CONCURRENTLY idx_3 ON tenant1.table3 (col); + +step s1-commit: + COMMIT; + +step s2-create-unique-index-concurrently: <... completed> +step s1-verify-distributed-schema: + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + +logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted +--------------------------------------------------------------------- +tenant1.table1|n | |t |s |f +tenant1.table2|n | |t |s |f +tenant1.table3|n | |t |s |f +(3 rows) + +citus_remove_node +--------------------------------------------------------------------- + +(1 row) + + +starting permutation: s1-schema-distribute s1-begin s1-schema-undistribute s2-create-unique-index-concurrently s1-commit s1-verify-distributed-schema +citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +step s1-schema-distribute: + SELECT citus_schema_distribute('tenant1'); + +citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + +step s1-schema-undistribute: + SELECT citus_schema_undistribute('tenant1'); + +citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +step s2-create-unique-index-concurrently: + CREATE UNIQUE INDEX CONCURRENTLY idx_3 ON tenant1.table3 (col); + +step s1-commit: + COMMIT; + +step s2-create-unique-index-concurrently: <... completed> +step s1-verify-distributed-schema: + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + +logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted +--------------------------------------------------------------------- +tenant1.table3|n | | |s |t +(1 row) + +citus_remove_node +--------------------------------------------------------------------- + +(1 row) + + +starting permutation: s2-create-unique-index s1-begin s1-schema-distribute s2-reindex-unique-concurrently s1-commit s1-verify-distributed-schema +citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +step s2-create-unique-index: + CREATE UNIQUE INDEX idx_2 ON tenant1.table3 (col); + +step s1-begin: + BEGIN; + +step s1-schema-distribute: + SELECT citus_schema_distribute('tenant1'); + +citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +step s2-reindex-unique-concurrently: + REINDEX INDEX CONCURRENTLY tenant1.idx_2; + +step s1-commit: + COMMIT; + +step s2-reindex-unique-concurrently: <... completed> +step s1-verify-distributed-schema: + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + +logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted +--------------------------------------------------------------------- +tenant1.table1|n | |t |s |f +tenant1.table2|n | |t |s |f +tenant1.table3|n | |t |s |f +(3 rows) + +citus_remove_node +--------------------------------------------------------------------- + +(1 row) + + +starting permutation: s2-create-unique-index s1-schema-distribute s1-begin s1-schema-undistribute s2-reindex-unique-concurrently s1-commit s1-verify-distributed-schema +citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +step s2-create-unique-index: + CREATE UNIQUE INDEX idx_2 ON tenant1.table3 (col); + +step s1-schema-distribute: + SELECT citus_schema_distribute('tenant1'); + +citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + +step s1-schema-undistribute: + SELECT citus_schema_undistribute('tenant1'); + +citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +step s2-reindex-unique-concurrently: + REINDEX INDEX CONCURRENTLY tenant1.idx_2; + +step s1-commit: + COMMIT; + +step s2-reindex-unique-concurrently: <... completed> +step s1-verify-distributed-schema: + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + +logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted +--------------------------------------------------------------------- +tenant1.table3|n | | |s |t +(1 row) + +citus_remove_node +--------------------------------------------------------------------- + +(1 row) + + +starting permutation: s1-begin s1-schema-distribute s2-insert s1-commit s1-verify-distributed-schema +citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + +step s1-schema-distribute: + SELECT citus_schema_distribute('tenant1'); + +citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +step s2-insert: + INSERT INTO public.ref SELECT i FROM generate_series(11, 20) i; + INSERT INTO tenant1.table3 SELECT i, 'asd', i*1000 FROM generate_series(11, 20) i; + +step s1-commit: + COMMIT; + +step s2-insert: <... completed> +step s1-verify-distributed-schema: + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + +logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted +--------------------------------------------------------------------- +tenant1.table1|n | |t |s |f +tenant1.table2|n | |t |s |f +tenant1.table3|n | |t |s |f +(3 rows) + +citus_remove_node +--------------------------------------------------------------------- + +(1 row) + + +starting permutation: s1-schema-distribute s1-begin s1-schema-undistribute s2-insert s1-commit s1-verify-distributed-schema +citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +step s1-schema-distribute: + SELECT citus_schema_distribute('tenant1'); + +citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + +step s1-schema-undistribute: + SELECT citus_schema_undistribute('tenant1'); + +citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +step s2-insert: + INSERT INTO public.ref SELECT i FROM generate_series(11, 20) i; + INSERT INTO tenant1.table3 SELECT i, 'asd', i*1000 FROM generate_series(11, 20) i; + +step s1-commit: + COMMIT; + +step s2-insert: <... completed> +step s1-verify-distributed-schema: + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + +logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted +--------------------------------------------------------------------- +tenant1.table3|n | | |s |t +(1 row) + +citus_remove_node +--------------------------------------------------------------------- + +(1 row) + + +starting permutation: s2-insert s1-begin s1-schema-distribute s2-update s1-commit s1-verify-distributed-schema +citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +step s2-insert: + INSERT INTO public.ref SELECT i FROM generate_series(11, 20) i; + INSERT INTO tenant1.table3 SELECT i, 'asd', i*1000 FROM generate_series(11, 20) i; + +step s1-begin: + BEGIN; + +step s1-schema-distribute: + SELECT citus_schema_distribute('tenant1'); + +citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +step s2-update: + UPDATE tenant1.table3 SET col = 11 WHERE col = 11; + +step s1-commit: + COMMIT; + +step s2-update: <... completed> +step s1-verify-distributed-schema: + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + +logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted +--------------------------------------------------------------------- +tenant1.table1|n | |t |s |f +tenant1.table2|n | |t |s |f +tenant1.table3|n | |t |s |f +(3 rows) + +citus_remove_node +--------------------------------------------------------------------- + +(1 row) + + +starting permutation: s2-insert s1-schema-distribute s1-begin s1-schema-undistribute s2-update s1-commit s1-verify-distributed-schema +citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +step s2-insert: + INSERT INTO public.ref SELECT i FROM generate_series(11, 20) i; + INSERT INTO tenant1.table3 SELECT i, 'asd', i*1000 FROM generate_series(11, 20) i; + +step s1-schema-distribute: + SELECT citus_schema_distribute('tenant1'); + +citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + +step s1-schema-undistribute: + SELECT citus_schema_undistribute('tenant1'); + +citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +step s2-update: + UPDATE tenant1.table3 SET col = 11 WHERE col = 11; + +step s1-commit: + COMMIT; + +step s2-update: <... completed> +step s1-verify-distributed-schema: + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + +logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted +--------------------------------------------------------------------- +tenant1.table3|n | | |s |t +(1 row) + +citus_remove_node +--------------------------------------------------------------------- + +(1 row) + + +starting permutation: s2-insert s1-begin s1-schema-distribute s2-delete s1-commit s1-verify-distributed-schema +citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +step s2-insert: + INSERT INTO public.ref SELECT i FROM generate_series(11, 20) i; + INSERT INTO tenant1.table3 SELECT i, 'asd', i*1000 FROM generate_series(11, 20) i; + +step s1-begin: + BEGIN; + +step s1-schema-distribute: + SELECT citus_schema_distribute('tenant1'); + +citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +step s2-delete: + DELETE FROM tenant1.table3; + +step s1-commit: + COMMIT; + +step s2-delete: <... completed> +step s1-verify-distributed-schema: + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + +logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted +--------------------------------------------------------------------- +tenant1.table1|n | |t |s |f +tenant1.table2|n | |t |s |f +tenant1.table3|n | |t |s |f +(3 rows) + +citus_remove_node +--------------------------------------------------------------------- + +(1 row) + + +starting permutation: s2-insert s1-schema-distribute s1-begin s1-schema-undistribute s2-delete s1-commit s1-verify-distributed-schema +citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +step s2-insert: + INSERT INTO public.ref SELECT i FROM generate_series(11, 20) i; + INSERT INTO tenant1.table3 SELECT i, 'asd', i*1000 FROM generate_series(11, 20) i; + +step s1-schema-distribute: + SELECT citus_schema_distribute('tenant1'); + +citus_schema_distribute +--------------------------------------------------------------------- + +(1 row) + +step s1-begin: + BEGIN; + +step s1-schema-undistribute: + SELECT citus_schema_undistribute('tenant1'); + +citus_schema_undistribute +--------------------------------------------------------------------- + +(1 row) + +step s2-delete: + DELETE FROM tenant1.table3; + +step s1-commit: + COMMIT; + +step s2-delete: <... completed> +step s1-verify-distributed-schema: + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + +logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted +--------------------------------------------------------------------- +tenant1.table3|n | | |s |t +(1 row) + +citus_remove_node +--------------------------------------------------------------------- + +(1 row) + diff --git a/src/test/regress/expected/multi_extension.out b/src/test/regress/expected/multi_extension.out index ef94ebdd1..beb73a5be 100644 --- a/src/test/regress/expected/multi_extension.out +++ b/src/test/regress/expected/multi_extension.out @@ -1362,8 +1362,10 @@ SELECT * FROM multi_extension.print_extension_changes(); | function citus_internal_add_tenant_schema(oid,integer) void | function citus_internal_delete_tenant_schema(oid) void | function citus_internal_unregister_tenant_schema_globally(oid,text) void + | function citus_schema_distribute(regnamespace) void + | function citus_schema_undistribute(regnamespace) void | table pg_dist_tenant_schema -(4 rows) +(6 rows) DROP TABLE multi_extension.prev_objects, multi_extension.extension_diff; -- show running version diff --git a/src/test/regress/expected/schema_based_sharding.out b/src/test/regress/expected/schema_based_sharding.out index 1180fe573..1bc5d7963 100644 --- a/src/test/regress/expected/schema_based_sharding.out +++ b/src/test/regress/expected/schema_based_sharding.out @@ -76,28 +76,28 @@ SELECT citus_add_local_table_to_metadata('tenant_2.test_table'); ERROR: table "test_table" is already distributed -- verify we don't allow update_distributed_table_colocation for tenant tables SELECT update_distributed_table_colocation('tenant_2.test_table', colocate_with => 'none'); -ERROR: tenant_2.test_table is not allowed for update_distributed_table_colocation because it is a tenant table +ERROR: tenant_2.test_table is not allowed for update_distributed_table_colocation because it belongs to a distributed schema -- verify we also don't allow colocate_with a tenant table SELECT update_distributed_table_colocation('regular_schema.test_table', colocate_with => 'tenant_2.test_table'); -ERROR: tenant_2.test_table is not allowed for colocate_with because it is a tenant table +ERROR: tenant_2.test_table is not allowed for colocate_with because it belongs to a distributed schema -- verify we don't allow undistribute_table for tenant tables SELECT undistribute_table('tenant_2.test_table'); -ERROR: tenant_2.test_table is not allowed for undistribute_table because it is a tenant table +ERROR: tenant_2.test_table is not allowed for undistribute_table because it belongs to a distributed schema -- verify we don't allow alter_distributed_table for tenant tables SELECT alter_distributed_table('tenant_2.test_table', colocate_with => 'none'); -ERROR: tenant_2.test_table is not allowed for alter_distributed_table because it is a tenant table +ERROR: tenant_2.test_table is not allowed for alter_distributed_table because it belongs to a distributed schema -- verify we also don't allow colocate_with a tenant table SELECT alter_distributed_table('regular_schema.test_table', colocate_with => 'tenant_2.test_table'); -ERROR: tenant_2.test_table is not allowed for colocate_with because it is a tenant table +ERROR: tenant_2.test_table is not allowed for colocate_with because it belongs to a distributed schema -- verify we don't allow ALTER TABLE SET SCHEMA for tenant tables ALTER TABLE tenant_2.test_table SET SCHEMA regular_schema; -ERROR: tenant_2.test_table is not allowed for ALTER TABLE SET SCHEMA because it is a tenant table +ERROR: tenant_2.test_table is not allowed for ALTER TABLE SET SCHEMA because it belongs to a distributed schema -- verify we don't allow ALTER TABLE SET SCHEMA for tenant schemas ALTER TABLE regular_schema.test_table SET SCHEMA tenant_2; ERROR: tenant_2 is not allowed for ALTER TABLE SET SCHEMA because it is a distributed schema -- the same, from tenant schema to tenant schema ALTER TABLE tenant_2.test_table SET SCHEMA tenant_3; -ERROR: tenant_2.test_table is not allowed for ALTER TABLE SET SCHEMA because it is a tenant table +ERROR: tenant_2.test_table is not allowed for ALTER TABLE SET SCHEMA because it belongs to a distributed schema -- (on coordinator) verify that colocation id is set for empty tenants too SELECT colocationid > 0 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text IN ('tenant_1', 'tenant_3'); @@ -200,7 +200,7 @@ CREATE FOREIGN TABLE tenant_4.foreign_table ( id bigint not null, full_name text not null default '' ) SERVER fake_fdw_server OPTIONS (encoding 'utf-8', compression 'true', table_name 'foreign_table'); -ERROR: cannot create a tenant table from a foreign table +ERROR: cannot create a foreign table in a distributed schema -- verify that we don't allow creating a foreign table in a tenant schema CREATE TEMPORARY TABLE tenant_4.temp_table (a int, b text); ERROR: cannot create temporary relation in non-temporary schema @@ -275,13 +275,13 @@ CREATE TABLE tenant_5.tbl_1(a int, b text); CREATE TABLE tenant_5.partitioned_table(a int, b text) PARTITION BY RANGE (a); -- verify that we don't allow creating a partition table that is child of a partitioned table in a different tenant schema CREATE TABLE tenant_4.partitioned_table_child_2 PARTITION OF tenant_5.partitioned_table FOR VALUES FROM (1) TO (2); -ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas +ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas -- verify that we don't allow creating a local partition table that is child of a tenant partitioned table CREATE TABLE regular_schema.local_child_table PARTITION OF tenant_5.partitioned_table FOR VALUES FROM (1) TO (2); -ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas +ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas SET citus.use_citus_managed_tables TO ON; CREATE TABLE regular_schema.local_child_table PARTITION OF tenant_5.partitioned_table FOR VALUES FROM (1) TO (2); -ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas +ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas RESET citus.use_citus_managed_tables; CREATE TABLE regular_schema.local_partitioned_table(a int, b text) PARTITION BY RANGE (a); CREATE TABLE regular_schema.citus_local_partitioned_table(a int, b text) PARTITION BY RANGE (a); @@ -300,11 +300,11 @@ SELECT create_distributed_table('regular_schema.dist_partitioned_table', 'a'); -- verify that we don't allow creating a partition table that is child of a non-tenant partitioned table CREATE TABLE tenant_4.partitioned_table_child_2 PARTITION OF regular_schema.local_partitioned_table FOR VALUES FROM (1) TO (2); -ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas +ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas CREATE TABLE tenant_4.partitioned_table_child_2 PARTITION OF regular_schema.citus_local_partitioned_table FOR VALUES FROM (1) TO (2); -ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas +ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas CREATE TABLE tenant_4.partitioned_table_child_2 PARTITION OF regular_schema.dist_partitioned_table FOR VALUES FROM (1) TO (2); -ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas +ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas CREATE TABLE tenant_4.parent_attach_test(a int, b text) PARTITION BY RANGE (a); CREATE TABLE tenant_4.child_attach_test(a int, b text); CREATE TABLE tenant_5.parent_attach_test(a int, b text) PARTITION BY RANGE (a); @@ -341,21 +341,21 @@ SELECT create_distributed_table('regular_schema.child_attach_test_dist', 'a'); -- verify that we don't allow attaching a tenant table into a tenant partitioned table, if they are not in the same schema ALTER TABLE tenant_4.parent_attach_test ATTACH PARTITION tenant_5.child_attach_test FOR VALUES FROM (1) TO (2); -ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas +ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas -- verify that we don't allow attaching a non-tenant table into a tenant partitioned table ALTER TABLE tenant_4.parent_attach_test ATTACH PARTITION regular_schema.child_attach_test_local FOR VALUES FROM (1) TO (2); -ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas +ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas ALTER TABLE tenant_4.parent_attach_test ATTACH PARTITION regular_schema.child_attach_test_citus_local FOR VALUES FROM (1) TO (2); -ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas +ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas ALTER TABLE tenant_4.parent_attach_test ATTACH PARTITION regular_schema.child_attach_test_dist FOR VALUES FROM (1) TO (2); -ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas +ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas -- verify that we don't allow attaching a tenant table into a non-tenant partitioned table ALTER TABLE regular_schema.parent_attach_test_local ATTACH PARTITION tenant_4.child_attach_test FOR VALUES FROM (1) TO (2); -ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas +ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas ALTER TABLE regular_schema.parent_attach_test_citus_local ATTACH PARTITION tenant_4.child_attach_test FOR VALUES FROM (1) TO (2); -ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas +ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas ALTER TABLE regular_schema.parent_attach_test_dist ATTACH PARTITION tenant_4.child_attach_test FOR VALUES FROM (1) TO (2); -ERROR: partitioning with tenant tables is not supported when the parent and the child are in different schemas +ERROR: partitioning within a distributed schema is not supported when the parent and the child are in different schemas ALTER TABLE tenant_4.parent_attach_test ATTACH PARTITION tenant_4.child_attach_test FOR VALUES FROM (1) TO (2); -- verify that we don't allow multi-level partitioning on tenant tables CREATE TABLE tenant_4.multi_level_test(a int, b text) PARTITION BY RANGE (a); @@ -1330,7 +1330,7 @@ DROP ROLE test_non_super_user; \c - - - :worker_1_port -- test creating a tenant table from workers CREATE TABLE tenant_3.tbl_1(a int, b text); -ERROR: cannot create a tenant table from a worker node +ERROR: cannot create tables in a distributed schema from a worker node HINT: Connect to the coordinator node and try again. -- test creating a tenant schema from workers SET citus.enable_schema_based_sharding TO ON; diff --git a/src/test/regress/expected/upgrade_list_citus_objects.out b/src/test/regress/expected/upgrade_list_citus_objects.out index 1d44bf5a3..6e30d4ac7 100644 --- a/src/test/regress/expected/upgrade_list_citus_objects.out +++ b/src/test/regress/expected/upgrade_list_citus_objects.out @@ -114,6 +114,8 @@ ORDER BY 1; function citus_remote_connection_stats() function citus_remove_node(text,integer) function citus_run_local_command(text) + function citus_schema_distribute(regnamespace) + function citus_schema_undistribute(regnamespace) function citus_server_id() function citus_set_coordinator_host(text,integer,noderole,name) function citus_set_default_rebalance_strategy(text) @@ -334,5 +336,5 @@ ORDER BY 1; view citus_stat_tenants_local view pg_dist_shard_placement view time_partitions -(326 rows) +(328 rows) diff --git a/src/test/regress/isolation_schedule b/src/test/regress/isolation_schedule index fed454ac3..1484c712f 100644 --- a/src/test/regress/isolation_schedule +++ b/src/test/regress/isolation_schedule @@ -77,6 +77,7 @@ test: isolation_global_pid test: isolation_citus_locks test: isolation_reference_table test: isolation_schema_based_sharding +test: isolation_citus_schema_distribute_undistribute # Rebalancer test: isolation_blocking_move_single_shard_commands diff --git a/src/test/regress/multi_1_schedule b/src/test/regress/multi_1_schedule index b9a1db0e2..67473e471 100644 --- a/src/test/regress/multi_1_schedule +++ b/src/test/regress/multi_1_schedule @@ -36,6 +36,7 @@ test: create_single_shard_table # don't parallelize single_shard_table_udfs to make sure colocation ids are sequential test: single_shard_table_udfs test: schema_based_sharding +test: citus_schema_distribute_undistribute test: multi_test_catalog_views test: multi_table_ddl diff --git a/src/test/regress/spec/isolation_citus_schema_distribute_undistribute.spec b/src/test/regress/spec/isolation_citus_schema_distribute_undistribute.spec new file mode 100644 index 000000000..24699c7cf --- /dev/null +++ b/src/test/regress/spec/isolation_citus_schema_distribute_undistribute.spec @@ -0,0 +1,171 @@ +setup +{ + SELECT citus_set_coordinator_host('localhost', 57636); + + CREATE SCHEMA tenant1; + CREATE TABLE tenant1.table1(id int PRIMARY KEY, name text, col bigint); + INSERT INTO tenant1.table1 SELECT i, 'asd', i*1000 FROM generate_series(11, 20) i; + + CREATE TABLE tenant1.table2(id int PRIMARY KEY, name text, col bigint); + + CREATE TABLE public.ref(id int PRIMARY KEY); + SELECT create_reference_table('public.ref'); + + CREATE TABLE tenant1.table3(id int PRIMARY KEY, name text, col1 int, col int REFERENCES public.ref(id)); + SELECT citus_add_local_table_to_metadata('tenant1.table3'); +} + +teardown +{ + DROP TABLE public.ref CASCADE; + DROP SCHEMA IF EXISTS tenant1, tenant2 CASCADE; + SELECT citus_remove_node('localhost', 57636); +} + +session "s1" + +step "s1-begin" +{ + BEGIN; +} + +step "s1-schema-distribute" +{ + SELECT citus_schema_distribute('tenant1'); +} + +step "s1-schema-undistribute" +{ + SELECT citus_schema_undistribute('tenant1'); +} + +step "s1-verify-distributed-schema" +{ + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; +} + +step "s1-commit" +{ + COMMIT; +} + +session "s2" + +step "s2-drop-schema" +{ + DROP SCHEMA tenant1 CASCADE; +} + +step "s2-rename-schema" +{ + ALTER SCHEMA tenant1 RENAME TO tenant2; +} + +step "s2-add-table" +{ + CREATE TABLE tenant1.table4(id int PRIMARY KEY, name text, col bigint); +} + +step "s2-drop-table" +{ + DROP TABLE tenant1.table3; +} + +step "s2-alter-col-type" +{ + ALTER TABLE tenant1.table3 ALTER COLUMN col1 TYPE text; +} + +step "s2-add-foreign-key" +{ + ALTER TABLE tenant1.table3 ADD CONSTRAINT table3_fk1 FOREIGN KEY (id) REFERENCES tenant1.table2 (id); +} + +step "s2-drop-foreign-key" +{ + ALTER TABLE tenant1.table3 DROP CONSTRAINT table3_col_fkey; +} + +step "s2-create-unique-index" +{ + CREATE UNIQUE INDEX idx_2 ON tenant1.table3 (col); +} + +step "s2-create-unique-index-concurrently" +{ + CREATE UNIQUE INDEX CONCURRENTLY idx_3 ON tenant1.table3 (col); +} + +step "s2-reindex-unique-concurrently" +{ + REINDEX INDEX CONCURRENTLY tenant1.idx_2; +} + +step "s2-insert" +{ + // we insert into public.ref table as well to prevent fkey violation + INSERT INTO public.ref SELECT i FROM generate_series(11, 20) i; + INSERT INTO tenant1.table3 SELECT i, 'asd', i*1000 FROM generate_series(11, 20) i; +} + +step "s2-delete" +{ + DELETE FROM tenant1.table3; +} + +step "s2-update" +{ + UPDATE tenant1.table3 SET col = 11 WHERE col = 11; +} + +// DROP SCHEMA +permutation "s1-begin" "s1-schema-distribute" "s2-drop-schema" "s1-commit" "s1-verify-distributed-schema" +permutation "s1-schema-distribute" "s1-begin" "s1-schema-undistribute" "s2-drop-schema" "s1-commit" "s1-verify-distributed-schema" + +// RENAME SCHEMA +permutation "s1-begin" "s1-schema-distribute" "s2-rename-schema" "s1-commit" "s1-verify-distributed-schema" +permutation "s1-schema-distribute" "s1-begin" "s1-schema-undistribute" "s2-rename-schema" "s1-commit" "s1-verify-distributed-schema" + +// CREATE TABLE +permutation "s1-begin" "s1-schema-distribute" "s2-add-table" "s1-commit" "s1-verify-distributed-schema" +permutation "s1-schema-distribute" "s1-begin" "s1-schema-undistribute" "s2-add-table" "s1-commit" "s1-verify-distributed-schema" + +// DROP TABLE +permutation "s1-begin" "s1-schema-distribute" "s2-drop-table" "s1-commit" "s1-verify-distributed-schema" +permutation "s1-schema-distribute" "s1-begin" "s1-schema-undistribute" "s2-drop-table" "s1-commit" "s1-verify-distributed-schema" + +// ALTER TABLE ALTER COLUMN TYPE +permutation "s1-begin" "s1-schema-distribute" "s2-alter-col-type" "s1-commit" "s1-verify-distributed-schema" +permutation "s1-schema-distribute" "s1-begin" "s1-schema-undistribute" "s2-alter-col-type" "s1-commit" "s1-verify-distributed-schema" + +// ADD FOREIGN KEY +permutation "s1-begin" "s1-schema-distribute" "s2-add-foreign-key" "s1-commit" "s1-verify-distributed-schema" +permutation "s1-schema-distribute" "s1-begin" "s1-schema-undistribute" "s2-add-foreign-key" "s1-commit" "s1-verify-distributed-schema" + +// DROP FOREIGN KEY +permutation "s1-begin" "s1-schema-distribute" "s2-drop-foreign-key" "s1-commit" "s1-verify-distributed-schema" +permutation "s1-schema-distribute" "s1-begin" "s1-schema-undistribute" "s2-drop-foreign-key" "s1-commit" "s1-verify-distributed-schema" + +// CREATE UNIQUE INDEX +permutation "s1-begin" "s1-schema-distribute" "s2-create-unique-index" "s1-commit" "s1-verify-distributed-schema" +permutation "s1-schema-distribute" "s1-begin" "s1-schema-undistribute" "s2-create-unique-index" "s1-commit" "s1-verify-distributed-schema" + +// CREATE UNIQUE INDEX CONCURRENTLY +permutation "s1-begin" "s1-schema-distribute" "s2-create-unique-index-concurrently" "s1-commit" "s1-verify-distributed-schema" +permutation "s1-schema-distribute" "s1-begin" "s1-schema-undistribute" "s2-create-unique-index-concurrently" "s1-commit" "s1-verify-distributed-schema" + +// REINDEX CONCURRENTLY +permutation "s2-create-unique-index" "s1-begin" "s1-schema-distribute" "s2-reindex-unique-concurrently" "s1-commit" "s1-verify-distributed-schema" +permutation "s2-create-unique-index" "s1-schema-distribute" "s1-begin" "s1-schema-undistribute" "s2-reindex-unique-concurrently" "s1-commit" "s1-verify-distributed-schema" + +// INSERT +permutation "s1-begin" "s1-schema-distribute" "s2-insert" "s1-commit" "s1-verify-distributed-schema" +permutation "s1-schema-distribute" "s1-begin" "s1-schema-undistribute" "s2-insert" "s1-commit" "s1-verify-distributed-schema" + +// UPDATE +permutation "s2-insert" "s1-begin" "s1-schema-distribute" "s2-update" "s1-commit" "s1-verify-distributed-schema" +permutation "s2-insert" "s1-schema-distribute" "s1-begin" "s1-schema-undistribute" "s2-update" "s1-commit" "s1-verify-distributed-schema" + +// DELETE +permutation "s2-insert" "s1-begin" "s1-schema-distribute" "s2-delete" "s1-commit" "s1-verify-distributed-schema" +permutation "s2-insert" "s1-schema-distribute" "s1-begin" "s1-schema-undistribute" "s2-delete" "s1-commit" "s1-verify-distributed-schema" diff --git a/src/test/regress/sql/citus_schema_distribute_undistribute.sql b/src/test/regress/sql/citus_schema_distribute_undistribute.sql new file mode 100644 index 000000000..f6b5ee32b --- /dev/null +++ b/src/test/regress/sql/citus_schema_distribute_undistribute.sql @@ -0,0 +1,437 @@ +SET citus.next_shard_id TO 1730000; +SET citus.shard_replication_factor TO 1; +SET client_min_messages TO WARNING; +SET citus.enable_schema_based_sharding TO off; + +SELECT 1 FROM citus_add_node('localhost', :master_port, groupid => 0); + +CREATE USER tenantuser superuser; +SET role tenantuser; + +-- check invalid input +SELECT citus_schema_distribute(1); +SELECT citus_schema_undistribute(1); + +-- noop +SELECT citus_schema_distribute(null); +SELECT citus_schema_undistribute(null); + +-- public and some others cannot be distributed as a tenant schema, but check what happens +-- if we try to call citus_schema_undistribute() for such a schema. +SELECT citus_schema_undistribute('public'); + +-- create non-tenant schema +CREATE SCHEMA citus_schema_distribute_undistribute; + +-- create tenant schema +CREATE SCHEMA tenant1; + +CREATE TABLE tenant1.table1(id int PRIMARY KEY, name text); +INSERT INTO tenant1.table1 SELECT i, 'asd'::text FROM generate_series(1,20) i; + +CREATE TABLE tenant1.table2(id int REFERENCES tenant1.table1(id), num bigint UNIQUE); +INSERT INTO tenant1.table2 SELECT i, i FROM generate_series(1,20) i; + +CREATE TABLE citus_schema_distribute_undistribute.ref(id int PRIMARY KEY); +SELECT create_reference_table('citus_schema_distribute_undistribute.ref'); +INSERT INTO citus_schema_distribute_undistribute.ref SELECT i FROM generate_series(1,100) i; + +-- autoconverted to Citus local table due to foreign key to reference table +CREATE TABLE tenant1.table3(id int REFERENCES citus_schema_distribute_undistribute.ref(id)); +INSERT INTO tenant1.table3 SELECT i FROM generate_series(1,100) i; + +-- Citus local table with autoconverted=false +CREATE TABLE tenant1.table3x(id int PRIMARY KEY REFERENCES citus_schema_distribute_undistribute.ref(id)); +SELECT citus_add_local_table_to_metadata('tenant1.table3x'); +INSERT INTO tenant1.table3x SELECT i FROM generate_series(1,100) i; + +-- foreign key to another local table in the same schema +CREATE TABLE tenant1.table3y(id int PRIMARY KEY REFERENCES tenant1.table3x(id)); +SELECT citus_add_local_table_to_metadata('tenant1.table3y'); +INSERT INTO tenant1.table3y SELECT i FROM generate_series(1,100) i; + +-- table with composite type +CREATE TYPE tenant1.catname AS ENUM ('baby', 'teen', 'mid'); +CREATE TYPE tenant1.agecat AS (below_age int, name tenant1.catname); +CREATE TABLE tenant1.table4(id int, age tenant1.agecat); + +-- create autoconverted partitioned table +CREATE TABLE tenant1.partitioned_table(id int REFERENCES citus_schema_distribute_undistribute.ref(id)) PARTITION BY RANGE(id); +CREATE TABLE tenant1.partition1 PARTITION OF tenant1.partitioned_table FOR VALUES FROM (1) TO (11); +CREATE TABLE tenant1.partition2 PARTITION OF tenant1.partitioned_table FOR VALUES FROM (11) TO (21); +INSERT INTO tenant1.partitioned_table SELECT i FROM generate_series(1,20) i; + +-- create view +CREATE VIEW tenant1.view1 AS SELECT * FROM tenant1.table1 JOIN tenant1.table2 USING(id); + +-- create view in regular schema +CREATE VIEW citus_schema_distribute_undistribute.view2 AS SELECT * FROM tenant1.view1; + +-- create materialized view +CREATE MATERIALIZED VIEW tenant1.view2 AS SELECT * FROM tenant1.table1; + +-- create collation +CREATE COLLATION citus_schema_distribute_undistribute.german_phonebook (provider = icu, locale = 'de-u-co-phonebk'); + +-- create type +CREATE TYPE citus_schema_distribute_undistribute.pair_type AS (a int, b int); + +-- Create function +CREATE FUNCTION citus_schema_distribute_undistribute.one_as_result() RETURNS INT LANGUAGE SQL AS +$$ + SELECT 1; +$$; + +-- create text search dictionary +CREATE TEXT SEARCH DICTIONARY citus_schema_distribute_undistribute.my_german_dict ( + template = snowball, + language = german, + stopwords = german +); + +-- create text search config +CREATE TEXT SEARCH CONFIGURATION citus_schema_distribute_undistribute.my_ts_config ( parser = default ); +ALTER TEXT SEARCH CONFIGURATION citus_schema_distribute_undistribute.my_ts_config ALTER MAPPING FOR asciiword WITH citus_schema_distribute_undistribute.my_german_dict; + +-- create sequence +CREATE SEQUENCE citus_schema_distribute_undistribute.seq; + +-- create complex table +CREATE TABLE tenant1.complextable (id int PRIMARY KEY default nextval('citus_schema_distribute_undistribute.seq'), col int default (citus_schema_distribute_undistribute.one_as_result()), myserial serial, phone text COLLATE citus_schema_distribute_undistribute.german_phonebook, initials citus_schema_distribute_undistribute.pair_type); +CREATE SEQUENCE tenant1.seq_owned OWNED BY tenant1.complextable.id; + +-- not allowed from workers +SELECT run_command_on_workers($$SELECT citus_schema_distribute('tenant1');$$); +SELECT run_command_on_workers($$SELECT citus_schema_undistribute('tenant1');$$); + +-- inherited table not allowed +CREATE TABLE citus_schema_distribute_undistribute.cities ( + name text, + population real, + elevation int +); +CREATE TABLE citus_schema_distribute_undistribute.capitals ( + state char(2) UNIQUE NOT NULL +) INHERITS (citus_schema_distribute_undistribute.cities); + +-- temporarily move "cities" into tenant1 (not a tenant schema yet) to test citus_schema_distribute() with a table that is inherited +ALTER TABLE citus_schema_distribute_undistribute.cities SET SCHEMA tenant1; +SELECT citus_schema_distribute('tenant1'); +ALTER TABLE tenant1.cities SET SCHEMA citus_schema_distribute_undistribute; + +-- temporarily move "capitals" into tenant1 (not a tenant schema yet) to test citus_schema_distribute() with a table that inherits +ALTER TABLE citus_schema_distribute_undistribute.capitals SET SCHEMA tenant1; +SELECT citus_schema_distribute('tenant1'); +ALTER TABLE tenant1.capitals SET SCHEMA citus_schema_distribute_undistribute; + +SELECT citus_schema_distribute('tenant1'); +SELECT citus_schema_undistribute('tenant1'); + +CREATE TABLE citus_schema_distribute_undistribute.illegal_partitioned_table(id int) PARTITION BY RANGE(id); +CREATE TABLE citus_schema_distribute_undistribute.illegal_partition1 PARTITION OF citus_schema_distribute_undistribute.illegal_partitioned_table FOR VALUES FROM (1) TO (11); + +-- temporarily move "illegal_partitioned_table" into tenant1 (not a tenant schema yet) to test citus_schema_distribute() with a partition table whose parent is created in another schema +ALTER TABLE citus_schema_distribute_undistribute.illegal_partitioned_table SET SCHEMA tenant1; +SELECT citus_schema_distribute('tenant1'); +ALTER TABLE tenant1.illegal_partitioned_table SET SCHEMA citus_schema_distribute_undistribute; + +-- temporarily move "illegal_partition1" into tenant1 (not a tenant schema yet) to test citus_schema_distribute() with a parent table whose partition is created in another schema +ALTER TABLE citus_schema_distribute_undistribute.illegal_partition1 SET SCHEMA tenant1; +SELECT citus_schema_distribute('tenant1'); +ALTER TABLE tenant1.illegal_partition1 SET SCHEMA citus_schema_distribute_undistribute; + +SELECT citus_schema_distribute('tenant1'); +SELECT citus_schema_undistribute('tenant1'); + +-- foreign key to a local table in another schema is not allowed +CREATE TABLE citus_schema_distribute_undistribute.tbl1(id int PRIMARY KEY); +CREATE TABLE tenant1.table3z(id int PRIMARY KEY REFERENCES citus_schema_distribute_undistribute.tbl1(id)); +SELECT citus_schema_distribute('tenant1'); +SELECT create_reference_table('citus_schema_distribute_undistribute.tbl1'); +SELECT citus_schema_distribute('tenant1'); +SELECT citus_schema_undistribute('tenant1'); + +-- foreign key to a distributed table in another schema is not allowed +CREATE TABLE citus_schema_distribute_undistribute.tbl2(id int PRIMARY KEY); +SELECT create_distributed_table('citus_schema_distribute_undistribute.tbl2','id'); +CREATE TABLE tenant1.table3w(id int PRIMARY KEY REFERENCES citus_schema_distribute_undistribute.tbl2(id)); +SELECT citus_schema_distribute('tenant1'); +DROP TABLE citus_schema_distribute_undistribute.tbl2 CASCADE; +SELECT citus_schema_distribute('tenant1'); +SELECT citus_schema_undistribute('tenant1'); + +-- foreign key from a local table in another schema is not allowed +CREATE TABLE tenant1.table3q(id int PRIMARY KEY); +CREATE TABLE citus_schema_distribute_undistribute.tbl3(id int PRIMARY KEY REFERENCES tenant1.table3q(id)); +SELECT citus_schema_distribute('tenant1'); +DROP TABLE citus_schema_distribute_undistribute.tbl3 CASCADE; +SELECT citus_schema_distribute('tenant1'); +SELECT citus_schema_undistribute('tenant1'); + +-- foreign key from a reference table in another schema is not allowed +CREATE TABLE tenant1.table3t(id int PRIMARY KEY); +CREATE TABLE citus_schema_distribute_undistribute.tbl4(id int PRIMARY KEY REFERENCES tenant1.table3t(id)); +SELECT create_reference_table('citus_schema_distribute_undistribute.tbl4'); +SELECT citus_schema_distribute('tenant1'); +DROP TABLE citus_schema_distribute_undistribute.tbl4 CASCADE; +SELECT citus_schema_distribute('tenant1'); +SELECT citus_schema_undistribute('tenant1'); + +-- only allowed for schema owner or superuser +CREATE USER dummyregular; +SET role dummyregular; +SELECT citus_schema_distribute('tenant1'); +SELECT citus_schema_undistribute('tenant1'); + +-- assign all tables to dummyregular except table5 +SET role tenantuser; +SELECT result FROM run_command_on_all_nodes($$ REASSIGN OWNED BY tenantuser TO dummyregular; $$); +CREATE TABLE tenant1.table5(id int); + +-- table owner check fails the distribution +SET role dummyregular; +SELECT citus_schema_distribute('tenant1'); + +-- alter table owner, then redistribute +SET role tenantuser; +ALTER TABLE tenant1.table5 OWNER TO dummyregular; +SET role dummyregular; +SELECT citus_schema_distribute('tenant1'); + +-- show the schema is a tenant schema now +SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset +-- below query verifies the same colocationid in pg_dist_tenant_schema, pg_dist_colocation and all entries in pg_dist_partition at the same time +SELECT '$$' || + ' SELECT colocationid = ' || :tenant1_colocid || + ' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' || + ' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''tenant1%'')' || + '$$' +AS verify_tenant_query \gset +SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*) AS total FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1%' $$); +SELECT result FROM run_command_on_all_nodes(:verify_tenant_query); + +SELECT citus_schema_undistribute('tenant1'); + +-- show the schema is a regular schema +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$); +-- below query verifies the tenant colocationid is removed from both pg_dist_colocation and all entries in pg_dist_partition at the same time +SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$); +SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$); + +RESET role; +SELECT result FROM run_command_on_all_nodes($$ REASSIGN OWNED BY dummyregular TO tenantuser; $$); +DROP USER dummyregular; + +CREATE USER dummysuper superuser; +SET role dummysuper; +SELECT citus_schema_distribute('tenant1'); + +-- show the schema is a tenant schema now +SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset +SELECT '$$' || + ' SELECT colocationid = ' || :tenant1_colocid || + ' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' || + ' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''tenant1%'')' || + '$$' +AS verify_tenant_query \gset +SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*) AS total FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1%' $$); +SELECT result FROM run_command_on_all_nodes(:verify_tenant_query); + +SELECT citus_schema_undistribute('tenant1'); + +-- show the schema is a regular schema +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$); +SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$); +SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$); + +RESET role; +DROP USER dummysuper; + +-- foreign table +CREATE TABLE tenant1.foreign_table_test (id integer NOT NULL, data text, a bigserial); +INSERT INTO tenant1.foreign_table_test SELECT i FROM generate_series(1,100) i; +CREATE EXTENSION postgres_fdw; +CREATE SERVER foreign_server + FOREIGN DATA WRAPPER postgres_fdw + OPTIONS (host 'localhost', port :'master_port', dbname 'regression'); +CREATE USER MAPPING FOR CURRENT_USER + SERVER foreign_server + OPTIONS (user 'postgres'); +CREATE FOREIGN TABLE tenant1.foreign_table ( + id integer NOT NULL, + data text, + a bigserial +) + SERVER foreign_server + OPTIONS (schema_name 'tenant1', table_name 'foreign_table_test'); + +-- foreign table not allowed +SELECT citus_schema_distribute('tenant1'); +ALTER FOREIGN TABLE tenant1.foreign_table SET SCHEMA citus_schema_distribute_undistribute; +SELECT citus_schema_distribute('tenant1'); +SELECT citus_schema_undistribute('tenant1'); + +-- already have distributed table error +CREATE TABLE tenant1.dist(id int); +SELECT create_distributed_table('tenant1.dist', 'id'); +SELECT citus_schema_distribute('tenant1'); +SELECT undistribute_table('tenant1.dist'); +SELECT citus_schema_distribute('tenant1'); +SELECT citus_schema_undistribute('tenant1'); + +CREATE TABLE tenant1.ref2(id int); +SELECT create_reference_table('tenant1.ref2'); +SELECT citus_schema_distribute('tenant1'); +SELECT undistribute_table('tenant1.ref2'); +SELECT citus_schema_distribute('tenant1'); +SELECT citus_schema_undistribute('tenant1'); + +BEGIN; +SELECT citus_schema_distribute('tenant1'); +ROLLBACK; + +-- show the schema is a regular schema +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$); +SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$); +SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$); + +-- errors not a tenant schema +SELECT citus_schema_undistribute('tenant1'); + +-- make it a tenant schema +BEGIN; +SELECT citus_schema_distribute('tenant1'); +COMMIT; + +-- show the schema is a tenant schema now +SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset +SELECT '$$' || + ' SELECT colocationid = ' || :tenant1_colocid || + ' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' || + ' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''tenant1%'')' || + '$$' +AS verify_tenant_query \gset +SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*) AS total FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1%' $$); +SELECT result FROM run_command_on_all_nodes(:verify_tenant_query); + +-- already a tenant schema notice +SET client_min_messages TO NOTICE; +SELECT citus_schema_distribute('tenant1'); +SET client_min_messages TO WARNING; + +-- convert back to a regular schema +SELECT citus_schema_undistribute('tenant1'); + +-- show the schema is a regular schema now +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$); +SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$); +SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$); + +-- tables still have valid data +SELECT COUNT(*) FROM tenant1.partitioned_table; +SELECT COUNT(*) FROM citus_schema_distribute_undistribute.foreign_table; +SELECT COUNT(*) FROM tenant1.table3; +TRUNCATE citus_schema_distribute_undistribute.ref CASCADE; +SELECT COUNT(*) FROM tenant1.table3; + +-- disallowed namespaces +SELECT citus_schema_distribute('public'); +SELECT citus_schema_distribute('pg_catalog'); +SELECT citus_schema_distribute('pg_toast'); +CREATE TEMP TABLE xx(id int); -- create a temp table in case we do not have any pg_temp_xx schema yet +SELECT nspname AS temp_schema_name FROM pg_namespace WHERE nspname LIKE 'pg_temp%' LIMIT 1 \gset +SELECT nspname AS temp_toast_schema_name FROM pg_namespace WHERE nspname LIKE 'pg_toast_temp%' LIMIT 1 \gset +SELECT citus_schema_distribute(:'temp_schema_name'); +SELECT citus_schema_distribute(:'temp_toast_schema_name'); +SELECT citus_schema_distribute('citus'); +CREATE SCHEMA extensionschema; +CREATE EXTENSION citext SCHEMA extensionschema; +SELECT citus_schema_distribute('extensionschema'); +DROP SCHEMA extensionschema CASCADE; +ALTER EXTENSION citus ADD TABLE tenant1.table1; +SELECT citus_schema_distribute('tenant1'); +ALTER EXTENSION citus DROP TABLE tenant1.table1; + +-- weird schema and table names +CREATE SCHEMA "CiTuS.TeeN"; +CREATE TABLE "CiTuS.TeeN"."TeeNTabLE.1!?!"(id int PRIMARY KEY, name text); +INSERT INTO "CiTuS.TeeN"."TeeNTabLE.1!?!" SELECT i, 'asd'::text FROM generate_series(1,20) i; +SELECT citus_schema_distribute('"CiTuS.TeeN"'); + +-- show the schema is a tenant schema now +SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset +SELECT '$$' || + ' SELECT colocationid = ' || :tenant1_colocid || + ' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' || + ' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''"CiTuS.TeeN"%'')' || + '$$' +AS verify_tenant_query \gset +SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*) AS total FROM pg_dist_partition WHERE logicalrelid::text LIKE '"CiTuS.TeeN"%' $$); +SELECT result FROM run_command_on_all_nodes(:verify_tenant_query); + +SELECT citus_schema_undistribute('"CiTuS.TeeN"'); + +-- show the schema is a regular schema +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$); +SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$); +SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$); + +-- try setting the schema again after adding a distributed table into the schema. It should complain about distributed table. +CREATE TABLE tenant1.new_dist(id int); +SELECT create_distributed_table('tenant1.new_dist', 'id'); +SELECT citus_schema_distribute('tenant1'); +SELECT undistribute_table('tenant1.new_dist'); +SELECT citus_schema_distribute('tenant1'); +SELECT citus_schema_undistribute('tenant1'); + +-- try setting the schema again after adding a single shard table into the schema. It should complain about distributed table. +CREATE TABLE tenant1.single_shard_t(id int); +SELECT create_distributed_table('tenant1.single_shard_t', NULL); +SELECT citus_schema_distribute('tenant1'); +SELECT undistribute_table('tenant1.single_shard_t'); + +-- try setting the schema again. It should succeed now. +SELECT citus_schema_distribute('tenant1'); + +-- show the schema is a tenant schema now +SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset +SELECT '$$' || + ' SELECT colocationid = ' || :tenant1_colocid || + ' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' || + ' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''tenant1%'')' || + '$$' +AS verify_tenant_query \gset +SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*) AS total FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1%' $$); +SELECT result FROM run_command_on_all_nodes(:verify_tenant_query); + +SELECT citus_schema_undistribute('tenant1'); + +-- show the schema is a regular schema now +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$); +SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$); +SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$); + +-- create an empty tenant schema to verify colocation id is removed successfully after we undistribute it +CREATE SCHEMA empty_tenant; +SELECT citus_schema_distribute('empty_tenant'); + +-- show the schema is a tenant schema now +SELECT colocationid AS empty_tenant_colocid FROM pg_dist_tenant_schema schemaid \gset +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace FROM pg_dist_colocation JOIN pg_dist_tenant_schema USING(colocationid) $$); + +SELECT citus_schema_undistribute('empty_tenant'); + +-- show the schema is a regular schema now +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$); +SELECT '$$' || 'SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = ' || :empty_tenant_colocid || '$$' +AS verify_empty_tenant_query \gset +SELECT result FROM run_command_on_all_nodes(:verify_empty_tenant_query); + +-- cleanup +DROP SCHEMA "CiTuS.TeeN" CASCADE; +DROP SCHEMA tenant1 CASCADE; +DROP SCHEMA empty_tenant CASCADE; +DROP EXTENSION postgres_fdw CASCADE; +DROP SCHEMA citus_schema_distribute_undistribute CASCADE; +DROP USER tenantuser; +SELECT citus_remove_node('localhost', :master_port); From b96d3171a2fca75212a4a8044aed20c6f032e4c2 Mon Sep 17 00:00:00 2001 From: Jelte Fennema Date: Mon, 12 Jun 2023 18:21:33 +0200 Subject: [PATCH 088/118] Small fix to cherry-pick instructions (#6997) It wasn't creating the branch --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 010842a5f..66d026b8b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -223,7 +223,7 @@ Any other SQL you can put directly in the main sql file, e.g. 1. Check out the release branch that you want to backport to `git checkout release-11.3` 2. Make sure you have the latest changes `git pull` -3. Create a new release branch with a unique name `git checkout release-11.3-` +3. Create a new release branch with a unique name `git checkout -b release-11.3-` 4. Cherry-pick the commit that you want to backport `git cherry-pick -x ` (the `-x` is important) 5. Push the branch `git push` 6. Wait for tests to pass From 5acbd735ca85a12e94d8e334ddbcea211fe44d21 Mon Sep 17 00:00:00 2001 From: aykut-bozkurt <51649454+aykut-bozkurt@users.noreply.github.com> Date: Tue, 13 Jun 2023 11:43:48 +0300 Subject: [PATCH 089/118] Move 2 functions to correct files (#7000) Followup item from https://github.com/citusdata/citus/pull/6933#discussion_r1217896933 --- .../distributed/commands/alter_table.c | 37 +++++++++ .../commands/create_distributed_table.c | 79 ------------------- .../distributed/commands/foreign_constraint.c | 42 ++++++++++ 3 files changed, 79 insertions(+), 79 deletions(-) diff --git a/src/backend/distributed/commands/alter_table.c b/src/backend/distributed/commands/alter_table.c index 86dbe48e9..d574e997a 100644 --- a/src/backend/distributed/commands/alter_table.c +++ b/src/backend/distributed/commands/alter_table.c @@ -361,6 +361,43 @@ worker_change_sequence_dependency(PG_FUNCTION_ARGS) } +/* + * DropFKeysAndUndistributeTable drops all foreign keys that relation with + * relationId is involved then undistributes it. + * Note that as UndistributeTable changes relationId of relation, this + * function also returns new relationId of relation. + * Also note that callers are responsible for storing & recreating foreign + * keys to be dropped if needed. + */ +Oid +DropFKeysAndUndistributeTable(Oid relationId) +{ + DropFKeysRelationInvolvedWithTableType(relationId, INCLUDE_ALL_TABLE_TYPES); + + /* store them before calling UndistributeTable as it changes relationId */ + char *relationName = get_rel_name(relationId); + Oid schemaId = get_rel_namespace(relationId); + + /* suppress notices messages not to be too verbose */ + TableConversionParameters params = { + .relationId = relationId, + .cascadeViaForeignKeys = false, + .suppressNoticeMessages = true + }; + UndistributeTable(¶ms); + + Oid newRelationId = get_relname_relid(relationName, schemaId); + + /* + * We don't expect this to happen but to be on the safe side let's error + * out here. + */ + EnsureRelationExists(newRelationId); + + return newRelationId; +} + + /* * UndistributeTables undistributes given relations. It first collects all foreign keys * to recreate them after the undistribution. Then, drops the foreign keys and diff --git a/src/backend/distributed/commands/create_distributed_table.c b/src/backend/distributed/commands/create_distributed_table.c index a1ae2d9ea..9849da9f4 100644 --- a/src/backend/distributed/commands/create_distributed_table.c +++ b/src/backend/distributed/commands/create_distributed_table.c @@ -1571,85 +1571,6 @@ EnsureDistributedSequencesHaveOneType(Oid relationId, List *seqInfoList) } -/* - * GetFKeyCreationCommandsRelationInvolvedWithTableType returns a list of DDL - * commands to recreate the foreign keys that relation with relationId is involved - * with given table type. - */ -List * -GetFKeyCreationCommandsRelationInvolvedWithTableType(Oid relationId, int tableTypeFlag) -{ - int referencingFKeysFlag = INCLUDE_REFERENCING_CONSTRAINTS | - tableTypeFlag; - List *referencingFKeyCreationCommands = - GetForeignConstraintCommandsInternal(relationId, referencingFKeysFlag); - - /* already captured self referencing foreign keys, so use EXCLUDE_SELF_REFERENCES */ - int referencedFKeysFlag = INCLUDE_REFERENCED_CONSTRAINTS | - EXCLUDE_SELF_REFERENCES | - tableTypeFlag; - List *referencedFKeyCreationCommands = - GetForeignConstraintCommandsInternal(relationId, referencedFKeysFlag); - return list_concat(referencingFKeyCreationCommands, referencedFKeyCreationCommands); -} - - -/* - * DropFKeysAndUndistributeTable drops all foreign keys that relation with - * relationId is involved then undistributes it. - * Note that as UndistributeTable changes relationId of relation, this - * function also returns new relationId of relation. - * Also note that callers are responsible for storing & recreating foreign - * keys to be dropped if needed. - */ -Oid -DropFKeysAndUndistributeTable(Oid relationId) -{ - DropFKeysRelationInvolvedWithTableType(relationId, INCLUDE_ALL_TABLE_TYPES); - - /* store them before calling UndistributeTable as it changes relationId */ - char *relationName = get_rel_name(relationId); - Oid schemaId = get_rel_namespace(relationId); - - /* suppress notices messages not to be too verbose */ - TableConversionParameters params = { - .relationId = relationId, - .cascadeViaForeignKeys = false, - .suppressNoticeMessages = true - }; - UndistributeTable(¶ms); - - Oid newRelationId = get_relname_relid(relationName, schemaId); - - /* - * We don't expect this to happen but to be on the safe side let's error - * out here. - */ - EnsureRelationExists(newRelationId); - - return newRelationId; -} - - -/* - * DropFKeysRelationInvolvedWithTableType drops foreign keys that relation - * with relationId is involved with given table type. - */ -void -DropFKeysRelationInvolvedWithTableType(Oid relationId, int tableTypeFlag) -{ - int referencingFKeysFlag = INCLUDE_REFERENCING_CONSTRAINTS | - tableTypeFlag; - DropRelationForeignKeys(relationId, referencingFKeysFlag); - - /* already captured self referencing foreign keys, so use EXCLUDE_SELF_REFERENCES */ - int referencedFKeysFlag = INCLUDE_REFERENCED_CONSTRAINTS | - EXCLUDE_SELF_REFERENCES | - tableTypeFlag; - DropRelationForeignKeys(relationId, referencedFKeysFlag); -} - - /* * DecideDistTableReplicationModel function decides which replication model should be * used for a distributed table depending on given distribution configuration. diff --git a/src/backend/distributed/commands/foreign_constraint.c b/src/backend/distributed/commands/foreign_constraint.c index 09133f5a3..b48b6c54a 100644 --- a/src/backend/distributed/commands/foreign_constraint.c +++ b/src/backend/distributed/commands/foreign_constraint.c @@ -896,6 +896,48 @@ GetForeignConstraintCommandsInternal(Oid relationId, int flags) } +/* + * GetFKeyCreationCommandsRelationInvolvedWithTableType returns a list of DDL + * commands to recreate the foreign keys that relation with relationId is involved + * with given table type. + */ +List * +GetFKeyCreationCommandsRelationInvolvedWithTableType(Oid relationId, int tableTypeFlag) +{ + int referencingFKeysFlag = INCLUDE_REFERENCING_CONSTRAINTS | + tableTypeFlag; + List *referencingFKeyCreationCommands = + GetForeignConstraintCommandsInternal(relationId, referencingFKeysFlag); + + /* already captured self referencing foreign keys, so use EXCLUDE_SELF_REFERENCES */ + int referencedFKeysFlag = INCLUDE_REFERENCED_CONSTRAINTS | + EXCLUDE_SELF_REFERENCES | + tableTypeFlag; + List *referencedFKeyCreationCommands = + GetForeignConstraintCommandsInternal(relationId, referencedFKeysFlag); + return list_concat(referencingFKeyCreationCommands, referencedFKeyCreationCommands); +} + + +/* + * DropFKeysRelationInvolvedWithTableType drops foreign keys that relation + * with relationId is involved with given table type. + */ +void +DropFKeysRelationInvolvedWithTableType(Oid relationId, int tableTypeFlag) +{ + int referencingFKeysFlag = INCLUDE_REFERENCING_CONSTRAINTS | + tableTypeFlag; + DropRelationForeignKeys(relationId, referencingFKeysFlag); + + /* already captured self referencing foreign keys, so use EXCLUDE_SELF_REFERENCES */ + int referencedFKeysFlag = INCLUDE_REFERENCED_CONSTRAINTS | + EXCLUDE_SELF_REFERENCES | + tableTypeFlag; + DropRelationForeignKeys(relationId, referencedFKeysFlag); +} + + /* * HasForeignKeyWithLocalTable returns true if relation has foreign key * relationship with a local table. From e0ccd155abc93323cba49b49acf55788f7d84579 Mon Sep 17 00:00:00 2001 From: Gokhan Gulbiz Date: Tue, 13 Jun 2023 14:11:45 +0300 Subject: [PATCH 090/118] Make citus_stat_tenants work with schema-based tenants. (#6936) DESCRIPTION: Enabling citus_stat_tenants to support schema-based tenants. This pull request modifies the existing logic to enable tenant monitoring with schema-based tenants. The changes made are as follows: - If a query has a partitionKeyValue (which serves as a tenant key/identifier for distributed tables), Citus annotates the query with both the partitionKeyValue and colocationId. This allows for accurate tracking of the query. - If a query does not have a partitionKeyValue, but its colocationId belongs to a distributed schema, Citus annotates the query with only the colocationId. The tenant monitor can then easily look up the schema to determine if it's a distributed schema and make a decision on whether to track the query. --------- Co-authored-by: Jelte Fennema --- .../distributed/executor/citus_custom_scan.c | 6 - .../distributed/executor/local_executor.c | 6 +- .../distributed/sql/citus--11.3-1--12.0-1.sql | 3 + .../sql/downgrades/citus--12.0-1--11.3-1.sql | 14 ++ .../udfs/citus_stat_tenants_local/12.0-1.sql | 69 ++++++++++ .../udfs/citus_stat_tenants_local/latest.sql | 39 +++++- .../distributed/utils/citus_stat_tenants.c | 106 ++++++++++++--- .../utils/tenant_schema_metadata.c | 5 +- .../distributed/tenant_schema_metadata.h | 1 + src/test/regress/bin/normalize.sed | 2 +- .../adaptive_executor_repartition.out | 9 +- .../regress/expected/citus_stat_tenants.out | 128 ++++++++++++++++++ src/test/regress/expected/multi_extension.out | 3 +- .../expected/upgrade_list_citus_objects.out | 3 +- .../sql/adaptive_executor_repartition.sql | 1 + src/test/regress/sql/citus_stat_tenants.sql | 64 +++++++++ 16 files changed, 410 insertions(+), 49 deletions(-) create mode 100644 src/backend/distributed/sql/udfs/citus_stat_tenants_local/12.0-1.sql diff --git a/src/backend/distributed/executor/citus_custom_scan.c b/src/backend/distributed/executor/citus_custom_scan.c index be04f38f4..3dc1f5068 100644 --- a/src/backend/distributed/executor/citus_custom_scan.c +++ b/src/backend/distributed/executor/citus_custom_scan.c @@ -896,12 +896,6 @@ SetJobColocationId(Job *job) { uint32 jobColocationId = INVALID_COLOCATION_ID; - if (!job->partitionKeyValue) - { - /* if the Job has no shard key, nothing to do */ - return; - } - List *rangeTableList = ExtractRangeTableEntryList(job->jobQuery); ListCell *rangeTableCell = NULL; foreach(rangeTableCell, rangeTableList) diff --git a/src/backend/distributed/executor/local_executor.c b/src/backend/distributed/executor/local_executor.c index c9f4c8fb9..5661403b9 100644 --- a/src/backend/distributed/executor/local_executor.c +++ b/src/backend/distributed/executor/local_executor.c @@ -84,6 +84,7 @@ #include "distributed/commands/utility_hook.h" #include "distributed/citus_custom_scan.h" #include "distributed/citus_ruleutils.h" +#include "distributed/colocation_utils.h" #include "distributed/query_utils.h" #include "distributed/deparse_shard_query.h" #include "distributed/listutils.h" @@ -382,13 +383,12 @@ ExecuteLocalTaskListExtended(List *taskList, /* * SetColocationIdAndPartitionKeyValueForTasks sets colocationId and partitionKeyValue - * for the tasks in the taskList if workerJob has a colocationId and partitionKeyValue. + * for the tasks in the taskList. */ static void SetColocationIdAndPartitionKeyValueForTasks(List *taskList, Job *workerJob) { - if (workerJob->colocationId != 0 && - workerJob->partitionKeyValue != NULL) + if (workerJob->colocationId != INVALID_COLOCATION_ID) { Task *task = NULL; foreach_ptr(task, taskList) diff --git a/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql b/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql index 6125f0d10..705f68a88 100644 --- a/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql +++ b/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql @@ -27,6 +27,9 @@ GRANT SELECT ON pg_catalog.pg_dist_tenant_schema TO public; #include "udfs/citus_tables/12.0-1.sql" #include "udfs/citus_shards/12.0-1.sql" +-- udfs used to include schema-based tenants in tenant monitoring +#include "udfs/citus_stat_tenants_local/12.0-1.sql" + -- udfs to convert a regular/tenant schema to a tenant/regular schema #include "udfs/citus_schema_distribute/12.0-1.sql" #include "udfs/citus_schema_undistribute/12.0-1.sql" diff --git a/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql b/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql index ef668c97f..104c16d7a 100644 --- a/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql +++ b/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql @@ -47,3 +47,17 @@ DROP FUNCTION pg_catalog.citus_internal_unregister_tenant_schema_globally(Oid, t #include "../udfs/citus_shards/11.1-1.sql" DROP TABLE pg_catalog.pg_dist_tenant_schema; + +DROP VIEW pg_catalog.citus_stat_tenants_local; +DROP FUNCTION pg_catalog.citus_stat_tenants_local_internal( + BOOLEAN, + OUT INT, + OUT TEXT, + OUT INT, + OUT INT, + OUT INT, + OUT INT, + OUT DOUBLE PRECISION, + OUT DOUBLE PRECISION, + OUT BIGINT); +#include "../udfs/citus_stat_tenants_local/11.3-1.sql" diff --git a/src/backend/distributed/sql/udfs/citus_stat_tenants_local/12.0-1.sql b/src/backend/distributed/sql/udfs/citus_stat_tenants_local/12.0-1.sql new file mode 100644 index 000000000..9323a4530 --- /dev/null +++ b/src/backend/distributed/sql/udfs/citus_stat_tenants_local/12.0-1.sql @@ -0,0 +1,69 @@ +CREATE OR REPLACE FUNCTION pg_catalog.citus_stat_tenants_local_internal( + return_all_tenants BOOLEAN DEFAULT FALSE, + OUT colocation_id INT, + OUT tenant_attribute TEXT, + OUT read_count_in_this_period INT, + OUT read_count_in_last_period INT, + OUT query_count_in_this_period INT, + OUT query_count_in_last_period INT, + OUT cpu_usage_in_this_period DOUBLE PRECISION, + OUT cpu_usage_in_last_period DOUBLE PRECISION, + OUT score BIGINT) +RETURNS SETOF RECORD +LANGUAGE C +AS 'citus', $$citus_stat_tenants_local$$; + +CREATE OR REPLACE FUNCTION pg_catalog.citus_stat_tenants_local( + return_all_tenants BOOLEAN DEFAULT FALSE, + OUT colocation_id INT, + OUT tenant_attribute TEXT, + OUT read_count_in_this_period INT, + OUT read_count_in_last_period INT, + OUT query_count_in_this_period INT, + OUT query_count_in_last_period INT, + OUT cpu_usage_in_this_period DOUBLE PRECISION, + OUT cpu_usage_in_last_period DOUBLE PRECISION, + OUT score BIGINT) +RETURNS SETOF RECORD +LANGUAGE plpgsql +AS $function$ +BEGIN + RETURN QUERY + SELECT + L.colocation_id, + CASE WHEN L.tenant_attribute IS NULL THEN N.nspname ELSE L.tenant_attribute END COLLATE "default" as tenant_attribute, + L.read_count_in_this_period, + L.read_count_in_last_period, + L.query_count_in_this_period, + L.query_count_in_last_period, + L.cpu_usage_in_this_period, + L.cpu_usage_in_last_period, + L.score + FROM pg_catalog.citus_stat_tenants_local_internal(return_all_tenants) L + LEFT JOIN pg_dist_tenant_schema S ON L.tenant_attribute IS NULL AND L.colocation_id = S.colocationid + LEFT JOIN pg_namespace N ON N.oid = S.schemaid + ORDER BY L.score DESC; +END; +$function$; + +CREATE OR REPLACE VIEW pg_catalog.citus_stat_tenants_local AS +SELECT + colocation_id, + tenant_attribute, + read_count_in_this_period, + read_count_in_last_period, + query_count_in_this_period, + query_count_in_last_period, + cpu_usage_in_this_period, + cpu_usage_in_last_period +FROM pg_catalog.citus_stat_tenants_local() +ORDER BY score DESC; + +REVOKE ALL ON FUNCTION pg_catalog.citus_stat_tenants_local_internal(BOOLEAN) FROM PUBLIC; +GRANT EXECUTE ON FUNCTION pg_catalog.citus_stat_tenants_local_internal(BOOLEAN) TO pg_monitor; + +REVOKE ALL ON FUNCTION pg_catalog.citus_stat_tenants_local(BOOLEAN) FROM PUBLIC; +GRANT EXECUTE ON FUNCTION pg_catalog.citus_stat_tenants_local(BOOLEAN) TO pg_monitor; + +REVOKE ALL ON pg_catalog.citus_stat_tenants_local FROM PUBLIC; +GRANT SELECT ON pg_catalog.citus_stat_tenants_local TO pg_monitor; diff --git a/src/backend/distributed/sql/udfs/citus_stat_tenants_local/latest.sql b/src/backend/distributed/sql/udfs/citus_stat_tenants_local/latest.sql index c3383241c..9323a4530 100644 --- a/src/backend/distributed/sql/udfs/citus_stat_tenants_local/latest.sql +++ b/src/backend/distributed/sql/udfs/citus_stat_tenants_local/latest.sql @@ -1,4 +1,4 @@ -CREATE OR REPLACE FUNCTION pg_catalog.citus_stat_tenants_local( +CREATE OR REPLACE FUNCTION pg_catalog.citus_stat_tenants_local_internal( return_all_tenants BOOLEAN DEFAULT FALSE, OUT colocation_id INT, OUT tenant_attribute TEXT, @@ -13,8 +13,40 @@ RETURNS SETOF RECORD LANGUAGE C AS 'citus', $$citus_stat_tenants_local$$; +CREATE OR REPLACE FUNCTION pg_catalog.citus_stat_tenants_local( + return_all_tenants BOOLEAN DEFAULT FALSE, + OUT colocation_id INT, + OUT tenant_attribute TEXT, + OUT read_count_in_this_period INT, + OUT read_count_in_last_period INT, + OUT query_count_in_this_period INT, + OUT query_count_in_last_period INT, + OUT cpu_usage_in_this_period DOUBLE PRECISION, + OUT cpu_usage_in_last_period DOUBLE PRECISION, + OUT score BIGINT) +RETURNS SETOF RECORD +LANGUAGE plpgsql +AS $function$ +BEGIN + RETURN QUERY + SELECT + L.colocation_id, + CASE WHEN L.tenant_attribute IS NULL THEN N.nspname ELSE L.tenant_attribute END COLLATE "default" as tenant_attribute, + L.read_count_in_this_period, + L.read_count_in_last_period, + L.query_count_in_this_period, + L.query_count_in_last_period, + L.cpu_usage_in_this_period, + L.cpu_usage_in_last_period, + L.score + FROM pg_catalog.citus_stat_tenants_local_internal(return_all_tenants) L + LEFT JOIN pg_dist_tenant_schema S ON L.tenant_attribute IS NULL AND L.colocation_id = S.colocationid + LEFT JOIN pg_namespace N ON N.oid = S.schemaid + ORDER BY L.score DESC; +END; +$function$; -CREATE OR REPLACE VIEW citus.citus_stat_tenants_local AS +CREATE OR REPLACE VIEW pg_catalog.citus_stat_tenants_local AS SELECT colocation_id, tenant_attribute, @@ -27,7 +59,8 @@ SELECT FROM pg_catalog.citus_stat_tenants_local() ORDER BY score DESC; -ALTER VIEW citus.citus_stat_tenants_local SET SCHEMA pg_catalog; +REVOKE ALL ON FUNCTION pg_catalog.citus_stat_tenants_local_internal(BOOLEAN) FROM PUBLIC; +GRANT EXECUTE ON FUNCTION pg_catalog.citus_stat_tenants_local_internal(BOOLEAN) TO pg_monitor; REVOKE ALL ON FUNCTION pg_catalog.citus_stat_tenants_local(BOOLEAN) FROM PUBLIC; GRANT EXECUTE ON FUNCTION pg_catalog.citus_stat_tenants_local(BOOLEAN) TO pg_monitor; diff --git a/src/backend/distributed/utils/citus_stat_tenants.c b/src/backend/distributed/utils/citus_stat_tenants.c index 68ec3dfa4..2f174f764 100644 --- a/src/backend/distributed/utils/citus_stat_tenants.c +++ b/src/backend/distributed/utils/citus_stat_tenants.c @@ -20,6 +20,7 @@ #include "distributed/listutils.h" #include "distributed/metadata_cache.h" #include "distributed/multi_executor.h" +#include "distributed/tenant_schema_metadata.h" #include "distributed/tuplestore.h" #include "distributed/utils/citus_stat_tenants.h" #include "executor/execdesc.h" @@ -30,7 +31,8 @@ #include "utils/builtins.h" #include "utils/datetime.h" #include "utils/json.h" - +#include "utils/lsyscache.h" +#include "utils/syscache.h" #include @@ -38,8 +40,9 @@ static void AttributeMetricsIfApplicable(void); ExecutorEnd_hook_type prev_ExecutorEnd = NULL; -#define ATTRIBUTE_PREFIX "/*{\"tId\":" -#define ATTRIBUTE_STRING_FORMAT "/*{\"tId\":%s,\"cId\":%d}*/" +#define ATTRIBUTE_PREFIX "/*{\"cId\":" +#define ATTRIBUTE_STRING_FORMAT "/*{\"cId\":%d,\"tId\":%s}*/" +#define ATTRIBUTE_STRING_FORMAT_WITHOUT_TID "/*{\"cId\":%d}*/" #define STAT_TENANTS_COLUMNS 9 #define ONE_QUERY_SCORE 1000000000 @@ -155,7 +158,17 @@ citus_stat_tenants_local(PG_FUNCTION_ARGS) TenantStats *tenantStats = stats[i]; values[0] = Int32GetDatum(tenantStats->key.colocationGroupId); - values[1] = PointerGetDatum(cstring_to_text(tenantStats->key.tenantAttribute)); + + if (tenantStats->key.tenantAttribute[0] == '\0') + { + isNulls[1] = true; + } + else + { + values[1] = PointerGetDatum(cstring_to_text( + tenantStats->key.tenantAttribute)); + } + values[2] = Int32GetDatum(tenantStats->readsInThisPeriod); values[3] = Int32GetDatum(tenantStats->readsInLastPeriod); values[4] = Int32GetDatum(tenantStats->readsInThisPeriod + @@ -221,7 +234,7 @@ AttributeQueryIfAnnotated(const char *query_string, CmdType commandType) return; } - strcpy_s(AttributeToTenant, sizeof(AttributeToTenant), ""); + AttributeToColocationGroupId = INVALID_COLOCATION_ID; if (query_string == NULL) { @@ -258,7 +271,7 @@ void AttributeTask(char *tenantId, int colocationId, CmdType commandType) { if (StatTenantsTrack == STAT_TENANTS_TRACK_NONE || - tenantId == NULL || colocationId == INVALID_COLOCATION_ID) + colocationId == INVALID_COLOCATION_ID) { return; } @@ -281,9 +294,28 @@ AttributeTask(char *tenantId, int colocationId, CmdType commandType) } } + /* + * if tenantId is NULL, it must be a schema-based tenant and + * we try to get the tenantId from the colocationId to lookup schema name and use it as a tenantId + */ + if (tenantId == NULL) + { + if (!IsTenantSchemaColocationGroup(colocationId)) + { + return; + } + } + AttributeToColocationGroupId = colocationId; - strncpy_s(AttributeToTenant, MAX_TENANT_ATTRIBUTE_LENGTH, tenantId, - MAX_TENANT_ATTRIBUTE_LENGTH - 1); + if (tenantId != NULL) + { + strncpy_s(AttributeToTenant, MAX_TENANT_ATTRIBUTE_LENGTH, tenantId, + MAX_TENANT_ATTRIBUTE_LENGTH - 1); + } + else + { + strcpy_s(AttributeToTenant, sizeof(AttributeToTenant), ""); + } AttributeToCommandType = commandType; QueryStartClock = clock(); } @@ -291,26 +323,51 @@ AttributeTask(char *tenantId, int colocationId, CmdType commandType) /* * AnnotateQuery annotates the query with tenant attributes. + * if the query has a partition key, we annotate it with the partition key value and colocationId + * if the query doesn't have a partition key and if it's a schema-based tenant, we annotate it with the colocationId only. */ char * AnnotateQuery(char *queryString, Const *partitionKeyValue, int colocationId) { - if (StatTenantsTrack == STAT_TENANTS_TRACK_NONE || partitionKeyValue == NULL) + if (StatTenantsTrack == STAT_TENANTS_TRACK_NONE || + colocationId == INVALID_COLOCATION_ID) { return queryString; } - char *partitionKeyValueString = DatumToString(partitionKeyValue->constvalue, - partitionKeyValue->consttype); - - char *commentCharsEscaped = EscapeCommentChars(partitionKeyValueString); - StringInfo escapedSourceName = makeStringInfo(); - - escape_json(escapedSourceName, commentCharsEscaped); - StringInfo newQuery = makeStringInfo(); - appendStringInfo(newQuery, ATTRIBUTE_STRING_FORMAT, escapedSourceName->data, - colocationId); + + /* if the query doesn't have a parititon key value, check if it is a tenant schema */ + if (partitionKeyValue == NULL) + { + if (IsTenantSchemaColocationGroup(colocationId)) + { + /* If it is a schema-based tenant, we only annotate the query with colocationId */ + appendStringInfo(newQuery, ATTRIBUTE_STRING_FORMAT_WITHOUT_TID, + colocationId); + } + else + { + /* If it is not a schema-based tenant query and doesn't have a parititon key, + * we don't annotate it + */ + return queryString; + } + } + else + { + /* if the query has a partition key value, we annotate it with both tenantId and colocationId */ + char *partitionKeyValueString = DatumToString(partitionKeyValue->constvalue, + partitionKeyValue->consttype); + + char *commentCharsEscaped = EscapeCommentChars(partitionKeyValueString); + StringInfo escapedSourceName = makeStringInfo(); + escape_json(escapedSourceName, commentCharsEscaped); + + appendStringInfo(newQuery, ATTRIBUTE_STRING_FORMAT, colocationId, + escapedSourceName->data + ); + } appendStringInfoString(newQuery, queryString); @@ -372,7 +429,7 @@ static void AttributeMetricsIfApplicable() { if (StatTenantsTrack == STAT_TENANTS_TRACK_NONE || - AttributeToTenant[0] == '\0') + AttributeToColocationGroupId == INVALID_COLOCATION_ID) { return; } @@ -449,7 +506,7 @@ AttributeMetricsIfApplicable() } LWLockRelease(&monitor->lock); - strcpy_s(AttributeToTenant, sizeof(AttributeToTenant), ""); + AttributeToColocationGroupId = INVALID_COLOCATION_ID; } @@ -761,7 +818,12 @@ FillTenantStatsHashKey(TenantStatsHashKey *key, char *tenantAttribute, uint32 colocationGroupId) { memset(key->tenantAttribute, 0, MAX_TENANT_ATTRIBUTE_LENGTH); - strlcpy(key->tenantAttribute, tenantAttribute, MAX_TENANT_ATTRIBUTE_LENGTH); + + if (tenantAttribute != NULL) + { + strlcpy(key->tenantAttribute, tenantAttribute, MAX_TENANT_ATTRIBUTE_LENGTH); + } + key->colocationGroupId = colocationGroupId; } diff --git a/src/backend/distributed/utils/tenant_schema_metadata.c b/src/backend/distributed/utils/tenant_schema_metadata.c index fa67f8875..0bac720b6 100644 --- a/src/backend/distributed/utils/tenant_schema_metadata.c +++ b/src/backend/distributed/utils/tenant_schema_metadata.c @@ -24,9 +24,6 @@ #include "utils/fmgroids.h" -static Oid ColocationIdGetTenantSchemaId(uint32 colocationId); - - /* * IsTenantSchema returns true if there is a tenant schema with given schemaId. */ @@ -125,7 +122,7 @@ SchemaIdGetTenantColocationId(Oid schemaId) * * Returns InvalidOid if there is no such tenant schema. */ -static Oid +Oid ColocationIdGetTenantSchemaId(uint32 colocationId) { if (colocationId == INVALID_COLOCATION_ID) diff --git a/src/include/distributed/tenant_schema_metadata.h b/src/include/distributed/tenant_schema_metadata.h index 1e5889c15..1db1c28c5 100644 --- a/src/include/distributed/tenant_schema_metadata.h +++ b/src/include/distributed/tenant_schema_metadata.h @@ -15,6 +15,7 @@ #include "postgres.h" /* accessors */ +extern Oid ColocationIdGetTenantSchemaId(uint32 colocationId); extern uint32 SchemaIdGetTenantColocationId(Oid schemaId); extern bool IsTenantSchema(Oid schemaId); extern bool IsTenantSchemaColocationGroup(uint32 colocationId); diff --git a/src/test/regress/bin/normalize.sed b/src/test/regress/bin/normalize.sed index 2fe05ce21..16ad804cb 100644 --- a/src/test/regress/bin/normalize.sed +++ b/src/test/regress/bin/normalize.sed @@ -309,7 +309,7 @@ s/(NOTICE: issuing SET LOCAL application_name TO 'citus_rebalancer gpid=)[0-9]+ # shard_rebalancer output, flaky improvement number s/improvement of 0.1[0-9]* is lower/improvement of 0.1xxxxx is lower/g # normalize tenants statistics annotations -s/\/\*\{"tId":.*\*\///g +s/\/\*\{"cId":.*\*\///g # Notice message that contains current columnar version that makes it harder to bump versions s/(NOTICE: issuing CREATE EXTENSION IF NOT EXISTS citus_columnar WITH SCHEMA pg_catalog VERSION )"[0-9]+\.[0-9]+-[0-9]+"/\1 "x.y-z"/ diff --git a/src/test/regress/expected/adaptive_executor_repartition.out b/src/test/regress/expected/adaptive_executor_repartition.out index 3ac9e6a13..b575e4baf 100644 --- a/src/test/regress/expected/adaptive_executor_repartition.out +++ b/src/test/regress/expected/adaptive_executor_repartition.out @@ -167,12 +167,5 @@ select count(*) from trips t1, cars r1, trips t2, cars r2 where t1.trip_id = t2. 829 (1 row) +SET client_min_messages TO WARNING; DROP SCHEMA adaptive_executor CASCADE; -NOTICE: drop cascades to 7 other objects -DETAIL: drop cascades to table ab -drop cascades to table single_hash_repartition_first -drop cascades to table single_hash_repartition_second -drop cascades to table ref_table -drop cascades to table ref_table_361397 -drop cascades to table cars -drop cascades to table trips diff --git a/src/test/regress/expected/citus_stat_tenants.out b/src/test/regress/expected/citus_stat_tenants.out index 3b3c728f8..090f0e10e 100644 --- a/src/test/regress/expected/citus_stat_tenants.out +++ b/src/test/regress/expected/citus_stat_tenants.out @@ -881,5 +881,133 @@ SELECT tenant_attribute, query_count_in_this_period FROM citus_stat_tenants ORDE äbc | 11 (2 rows) +-- single shard distributed table, which is not part of a tenant schema +SELECT citus_stat_tenants_reset(); + citus_stat_tenants_reset +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE dist_tbl_text_single_shard(a text, b int); +select create_distributed_table('dist_tbl_text_single_shard', NULL); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO dist_tbl_text_single_shard VALUES ('/b*c/de', 1); +SELECT count(*)>=0 FROM dist_tbl_text_single_shard WHERE a = '/b*c/de'; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +DELETE FROM dist_tbl_text_single_shard WHERE a = '/b*c/de'; +UPDATE dist_tbl_text_single_shard SET b = 1 WHERE a = '/b*c/de'; +SELECT tenant_attribute, query_count_in_this_period FROM citus_stat_tenants; + tenant_attribute | query_count_in_this_period +--------------------------------------------------------------------- +(0 rows) + +-- schema based tenants +SELECT citus_stat_tenants_reset(); + citus_stat_tenants_reset +--------------------------------------------------------------------- + +(1 row) + +SET citus.enable_schema_based_sharding TO ON; +CREATE SCHEMA citus_stat_tenants_t1; +CREATE TABLE citus_stat_tenants_t1.users(id int); +SELECT id FROM citus_stat_tenants_t1.users WHERE id = 2; + id +--------------------------------------------------------------------- +(0 rows) + +INSERT INTO citus_stat_tenants_t1.users VALUES (1); +UPDATE citus_stat_tenants_t1.users SET id = 2 WHERE id = 1; +DELETE FROM citus_stat_tenants_t1.users WHERE id = 2; +SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants ORDER BY tenant_attribute; + tenant_attribute | read_count_in_this_period | read_count_in_last_period | query_count_in_this_period | query_count_in_last_period +--------------------------------------------------------------------- + citus_stat_tenants_t1 | 1 | 0 | 4 | 0 +(1 row) + +SELECT citus_stat_tenants_reset(); + citus_stat_tenants_reset +--------------------------------------------------------------------- + +(1 row) + +PREPARE schema_tenant_insert_plan (int) AS insert into citus_stat_tenants_t1.users values ($1); +EXECUTE schema_tenant_insert_plan(1); +PREPARE schema_tenant_select_plan (int) AS SELECT count(*) > 1 FROM citus_stat_tenants_t1.users where Id = $1; +EXECUTE schema_tenant_select_plan(1); + ?column? +--------------------------------------------------------------------- + f +(1 row) + +SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants ORDER BY tenant_attribute; + tenant_attribute | read_count_in_this_period | read_count_in_last_period | query_count_in_this_period | query_count_in_last_period +--------------------------------------------------------------------- + citus_stat_tenants_t1 | 1 | 0 | 2 | 0 +(1 row) + +SELECT citus_stat_tenants_reset(); + citus_stat_tenants_reset +--------------------------------------------------------------------- + +(1 row) + +-- local execution & prepared statements +\c - - - :worker_2_port +SET search_path TO citus_stat_tenants; +PREPARE schema_tenant_insert_plan (int) AS insert into citus_stat_tenants_t1.users values ($1); +EXECUTE schema_tenant_insert_plan(1); +EXECUTE schema_tenant_insert_plan(1); +EXECUTE schema_tenant_insert_plan(1); +EXECUTE schema_tenant_insert_plan(1); +EXECUTE schema_tenant_insert_plan(1); +PREPARE schema_tenant_select_plan (int) AS SELECT count(*) > 1 FROM citus_stat_tenants_t1.users where Id = $1; +EXECUTE schema_tenant_select_plan(1); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +EXECUTE schema_tenant_select_plan(1); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +EXECUTE schema_tenant_select_plan(1); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +EXECUTE schema_tenant_select_plan(1); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +EXECUTE schema_tenant_select_plan(1); + ?column? +--------------------------------------------------------------------- + t +(1 row) + +SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants ORDER BY tenant_attribute; + tenant_attribute | read_count_in_this_period | read_count_in_last_period | query_count_in_this_period | query_count_in_last_period +--------------------------------------------------------------------- + citus_stat_tenants_t1 | 5 | 0 | 10 | 0 +(1 row) + +\c - - - :master_port +SET search_path TO citus_stat_tenants; SET client_min_messages TO ERROR; DROP SCHEMA citus_stat_tenants CASCADE; +DROP SCHEMA citus_stat_tenants_t1 CASCADE; diff --git a/src/test/regress/expected/multi_extension.out b/src/test/regress/expected/multi_extension.out index beb73a5be..0ef234e30 100644 --- a/src/test/regress/expected/multi_extension.out +++ b/src/test/regress/expected/multi_extension.out @@ -1364,8 +1364,9 @@ SELECT * FROM multi_extension.print_extension_changes(); | function citus_internal_unregister_tenant_schema_globally(oid,text) void | function citus_schema_distribute(regnamespace) void | function citus_schema_undistribute(regnamespace) void + | function citus_stat_tenants_local_internal(boolean) SETOF record | table pg_dist_tenant_schema -(6 rows) +(7 rows) DROP TABLE multi_extension.prev_objects, multi_extension.extension_diff; -- show running version diff --git a/src/test/regress/expected/upgrade_list_citus_objects.out b/src/test/regress/expected/upgrade_list_citus_objects.out index 6e30d4ac7..d5854bdeb 100644 --- a/src/test/regress/expected/upgrade_list_citus_objects.out +++ b/src/test/regress/expected/upgrade_list_citus_objects.out @@ -132,6 +132,7 @@ ORDER BY 1; function citus_stat_statements_reset() function citus_stat_tenants(boolean) function citus_stat_tenants_local(boolean) + function citus_stat_tenants_local_internal(boolean) function citus_stat_tenants_local_reset() function citus_stat_tenants_reset() function citus_table_is_visible(oid) @@ -336,5 +337,5 @@ ORDER BY 1; view citus_stat_tenants_local view pg_dist_shard_placement view time_partitions -(328 rows) +(329 rows) diff --git a/src/test/regress/sql/adaptive_executor_repartition.sql b/src/test/regress/sql/adaptive_executor_repartition.sql index 1f2e21951..06f54b82b 100644 --- a/src/test/regress/sql/adaptive_executor_repartition.sql +++ b/src/test/regress/sql/adaptive_executor_repartition.sql @@ -79,4 +79,5 @@ select count(*) from trips t1, cars r1, trips t2, cars r2 where t1.trip_id = t2. set citus.enable_single_hash_repartition_joins to on; select count(*) from trips t1, cars r1, trips t2, cars r2 where t1.trip_id = t2.trip_id and t1.car_id = r1.car_id and t2.car_id = r2.car_id; +SET client_min_messages TO WARNING; DROP SCHEMA adaptive_executor CASCADE; diff --git a/src/test/regress/sql/citus_stat_tenants.sql b/src/test/regress/sql/citus_stat_tenants.sql index efe77ec84..2a6a20335 100644 --- a/src/test/regress/sql/citus_stat_tenants.sql +++ b/src/test/regress/sql/citus_stat_tenants.sql @@ -313,5 +313,69 @@ SELECT count(*)>=0 FROM select_from_dist_tbl_text_view WHERE a = U&'\0061\0308bc SELECT tenant_attribute, query_count_in_this_period FROM citus_stat_tenants ORDER BY tenant_attribute; +-- single shard distributed table, which is not part of a tenant schema +SELECT citus_stat_tenants_reset(); + +CREATE TABLE dist_tbl_text_single_shard(a text, b int); +select create_distributed_table('dist_tbl_text_single_shard', NULL); + +INSERT INTO dist_tbl_text_single_shard VALUES ('/b*c/de', 1); +SELECT count(*)>=0 FROM dist_tbl_text_single_shard WHERE a = '/b*c/de'; +DELETE FROM dist_tbl_text_single_shard WHERE a = '/b*c/de'; +UPDATE dist_tbl_text_single_shard SET b = 1 WHERE a = '/b*c/de'; + +SELECT tenant_attribute, query_count_in_this_period FROM citus_stat_tenants; + +-- schema based tenants +SELECT citus_stat_tenants_reset(); + +SET citus.enable_schema_based_sharding TO ON; + +CREATE SCHEMA citus_stat_tenants_t1; +CREATE TABLE citus_stat_tenants_t1.users(id int); + +SELECT id FROM citus_stat_tenants_t1.users WHERE id = 2; +INSERT INTO citus_stat_tenants_t1.users VALUES (1); +UPDATE citus_stat_tenants_t1.users SET id = 2 WHERE id = 1; +DELETE FROM citus_stat_tenants_t1.users WHERE id = 2; + +SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants ORDER BY tenant_attribute; + +SELECT citus_stat_tenants_reset(); + +PREPARE schema_tenant_insert_plan (int) AS insert into citus_stat_tenants_t1.users values ($1); +EXECUTE schema_tenant_insert_plan(1); + +PREPARE schema_tenant_select_plan (int) AS SELECT count(*) > 1 FROM citus_stat_tenants_t1.users where Id = $1; +EXECUTE schema_tenant_select_plan(1); + +SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants ORDER BY tenant_attribute; + +SELECT citus_stat_tenants_reset(); + +-- local execution & prepared statements +\c - - - :worker_2_port +SET search_path TO citus_stat_tenants; + +PREPARE schema_tenant_insert_plan (int) AS insert into citus_stat_tenants_t1.users values ($1); +EXECUTE schema_tenant_insert_plan(1); +EXECUTE schema_tenant_insert_plan(1); +EXECUTE schema_tenant_insert_plan(1); +EXECUTE schema_tenant_insert_plan(1); +EXECUTE schema_tenant_insert_plan(1); + +PREPARE schema_tenant_select_plan (int) AS SELECT count(*) > 1 FROM citus_stat_tenants_t1.users where Id = $1; +EXECUTE schema_tenant_select_plan(1); +EXECUTE schema_tenant_select_plan(1); +EXECUTE schema_tenant_select_plan(1); +EXECUTE schema_tenant_select_plan(1); +EXECUTE schema_tenant_select_plan(1); + +SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants ORDER BY tenant_attribute; + +\c - - - :master_port +SET search_path TO citus_stat_tenants; + SET client_min_messages TO ERROR; DROP SCHEMA citus_stat_tenants CASCADE; +DROP SCHEMA citus_stat_tenants_t1 CASCADE; From 772d1943578551a48944abb22ea22f52b0d47d7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20Ozan=20Akg=C3=BCl?= Date: Tue, 13 Jun 2023 16:36:35 +0300 Subject: [PATCH 091/118] Changes citus_shard_sizes view's Shard Name Column to Shard Id (#7003) citus_shard_sizes view had a shard name column we use to extract shard id. This PR changes the column to shard id so we don't do unnecessary string operation. --- .../distributed/metadata/metadata_utility.c | 29 ++++++++----------- .../distributed/operations/stage_protocol.c | 2 +- .../distributed/sql/citus--11.3-1--12.0-1.sql | 4 +++ .../sql/downgrades/citus--12.0-1--11.3-1.sql | 8 +++++ .../sql/udfs/citus_shard_sizes/12.0-1.sql | 6 ++++ .../sql/udfs/citus_shard_sizes/latest.sql | 4 +-- .../sql/udfs/citus_shards/12.0-1.sql | 5 ++-- .../sql/udfs/citus_shards/latest.sql | 5 ++-- src/include/distributed/metadata_utility.h | 2 +- .../citus_update_table_statistics.out | 12 ++++---- .../expected/single_shard_table_udfs.out | 22 ++++++++++++-- .../regress/sql/single_shard_table_udfs.sql | 10 ++++++- 12 files changed, 74 insertions(+), 35 deletions(-) create mode 100644 src/backend/distributed/sql/udfs/citus_shard_sizes/12.0-1.sql diff --git a/src/backend/distributed/metadata/metadata_utility.c b/src/backend/distributed/metadata/metadata_utility.c index 7585769ad..a6ff93220 100644 --- a/src/backend/distributed/metadata/metadata_utility.c +++ b/src/backend/distributed/metadata/metadata_utility.c @@ -101,9 +101,9 @@ static char * GenerateAllShardStatisticsQueryForNode(WorkerNode *workerNode, static List * GenerateShardStatisticsQueryList(List *workerNodeList, List *citusTableIds); static void ErrorIfNotSuitableToGetSize(Oid relationId); static List * OpenConnectionToNodes(List *workerNodeList); -static void ReceiveShardNameAndSizeResults(List *connectionList, - Tuplestorestate *tupleStore, - TupleDesc tupleDescriptor); +static void ReceiveShardIdAndSizeResults(List *connectionList, + Tuplestorestate *tupleStore, + TupleDesc tupleDescriptor); static void AppendShardSizeQuery(StringInfo selectQuery, ShardInterval *shardInterval); static HeapTuple CreateDiskSpaceTuple(TupleDesc tupleDesc, uint64 availableBytes, @@ -253,7 +253,7 @@ GetNodeDiskSpaceStatsForConnection(MultiConnection *connection, uint64 *availabl /* - * citus_shard_sizes returns all shard names and their sizes. + * citus_shard_sizes returns all shard ids and their sizes. */ Datum citus_shard_sizes(PG_FUNCTION_ARGS) @@ -271,7 +271,7 @@ citus_shard_sizes(PG_FUNCTION_ARGS) TupleDesc tupleDescriptor = NULL; Tuplestorestate *tupleStore = SetupTuplestore(fcinfo, &tupleDescriptor); - ReceiveShardNameAndSizeResults(connectionList, tupleStore, tupleDescriptor); + ReceiveShardIdAndSizeResults(connectionList, tupleStore, tupleDescriptor); PG_RETURN_VOID(); } @@ -446,12 +446,12 @@ GenerateShardStatisticsQueryList(List *workerNodeList, List *citusTableIds) /* - * ReceiveShardNameAndSizeResults receives shard name and size results from the given + * ReceiveShardIdAndSizeResults receives shard id and size results from the given * connection list. */ static void -ReceiveShardNameAndSizeResults(List *connectionList, Tuplestorestate *tupleStore, - TupleDesc tupleDescriptor) +ReceiveShardIdAndSizeResults(List *connectionList, Tuplestorestate *tupleStore, + TupleDesc tupleDescriptor) { MultiConnection *connection = NULL; foreach_ptr(connection, connectionList) @@ -488,13 +488,9 @@ ReceiveShardNameAndSizeResults(List *connectionList, Tuplestorestate *tupleStore memset(values, 0, sizeof(values)); memset(isNulls, false, sizeof(isNulls)); - /* format is [0] shard id, [1] shard name, [2] size */ - char *tableName = PQgetvalue(result, rowIndex, 1); - Datum resultStringDatum = CStringGetDatum(tableName); - Datum textDatum = DirectFunctionCall1(textin, resultStringDatum); - - values[0] = textDatum; - values[1] = ParseIntField(result, rowIndex, 2); + /* format is [0] shard id, [1] size */ + values[0] = ParseIntField(result, rowIndex, 0); + values[1] = ParseIntField(result, rowIndex, 1); tuplestore_putvalues(tupleStore, tupleDescriptor, values, isNulls); } @@ -942,7 +938,7 @@ GenerateAllShardStatisticsQueryForNode(WorkerNode *workerNode, List *citusTableI } /* Add a dummy entry so that UNION ALL doesn't complain */ - appendStringInfo(allShardStatisticsQuery, "SELECT 0::bigint, NULL::text, 0::bigint;"); + appendStringInfo(allShardStatisticsQuery, "SELECT 0::bigint, 0::bigint;"); return allShardStatisticsQuery->data; } @@ -986,7 +982,6 @@ AppendShardSizeQuery(StringInfo selectQuery, ShardInterval *shardInterval) char *quotedShardName = quote_literal_cstr(shardQualifiedName); appendStringInfo(selectQuery, "SELECT " UINT64_FORMAT " AS shard_id, ", shardId); - appendStringInfo(selectQuery, "%s AS shard_name, ", quotedShardName); appendStringInfo(selectQuery, PG_TOTAL_RELATION_SIZE_FUNCTION, quotedShardName); } diff --git a/src/backend/distributed/operations/stage_protocol.c b/src/backend/distributed/operations/stage_protocol.c index db7ebefca..ddab3453b 100644 --- a/src/backend/distributed/operations/stage_protocol.c +++ b/src/backend/distributed/operations/stage_protocol.c @@ -863,7 +863,7 @@ ProcessShardStatisticsRow(PGresult *result, int64 rowIndex, uint64 *shardId, return false; } - *shardSize = ParseIntField(result, rowIndex, 2); + *shardSize = ParseIntField(result, rowIndex, 1); return true; } diff --git a/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql b/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql index 705f68a88..90ff57412 100644 --- a/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql +++ b/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql @@ -24,6 +24,10 @@ GRANT SELECT ON pg_catalog.pg_dist_tenant_schema TO public; #include "udfs/citus_internal_unregister_tenant_schema_globally/12.0-1.sql" #include "udfs/citus_drop_trigger/12.0-1.sql" +DROP VIEW citus_shards; +DROP FUNCTION citus_shard_sizes; +#include "udfs/citus_shard_sizes/12.0-1.sql" + #include "udfs/citus_tables/12.0-1.sql" #include "udfs/citus_shards/12.0-1.sql" diff --git a/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql b/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql index 104c16d7a..66e1001eb 100644 --- a/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql +++ b/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql @@ -43,6 +43,14 @@ DROP FUNCTION pg_catalog.citus_internal_unregister_tenant_schema_globally(Oid, t #include "../udfs/citus_drop_trigger/10.2-1.sql" +DROP VIEW pg_catalog.citus_shards; +DROP FUNCTION pg_catalog.citus_shard_sizes; +#include "../udfs/citus_shard_sizes/10.0-1.sql" +-- citus_shards/11.1-1.sql tries to create citus_shards in pg_catalog but it is not allowed. +-- Here we use citus_shards/10.0-1.sql to properly create the view in citus schema and +-- then alter it to pg_catalog, so citus_shards/11.1-1.sql can REPLACE it without any errors. +#include "../udfs/citus_shards/10.0-1.sql" + #include "../udfs/citus_tables/11.1-1.sql" #include "../udfs/citus_shards/11.1-1.sql" diff --git a/src/backend/distributed/sql/udfs/citus_shard_sizes/12.0-1.sql b/src/backend/distributed/sql/udfs/citus_shard_sizes/12.0-1.sql new file mode 100644 index 000000000..3a647a57b --- /dev/null +++ b/src/backend/distributed/sql/udfs/citus_shard_sizes/12.0-1.sql @@ -0,0 +1,6 @@ +CREATE OR REPLACE FUNCTION pg_catalog.citus_shard_sizes(OUT shard_id int, OUT size bigint) + RETURNS SETOF RECORD + LANGUAGE C STRICT + AS 'MODULE_PATHNAME', $$citus_shard_sizes$$; + COMMENT ON FUNCTION pg_catalog.citus_shard_sizes(OUT shard_id int, OUT size bigint) + IS 'returns shards sizes across citus cluster'; diff --git a/src/backend/distributed/sql/udfs/citus_shard_sizes/latest.sql b/src/backend/distributed/sql/udfs/citus_shard_sizes/latest.sql index fd619b0a2..3a647a57b 100644 --- a/src/backend/distributed/sql/udfs/citus_shard_sizes/latest.sql +++ b/src/backend/distributed/sql/udfs/citus_shard_sizes/latest.sql @@ -1,6 +1,6 @@ -CREATE FUNCTION pg_catalog.citus_shard_sizes(OUT table_name text, OUT size bigint) +CREATE OR REPLACE FUNCTION pg_catalog.citus_shard_sizes(OUT shard_id int, OUT size bigint) RETURNS SETOF RECORD LANGUAGE C STRICT AS 'MODULE_PATHNAME', $$citus_shard_sizes$$; - COMMENT ON FUNCTION pg_catalog.citus_shard_sizes(OUT table_name text, OUT size bigint) + COMMENT ON FUNCTION pg_catalog.citus_shard_sizes(OUT shard_id int, OUT size bigint) IS 'returns shards sizes across citus cluster'; diff --git a/src/backend/distributed/sql/udfs/citus_shards/12.0-1.sql b/src/backend/distributed/sql/udfs/citus_shards/12.0-1.sql index 8215ab64a..be9b25167 100644 --- a/src/backend/distributed/sql/udfs/citus_shards/12.0-1.sql +++ b/src/backend/distributed/sql/udfs/citus_shards/12.0-1.sql @@ -1,4 +1,4 @@ -CREATE OR REPLACE VIEW pg_catalog.citus_shards AS +CREATE OR REPLACE VIEW citus.citus_shards AS SELECT pg_dist_shard.logicalrelid AS table_name, pg_dist_shard.shardid, @@ -27,7 +27,7 @@ JOIN ON pg_dist_partition.logicalrelid = pg_dist_shard.logicalrelid LEFT JOIN - (SELECT (regexp_matches(table_name,'_(\d+)$'))[1]::int as shard_id, max(size) as size from citus_shard_sizes() GROUP BY shard_id) as shard_sizes + (SELECT shard_id, max(size) as size from citus_shard_sizes() GROUP BY shard_id) as shard_sizes ON pg_dist_shard.shardid = shard_sizes.shard_id WHERE @@ -46,4 +46,5 @@ ORDER BY pg_dist_shard.logicalrelid::text, shardid ; +ALTER VIEW citus.citus_shards SET SCHEMA pg_catalog; GRANT SELECT ON pg_catalog.citus_shards TO public; diff --git a/src/backend/distributed/sql/udfs/citus_shards/latest.sql b/src/backend/distributed/sql/udfs/citus_shards/latest.sql index 8215ab64a..be9b25167 100644 --- a/src/backend/distributed/sql/udfs/citus_shards/latest.sql +++ b/src/backend/distributed/sql/udfs/citus_shards/latest.sql @@ -1,4 +1,4 @@ -CREATE OR REPLACE VIEW pg_catalog.citus_shards AS +CREATE OR REPLACE VIEW citus.citus_shards AS SELECT pg_dist_shard.logicalrelid AS table_name, pg_dist_shard.shardid, @@ -27,7 +27,7 @@ JOIN ON pg_dist_partition.logicalrelid = pg_dist_shard.logicalrelid LEFT JOIN - (SELECT (regexp_matches(table_name,'_(\d+)$'))[1]::int as shard_id, max(size) as size from citus_shard_sizes() GROUP BY shard_id) as shard_sizes + (SELECT shard_id, max(size) as size from citus_shard_sizes() GROUP BY shard_id) as shard_sizes ON pg_dist_shard.shardid = shard_sizes.shard_id WHERE @@ -46,4 +46,5 @@ ORDER BY pg_dist_shard.logicalrelid::text, shardid ; +ALTER VIEW citus.citus_shards SET SCHEMA pg_catalog; GRANT SELECT ON pg_catalog.citus_shards TO public; diff --git a/src/include/distributed/metadata_utility.h b/src/include/distributed/metadata_utility.h index abed55e48..473d105e8 100644 --- a/src/include/distributed/metadata_utility.h +++ b/src/include/distributed/metadata_utility.h @@ -41,7 +41,7 @@ #define WORKER_PARTITIONED_RELATION_TOTAL_SIZE_FUNCTION \ "worker_partitioned_relation_total_size(%s)" -#define SHARD_SIZES_COLUMN_COUNT (3) +#define SHARD_SIZES_COLUMN_COUNT (2) /* * Flag to keep track of whether the process is currently in a function converting the diff --git a/src/test/regress/expected/citus_update_table_statistics.out b/src/test/regress/expected/citus_update_table_statistics.out index d908e433d..a8f90945b 100644 --- a/src/test/regress/expected/citus_update_table_statistics.out +++ b/src/test/regress/expected/citus_update_table_statistics.out @@ -64,15 +64,15 @@ SET citus.multi_shard_modify_mode TO sequential; SELECT citus_update_table_statistics('test_table_statistics_hash'); 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 SELECT 0::bigint, NULL::text, 0::bigint; +NOTICE: issuing SELECT 0::bigint, 0::bigint; DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx 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 SELECT 981000 AS shard_id, 'public.test_table_statistics_hash_981000' AS shard_name, pg_total_relation_size('public.test_table_statistics_hash_981000') UNION ALL SELECT 981001 AS shard_id, 'public.test_table_statistics_hash_981001' AS shard_name, pg_total_relation_size('public.test_table_statistics_hash_981001') UNION ALL SELECT 981002 AS shard_id, 'public.test_table_statistics_hash_981002' AS shard_name, pg_total_relation_size('public.test_table_statistics_hash_981002') UNION ALL SELECT 981003 AS shard_id, 'public.test_table_statistics_hash_981003' AS shard_name, pg_total_relation_size('public.test_table_statistics_hash_981003') UNION ALL SELECT 981004 AS shard_id, 'public.test_table_statistics_hash_981004' AS shard_name, pg_total_relation_size('public.test_table_statistics_hash_981004') UNION ALL SELECT 981005 AS shard_id, 'public.test_table_statistics_hash_981005' AS shard_name, pg_total_relation_size('public.test_table_statistics_hash_981005') UNION ALL SELECT 981006 AS shard_id, 'public.test_table_statistics_hash_981006' AS shard_name, pg_total_relation_size('public.test_table_statistics_hash_981006') UNION ALL SELECT 981007 AS shard_id, 'public.test_table_statistics_hash_981007' AS shard_name, pg_total_relation_size('public.test_table_statistics_hash_981007') UNION ALL SELECT 0::bigint, NULL::text, 0::bigint; +NOTICE: issuing SELECT 981000 AS shard_id, pg_total_relation_size('public.test_table_statistics_hash_981000') UNION ALL SELECT 981001 AS shard_id, pg_total_relation_size('public.test_table_statistics_hash_981001') UNION ALL SELECT 981002 AS shard_id, pg_total_relation_size('public.test_table_statistics_hash_981002') UNION ALL SELECT 981003 AS shard_id, pg_total_relation_size('public.test_table_statistics_hash_981003') UNION ALL SELECT 981004 AS shard_id, pg_total_relation_size('public.test_table_statistics_hash_981004') UNION ALL SELECT 981005 AS shard_id, pg_total_relation_size('public.test_table_statistics_hash_981005') UNION ALL SELECT 981006 AS shard_id, pg_total_relation_size('public.test_table_statistics_hash_981006') UNION ALL SELECT 981007 AS shard_id, pg_total_relation_size('public.test_table_statistics_hash_981007') UNION ALL SELECT 0::bigint, 0::bigint; DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx 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 SELECT 981000 AS shard_id, 'public.test_table_statistics_hash_981000' AS shard_name, pg_total_relation_size('public.test_table_statistics_hash_981000') UNION ALL SELECT 981001 AS shard_id, 'public.test_table_statistics_hash_981001' AS shard_name, pg_total_relation_size('public.test_table_statistics_hash_981001') UNION ALL SELECT 981002 AS shard_id, 'public.test_table_statistics_hash_981002' AS shard_name, pg_total_relation_size('public.test_table_statistics_hash_981002') UNION ALL SELECT 981003 AS shard_id, 'public.test_table_statistics_hash_981003' AS shard_name, pg_total_relation_size('public.test_table_statistics_hash_981003') UNION ALL SELECT 981004 AS shard_id, 'public.test_table_statistics_hash_981004' AS shard_name, pg_total_relation_size('public.test_table_statistics_hash_981004') UNION ALL SELECT 981005 AS shard_id, 'public.test_table_statistics_hash_981005' AS shard_name, pg_total_relation_size('public.test_table_statistics_hash_981005') UNION ALL SELECT 981006 AS shard_id, 'public.test_table_statistics_hash_981006' AS shard_name, pg_total_relation_size('public.test_table_statistics_hash_981006') UNION ALL SELECT 981007 AS shard_id, 'public.test_table_statistics_hash_981007' AS shard_name, pg_total_relation_size('public.test_table_statistics_hash_981007') UNION ALL SELECT 0::bigint, NULL::text, 0::bigint; +NOTICE: issuing SELECT 981000 AS shard_id, pg_total_relation_size('public.test_table_statistics_hash_981000') UNION ALL SELECT 981001 AS shard_id, pg_total_relation_size('public.test_table_statistics_hash_981001') UNION ALL SELECT 981002 AS shard_id, pg_total_relation_size('public.test_table_statistics_hash_981002') UNION ALL SELECT 981003 AS shard_id, pg_total_relation_size('public.test_table_statistics_hash_981003') UNION ALL SELECT 981004 AS shard_id, pg_total_relation_size('public.test_table_statistics_hash_981004') UNION ALL SELECT 981005 AS shard_id, pg_total_relation_size('public.test_table_statistics_hash_981005') UNION ALL SELECT 981006 AS shard_id, pg_total_relation_size('public.test_table_statistics_hash_981006') UNION ALL SELECT 981007 AS shard_id, pg_total_relation_size('public.test_table_statistics_hash_981007') UNION ALL SELECT 0::bigint, 0::bigint; DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing COMMIT DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx @@ -158,15 +158,15 @@ SET citus.multi_shard_modify_mode TO sequential; SELECT citus_update_table_statistics('test_table_statistics_append'); 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 SELECT 0::bigint, NULL::text, 0::bigint; +NOTICE: issuing SELECT 0::bigint, 0::bigint; DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx 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 SELECT 981008 AS shard_id, 'public.test_table_statistics_append_981008' AS shard_name, pg_total_relation_size('public.test_table_statistics_append_981008') UNION ALL SELECT 981009 AS shard_id, 'public.test_table_statistics_append_981009' AS shard_name, pg_total_relation_size('public.test_table_statistics_append_981009') UNION ALL SELECT 0::bigint, NULL::text, 0::bigint; +NOTICE: issuing SELECT 981008 AS shard_id, pg_total_relation_size('public.test_table_statistics_append_981008') UNION ALL SELECT 981009 AS shard_id, pg_total_relation_size('public.test_table_statistics_append_981009') UNION ALL SELECT 0::bigint, 0::bigint; DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx 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 SELECT 981008 AS shard_id, 'public.test_table_statistics_append_981008' AS shard_name, pg_total_relation_size('public.test_table_statistics_append_981008') UNION ALL SELECT 981009 AS shard_id, 'public.test_table_statistics_append_981009' AS shard_name, pg_total_relation_size('public.test_table_statistics_append_981009') UNION ALL SELECT 0::bigint, NULL::text, 0::bigint; +NOTICE: issuing SELECT 981008 AS shard_id, pg_total_relation_size('public.test_table_statistics_append_981008') UNION ALL SELECT 981009 AS shard_id, pg_total_relation_size('public.test_table_statistics_append_981009') UNION ALL SELECT 0::bigint, 0::bigint; DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing COMMIT DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx diff --git a/src/test/regress/expected/single_shard_table_udfs.out b/src/test/regress/expected/single_shard_table_udfs.out index 78b4ee25a..5d3314070 100644 --- a/src/test/regress/expected/single_shard_table_udfs.out +++ b/src/test/regress/expected/single_shard_table_udfs.out @@ -76,10 +76,11 @@ SELECT citus_relation_size('null_dist_key_table'); 8192 (1 row) -SELECT * FROM pg_catalog.citus_shard_sizes() WHERE table_name LIKE '%null_dist_key_table%'; - table_name | size +SELECT shard_name, shard_size FROM pg_catalog.citus_shard_sizes(), citus_shards +WHERE shardid = shard_id AND shard_name LIKE '%null_dist_key_table%' AND nodeport IN (:worker_1_port, :worker_2_port); + shard_name | shard_size --------------------------------------------------------------------- - null_dist_key_udfs.null_dist_key_table_1820000 | 24576 + null_dist_key_udfs.null_dist_key_table_1820000 | 24576 (1 row) BEGIN; @@ -1302,5 +1303,20 @@ SELECT :logseq = :txnlog; t (1 row) +-- test table with space in its name in citus_shards +CREATE TABLE "t b l" (a INT); +SELECT create_distributed_table('"t b l"', NULL, colocate_with:='none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT table_name, shard_size FROM citus_shards +WHERE table_name = '"t b l"'::regclass AND nodeport IN (:worker_1_port, :worker_2_port); + table_name | shard_size +--------------------------------------------------------------------- + "t b l" | 0 +(1 row) + SET client_min_messages TO WARNING; DROP SCHEMA null_dist_key_udfs CASCADE; diff --git a/src/test/regress/sql/single_shard_table_udfs.sql b/src/test/regress/sql/single_shard_table_udfs.sql index aac01f085..0e516e107 100644 --- a/src/test/regress/sql/single_shard_table_udfs.sql +++ b/src/test/regress/sql/single_shard_table_udfs.sql @@ -58,7 +58,8 @@ CREATE INDEX null_dist_key_idx ON null_dist_key_table(a); SELECT citus_table_size('null_dist_key_table'); SELECT citus_total_relation_size('null_dist_key_table'); SELECT citus_relation_size('null_dist_key_table'); -SELECT * FROM pg_catalog.citus_shard_sizes() WHERE table_name LIKE '%null_dist_key_table%'; +SELECT shard_name, shard_size FROM pg_catalog.citus_shard_sizes(), citus_shards +WHERE shardid = shard_id AND shard_name LIKE '%null_dist_key_table%' AND nodeport IN (:worker_1_port, :worker_2_port); BEGIN; SELECT lock_relation_if_exists('null_dist_key_table', 'ACCESS SHARE'); @@ -654,5 +655,12 @@ WHERE nodeport = :clock_shard_nodeport \gset SELECT cluster_clock_logical(:'txnclock') as txnlog \gset SELECT :logseq = :txnlog; +-- test table with space in its name in citus_shards +CREATE TABLE "t b l" (a INT); +SELECT create_distributed_table('"t b l"', NULL, colocate_with:='none'); + +SELECT table_name, shard_size FROM citus_shards +WHERE table_name = '"t b l"'::regclass AND nodeport IN (:worker_1_port, :worker_2_port); + SET client_min_messages TO WARNING; DROP SCHEMA null_dist_key_udfs CASCADE; From 7b0bc62173d73ce3a5c651bc80cbe279efe99372 Mon Sep 17 00:00:00 2001 From: Ahmet Gedemenli Date: Tue, 13 Jun 2023 17:54:09 +0300 Subject: [PATCH 092/118] Support CREATE TABLE .. AS SELECT .. commands for tenant tables (#6998) Support CREATE TABLE .. AS SELECT .. commands for tenant tables --- src/backend/distributed/commands/table.c | 11 ++-- .../expected/schema_based_sharding.out | 55 ++++++++++++++++--- .../regress/sql/schema_based_sharding.sql | 22 ++++++-- 3 files changed, 70 insertions(+), 18 deletions(-) diff --git a/src/backend/distributed/commands/table.c b/src/backend/distributed/commands/table.c index aa4570f5e..4a1e69e14 100644 --- a/src/backend/distributed/commands/table.c +++ b/src/backend/distributed/commands/table.c @@ -4130,10 +4130,13 @@ ConvertNewTableIfNecessary(Node *createStmt) if (ShouldCreateTenantSchemaTable(createdRelationId)) { - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot create a tenant table using " - "CREATE TABLE AS or SELECT INTO " - "statements"))); + /* not try to convert the table if it already exists and IF NOT EXISTS syntax is used */ + if (createTableAsStmt->if_not_exists && IsCitusTable(createdRelationId)) + { + return; + } + + CreateTenantSchemaTable(createdRelationId); } /* diff --git a/src/test/regress/expected/schema_based_sharding.out b/src/test/regress/expected/schema_based_sharding.out index 1bc5d7963..06d759e1f 100644 --- a/src/test/regress/expected/schema_based_sharding.out +++ b/src/test/regress/expected/schema_based_sharding.out @@ -384,13 +384,36 @@ SELECT EXISTS( t (1 row) --- verify that we don't allow creating tenant tables by using CREATE TABLE AS / SELECT INTO commands +-- verify that we allow creating tenant tables by using CREATE TABLE AS / SELECT INTO commands CREATE TABLE tenant_4.tbl_3 AS SELECT 1 AS a, 'text' as b; -ERROR: cannot create a tenant table using CREATE TABLE AS or SELECT INTO statements -CREATE TABLE IF NOT EXISTS tenant_4.tbl_3 AS SELECT 1 as a, 'text' as b; -ERROR: cannot create a tenant table using CREATE TABLE AS or SELECT INTO statements -SELECT 1 as a, 'text' as b INTO tenant_4.tbl_3; -ERROR: cannot create a tenant table using CREATE TABLE AS or SELECT INTO statements +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$tenant_4.tbl_3$$) +CREATE TEMP TABLE IF NOT EXISTS tenant_4.tbl_4 AS SELECT 1 as a, 'text' as b; +ERROR: cannot create temporary relation in non-temporary schema +CREATE UNLOGGED TABLE IF NOT EXISTS tenant_4.tbl_4 AS SELECT 1 as a, 'text' as b WITH NO DATA; +-- the same command, no changes because of IF NOT EXISTS +CREATE UNLOGGED TABLE IF NOT EXISTS tenant_4.tbl_4 AS SELECT 1 as a, 'text' as b WITH NO DATA; +NOTICE: relation "tbl_4" already exists, skipping +SELECT 1 as a, 'text' as b INTO tenant_4.tbl_5; +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$tenant_4.tbl_5$$) +-- verify we can query the newly created tenant tables +SELECT * FROM tenant_4.tbl_3; + a | b +--------------------------------------------------------------------- + 1 | text +(1 row) + +SELECT COUNT(*) FROM tenant_4.tbl_5; + count +--------------------------------------------------------------------- + 1 +(1 row) + CREATE TYPE employee_type AS (name text, salary numeric); -- verify that we don't allow creating tenant tables by using CREATE TABLE OF commands CREATE TABLE tenant_4.employees OF employee_type ( @@ -399,9 +422,23 @@ CREATE TABLE tenant_4.employees OF employee_type ( ); ERROR: cannot create a tenant table by using CREATE TABLE OF syntax -- verify that we act accordingly when if not exists is used -CREATE TABLE IF NOT EXISTS tenant_4.tbl_3(a int, b text); -CREATE TABLE IF NOT EXISTS tenant_4.tbl_3(a int, b text); -NOTICE: relation "tbl_3" already exists, skipping +CREATE TABLE IF NOT EXISTS tenant_4.tbl_6(a int, b text); +CREATE TABLE IF NOT EXISTS tenant_4.tbl_6(a int, b text); +NOTICE: relation "tbl_6" already exists, skipping +SELECT logicalrelid, partmethod + FROM pg_dist_partition + WHERE logicalrelid::text LIKE 'tenant_4.tbl%' + ORDER BY logicalrelid; + logicalrelid | partmethod +--------------------------------------------------------------------- + tenant_4.tbl_1 | n + tenant_4.tbl_2 | n + tenant_4.tbl_3 | n + tenant_4.tbl_4 | n + tenant_4.tbl_5 | n + tenant_4.tbl_6 | n +(6 rows) + CREATE TABLE regular_schema.local(a int, b text); CREATE TABLE regular_schema.citus_local(a int, b text); SELECT citus_add_local_table_to_metadata('regular_schema.citus_local'); diff --git a/src/test/regress/sql/schema_based_sharding.sql b/src/test/regress/sql/schema_based_sharding.sql index 4dda95dcd..ad20757ee 100644 --- a/src/test/regress/sql/schema_based_sharding.sql +++ b/src/test/regress/sql/schema_based_sharding.sql @@ -273,10 +273,17 @@ SELECT EXISTS( inhparent = 'tenant_4.parent_attach_test'::regclass ) AS is_partition; --- verify that we don't allow creating tenant tables by using CREATE TABLE AS / SELECT INTO commands +-- verify that we allow creating tenant tables by using CREATE TABLE AS / SELECT INTO commands CREATE TABLE tenant_4.tbl_3 AS SELECT 1 AS a, 'text' as b; -CREATE TABLE IF NOT EXISTS tenant_4.tbl_3 AS SELECT 1 as a, 'text' as b; -SELECT 1 as a, 'text' as b INTO tenant_4.tbl_3; +CREATE TEMP TABLE IF NOT EXISTS tenant_4.tbl_4 AS SELECT 1 as a, 'text' as b; +CREATE UNLOGGED TABLE IF NOT EXISTS tenant_4.tbl_4 AS SELECT 1 as a, 'text' as b WITH NO DATA; +-- the same command, no changes because of IF NOT EXISTS +CREATE UNLOGGED TABLE IF NOT EXISTS tenant_4.tbl_4 AS SELECT 1 as a, 'text' as b WITH NO DATA; +SELECT 1 as a, 'text' as b INTO tenant_4.tbl_5; + +-- verify we can query the newly created tenant tables +SELECT * FROM tenant_4.tbl_3; +SELECT COUNT(*) FROM tenant_4.tbl_5; CREATE TYPE employee_type AS (name text, salary numeric); @@ -287,8 +294,13 @@ CREATE TABLE tenant_4.employees OF employee_type ( ); -- verify that we act accordingly when if not exists is used -CREATE TABLE IF NOT EXISTS tenant_4.tbl_3(a int, b text); -CREATE TABLE IF NOT EXISTS tenant_4.tbl_3(a int, b text); +CREATE TABLE IF NOT EXISTS tenant_4.tbl_6(a int, b text); +CREATE TABLE IF NOT EXISTS tenant_4.tbl_6(a int, b text); + +SELECT logicalrelid, partmethod + FROM pg_dist_partition + WHERE logicalrelid::text LIKE 'tenant_4.tbl%' + ORDER BY logicalrelid; CREATE TABLE regular_schema.local(a int, b text); From ba40eb363c8385134fa1fc99f73376aa6833a7b4 Mon Sep 17 00:00:00 2001 From: Naisila Puka <37271756+naisila@users.noreply.github.com> Date: Wed, 14 Jun 2023 11:55:52 +0300 Subject: [PATCH 093/118] Fix some gucs' initial and boot values, and flag combinations (#6957) PG16beta1 added some sanity checks for GUCS, find the Relevant PG commits below: 1- Add check on initial and boot values when loading GUCs https://github.com/postgres/postgres/commit/a73952b795632b2cf5acada8476e7cf75857e9be 2- Extend check_GUC_init() with checks on flag combinations when loading GUCs https://github.com/postgres/postgres/commit/009f8d17146da72478fcb8f544b793c443fa254c I fixed our currently problematic GUCS, we can merge this directly into main as these make sense for any PG version. There was a particular NodeConninfo issue: Previously we would rely on the fact that NodeConninfo initial value is an empty string. However, with PG16 enforcing same initial and boot values, we can't use an empty initial value for NodeConninfo anymore. Therefore we add a new flag to indicate whether we are at boot check. --- src/backend/columnar/columnar_customscan.c | 8 +- src/backend/columnar/columnar_tableam.c | 2 +- .../citus_add_local_table_to_metadata.c | 2 +- .../connection/connection_configuration.c | 14 +- .../executor/multi_server_executor.c | 2 +- .../planner/multi_physical_planner.c | 2 +- src/backend/distributed/shared_library_init.c | 138 +++++++++--------- .../distributed/connection_management.h | 1 + 8 files changed, 92 insertions(+), 77 deletions(-) diff --git a/src/backend/columnar/columnar_customscan.c b/src/backend/columnar/columnar_customscan.c index 74c50e4f6..bd01c4faa 100644 --- a/src/backend/columnar/columnar_customscan.c +++ b/src/backend/columnar/columnar_customscan.c @@ -198,7 +198,7 @@ columnar_customscan_init() &EnableColumnarCustomScan, true, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( "columnar.enable_qual_pushdown", @@ -208,7 +208,7 @@ columnar_customscan_init() &EnableColumnarQualPushdown, true, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomRealVariable( "columnar.qual_pushdown_correlation_threshold", @@ -222,7 +222,7 @@ columnar_customscan_init() 0.0, 1.0, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomIntVariable( "columnar.max_custom_scan_paths", @@ -234,7 +234,7 @@ columnar_customscan_init() 1, 1024, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomEnumVariable( "columnar.planner_debug_level", diff --git a/src/backend/columnar/columnar_tableam.c b/src/backend/columnar/columnar_tableam.c index 07175dcbe..9fd6ba62d 100644 --- a/src/backend/columnar/columnar_tableam.c +++ b/src/backend/columnar/columnar_tableam.c @@ -2018,7 +2018,7 @@ columnar_tableam_init() &EnableVersionChecksColumnar, true, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); } diff --git a/src/backend/distributed/commands/citus_add_local_table_to_metadata.c b/src/backend/distributed/commands/citus_add_local_table_to_metadata.c index 4b2b76995..d1ba25c22 100644 --- a/src/backend/distributed/commands/citus_add_local_table_to_metadata.c +++ b/src/backend/distributed/commands/citus_add_local_table_to_metadata.c @@ -55,7 +55,7 @@ * This is used after every CREATE TABLE statement in utility_hook.c * If this variable is set to true, we add all created tables to metadata. */ -bool AddAllLocalTablesToMetadata = true; +bool AddAllLocalTablesToMetadata = false; static void citus_add_local_table_to_metadata_internal(Oid relationId, bool cascadeViaForeignKeys); diff --git a/src/backend/distributed/connection/connection_configuration.c b/src/backend/distributed/connection/connection_configuration.c index ff63c717c..bf61f7fac 100644 --- a/src/backend/distributed/connection/connection_configuration.c +++ b/src/backend/distributed/connection/connection_configuration.c @@ -24,7 +24,19 @@ #include "utils/builtins.h" /* stores the string representation of our node connection GUC */ -char *NodeConninfo = ""; +#ifdef USE_SSL +char *NodeConninfo = "sslmode=require"; +#else +char *NodeConninfo = "sslmode=prefer"; +#endif + +/* + * Previously we would use an empty initial value for NodeConnInfo + * PG16 however requires same initial and boot values for configuration parameters + * Therefore we now use this flag in NodeConninfoGucAssignHook + */ +bool checkAtBootPassed = false; + char *LocalHostName = "localhost"; /* represents a list of libpq parameter settings */ diff --git a/src/backend/distributed/executor/multi_server_executor.c b/src/backend/distributed/executor/multi_server_executor.c index d92b39bfb..07f3d6856 100644 --- a/src/backend/distributed/executor/multi_server_executor.c +++ b/src/backend/distributed/executor/multi_server_executor.c @@ -30,7 +30,7 @@ #include "distributed/worker_protocol.h" #include "utils/lsyscache.h" -int RemoteTaskCheckInterval = 100; /* per cycle sleep interval in millisecs */ +int RemoteTaskCheckInterval = 10; /* per cycle sleep interval in millisecs */ int TaskExecutorType = MULTI_EXECUTOR_ADAPTIVE; /* distributed executor type */ bool EnableRepartitionJoins = false; diff --git a/src/backend/distributed/planner/multi_physical_planner.c b/src/backend/distributed/planner/multi_physical_planner.c index cef21d33e..f0cb3122c 100644 --- a/src/backend/distributed/planner/multi_physical_planner.c +++ b/src/backend/distributed/planner/multi_physical_planner.c @@ -85,7 +85,7 @@ #include "utils/typcache.h" /* RepartitionJoinBucketCountPerNode determines bucket amount during repartitions */ -int RepartitionJoinBucketCountPerNode = 8; +int RepartitionJoinBucketCountPerNode = 4; /* Policy to use when assigning tasks to worker nodes */ int TaskAssignmentPolicy = TASK_ASSIGNMENT_GREEDY; diff --git a/src/backend/distributed/shared_library_init.c b/src/backend/distributed/shared_library_init.c index 0ed4c17c6..57bfb7197 100644 --- a/src/backend/distributed/shared_library_init.c +++ b/src/backend/distributed/shared_library_init.c @@ -885,7 +885,7 @@ RegisterCitusConfigVariables(void) &AllowModificationsFromWorkersToReplicatedTables, true, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -902,7 +902,7 @@ RegisterCitusConfigVariables(void) &AllowNestedDistributedExecution, false, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -922,7 +922,7 @@ RegisterCitusConfigVariables(void) &AllowUnsafeConstraints, false, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -942,7 +942,7 @@ RegisterCitusConfigVariables(void) &EnableAcquiringUnsafeLockFromWorkers, false, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomIntVariable( @@ -963,7 +963,7 @@ RegisterCitusConfigVariables(void) &CheckAvailableSpaceBeforeMove, true, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomStringVariable( @@ -1002,7 +1002,7 @@ RegisterCitusConfigVariables(void) &CopySwitchOverThresholdBytes, 4 * 1024 * 1024, 1, INT_MAX, PGC_USERSET, - GUC_UNIT_BYTE | GUC_NO_SHOW_ALL, + GUC_UNIT_BYTE | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomRealVariable( @@ -1073,7 +1073,7 @@ RegisterCitusConfigVariables(void) &CreateObjectPropagationMode, CREATE_OBJECT_PROPAGATION_IMMEDIATE, create_object_propagation_options, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -1146,7 +1146,7 @@ RegisterCitusConfigVariables(void) &EnableAlterDatabaseOwner, true, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -1157,7 +1157,7 @@ RegisterCitusConfigVariables(void) &EnableAlterRolePropagation, true, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -1167,7 +1167,7 @@ RegisterCitusConfigVariables(void) &EnableAlterRoleSetPropagation, true, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -1206,7 +1206,7 @@ RegisterCitusConfigVariables(void) &EnableClusterClock, true, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( "citus.enable_cost_based_connection_establishment", @@ -1217,7 +1217,7 @@ RegisterCitusConfigVariables(void) &EnableCostBasedConnectionEstablishment, true, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -1238,7 +1238,7 @@ RegisterCitusConfigVariables(void) &EnableCreateTypePropagation, true, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -1248,7 +1248,7 @@ RegisterCitusConfigVariables(void) &EnableDDLPropagation, true, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -1273,7 +1273,7 @@ RegisterCitusConfigVariables(void) &EnableFastPathRouterPlanner, true, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -1307,7 +1307,7 @@ RegisterCitusConfigVariables(void) &EnableManualChangesToShards, false, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomStringVariable( @@ -1318,7 +1318,7 @@ RegisterCitusConfigVariables(void) &EnableManualMetadataChangesForUser, "", PGC_SIGHUP, - GUC_SUPERUSER_ONLY | GUC_NO_SHOW_ALL, + GUC_SUPERUSER_ONLY | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -1328,7 +1328,7 @@ RegisterCitusConfigVariables(void) &EnableMetadataSync, true, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -1348,7 +1348,7 @@ RegisterCitusConfigVariables(void) &EnableNonColocatedRouterQueryPushdown, false, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -1368,7 +1368,7 @@ RegisterCitusConfigVariables(void) &EnableRepartitionedInsertSelect, true, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -1378,7 +1378,7 @@ RegisterCitusConfigVariables(void) &EnableRouterExecution, true, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -1403,7 +1403,7 @@ RegisterCitusConfigVariables(void) &EnableSingleHashRepartitioning, false, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -1414,7 +1414,7 @@ RegisterCitusConfigVariables(void) "and operating system name. This configuration value controls " "whether these reports are sent."), &EnableStatisticsCollection, -#if defined(HAVE_LIBCURL) && defined(ENABLE_CITUS_STATISTICS_COLLECTION) +#if defined(HAVE_LIBCURL) true, #else false, @@ -1433,7 +1433,7 @@ RegisterCitusConfigVariables(void) &EnableUniqueJobIds, true, PGC_USERSET, - GUC_SUPERUSER_ONLY | GUC_NO_SHOW_ALL, + GUC_SUPERUSER_ONLY | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -1444,7 +1444,7 @@ RegisterCitusConfigVariables(void) &EnableUnsafeTriggers, false, PGC_USERSET, - GUC_STANDARD | GUC_NO_SHOW_ALL, + GUC_STANDARD | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -1455,7 +1455,7 @@ RegisterCitusConfigVariables(void) &EnableUnsupportedFeatureMessages, true, PGC_SUSET, - GUC_SUPERUSER_ONLY | GUC_NO_SHOW_ALL, + GUC_SUPERUSER_ONLY | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -1465,7 +1465,7 @@ RegisterCitusConfigVariables(void) &EnableVersionChecks, true, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -1478,7 +1478,7 @@ RegisterCitusConfigVariables(void) &EnforceForeignKeyRestrictions, true, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -1489,7 +1489,7 @@ RegisterCitusConfigVariables(void) &EnforceLocalObjectRestrictions, true, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomIntVariable( @@ -1505,7 +1505,7 @@ RegisterCitusConfigVariables(void) &ExecutorSlowStartInterval, 10, 0, INT_MAX, PGC_USERSET, - GUC_UNIT_MS | GUC_NO_SHOW_ALL, + GUC_UNIT_MS | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -1545,7 +1545,7 @@ RegisterCitusConfigVariables(void) &ExplainDistributedQueries, true, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -1561,7 +1561,7 @@ RegisterCitusConfigVariables(void) &ForceMaxQueryParallelization, false, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -1575,7 +1575,7 @@ RegisterCitusConfigVariables(void) &FunctionOpensTransactionBlock, true, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomStringVariable( @@ -1587,7 +1587,7 @@ RegisterCitusConfigVariables(void) &GrepRemoteCommands, "", PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -1599,7 +1599,7 @@ RegisterCitusConfigVariables(void) &HideCitusDependentObjects, false, PGC_USERSET, - GUC_SUPERUSER_ONLY | GUC_NO_SHOW_ALL, + GUC_SUPERUSER_ONLY | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); /* @@ -1619,7 +1619,7 @@ RegisterCitusConfigVariables(void) &DeprecatedEmptyString, "", PGC_SUSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomIntVariable( @@ -1629,7 +1629,7 @@ RegisterCitusConfigVariables(void) &IsolationTestSessionProcessID, -1, -1, INT_MAX, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomIntVariable( @@ -1639,7 +1639,7 @@ RegisterCitusConfigVariables(void) &IsolationTestSessionRemoteProcessID, -1, -1, INT_MAX, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomIntVariable( @@ -1664,7 +1664,7 @@ RegisterCitusConfigVariables(void) &LocalCopyFlushThresholdByte, 512 * 1024, 1, INT_MAX, PGC_USERSET, - GUC_UNIT_BYTE | GUC_NO_SHOW_ALL, + GUC_UNIT_BYTE | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomStringVariable( @@ -1721,7 +1721,7 @@ RegisterCitusConfigVariables(void) &LogDistributedDeadlockDetection, false, PGC_SIGHUP, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -1731,7 +1731,7 @@ RegisterCitusConfigVariables(void) &LogIntermediateResults, false, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -1742,7 +1742,7 @@ RegisterCitusConfigVariables(void) &LogLocalCommands, false, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -1753,7 +1753,7 @@ RegisterCitusConfigVariables(void) &LogMultiJoinOrder, false, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -1775,7 +1775,7 @@ RegisterCitusConfigVariables(void) &LogicalReplicationTimeout, 2 * 60 * 60 * 1000, 0, 7 * 24 * 3600 * 1000, PGC_SIGHUP, - GUC_NO_SHOW_ALL | GUC_UNIT_MS, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE | GUC_UNIT_MS, NULL, NULL, NULL); DefineCustomIntVariable( @@ -1910,7 +1910,7 @@ RegisterCitusConfigVariables(void) &MaxRebalancerLoggedIgnoredMoves, 5, -1, INT_MAX, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomIntVariable( @@ -1955,7 +1955,7 @@ RegisterCitusConfigVariables(void) &MetadataSyncInterval, 60 * MS_PER_SECOND, 1, 7 * MS_PER_DAY, PGC_SIGHUP, - GUC_UNIT_MS | GUC_NO_SHOW_ALL, + GUC_UNIT_MS | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomEnumVariable( @@ -1970,7 +1970,7 @@ RegisterCitusConfigVariables(void) &MetadataSyncTransMode, METADATA_SYNC_TRANSACTIONAL, metadata_sync_mode_options, PGC_SUSET, - GUC_SUPERUSER_ONLY | GUC_NO_SHOW_ALL, + GUC_SUPERUSER_ONLY | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomIntVariable( @@ -1982,7 +1982,7 @@ RegisterCitusConfigVariables(void) &MetadataSyncRetryInterval, 5 * MS_PER_SECOND, 1, 7 * MS_PER_DAY, PGC_SIGHUP, - GUC_UNIT_MS | GUC_NO_SHOW_ALL, + GUC_UNIT_MS | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); /* @@ -2000,7 +2000,7 @@ RegisterCitusConfigVariables(void) &MitmfifoEmptyString, "", PGC_SUSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomEnumVariable( @@ -2035,7 +2035,7 @@ RegisterCitusConfigVariables(void) &NextCleanupRecordId, 0, 0, INT_MAX, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomIntVariable( @@ -2050,7 +2050,7 @@ RegisterCitusConfigVariables(void) &NextOperationId, 0, 0, INT_MAX, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomIntVariable( @@ -2065,7 +2065,7 @@ RegisterCitusConfigVariables(void) &NextPlacementId, 0, 0, INT_MAX, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomIntVariable( @@ -2080,7 +2080,7 @@ RegisterCitusConfigVariables(void) &NextShardId, 0, 0, INT_MAX, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomIntVariable( @@ -2119,7 +2119,7 @@ RegisterCitusConfigVariables(void) &OverrideTableVisibility, true, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -2134,7 +2134,7 @@ RegisterCitusConfigVariables(void) &PreventIncompleteConnectionEstablishment, true, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -2145,7 +2145,7 @@ RegisterCitusConfigVariables(void) &PropagateSessionSettingsForLoopbackConnection, false, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomEnumVariable( @@ -2183,7 +2183,7 @@ RegisterCitusConfigVariables(void) &RemoteCopyFlushThreshold, 8 * 1024 * 1024, 0, INT_MAX, PGC_USERSET, - GUC_UNIT_BYTE | GUC_NO_SHOW_ALL, + GUC_UNIT_BYTE | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomIntVariable( @@ -2207,7 +2207,7 @@ RegisterCitusConfigVariables(void) &RepartitionJoinBucketCountPerNode, 4, 1, INT_MAX, PGC_SIGHUP, - GUC_STANDARD | GUC_NO_SHOW_ALL, + GUC_STANDARD | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); /* deprecated setting */ @@ -2218,7 +2218,7 @@ RegisterCitusConfigVariables(void) &DeprecatedReplicateReferenceTablesOnActivate, true, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomEnumVariable( @@ -2231,7 +2231,7 @@ RegisterCitusConfigVariables(void) REPLICATION_MODEL_STREAMING, replication_model_options, PGC_SUSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, WarnIfReplicationModelIsSet, NULL, NULL); DefineCustomBoolVariable( @@ -2244,7 +2244,7 @@ RegisterCitusConfigVariables(void) &RunningUnderIsolationTest, false, PGC_SUSET, - GUC_SUPERUSER_ONLY | GUC_NO_SHOW_ALL, + GUC_SUPERUSER_ONLY | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -2261,7 +2261,7 @@ RegisterCitusConfigVariables(void) &SelectOpensTransactionBlock, true, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomIntVariable( @@ -2318,7 +2318,7 @@ RegisterCitusConfigVariables(void) &SkipAdvisoryLockPermissionChecks, false, GUC_SUPERUSER_ONLY, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomBoolVariable( @@ -2363,7 +2363,7 @@ RegisterCitusConfigVariables(void) &SortReturning, false, PGC_SUSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); /* @@ -2379,7 +2379,7 @@ RegisterCitusConfigVariables(void) &StatStatementsMax, 50000, 1000, 10000000, PGC_POSTMASTER, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomIntVariable( @@ -2390,7 +2390,7 @@ RegisterCitusConfigVariables(void) &StatStatementsPurgeInterval, 10, -1, INT_MAX, PGC_SIGHUP, - GUC_UNIT_MS | GUC_NO_SHOW_ALL, + GUC_UNIT_MS | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NULL, NULL, NULL); DefineCustomEnumVariable( @@ -2476,7 +2476,7 @@ RegisterCitusConfigVariables(void) &SubqueryPushdown, false, PGC_USERSET, - GUC_NO_SHOW_ALL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, NoticeIfSubqueryPushdownEnabled, NULL, NULL); DefineCustomEnumVariable( @@ -2876,12 +2876,14 @@ NodeConninfoGucAssignHook(const char *newval, void *extra) newval = ""; } - if (strcmp(newval, NodeConninfo) == 0) + if (strcmp(newval, NodeConninfo) == 0 && checkAtBootPassed) { /* It did not change, no need to do anything */ return; } + checkAtBootPassed = true; + PQconninfoOption *optionArray = PQconninfoParse(newval, NULL); if (optionArray == NULL) { diff --git a/src/include/distributed/connection_management.h b/src/include/distributed/connection_management.h index 8c2584451..96bed3457 100644 --- a/src/include/distributed/connection_management.h +++ b/src/include/distributed/connection_management.h @@ -285,6 +285,7 @@ extern int MaxCachedConnectionLifetime; /* parameters used for outbound connections */ extern char *NodeConninfo; extern char *LocalHostName; +extern bool checkAtBootPassed; /* the hash tables are externally accessiable */ extern HTAB *ConnectionHash; From dbdf04e8bad0352a2e352ad0498f0c11f12f032e Mon Sep 17 00:00:00 2001 From: Onur Tirtir Date: Wed, 14 Jun 2023 12:12:15 +0300 Subject: [PATCH 094/118] Rename pg_dist tenant_schema to pg_dist_schema (#7001) --- .../distributed/metadata/metadata_cache.c | 12 +- .../distributed/metadata/metadata_sync.c | 10 +- .../distributed/sql/citus--11.3-1--12.0-1.sql | 14 +- .../sql/downgrades/citus--12.0-1--11.3-1.sql | 4 +- .../sql/udfs/citus_drop_trigger/12.0-1.sql | 4 +- .../sql/udfs/citus_drop_trigger/latest.sql | 4 +- .../udfs/citus_finish_pg_upgrade/12.0-1.sql | 4 +- .../udfs/citus_finish_pg_upgrade/latest.sql | 4 +- .../12.0-1.sql | 2 +- .../latest.sql | 2 +- .../12.0-1.sql | 2 +- .../latest.sql | 2 +- .../udfs/citus_prepare_pg_upgrade/12.0-1.sql | 4 +- .../udfs/citus_prepare_pg_upgrade/latest.sql | 4 +- .../sql/udfs/citus_shards/12.0-1.sql | 2 +- .../sql/udfs/citus_shards/latest.sql | 2 +- .../udfs/citus_stat_tenants_local/12.0-1.sql | 2 +- .../udfs/citus_stat_tenants_local/latest.sql | 2 +- .../sql/udfs/citus_tables/12.0-1.sql | 2 +- .../sql/udfs/citus_tables/latest.sql | 2 +- .../utils/tenant_schema_metadata.c | 28 ++-- src/include/distributed/metadata_sync.h | 2 +- src/include/distributed/pg_dist_schema.h | 41 +++++ .../distributed/pg_dist_tenant_schema.h | 41 ----- .../citus_schema_distribute_undistribute.out | 40 ++--- ...n_citus_schema_distribute_undistribute.out | 100 ++++++------ src/test/regress/expected/multi_extension.out | 6 +- .../regress/expected/multi_metadata_sync.out | 14 +- .../expected/multi_metadata_sync_0.out | 14 +- .../expected/schema_based_sharding.out | 154 +++++++++--------- src/test/regress/expected/single_node.out | 4 +- src/test/regress/expected/single_node_0.out | 4 +- .../expected/upgrade_list_citus_objects.out | 2 +- .../upgrade_schema_based_sharding_after.out | 14 +- ..._citus_schema_distribute_undistribute.spec | 2 +- .../citus_schema_distribute_undistribute.sql | 40 ++--- src/test/regress/sql/multi_extension.sql | 4 +- .../regress/sql/schema_based_sharding.sql | 154 +++++++++--------- src/test/regress/sql/single_node.sql | 4 +- .../upgrade_schema_based_sharding_after.sql | 14 +- 40 files changed, 383 insertions(+), 383 deletions(-) create mode 100644 src/include/distributed/pg_dist_schema.h delete mode 100644 src/include/distributed/pg_dist_tenant_schema.h diff --git a/src/backend/distributed/metadata/metadata_cache.c b/src/backend/distributed/metadata/metadata_cache.c index ae81e6d8e..ebc048376 100644 --- a/src/backend/distributed/metadata/metadata_cache.c +++ b/src/backend/distributed/metadata/metadata_cache.c @@ -2846,33 +2846,33 @@ DistColocationConfigurationIndexId(void) } -/* return oid of pg_dist_tenant_schema relation */ +/* return oid of pg_dist_schema relation */ Oid DistTenantSchemaRelationId(void) { - CachedRelationLookup("pg_dist_tenant_schema", + CachedRelationLookup("pg_dist_schema", &MetadataCache.distTenantSchemaRelationId); return MetadataCache.distTenantSchemaRelationId; } -/* return oid of pg_dist_tenant_schema_pkey index */ +/* return oid of pg_dist_schema_pkey index */ Oid DistTenantSchemaPrimaryKeyIndexId(void) { - CachedRelationLookup("pg_dist_tenant_schema_pkey", + CachedRelationLookup("pg_dist_schema_pkey", &MetadataCache.distTenantSchemaPrimaryKeyIndexId); return MetadataCache.distTenantSchemaPrimaryKeyIndexId; } -/* return oid of pg_dist_tenant_schema_unique_colocationid_index index */ +/* return oid of pg_dist_schema_unique_colocationid_index index */ Oid DistTenantSchemaUniqueColocationIdIndexId(void) { - CachedRelationLookup("pg_dist_tenant_schema_unique_colocationid_index", + CachedRelationLookup("pg_dist_schema_unique_colocationid_index", &MetadataCache.distTenantSchemaUniqueColocationIdIndexId); return MetadataCache.distTenantSchemaUniqueColocationIdIndexId; diff --git a/src/backend/distributed/metadata/metadata_sync.c b/src/backend/distributed/metadata/metadata_sync.c index 2dfc59eeb..5f8f76bd6 100644 --- a/src/backend/distributed/metadata/metadata_sync.c +++ b/src/backend/distributed/metadata/metadata_sync.c @@ -61,7 +61,7 @@ #include "distributed/pg_dist_colocation.h" #include "distributed/pg_dist_node.h" #include "distributed/pg_dist_shard.h" -#include "distributed/pg_dist_tenant_schema.h" +#include "distributed/pg_dist_schema.h" #include "distributed/relation_access_tracking.h" #include "distributed/remote_commands.h" #include "distributed/resource_lock.h" @@ -4452,7 +4452,7 @@ SyncDistributedObjects(MetadataSyncContext *context) SendDistObjectCommands(context); /* - * Commands to insert pg_dist_tenant_schema entries. + * Commands to insert pg_dist_schema entries. * * Need to be done after syncing distributed objects because the schemas * need to exist on the worker. @@ -4532,7 +4532,7 @@ SendMetadataDeletionCommands(MetadataSyncContext *context) /* remove pg_dist_colocation entries */ SendOrCollectCommandListToActivatedNodes(context, list_make1(DELETE_ALL_COLOCATION)); - /* remove pg_dist_tenant_schema entries */ + /* remove pg_dist_schema entries */ SendOrCollectCommandListToActivatedNodes(context, list_make1(DELETE_ALL_TENANT_SCHEMAS)); } @@ -4662,8 +4662,8 @@ SendTenantSchemaMetadataCommands(MetadataSyncContext *context) break; } - Form_pg_dist_tenant_schema tenantSchemaForm = - (Form_pg_dist_tenant_schema) GETSTRUCT(heapTuple); + Form_pg_dist_schema tenantSchemaForm = + (Form_pg_dist_schema) GETSTRUCT(heapTuple); StringInfo insertTenantSchemaCommand = makeStringInfo(); appendStringInfo(insertTenantSchemaCommand, diff --git a/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql b/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql index 90ff57412..7f075ba14 100644 --- a/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql +++ b/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql @@ -2,25 +2,25 @@ -- bump version to 12.0-1 -CREATE TABLE citus.pg_dist_tenant_schema ( +CREATE TABLE citus.pg_dist_schema ( schemaid oid NOT NULL, colocationid int NOT NULL, - CONSTRAINT pg_dist_tenant_schema_pkey PRIMARY KEY (schemaid), - CONSTRAINT pg_dist_tenant_schema_unique_colocationid_index UNIQUE (colocationid) + CONSTRAINT pg_dist_schema_pkey PRIMARY KEY (schemaid), + CONSTRAINT pg_dist_schema_unique_colocationid_index UNIQUE (colocationid) ); -ALTER TABLE citus.pg_dist_tenant_schema SET SCHEMA pg_catalog; +ALTER TABLE citus.pg_dist_schema SET SCHEMA pg_catalog; -GRANT SELECT ON pg_catalog.pg_dist_tenant_schema TO public; +GRANT SELECT ON pg_catalog.pg_dist_schema TO public; --- udfs used to modify pg_dist_tenant_schema on workers, to sync metadata +-- udfs used to modify pg_dist_schema on workers, to sync metadata #include "udfs/citus_internal_add_tenant_schema/12.0-1.sql" #include "udfs/citus_internal_delete_tenant_schema/12.0-1.sql" #include "udfs/citus_prepare_pg_upgrade/12.0-1.sql" #include "udfs/citus_finish_pg_upgrade/12.0-1.sql" --- udfs used to modify pg_dist_tenant_schema globally via drop trigger +-- udfs used to modify pg_dist_schema globally via drop trigger #include "udfs/citus_internal_unregister_tenant_schema_globally/12.0-1.sql" #include "udfs/citus_drop_trigger/12.0-1.sql" diff --git a/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql b/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql index 66e1001eb..251c22366 100644 --- a/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql +++ b/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql @@ -3,7 +3,7 @@ DO $$ BEGIN -- Throw an error if user has created any tenant schemas. - IF EXISTS (SELECT 1 FROM pg_catalog.pg_dist_tenant_schema) + IF EXISTS (SELECT 1 FROM pg_catalog.pg_dist_schema) THEN RAISE EXCEPTION 'cannot downgrade Citus because there are ' 'tenant schemas created.' @@ -54,7 +54,7 @@ DROP FUNCTION pg_catalog.citus_shard_sizes; #include "../udfs/citus_tables/11.1-1.sql" #include "../udfs/citus_shards/11.1-1.sql" -DROP TABLE pg_catalog.pg_dist_tenant_schema; +DROP TABLE pg_catalog.pg_dist_schema; DROP VIEW pg_catalog.citus_stat_tenants_local; DROP FUNCTION pg_catalog.citus_stat_tenants_local_internal( diff --git a/src/backend/distributed/sql/udfs/citus_drop_trigger/12.0-1.sql b/src/backend/distributed/sql/udfs/citus_drop_trigger/12.0-1.sql index 494331d4d..312099aeb 100644 --- a/src/backend/distributed/sql/udfs/citus_drop_trigger/12.0-1.sql +++ b/src/backend/distributed/sql/udfs/citus_drop_trigger/12.0-1.sql @@ -36,13 +36,13 @@ BEGIN FOR v_obj IN SELECT * FROM pg_event_trigger_dropped_objects() LOOP - -- Remove entries from pg_catalog.pg_dist_tenant_schema for all dropped tenant schemas. + -- Remove entries from pg_catalog.pg_dist_schema for all dropped tenant schemas. -- Also delete the corresponding colocation group from pg_catalog.pg_dist_colocation. -- -- Although normally we automatically delete the colocation groups when they become empty, -- we don't do so for the colocation groups that are created for tenant schemas. For this -- reason, here we need to delete the colocation group when the tenant schema is dropped. - IF v_obj.object_type = 'schema' AND EXISTS (SELECT 1 FROM pg_catalog.pg_dist_tenant_schema WHERE schemaid = v_obj.objid) + IF v_obj.object_type = 'schema' AND EXISTS (SELECT 1 FROM pg_catalog.pg_dist_schema WHERE schemaid = v_obj.objid) THEN PERFORM pg_catalog.citus_internal_unregister_tenant_schema_globally(v_obj.objid, v_obj.object_name); END IF; diff --git a/src/backend/distributed/sql/udfs/citus_drop_trigger/latest.sql b/src/backend/distributed/sql/udfs/citus_drop_trigger/latest.sql index 494331d4d..312099aeb 100644 --- a/src/backend/distributed/sql/udfs/citus_drop_trigger/latest.sql +++ b/src/backend/distributed/sql/udfs/citus_drop_trigger/latest.sql @@ -36,13 +36,13 @@ BEGIN FOR v_obj IN SELECT * FROM pg_event_trigger_dropped_objects() LOOP - -- Remove entries from pg_catalog.pg_dist_tenant_schema for all dropped tenant schemas. + -- Remove entries from pg_catalog.pg_dist_schema for all dropped tenant schemas. -- Also delete the corresponding colocation group from pg_catalog.pg_dist_colocation. -- -- Although normally we automatically delete the colocation groups when they become empty, -- we don't do so for the colocation groups that are created for tenant schemas. For this -- reason, here we need to delete the colocation group when the tenant schema is dropped. - IF v_obj.object_type = 'schema' AND EXISTS (SELECT 1 FROM pg_catalog.pg_dist_tenant_schema WHERE schemaid = v_obj.objid) + IF v_obj.object_type = 'schema' AND EXISTS (SELECT 1 FROM pg_catalog.pg_dist_schema WHERE schemaid = v_obj.objid) THEN PERFORM pg_catalog.citus_internal_unregister_tenant_schema_globally(v_obj.objid, v_obj.object_name); END IF; diff --git a/src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/12.0-1.sql b/src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/12.0-1.sql index 9b3943cf3..6dd46607a 100644 --- a/src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/12.0-1.sql +++ b/src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/12.0-1.sql @@ -63,7 +63,7 @@ BEGIN INSERT INTO pg_catalog.pg_dist_transaction SELECT * FROM public.pg_dist_transaction; INSERT INTO pg_catalog.pg_dist_colocation SELECT * FROM public.pg_dist_colocation; INSERT INTO pg_catalog.pg_dist_cleanup SELECT * FROM public.pg_dist_cleanup; - INSERT INTO pg_catalog.pg_dist_tenant_schema SELECT schemaname::regnamespace, colocationid FROM public.pg_dist_tenant_schema; + INSERT INTO pg_catalog.pg_dist_schema SELECT schemaname::regnamespace, colocationid FROM public.pg_dist_schema; -- enterprise catalog tables INSERT INTO pg_catalog.pg_dist_authinfo SELECT * FROM public.pg_dist_authinfo; INSERT INTO pg_catalog.pg_dist_poolinfo SELECT * FROM public.pg_dist_poolinfo; @@ -94,7 +94,7 @@ BEGIN DROP TABLE public.pg_dist_transaction; DROP TABLE public.pg_dist_rebalance_strategy; DROP TABLE public.pg_dist_cleanup; - DROP TABLE public.pg_dist_tenant_schema; + DROP TABLE public.pg_dist_schema; -- -- reset sequences -- diff --git a/src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/latest.sql b/src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/latest.sql index 9b3943cf3..6dd46607a 100644 --- a/src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/latest.sql +++ b/src/backend/distributed/sql/udfs/citus_finish_pg_upgrade/latest.sql @@ -63,7 +63,7 @@ BEGIN INSERT INTO pg_catalog.pg_dist_transaction SELECT * FROM public.pg_dist_transaction; INSERT INTO pg_catalog.pg_dist_colocation SELECT * FROM public.pg_dist_colocation; INSERT INTO pg_catalog.pg_dist_cleanup SELECT * FROM public.pg_dist_cleanup; - INSERT INTO pg_catalog.pg_dist_tenant_schema SELECT schemaname::regnamespace, colocationid FROM public.pg_dist_tenant_schema; + INSERT INTO pg_catalog.pg_dist_schema SELECT schemaname::regnamespace, colocationid FROM public.pg_dist_schema; -- enterprise catalog tables INSERT INTO pg_catalog.pg_dist_authinfo SELECT * FROM public.pg_dist_authinfo; INSERT INTO pg_catalog.pg_dist_poolinfo SELECT * FROM public.pg_dist_poolinfo; @@ -94,7 +94,7 @@ BEGIN DROP TABLE public.pg_dist_transaction; DROP TABLE public.pg_dist_rebalance_strategy; DROP TABLE public.pg_dist_cleanup; - DROP TABLE public.pg_dist_tenant_schema; + DROP TABLE public.pg_dist_schema; -- -- reset sequences -- diff --git a/src/backend/distributed/sql/udfs/citus_internal_add_tenant_schema/12.0-1.sql b/src/backend/distributed/sql/udfs/citus_internal_add_tenant_schema/12.0-1.sql index a97a2544d..56b3cae84 100644 --- a/src/backend/distributed/sql/udfs/citus_internal_add_tenant_schema/12.0-1.sql +++ b/src/backend/distributed/sql/udfs/citus_internal_add_tenant_schema/12.0-1.sql @@ -5,4 +5,4 @@ CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_add_tenant_schema(schema_id AS 'MODULE_PATHNAME'; COMMENT ON FUNCTION pg_catalog.citus_internal_add_tenant_schema(Oid, int) IS - 'insert given tenant schema into pg_dist_tenant_schema with given colocation id'; + 'insert given tenant schema into pg_dist_schema with given colocation id'; diff --git a/src/backend/distributed/sql/udfs/citus_internal_add_tenant_schema/latest.sql b/src/backend/distributed/sql/udfs/citus_internal_add_tenant_schema/latest.sql index a97a2544d..56b3cae84 100644 --- a/src/backend/distributed/sql/udfs/citus_internal_add_tenant_schema/latest.sql +++ b/src/backend/distributed/sql/udfs/citus_internal_add_tenant_schema/latest.sql @@ -5,4 +5,4 @@ CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_add_tenant_schema(schema_id AS 'MODULE_PATHNAME'; COMMENT ON FUNCTION pg_catalog.citus_internal_add_tenant_schema(Oid, int) IS - 'insert given tenant schema into pg_dist_tenant_schema with given colocation id'; + 'insert given tenant schema into pg_dist_schema with given colocation id'; diff --git a/src/backend/distributed/sql/udfs/citus_internal_delete_tenant_schema/12.0-1.sql b/src/backend/distributed/sql/udfs/citus_internal_delete_tenant_schema/12.0-1.sql index 839a5b18a..4a2bf0067 100644 --- a/src/backend/distributed/sql/udfs/citus_internal_delete_tenant_schema/12.0-1.sql +++ b/src/backend/distributed/sql/udfs/citus_internal_delete_tenant_schema/12.0-1.sql @@ -5,4 +5,4 @@ CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_delete_tenant_schema(schema AS 'MODULE_PATHNAME'; COMMENT ON FUNCTION pg_catalog.citus_internal_delete_tenant_schema(Oid) IS - 'delete given tenant schema from pg_dist_tenant_schema'; + 'delete given tenant schema from pg_dist_schema'; diff --git a/src/backend/distributed/sql/udfs/citus_internal_delete_tenant_schema/latest.sql b/src/backend/distributed/sql/udfs/citus_internal_delete_tenant_schema/latest.sql index 839a5b18a..4a2bf0067 100644 --- a/src/backend/distributed/sql/udfs/citus_internal_delete_tenant_schema/latest.sql +++ b/src/backend/distributed/sql/udfs/citus_internal_delete_tenant_schema/latest.sql @@ -5,4 +5,4 @@ CREATE OR REPLACE FUNCTION pg_catalog.citus_internal_delete_tenant_schema(schema AS 'MODULE_PATHNAME'; COMMENT ON FUNCTION pg_catalog.citus_internal_delete_tenant_schema(Oid) IS - 'delete given tenant schema from pg_dist_tenant_schema'; + 'delete given tenant schema from pg_dist_schema'; diff --git a/src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/12.0-1.sql b/src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/12.0-1.sql index a7c368930..31fa793a6 100644 --- a/src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/12.0-1.sql +++ b/src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/12.0-1.sql @@ -33,7 +33,7 @@ BEGIN DROP TABLE IF EXISTS public.pg_dist_rebalance_strategy; DROP TABLE IF EXISTS public.pg_dist_object; DROP TABLE IF EXISTS public.pg_dist_cleanup; - DROP TABLE IF EXISTS public.pg_dist_tenant_schema; + DROP TABLE IF EXISTS public.pg_dist_schema; DROP TABLE IF EXISTS public.pg_dist_clock_logical_seq; -- @@ -49,7 +49,7 @@ BEGIN CREATE TABLE public.pg_dist_colocation AS SELECT * FROM pg_catalog.pg_dist_colocation; CREATE TABLE public.pg_dist_cleanup AS SELECT * FROM pg_catalog.pg_dist_cleanup; -- save names of the tenant schemas instead of their oids because the oids might change after pg upgrade - CREATE TABLE public.pg_dist_tenant_schema AS SELECT schemaid::regnamespace::text AS schemaname, colocationid FROM pg_catalog.pg_dist_tenant_schema; + CREATE TABLE public.pg_dist_schema AS SELECT schemaid::regnamespace::text AS schemaname, colocationid FROM pg_catalog.pg_dist_schema; -- enterprise catalog tables CREATE TABLE public.pg_dist_authinfo AS SELECT * FROM pg_catalog.pg_dist_authinfo; CREATE TABLE public.pg_dist_poolinfo AS SELECT * FROM pg_catalog.pg_dist_poolinfo; diff --git a/src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/latest.sql b/src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/latest.sql index a7c368930..31fa793a6 100644 --- a/src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/latest.sql +++ b/src/backend/distributed/sql/udfs/citus_prepare_pg_upgrade/latest.sql @@ -33,7 +33,7 @@ BEGIN DROP TABLE IF EXISTS public.pg_dist_rebalance_strategy; DROP TABLE IF EXISTS public.pg_dist_object; DROP TABLE IF EXISTS public.pg_dist_cleanup; - DROP TABLE IF EXISTS public.pg_dist_tenant_schema; + DROP TABLE IF EXISTS public.pg_dist_schema; DROP TABLE IF EXISTS public.pg_dist_clock_logical_seq; -- @@ -49,7 +49,7 @@ BEGIN CREATE TABLE public.pg_dist_colocation AS SELECT * FROM pg_catalog.pg_dist_colocation; CREATE TABLE public.pg_dist_cleanup AS SELECT * FROM pg_catalog.pg_dist_cleanup; -- save names of the tenant schemas instead of their oids because the oids might change after pg upgrade - CREATE TABLE public.pg_dist_tenant_schema AS SELECT schemaid::regnamespace::text AS schemaname, colocationid FROM pg_catalog.pg_dist_tenant_schema; + CREATE TABLE public.pg_dist_schema AS SELECT schemaid::regnamespace::text AS schemaname, colocationid FROM pg_catalog.pg_dist_schema; -- enterprise catalog tables CREATE TABLE public.pg_dist_authinfo AS SELECT * FROM pg_catalog.pg_dist_authinfo; CREATE TABLE public.pg_dist_poolinfo AS SELECT * FROM pg_catalog.pg_dist_poolinfo; diff --git a/src/backend/distributed/sql/udfs/citus_shards/12.0-1.sql b/src/backend/distributed/sql/udfs/citus_shards/12.0-1.sql index be9b25167..f1be9219d 100644 --- a/src/backend/distributed/sql/udfs/citus_shards/12.0-1.sql +++ b/src/backend/distributed/sql/udfs/citus_shards/12.0-1.sql @@ -3,7 +3,7 @@ SELECT pg_dist_shard.logicalrelid AS table_name, pg_dist_shard.shardid, shard_name(pg_dist_shard.logicalrelid, pg_dist_shard.shardid) as shard_name, - CASE WHEN colocationid IN (SELECT colocationid FROM pg_dist_tenant_schema) THEN 'schema' + CASE WHEN colocationid IN (SELECT colocationid FROM pg_dist_schema) THEN 'schema' WHEN partkey IS NOT NULL THEN 'distributed' WHEN repmodel = 't' THEN 'reference' WHEN colocationid = 0 THEN 'local' diff --git a/src/backend/distributed/sql/udfs/citus_shards/latest.sql b/src/backend/distributed/sql/udfs/citus_shards/latest.sql index be9b25167..f1be9219d 100644 --- a/src/backend/distributed/sql/udfs/citus_shards/latest.sql +++ b/src/backend/distributed/sql/udfs/citus_shards/latest.sql @@ -3,7 +3,7 @@ SELECT pg_dist_shard.logicalrelid AS table_name, pg_dist_shard.shardid, shard_name(pg_dist_shard.logicalrelid, pg_dist_shard.shardid) as shard_name, - CASE WHEN colocationid IN (SELECT colocationid FROM pg_dist_tenant_schema) THEN 'schema' + CASE WHEN colocationid IN (SELECT colocationid FROM pg_dist_schema) THEN 'schema' WHEN partkey IS NOT NULL THEN 'distributed' WHEN repmodel = 't' THEN 'reference' WHEN colocationid = 0 THEN 'local' diff --git a/src/backend/distributed/sql/udfs/citus_stat_tenants_local/12.0-1.sql b/src/backend/distributed/sql/udfs/citus_stat_tenants_local/12.0-1.sql index 9323a4530..65b398a0a 100644 --- a/src/backend/distributed/sql/udfs/citus_stat_tenants_local/12.0-1.sql +++ b/src/backend/distributed/sql/udfs/citus_stat_tenants_local/12.0-1.sql @@ -40,7 +40,7 @@ BEGIN L.cpu_usage_in_last_period, L.score FROM pg_catalog.citus_stat_tenants_local_internal(return_all_tenants) L - LEFT JOIN pg_dist_tenant_schema S ON L.tenant_attribute IS NULL AND L.colocation_id = S.colocationid + LEFT JOIN pg_dist_schema S ON L.tenant_attribute IS NULL AND L.colocation_id = S.colocationid LEFT JOIN pg_namespace N ON N.oid = S.schemaid ORDER BY L.score DESC; END; diff --git a/src/backend/distributed/sql/udfs/citus_stat_tenants_local/latest.sql b/src/backend/distributed/sql/udfs/citus_stat_tenants_local/latest.sql index 9323a4530..65b398a0a 100644 --- a/src/backend/distributed/sql/udfs/citus_stat_tenants_local/latest.sql +++ b/src/backend/distributed/sql/udfs/citus_stat_tenants_local/latest.sql @@ -40,7 +40,7 @@ BEGIN L.cpu_usage_in_last_period, L.score FROM pg_catalog.citus_stat_tenants_local_internal(return_all_tenants) L - LEFT JOIN pg_dist_tenant_schema S ON L.tenant_attribute IS NULL AND L.colocation_id = S.colocationid + LEFT JOIN pg_dist_schema S ON L.tenant_attribute IS NULL AND L.colocation_id = S.colocationid LEFT JOIN pg_namespace N ON N.oid = S.schemaid ORDER BY L.score DESC; END; diff --git a/src/backend/distributed/sql/udfs/citus_tables/12.0-1.sql b/src/backend/distributed/sql/udfs/citus_tables/12.0-1.sql index 565b263f7..608fe8387 100644 --- a/src/backend/distributed/sql/udfs/citus_tables/12.0-1.sql +++ b/src/backend/distributed/sql/udfs/citus_tables/12.0-1.sql @@ -6,7 +6,7 @@ citus_tables_create_query=$CTCQ$ CREATE OR REPLACE VIEW %I.citus_tables AS SELECT logicalrelid AS table_name, - CASE WHEN colocationid IN (SELECT colocationid FROM pg_dist_tenant_schema) THEN 'schema' + CASE WHEN colocationid IN (SELECT colocationid FROM pg_dist_schema) THEN 'schema' WHEN partkey IS NOT NULL THEN 'distributed' WHEN repmodel = 't' THEN 'reference' WHEN colocationid = 0 THEN 'local' diff --git a/src/backend/distributed/sql/udfs/citus_tables/latest.sql b/src/backend/distributed/sql/udfs/citus_tables/latest.sql index 565b263f7..608fe8387 100644 --- a/src/backend/distributed/sql/udfs/citus_tables/latest.sql +++ b/src/backend/distributed/sql/udfs/citus_tables/latest.sql @@ -6,7 +6,7 @@ citus_tables_create_query=$CTCQ$ CREATE OR REPLACE VIEW %I.citus_tables AS SELECT logicalrelid AS table_name, - CASE WHEN colocationid IN (SELECT colocationid FROM pg_dist_tenant_schema) THEN 'schema' + CASE WHEN colocationid IN (SELECT colocationid FROM pg_dist_schema) THEN 'schema' WHEN partkey IS NOT NULL THEN 'distributed' WHEN repmodel = 't' THEN 'reference' WHEN colocationid = 0 THEN 'local' diff --git a/src/backend/distributed/utils/tenant_schema_metadata.c b/src/backend/distributed/utils/tenant_schema_metadata.c index 0bac720b6..a83842541 100644 --- a/src/backend/distributed/utils/tenant_schema_metadata.c +++ b/src/backend/distributed/utils/tenant_schema_metadata.c @@ -17,7 +17,7 @@ #include "distributed/colocation_utils.h" #include "distributed/metadata_cache.h" #include "distributed/metadata_sync.h" -#include "distributed/pg_dist_tenant_schema.h" +#include "distributed/pg_dist_schema.h" #include "distributed/tenant_schema_metadata.h" #include "storage/lockdefs.h" #include "utils/relcache.h" @@ -33,7 +33,7 @@ IsTenantSchema(Oid schemaId) /* * We don't allow creating tenant schemas when there is a version * mismatch. Even more, SchemaIdGetTenantColocationId() would throw an - * error if the underlying pg_dist_tenant_schema metadata table has not + * error if the underlying pg_dist_schema metadata table has not * been created yet, which is the case in older versions. For this reason, * it's safe to assume that it cannot be a tenant schema when there is a * version mismatch. @@ -45,7 +45,7 @@ IsTenantSchema(Oid schemaId) * fail when deciding whether we should create a tenant table or not. * * The downside of doing so is that, for example, we will skip deleting - * the tenant schema entry from pg_dist_tenant_schema when dropping a + * the tenant schema entry from pg_dist_schema when dropping a * tenant schema while the version checks are disabled even if there was * no version mismatch. But we're okay with that because we don't expect * users to disable version checks anyway. @@ -89,7 +89,7 @@ SchemaIdGetTenantColocationId(Oid schemaId) Relation pgDistTenantSchema = table_open(DistTenantSchemaRelationId(), AccessShareLock); ScanKeyData scanKey[1]; - ScanKeyInit(&scanKey[0], Anum_pg_dist_tenant_schema_schemaid, BTEqualStrategyNumber, + ScanKeyInit(&scanKey[0], Anum_pg_dist_schema_schemaid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(schemaId)); bool indexOk = true; @@ -103,7 +103,7 @@ SchemaIdGetTenantColocationId(Oid schemaId) bool isNull = false; colocationId = DatumGetUInt32( heap_getattr(heapTuple, - Anum_pg_dist_tenant_schema_colocationid, + Anum_pg_dist_schema_colocationid, RelationGetDescr(pgDistTenantSchema), &isNull)); Assert(!isNull); @@ -133,7 +133,7 @@ ColocationIdGetTenantSchemaId(uint32 colocationId) Relation pgDistTenantSchema = table_open(DistTenantSchemaRelationId(), AccessShareLock); ScanKeyData scanKey[1]; - ScanKeyInit(&scanKey[0], Anum_pg_dist_tenant_schema_colocationid, + ScanKeyInit(&scanKey[0], Anum_pg_dist_schema_colocationid, BTEqualStrategyNumber, F_INT4EQ, UInt32GetDatum(colocationId)); bool indexOk = true; @@ -146,7 +146,7 @@ ColocationIdGetTenantSchemaId(uint32 colocationId) if (HeapTupleIsValid(heapTuple)) { bool isNull = false; - schemaId = heap_getattr(heapTuple, Anum_pg_dist_tenant_schema_schemaid, + schemaId = heap_getattr(heapTuple, Anum_pg_dist_schema_schemaid, RelationGetDescr(pgDistTenantSchema), &isNull); Assert(!isNull); } @@ -159,7 +159,7 @@ ColocationIdGetTenantSchemaId(uint32 colocationId) /* - * InsertTenantSchemaLocally inserts an entry into pg_dist_tenant_schema + * InsertTenantSchemaLocally inserts an entry into pg_dist_schema * with given schemaId and colocationId. * * Throws a constraint violation error if there is already an entry with @@ -179,11 +179,11 @@ InsertTenantSchemaLocally(Oid schemaId, uint32 colocationId) ereport(ERROR, (errmsg("colocation id is invalid"))); } - Datum values[Natts_pg_dist_tenant_schema] = { 0 }; - bool isNulls[Natts_pg_dist_tenant_schema] = { 0 }; + Datum values[Natts_pg_dist_schema] = { 0 }; + bool isNulls[Natts_pg_dist_schema] = { 0 }; - values[Anum_pg_dist_tenant_schema_schemaid - 1] = ObjectIdGetDatum(schemaId); - values[Anum_pg_dist_tenant_schema_colocationid - 1] = UInt32GetDatum(colocationId); + values[Anum_pg_dist_schema_schemaid - 1] = ObjectIdGetDatum(schemaId); + values[Anum_pg_dist_schema_colocationid - 1] = UInt32GetDatum(colocationId); Relation pgDistTenantSchema = table_open(DistTenantSchemaRelationId(), RowExclusiveLock); @@ -199,7 +199,7 @@ InsertTenantSchemaLocally(Oid schemaId, uint32 colocationId) /* * DeleteTenantSchemaLocally deletes the entry for given schemaId from - * pg_dist_tenant_schema. + * pg_dist_schema. * * Throws an error if there is no such tenant schema. */ @@ -214,7 +214,7 @@ DeleteTenantSchemaLocally(Oid schemaId) Relation pgDistTenantSchema = table_open(DistTenantSchemaRelationId(), RowExclusiveLock); ScanKeyData scanKey[1]; - ScanKeyInit(&scanKey[0], Anum_pg_dist_tenant_schema_schemaid, BTEqualStrategyNumber, + ScanKeyInit(&scanKey[0], Anum_pg_dist_schema_schemaid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(schemaId)); bool indexOk = true; diff --git a/src/include/distributed/metadata_sync.h b/src/include/distributed/metadata_sync.h index 3153e42d7..64e684000 100644 --- a/src/include/distributed/metadata_sync.h +++ b/src/include/distributed/metadata_sync.h @@ -177,7 +177,7 @@ extern void SendInterTableRelationshipCommands(MetadataSyncContext *context); #define DELETE_ALL_DISTRIBUTED_OBJECTS "DELETE FROM pg_catalog.pg_dist_object" #define DELETE_ALL_PARTITIONS "DELETE FROM pg_dist_partition" #define DELETE_ALL_COLOCATION "DELETE FROM pg_catalog.pg_dist_colocation" -#define DELETE_ALL_TENANT_SCHEMAS "DELETE FROM pg_catalog.pg_dist_tenant_schema" +#define DELETE_ALL_TENANT_SCHEMAS "DELETE FROM pg_catalog.pg_dist_schema" #define WORKER_DROP_ALL_SHELL_TABLES \ "CALL pg_catalog.worker_drop_all_shell_tables(%s)" #define CITUS_INTERNAL_MARK_NODE_NOT_SYNCED \ diff --git a/src/include/distributed/pg_dist_schema.h b/src/include/distributed/pg_dist_schema.h new file mode 100644 index 000000000..528f47ae7 --- /dev/null +++ b/src/include/distributed/pg_dist_schema.h @@ -0,0 +1,41 @@ +/*------------------------------------------------------------------------- + * + * pg_dist_schema.h + * definition of the system catalog for the schemas used for schema-based + * sharding in Citus. + * + *------------------------------------------------------------------------- + */ + +#ifndef PG_DIST_SCHEMA_H +#define PG_DIST_SCHEMA_H + +#include "postgres.h" + + +/* ---------------- + * pg_dist_schema definition. + * ---------------- + */ +typedef struct FormData_pg_dist_schema +{ + Oid schemaid; + uint32 colocationid; +} FormData_pg_dist_schema; + +/* ---------------- + * Form_pg_dist_schema corresponds to a pointer to a tuple with + * the format of pg_dist_schema relation. + * ---------------- + */ +typedef FormData_pg_dist_schema *Form_pg_dist_schema; + +/* ---------------- + * compiler constants for pg_dist_schema + * ---------------- + */ +#define Natts_pg_dist_schema 2 +#define Anum_pg_dist_schema_schemaid 1 +#define Anum_pg_dist_schema_colocationid 2 + +#endif /* PG_DIST_SCHEMA_H */ diff --git a/src/include/distributed/pg_dist_tenant_schema.h b/src/include/distributed/pg_dist_tenant_schema.h deleted file mode 100644 index 8364853db..000000000 --- a/src/include/distributed/pg_dist_tenant_schema.h +++ /dev/null @@ -1,41 +0,0 @@ -/*------------------------------------------------------------------------- - * - * pg_dist_tenant_schema.h - * definition of the system catalog for the schemas used for schema-based - * sharding in Citus. - * - *------------------------------------------------------------------------- - */ - -#ifndef PG_DIST_TENANT_SCHEMA_H -#define PG_DIST_TENANT_SCHEMA_H - -#include "postgres.h" - - -/* ---------------- - * pg_dist_tenant_schema definition. - * ---------------- - */ -typedef struct FormData_pg_dist_tenant_schema -{ - Oid schemaid; - uint32 colocationid; -} FormData_pg_dist_tenant_schema; - -/* ---------------- - * Form_pg_dist_tenant_schema corresponds to a pointer to a tuple with - * the format of pg_dist_tenant_schema relation. - * ---------------- - */ -typedef FormData_pg_dist_tenant_schema *Form_pg_dist_tenant_schema; - -/* ---------------- - * compiler constants for pg_dist_tenant_schema - * ---------------- - */ -#define Natts_pg_dist_tenant_schema 2 -#define Anum_pg_dist_tenant_schema_schemaid 1 -#define Anum_pg_dist_tenant_schema_colocationid 2 - -#endif /* PG_DIST_TENANT_SCHEMA_H */ diff --git a/src/test/regress/expected/citus_schema_distribute_undistribute.out b/src/test/regress/expected/citus_schema_distribute_undistribute.out index ffdc20154..8e6803b6f 100644 --- a/src/test/regress/expected/citus_schema_distribute_undistribute.out +++ b/src/test/regress/expected/citus_schema_distribute_undistribute.out @@ -309,11 +309,11 @@ SELECT citus_schema_distribute('tenant1'); (1 row) -- show the schema is a tenant schema now -SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset --- below query verifies the same colocationid in pg_dist_tenant_schema, pg_dist_colocation and all entries in pg_dist_partition at the same time +SELECT colocationid AS tenant1_colocid FROM pg_dist_schema schemaid \gset +-- below query verifies the same colocationid in pg_dist_schema, pg_dist_colocation and all entries in pg_dist_partition at the same time SELECT '$$' || ' SELECT colocationid = ' || :tenant1_colocid || - ' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' || + ' FROM pg_dist_schema JOIN pg_dist_colocation USING(colocationid)' || ' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''tenant1%'')' || '$$' AS verify_tenant_query \gset @@ -340,7 +340,7 @@ SELECT citus_schema_undistribute('tenant1'); (1 row) -- show the schema is a regular schema -SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$); +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_schema $$); result --------------------------------------------------------------------- @@ -384,10 +384,10 @@ SELECT citus_schema_distribute('tenant1'); (1 row) -- show the schema is a tenant schema now -SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset +SELECT colocationid AS tenant1_colocid FROM pg_dist_schema schemaid \gset SELECT '$$' || ' SELECT colocationid = ' || :tenant1_colocid || - ' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' || + ' FROM pg_dist_schema JOIN pg_dist_colocation USING(colocationid)' || ' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''tenant1%'')' || '$$' AS verify_tenant_query \gset @@ -414,7 +414,7 @@ SELECT citus_schema_undistribute('tenant1'); (1 row) -- show the schema is a regular schema -SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$); +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_schema $$); result --------------------------------------------------------------------- @@ -539,7 +539,7 @@ SELECT citus_schema_distribute('tenant1'); ROLLBACK; -- show the schema is a regular schema -SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$); +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_schema $$); result --------------------------------------------------------------------- @@ -576,10 +576,10 @@ SELECT citus_schema_distribute('tenant1'); COMMIT; -- show the schema is a tenant schema now -SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset +SELECT colocationid AS tenant1_colocid FROM pg_dist_schema schemaid \gset SELECT '$$' || ' SELECT colocationid = ' || :tenant1_colocid || - ' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' || + ' FROM pg_dist_schema JOIN pg_dist_colocation USING(colocationid)' || ' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''tenant1%'')' || '$$' AS verify_tenant_query \gset @@ -617,7 +617,7 @@ SELECT citus_schema_undistribute('tenant1'); (1 row) -- show the schema is a regular schema now -SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$); +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_schema $$); result --------------------------------------------------------------------- @@ -703,10 +703,10 @@ SELECT citus_schema_distribute('"CiTuS.TeeN"'); (1 row) -- show the schema is a tenant schema now -SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset +SELECT colocationid AS tenant1_colocid FROM pg_dist_schema schemaid \gset SELECT '$$' || ' SELECT colocationid = ' || :tenant1_colocid || - ' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' || + ' FROM pg_dist_schema JOIN pg_dist_colocation USING(colocationid)' || ' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''"CiTuS.TeeN"%'')' || '$$' AS verify_tenant_query \gset @@ -733,7 +733,7 @@ SELECT citus_schema_undistribute('"CiTuS.TeeN"'); (1 row) -- show the schema is a regular schema -SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$); +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_schema $$); result --------------------------------------------------------------------- @@ -811,10 +811,10 @@ SELECT citus_schema_distribute('tenant1'); (1 row) -- show the schema is a tenant schema now -SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset +SELECT colocationid AS tenant1_colocid FROM pg_dist_schema schemaid \gset SELECT '$$' || ' SELECT colocationid = ' || :tenant1_colocid || - ' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' || + ' FROM pg_dist_schema JOIN pg_dist_colocation USING(colocationid)' || ' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''tenant1%'')' || '$$' AS verify_tenant_query \gset @@ -841,7 +841,7 @@ SELECT citus_schema_undistribute('tenant1'); (1 row) -- show the schema is a regular schema now -SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$); +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_schema $$); result --------------------------------------------------------------------- @@ -874,8 +874,8 @@ SELECT citus_schema_distribute('empty_tenant'); (1 row) -- show the schema is a tenant schema now -SELECT colocationid AS empty_tenant_colocid FROM pg_dist_tenant_schema schemaid \gset -SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace FROM pg_dist_colocation JOIN pg_dist_tenant_schema USING(colocationid) $$); +SELECT colocationid AS empty_tenant_colocid FROM pg_dist_schema schemaid \gset +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace FROM pg_dist_colocation JOIN pg_dist_schema USING(colocationid) $$); result --------------------------------------------------------------------- empty_tenant @@ -890,7 +890,7 @@ SELECT citus_schema_undistribute('empty_tenant'); (1 row) -- show the schema is a regular schema now -SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$); +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_schema $$); result --------------------------------------------------------------------- diff --git a/src/test/regress/expected/isolation_citus_schema_distribute_undistribute.out b/src/test/regress/expected/isolation_citus_schema_distribute_undistribute.out index 4fd00cc00..a392a0129 100644 --- a/src/test/regress/expected/isolation_citus_schema_distribute_undistribute.out +++ b/src/test/regress/expected/isolation_citus_schema_distribute_undistribute.out @@ -20,12 +20,12 @@ citus_schema_distribute step s2-drop-schema: DROP SCHEMA tenant1 CASCADE; -step s1-commit: +step s1-commit: COMMIT; step s2-drop-schema: <... completed> step s1-verify-distributed-schema: - SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; logicalrelid|partmethod|partkey|is_correct_colocid|repmodel|autoconverted --------------------------------------------------------------------- @@ -65,12 +65,12 @@ citus_schema_undistribute step s2-drop-schema: DROP SCHEMA tenant1 CASCADE; -step s1-commit: +step s1-commit: COMMIT; step s2-drop-schema: <... completed> step s1-verify-distributed-schema: - SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; logicalrelid|partmethod|partkey|is_correct_colocid|repmodel|autoconverted --------------------------------------------------------------------- @@ -106,7 +106,7 @@ step s1-commit: COMMIT; step s1-verify-distributed-schema: - SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted --------------------------------------------------------------------- @@ -153,7 +153,7 @@ step s1-commit: COMMIT; step s1-verify-distributed-schema: - SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted --------------------------------------------------------------------- @@ -186,12 +186,12 @@ citus_schema_distribute step s2-add-table: CREATE TABLE tenant1.table4(id int PRIMARY KEY, name text, col bigint); -step s1-commit: +step s1-commit: COMMIT; step s2-add-table: <... completed> step s1-verify-distributed-schema: - SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted --------------------------------------------------------------------- @@ -235,12 +235,12 @@ citus_schema_undistribute step s2-add-table: CREATE TABLE tenant1.table4(id int PRIMARY KEY, name text, col bigint); -step s1-commit: +step s1-commit: COMMIT; step s2-add-table: <... completed> step s1-verify-distributed-schema: - SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted --------------------------------------------------------------------- @@ -273,12 +273,12 @@ citus_schema_distribute step s2-drop-table: DROP TABLE tenant1.table3; -step s1-commit: +step s1-commit: COMMIT; step s2-drop-table: <... completed> step s1-verify-distributed-schema: - SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted --------------------------------------------------------------------- @@ -320,12 +320,12 @@ citus_schema_undistribute step s2-drop-table: DROP TABLE tenant1.table3; -step s1-commit: +step s1-commit: COMMIT; step s2-drop-table: <... completed> step s1-verify-distributed-schema: - SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; logicalrelid|partmethod|partkey|is_correct_colocid|repmodel|autoconverted --------------------------------------------------------------------- @@ -357,12 +357,12 @@ citus_schema_distribute step s2-alter-col-type: ALTER TABLE tenant1.table3 ALTER COLUMN col1 TYPE text; -step s1-commit: +step s1-commit: COMMIT; step s2-alter-col-type: <... completed> step s1-verify-distributed-schema: - SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted --------------------------------------------------------------------- @@ -405,12 +405,12 @@ citus_schema_undistribute step s2-alter-col-type: ALTER TABLE tenant1.table3 ALTER COLUMN col1 TYPE text; -step s1-commit: +step s1-commit: COMMIT; step s2-alter-col-type: <... completed> step s1-verify-distributed-schema: - SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted --------------------------------------------------------------------- @@ -443,12 +443,12 @@ citus_schema_distribute step s2-add-foreign-key: ALTER TABLE tenant1.table3 ADD CONSTRAINT table3_fk1 FOREIGN KEY (id) REFERENCES tenant1.table2 (id); -step s1-commit: +step s1-commit: COMMIT; step s2-add-foreign-key: <... completed> step s1-verify-distributed-schema: - SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted --------------------------------------------------------------------- @@ -491,12 +491,12 @@ citus_schema_undistribute step s2-add-foreign-key: ALTER TABLE tenant1.table3 ADD CONSTRAINT table3_fk1 FOREIGN KEY (id) REFERENCES tenant1.table2 (id); -step s1-commit: +step s1-commit: COMMIT; step s2-add-foreign-key: <... completed> step s1-verify-distributed-schema: - SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted --------------------------------------------------------------------- @@ -530,12 +530,12 @@ citus_schema_distribute step s2-drop-foreign-key: ALTER TABLE tenant1.table3 DROP CONSTRAINT table3_col_fkey; -step s1-commit: +step s1-commit: COMMIT; step s2-drop-foreign-key: <... completed> step s1-verify-distributed-schema: - SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted --------------------------------------------------------------------- @@ -578,12 +578,12 @@ citus_schema_undistribute step s2-drop-foreign-key: ALTER TABLE tenant1.table3 DROP CONSTRAINT table3_col_fkey; -step s1-commit: +step s1-commit: COMMIT; step s2-drop-foreign-key: <... completed> step s1-verify-distributed-schema: - SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; logicalrelid|partmethod|partkey|is_correct_colocid|repmodel|autoconverted --------------------------------------------------------------------- @@ -615,12 +615,12 @@ citus_schema_distribute step s2-create-unique-index: CREATE UNIQUE INDEX idx_2 ON tenant1.table3 (col); -step s1-commit: +step s1-commit: COMMIT; step s2-create-unique-index: <... completed> step s1-verify-distributed-schema: - SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted --------------------------------------------------------------------- @@ -663,12 +663,12 @@ citus_schema_undistribute step s2-create-unique-index: CREATE UNIQUE INDEX idx_2 ON tenant1.table3 (col); -step s1-commit: +step s1-commit: COMMIT; step s2-create-unique-index: <... completed> step s1-verify-distributed-schema: - SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted --------------------------------------------------------------------- @@ -701,12 +701,12 @@ citus_schema_distribute step s2-create-unique-index-concurrently: CREATE UNIQUE INDEX CONCURRENTLY idx_3 ON tenant1.table3 (col); -step s1-commit: +step s1-commit: COMMIT; step s2-create-unique-index-concurrently: <... completed> step s1-verify-distributed-schema: - SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted --------------------------------------------------------------------- @@ -749,12 +749,12 @@ citus_schema_undistribute step s2-create-unique-index-concurrently: CREATE UNIQUE INDEX CONCURRENTLY idx_3 ON tenant1.table3 (col); -step s1-commit: +step s1-commit: COMMIT; step s2-create-unique-index-concurrently: <... completed> step s1-verify-distributed-schema: - SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted --------------------------------------------------------------------- @@ -790,12 +790,12 @@ citus_schema_distribute step s2-reindex-unique-concurrently: REINDEX INDEX CONCURRENTLY tenant1.idx_2; -step s1-commit: +step s1-commit: COMMIT; step s2-reindex-unique-concurrently: <... completed> step s1-verify-distributed-schema: - SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted --------------------------------------------------------------------- @@ -841,12 +841,12 @@ citus_schema_undistribute step s2-reindex-unique-concurrently: REINDEX INDEX CONCURRENTLY tenant1.idx_2; -step s1-commit: +step s1-commit: COMMIT; step s2-reindex-unique-concurrently: <... completed> step s1-verify-distributed-schema: - SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted --------------------------------------------------------------------- @@ -880,12 +880,12 @@ step s2-insert: INSERT INTO public.ref SELECT i FROM generate_series(11, 20) i; INSERT INTO tenant1.table3 SELECT i, 'asd', i*1000 FROM generate_series(11, 20) i; -step s1-commit: +step s1-commit: COMMIT; step s2-insert: <... completed> step s1-verify-distributed-schema: - SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted --------------------------------------------------------------------- @@ -929,12 +929,12 @@ step s2-insert: INSERT INTO public.ref SELECT i FROM generate_series(11, 20) i; INSERT INTO tenant1.table3 SELECT i, 'asd', i*1000 FROM generate_series(11, 20) i; -step s1-commit: +step s1-commit: COMMIT; step s2-insert: <... completed> step s1-verify-distributed-schema: - SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted --------------------------------------------------------------------- @@ -971,12 +971,12 @@ citus_schema_distribute step s2-update: UPDATE tenant1.table3 SET col = 11 WHERE col = 11; -step s1-commit: +step s1-commit: COMMIT; step s2-update: <... completed> step s1-verify-distributed-schema: - SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted --------------------------------------------------------------------- @@ -1023,12 +1023,12 @@ citus_schema_undistribute step s2-update: UPDATE tenant1.table3 SET col = 11 WHERE col = 11; -step s1-commit: +step s1-commit: COMMIT; step s2-update: <... completed> step s1-verify-distributed-schema: - SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted --------------------------------------------------------------------- @@ -1065,12 +1065,12 @@ citus_schema_distribute step s2-delete: DELETE FROM tenant1.table3; -step s1-commit: +step s1-commit: COMMIT; step s2-delete: <... completed> step s1-verify-distributed-schema: - SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted --------------------------------------------------------------------- @@ -1117,12 +1117,12 @@ citus_schema_undistribute step s2-delete: DELETE FROM tenant1.table3; -step s1-commit: +step s1-commit: COMMIT; step s2-delete: <... completed> step s1-verify-distributed-schema: - SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; logicalrelid |partmethod|partkey|is_correct_colocid|repmodel|autoconverted --------------------------------------------------------------------- diff --git a/src/test/regress/expected/multi_extension.out b/src/test/regress/expected/multi_extension.out index 0ef234e30..fd297ef85 100644 --- a/src/test/regress/expected/multi_extension.out +++ b/src/test/regress/expected/multi_extension.out @@ -1365,7 +1365,7 @@ SELECT * FROM multi_extension.print_extension_changes(); | function citus_schema_distribute(regnamespace) void | function citus_schema_undistribute(regnamespace) void | function citus_stat_tenants_local_internal(boolean) SETOF record - | table pg_dist_tenant_schema + | table pg_dist_schema (7 rows) DROP TABLE multi_extension.prev_objects, multi_extension.extension_diff; @@ -1791,7 +1791,7 @@ CREATE TABLE tenant_schema.test(x int, y int); SELECT colocationid = ( SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_schema.test'::regclass ) -FROM pg_dist_tenant_schema +FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_schema'; ?column? --------------------------------------------------------------------- @@ -1861,7 +1861,7 @@ DROP TABLE test; DROP EXTENSION citus_columnar; SET citus.enable_schema_based_sharding TO ON; CREATE EXTENSION citus_columnar; -SELECT COUNT(*)=0 FROM pg_dist_tenant_schema +SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid IN ('columnar'::regnamespace, 'columnar_internal'::regnamespace); ?column? --------------------------------------------------------------------- diff --git a/src/test/regress/expected/multi_metadata_sync.out b/src/test/regress/expected/multi_metadata_sync.out index e92b9aa1b..168d28e8a 100644 --- a/src/test/regress/expected/multi_metadata_sync.out +++ b/src/test/regress/expected/multi_metadata_sync.out @@ -76,7 +76,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; CREATE SCHEMA IF NOT EXISTS public AUTHORIZATION pg_database_owner DELETE FROM pg_catalog.pg_dist_colocation DELETE FROM pg_catalog.pg_dist_object - DELETE FROM pg_catalog.pg_dist_tenant_schema + DELETE FROM pg_catalog.pg_dist_schema DELETE FROM pg_dist_node DELETE FROM pg_dist_partition DELETE FROM pg_dist_placement @@ -147,7 +147,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; CREATE TABLE public.single_shard_tbl (a integer) USING heap DELETE FROM pg_catalog.pg_dist_colocation DELETE FROM pg_catalog.pg_dist_object - DELETE FROM pg_catalog.pg_dist_tenant_schema + DELETE FROM pg_catalog.pg_dist_schema DELETE FROM pg_dist_node DELETE FROM pg_dist_partition DELETE FROM pg_dist_placement @@ -216,7 +216,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; CREATE TABLE public.mx_test_table (col_1 integer, col_2 text NOT NULL, col_3 bigint DEFAULT nextval('public.mx_test_table_col_3_seq'::regclass) NOT NULL, col_4 bigint DEFAULT nextval('public.user_defined_seq'::regclass)) USING heap DELETE FROM pg_catalog.pg_dist_colocation DELETE FROM pg_catalog.pg_dist_object - DELETE FROM pg_catalog.pg_dist_tenant_schema + DELETE FROM pg_catalog.pg_dist_schema DELETE FROM pg_dist_node DELETE FROM pg_dist_partition DELETE FROM pg_dist_placement @@ -277,7 +277,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; CREATE TABLE mx_testing_schema.mx_test_table (col_1 integer, col_2 text NOT NULL, col_3 bigint DEFAULT nextval('mx_testing_schema.mx_test_table_col_3_seq'::regclass) NOT NULL, col_4 bigint DEFAULT nextval('public.user_defined_seq'::regclass)) USING heap DELETE FROM pg_catalog.pg_dist_colocation DELETE FROM pg_catalog.pg_dist_object - DELETE FROM pg_catalog.pg_dist_tenant_schema + DELETE FROM pg_catalog.pg_dist_schema DELETE FROM pg_dist_node DELETE FROM pg_dist_partition DELETE FROM pg_dist_placement @@ -345,7 +345,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; CREATE TABLE mx_testing_schema.mx_test_table (col_1 integer, col_2 text NOT NULL, col_3 bigint DEFAULT nextval('mx_testing_schema.mx_test_table_col_3_seq'::regclass) NOT NULL, col_4 bigint DEFAULT nextval('public.user_defined_seq'::regclass)) USING heap DELETE FROM pg_catalog.pg_dist_colocation DELETE FROM pg_catalog.pg_dist_object - DELETE FROM pg_catalog.pg_dist_tenant_schema + DELETE FROM pg_catalog.pg_dist_schema DELETE FROM pg_dist_node DELETE FROM pg_dist_partition DELETE FROM pg_dist_placement @@ -406,7 +406,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; CREATE TABLE mx_testing_schema.mx_test_table (col_1 integer, col_2 text NOT NULL, col_3 bigint DEFAULT nextval('mx_testing_schema.mx_test_table_col_3_seq'::regclass) NOT NULL, col_4 bigint DEFAULT nextval('public.user_defined_seq'::regclass)) USING heap DELETE FROM pg_catalog.pg_dist_colocation DELETE FROM pg_catalog.pg_dist_object - DELETE FROM pg_catalog.pg_dist_tenant_schema + DELETE FROM pg_catalog.pg_dist_schema DELETE FROM pg_dist_node DELETE FROM pg_dist_partition DELETE FROM pg_dist_placement @@ -1977,7 +1977,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; CREATE TABLE public.test_table (id integer DEFAULT worker_nextval('public.mx_test_sequence_0'::regclass), id2 integer DEFAULT worker_nextval('public.mx_test_sequence_1'::regclass)) USING heap DELETE FROM pg_catalog.pg_dist_colocation DELETE FROM pg_catalog.pg_dist_object - DELETE FROM pg_catalog.pg_dist_tenant_schema + DELETE FROM pg_catalog.pg_dist_schema DELETE FROM pg_dist_node DELETE FROM pg_dist_partition DELETE FROM pg_dist_placement diff --git a/src/test/regress/expected/multi_metadata_sync_0.out b/src/test/regress/expected/multi_metadata_sync_0.out index 9ab50664c..87800915d 100644 --- a/src/test/regress/expected/multi_metadata_sync_0.out +++ b/src/test/regress/expected/multi_metadata_sync_0.out @@ -76,7 +76,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; CREATE SCHEMA IF NOT EXISTS public AUTHORIZATION postgres DELETE FROM pg_catalog.pg_dist_colocation DELETE FROM pg_catalog.pg_dist_object - DELETE FROM pg_catalog.pg_dist_tenant_schema + DELETE FROM pg_catalog.pg_dist_schema DELETE FROM pg_dist_node DELETE FROM pg_dist_partition DELETE FROM pg_dist_placement @@ -147,7 +147,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; CREATE TABLE public.single_shard_tbl (a integer) USING heap DELETE FROM pg_catalog.pg_dist_colocation DELETE FROM pg_catalog.pg_dist_object - DELETE FROM pg_catalog.pg_dist_tenant_schema + DELETE FROM pg_catalog.pg_dist_schema DELETE FROM pg_dist_node DELETE FROM pg_dist_partition DELETE FROM pg_dist_placement @@ -216,7 +216,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; CREATE TABLE public.mx_test_table (col_1 integer, col_2 text NOT NULL, col_3 bigint DEFAULT nextval('public.mx_test_table_col_3_seq'::regclass) NOT NULL, col_4 bigint DEFAULT nextval('public.user_defined_seq'::regclass)) USING heap DELETE FROM pg_catalog.pg_dist_colocation DELETE FROM pg_catalog.pg_dist_object - DELETE FROM pg_catalog.pg_dist_tenant_schema + DELETE FROM pg_catalog.pg_dist_schema DELETE FROM pg_dist_node DELETE FROM pg_dist_partition DELETE FROM pg_dist_placement @@ -277,7 +277,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; CREATE TABLE mx_testing_schema.mx_test_table (col_1 integer, col_2 text NOT NULL, col_3 bigint DEFAULT nextval('mx_testing_schema.mx_test_table_col_3_seq'::regclass) NOT NULL, col_4 bigint DEFAULT nextval('public.user_defined_seq'::regclass)) USING heap DELETE FROM pg_catalog.pg_dist_colocation DELETE FROM pg_catalog.pg_dist_object - DELETE FROM pg_catalog.pg_dist_tenant_schema + DELETE FROM pg_catalog.pg_dist_schema DELETE FROM pg_dist_node DELETE FROM pg_dist_partition DELETE FROM pg_dist_placement @@ -345,7 +345,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; CREATE TABLE mx_testing_schema.mx_test_table (col_1 integer, col_2 text NOT NULL, col_3 bigint DEFAULT nextval('mx_testing_schema.mx_test_table_col_3_seq'::regclass) NOT NULL, col_4 bigint DEFAULT nextval('public.user_defined_seq'::regclass)) USING heap DELETE FROM pg_catalog.pg_dist_colocation DELETE FROM pg_catalog.pg_dist_object - DELETE FROM pg_catalog.pg_dist_tenant_schema + DELETE FROM pg_catalog.pg_dist_schema DELETE FROM pg_dist_node DELETE FROM pg_dist_partition DELETE FROM pg_dist_placement @@ -406,7 +406,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; CREATE TABLE mx_testing_schema.mx_test_table (col_1 integer, col_2 text NOT NULL, col_3 bigint DEFAULT nextval('mx_testing_schema.mx_test_table_col_3_seq'::regclass) NOT NULL, col_4 bigint DEFAULT nextval('public.user_defined_seq'::regclass)) USING heap DELETE FROM pg_catalog.pg_dist_colocation DELETE FROM pg_catalog.pg_dist_object - DELETE FROM pg_catalog.pg_dist_tenant_schema + DELETE FROM pg_catalog.pg_dist_schema DELETE FROM pg_dist_node DELETE FROM pg_dist_partition DELETE FROM pg_dist_placement @@ -1977,7 +1977,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; CREATE TABLE public.test_table (id integer DEFAULT worker_nextval('public.mx_test_sequence_0'::regclass), id2 integer DEFAULT worker_nextval('public.mx_test_sequence_1'::regclass)) USING heap DELETE FROM pg_catalog.pg_dist_colocation DELETE FROM pg_catalog.pg_dist_object - DELETE FROM pg_catalog.pg_dist_tenant_schema + DELETE FROM pg_catalog.pg_dist_schema DELETE FROM pg_dist_node DELETE FROM pg_dist_partition DELETE FROM pg_dist_placement diff --git a/src/test/regress/expected/schema_based_sharding.out b/src/test/regress/expected/schema_based_sharding.out index 06d759e1f..6dba7227d 100644 --- a/src/test/regress/expected/schema_based_sharding.out +++ b/src/test/regress/expected/schema_based_sharding.out @@ -41,8 +41,8 @@ SELECT create_distributed_table('regular_schema.test_table', 'a'); (1 row) SET citus.enable_schema_based_sharding TO ON; --- show that regular_schema doesn't show up in pg_dist_tenant_schema -SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'regular_schema'; +-- show that regular_schema doesn't show up in pg_dist_schema +SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'regular_schema'; ?column? --------------------------------------------------------------------- t @@ -99,7 +99,7 @@ ERROR: tenant_2 is not allowed for ALTER TABLE SET SCHEMA because it is a distr ALTER TABLE tenant_2.test_table SET SCHEMA tenant_3; ERROR: tenant_2.test_table is not allowed for ALTER TABLE SET SCHEMA because it belongs to a distributed schema -- (on coordinator) verify that colocation id is set for empty tenants too -SELECT colocationid > 0 FROM pg_dist_tenant_schema +SELECT colocationid > 0 FROM pg_dist_schema WHERE schemaid::regnamespace::text IN ('tenant_1', 'tenant_3'); ?column? --------------------------------------------------------------------- @@ -109,7 +109,7 @@ WHERE schemaid::regnamespace::text IN ('tenant_1', 'tenant_3'); -- (on workers) verify that colocation id is set for empty tenants too SELECT result FROM run_command_on_workers($$ - SELECT array_agg(colocationid > 0) FROM pg_dist_tenant_schema + SELECT array_agg(colocationid > 0) FROM pg_dist_schema WHERE schemaid::regnamespace::text IN ('tenant_1', 'tenant_3'); $$); result @@ -132,7 +132,7 @@ WHERE logicalrelid = 'tenant_2.test_table'::regclass AND SELECT colocationid = ( SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_2.test_table'::regclass ) -FROM pg_dist_tenant_schema +FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_2'; ?column? --------------------------------------------------------------------- @@ -144,7 +144,7 @@ SELECT result FROM run_command_on_workers($$ SELECT colocationid = ( SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_2.test_table'::regclass ) - FROM pg_dist_tenant_schema + FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_2'; $$); result @@ -159,7 +159,7 @@ CREATE TABLE tenant_1.test_table(a int, b text); SELECT colocationid = ( SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_1.test_table'::regclass ) -FROM pg_dist_tenant_schema +FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_1'; ?column? --------------------------------------------------------------------- @@ -171,7 +171,7 @@ SELECT result FROM run_command_on_workers($$ SELECT colocationid = ( SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_1.test_table'::regclass ) - FROM pg_dist_tenant_schema + FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_1'; $$); result @@ -181,7 +181,7 @@ $$); (2 rows) -- verify that tenant_1 and tenant_2 have different colocation ids -SELECT COUNT(DISTINCT(colocationid))=2 FROM pg_dist_tenant_schema +SELECT COUNT(DISTINCT(colocationid))=2 FROM pg_dist_schema WHERE schemaid::regnamespace::text IN ('tenant_1', 'tenant_2'); ?column? --------------------------------------------------------------------- @@ -465,7 +465,7 @@ SELECT COUNT(*) = 5 FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant_5.test_table_like_%' AND partmethod = 'n' AND repmodel = 's' AND colocationid = ( - SELECT colocationid FROM pg_dist_tenant_schema + SELECT colocationid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_5' ); ?column? @@ -499,10 +499,10 @@ ERROR: tenant tables cannot inherit or be inherited CREATE TABLE tenant_5.tbl_2(a int, b text); CREATE SCHEMA "CiTuS.TeeN_108"; ALTER SCHEMA "CiTuS.TeeN_108" RENAME TO citus_teen_proper; -SELECT schemaid AS citus_teen_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'citus_teen_proper' \gset -SELECT colocationid AS citus_teen_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'citus_teen_proper' \gset +SELECT schemaid AS citus_teen_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'citus_teen_proper' \gset +SELECT colocationid AS citus_teen_colocationid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'citus_teen_proper' \gset SELECT result FROM run_command_on_workers($$ - SELECT schemaid INTO citus_teen_schemaid FROM pg_dist_tenant_schema + SELECT schemaid INTO citus_teen_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'citus_teen_proper' $$); result @@ -521,7 +521,7 @@ SELECT :citus_teen_colocationid > 0; -- (on workers) verify that the same colocation id is used on workers too SELECT format( 'SELECT result FROM run_command_on_workers($$ - SELECT COUNT(*)=1 FROM pg_dist_tenant_schema + SELECT COUNT(*)=1 FROM pg_dist_schema WHERE schemaid::regnamespace::text = ''citus_teen_proper'' AND colocationid = %s; $$);', @@ -571,10 +571,10 @@ WHERE logicalrelid::regclass::text LIKE 'tenant_5.%'; t (1 row) -SELECT schemaid AS tenant_4_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_4' \gset -SELECT colocationid AS tenant_4_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_4' \gset +SELECT schemaid AS tenant_4_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_4' \gset +SELECT colocationid AS tenant_4_colocationid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_4' \gset SELECT result FROM run_command_on_workers($$ - SELECT schemaid INTO tenant_4_schemaid FROM pg_dist_tenant_schema + SELECT schemaid INTO tenant_4_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_4' $$); result @@ -585,14 +585,14 @@ $$); SET client_min_messages TO WARNING; -- Rename it to a name that contains a single quote to verify that we properly --- escape its name when sending the command to delete the pg_dist_tenant_schema +-- escape its name when sending the command to delete the pg_dist_schema -- entry on workers. ALTER SCHEMA tenant_4 RENAME TO "tenant\'_4"; DROP SCHEMA "tenant\'_4", "CiTuS.TeeN_108" CASCADE; SET client_min_messages TO NOTICE; -- (on coordinator) Verify that dropping a tenant schema deletes the associated --- pg_dist_tenant_schema entry and pg_dist_colocation too. -SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid = :tenant_4_schemaid; +-- pg_dist_schema entry and pg_dist_colocation too. +SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid = :tenant_4_schemaid; ?column? --------------------------------------------------------------------- t @@ -604,7 +604,7 @@ SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = :tenant_4_colocat t (1 row) -SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid = :citus_teen_schemaid; +SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid = :citus_teen_schemaid; ?column? --------------------------------------------------------------------- t @@ -617,9 +617,9 @@ SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = :citus_teen_coloc (1 row) -- (on workers) Verify that dropping a tenant schema deletes the associated --- pg_dist_tenant_schema entry and pg_dist_colocation too. +-- pg_dist_schema entry and pg_dist_colocation too. SELECT result FROM run_command_on_workers($$ - SELECT COUNT(*)=0 FROM pg_dist_tenant_schema + SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid = (SELECT schemaid FROM tenant_4_schemaid) $$); result @@ -629,7 +629,7 @@ $$); (2 rows) SELECT result FROM run_command_on_workers($$ - SELECT COUNT(*)=0 FROM pg_dist_tenant_schema + SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid = (SELECT schemaid FROM citus_teen_schemaid) $$); result @@ -698,7 +698,7 @@ SELECT create_distributed_table('regular_schema.null_shard_key_table_2', null); -- Show that we don't chose to colocate regular single-shard tables with -- tenant tables by default. -SELECT * FROM pg_dist_tenant_schema WHERE colocationid = ( +SELECT * FROM pg_dist_schema WHERE colocationid = ( SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'regular_schema.null_shard_key_table_2'::regclass ); schemaid | colocationid @@ -706,13 +706,13 @@ SELECT * FROM pg_dist_tenant_schema WHERE colocationid = ( (0 rows) -- save the colocation id used for tenant_5 -SELECT colocationid AS tenant_5_old_colocationid FROM pg_dist_tenant_schema +SELECT colocationid AS tenant_5_old_colocationid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_5' \gset -- drop all the tables that belong to tenant_5 and create a new one DROP TABLE tenant_5.tbl_1, tenant_5.tbl_2, tenant_5.tbl_3; CREATE TABLE tenant_5.tbl_4(a int, b text); -- (on coordinator) verify that tenant_5 is still associated with the same colocation id -SELECT colocationid = :tenant_5_old_colocationid FROM pg_dist_tenant_schema +SELECT colocationid = :tenant_5_old_colocationid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_5'; ?column? --------------------------------------------------------------------- @@ -722,7 +722,7 @@ WHERE schemaid::regnamespace::text = 'tenant_5'; -- (on workers) verify that tenant_5 is still associated with the same colocation id SELECT format( 'SELECT result FROM run_command_on_workers($$ - SELECT colocationid = %s FROM pg_dist_tenant_schema + SELECT colocationid = %s FROM pg_dist_schema WHERE schemaid::regnamespace::text = ''tenant_5''; $$);', :tenant_5_old_colocationid) AS verify_workers_query \gset @@ -733,12 +733,12 @@ SELECT format( t (2 rows) -SELECT schemaid AS tenant_1_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_1' \gset -SELECT colocationid AS tenant_1_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_1' \gset -SELECT schemaid AS tenant_2_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_2' \gset -SELECT colocationid AS tenant_2_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_2' \gset +SELECT schemaid AS tenant_1_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_1' \gset +SELECT colocationid AS tenant_1_colocationid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_1' \gset +SELECT schemaid AS tenant_2_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_2' \gset +SELECT colocationid AS tenant_2_colocationid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_2' \gset SELECT result FROM run_command_on_workers($$ - SELECT schemaid INTO tenant_1_schemaid FROM pg_dist_tenant_schema + SELECT schemaid INTO tenant_1_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_1' $$); result @@ -748,7 +748,7 @@ $$); (2 rows) SELECT result FROM run_command_on_workers($$ - SELECT schemaid INTO tenant_2_schemaid FROM pg_dist_tenant_schema + SELECT schemaid INTO tenant_2_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_2' $$); result @@ -788,9 +788,9 @@ DROP OWNED BY test_non_super_user CASCADE; DROP ROLE test_non_super_user; SET client_min_messages TO NOTICE; -- (on coordinator) Verify that dropping a tenant schema always deletes --- the associated pg_dist_tenant_schema entry even if the the schema was +-- the associated pg_dist_schema entry even if the the schema was -- dropped while the GUC was set to off. -SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid IN (:tenant_1_schemaid, :tenant_2_schemaid); +SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid IN (:tenant_1_schemaid, :tenant_2_schemaid); ?column? --------------------------------------------------------------------- t @@ -803,10 +803,10 @@ SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid IN (:tenant_1_coloc (1 row) -- (on workers) Verify that dropping a tenant schema always deletes --- the associated pg_dist_tenant_schema entry even if the the schema was +-- the associated pg_dist_schema entry even if the the schema was -- dropped while the GUC was set to off. SELECT result FROM run_command_on_workers($$ - SELECT COUNT(*)=0 FROM pg_dist_tenant_schema + SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid IN (SELECT schemaid FROM tenant_1_schemaid UNION SELECT schemaid FROM tenant_2_schemaid) $$); result @@ -847,29 +847,29 @@ $$); SET citus.enable_schema_based_sharding TO ON; SET client_min_messages TO NOTICE; --- show that all schemaid values are unique and non-null in pg_dist_tenant_schema -SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid IS NULL; +-- show that all schemaid values are unique and non-null in pg_dist_schema +SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid IS NULL; ?column? --------------------------------------------------------------------- t (1 row) -SELECT (SELECT COUNT(*) FROM pg_dist_tenant_schema) = - (SELECT COUNT(DISTINCT(schemaid)) FROM pg_dist_tenant_schema); +SELECT (SELECT COUNT(*) FROM pg_dist_schema) = + (SELECT COUNT(DISTINCT(schemaid)) FROM pg_dist_schema); ?column? --------------------------------------------------------------------- t (1 row) --- show that all colocationid values are unique and non-null in pg_dist_tenant_schema -SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE colocationid IS NULL; +-- show that all colocationid values are unique and non-null in pg_dist_schema +SELECT COUNT(*)=0 FROM pg_dist_schema WHERE colocationid IS NULL; ?column? --------------------------------------------------------------------- t (1 row) -SELECT (SELECT COUNT(*) FROM pg_dist_tenant_schema) = - (SELECT COUNT(DISTINCT(colocationid)) FROM pg_dist_tenant_schema); +SELECT (SELECT COUNT(*) FROM pg_dist_schema) = + (SELECT COUNT(DISTINCT(colocationid)) FROM pg_dist_schema); ?column? --------------------------------------------------------------------- t @@ -877,7 +877,7 @@ SELECT (SELECT COUNT(*) FROM pg_dist_tenant_schema) = CREATE TABLE public.cannot_be_a_tenant_table(a int, b text); -- show that we don't consider public schema as a tenant schema -SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'public'; +SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'public'; ?column? --------------------------------------------------------------------- t @@ -889,7 +889,7 @@ BEGIN; CREATE SCHEMA public; -- Show that we don't consider public schema as a tenant schema, -- even if it's recreated. - SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'public'; + SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'public'; ?column? --------------------------------------------------------------------- t @@ -898,7 +898,7 @@ BEGIN; ROLLBACK; CREATE TEMPORARY TABLE temp_table(a int, b text); -- show that we don't consider temporary schemas as tenant schemas -SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = '%pg_temp%'; +SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid::regnamespace::text = '%pg_temp%'; ?column? --------------------------------------------------------------------- t @@ -913,7 +913,7 @@ BEGIN; SELECT colocationid = ( SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_7.tbl_1'::regclass ) - FROM pg_dist_tenant_schema + FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_7'; ?column? --------------------------------------------------------------------- @@ -936,7 +936,7 @@ BEGIN; CREATE TABLE tenant_8.tbl_1(a int, b text); CREATE TABLE tenant_8.tbl_2(a int, b text); ROLLBACK; -SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_8'; +SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_8'; ?column? --------------------------------------------------------------------- t @@ -1003,10 +1003,10 @@ CREATE TABLE tenant_5.tbl_5(a int, b text, FOREIGN KEY(a) REFERENCES tenant_7.tb ERROR: cannot create foreign key constraint since relations are not colocated or not referencing a reference table DETAIL: A distributed table can only have foreign keys if it is referencing another colocated hash distributed table or a reference table CREATE SCHEMA tenant_9; -SELECT schemaid AS tenant_9_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_9' \gset -SELECT colocationid AS tenant_9_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_9' \gset +SELECT schemaid AS tenant_9_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_9' \gset +SELECT colocationid AS tenant_9_colocationid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_9' \gset SELECT result FROM run_command_on_workers($$ - SELECT schemaid INTO tenant_9_schemaid FROM pg_dist_tenant_schema + SELECT schemaid INTO tenant_9_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_9' $$); result @@ -1017,9 +1017,9 @@ $$); DROP SCHEMA tenant_9; -- (on coordinator) Make sure that dropping an empty tenant schema --- doesn't leave any dangling entries in pg_dist_tenant_schema and +-- doesn't leave any dangling entries in pg_dist_schema and -- pg_dist_colocation. -SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid = :tenant_9_schemaid; +SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid = :tenant_9_schemaid; ?column? --------------------------------------------------------------------- t @@ -1032,10 +1032,10 @@ SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = :tenant_9_colocat (1 row) -- (on workers) Make sure that dropping an empty tenant schema --- doesn't leave any dangling entries in pg_dist_tenant_schema and +-- doesn't leave any dangling entries in pg_dist_schema and -- pg_dist_colocation. SELECT result FROM run_command_on_workers($$ - SELECT COUNT(*)=0 FROM pg_dist_tenant_schema + SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid = (SELECT schemaid FROM tenant_9_schemaid) $$); result @@ -1147,10 +1147,10 @@ SET citus.shard_count TO 32; SET citus.shard_replication_factor TO 1; SET client_min_messages TO NOTICE; SET citus.enable_schema_based_sharding TO ON; -SELECT schemaid AS tenant_9_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_9' \gset -SELECT colocationid AS tenant_9_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_9' \gset +SELECT schemaid AS tenant_9_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_9' \gset +SELECT colocationid AS tenant_9_colocationid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_9' \gset SELECT result FROM run_command_on_workers($$ - SELECT schemaid INTO tenant_9_schemaid FROM pg_dist_tenant_schema + SELECT schemaid INTO tenant_9_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_9' $$); result @@ -1162,8 +1162,8 @@ $$); DROP OWNED BY test_other_super_user; -- (on coordinator) Make sure that dropping an empty tenant schema -- (via DROP OWNED BY) doesn't leave any dangling entries in --- pg_dist_tenant_schema and pg_dist_colocation. -SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid = :tenant_9_schemaid; +-- pg_dist_schema and pg_dist_colocation. +SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid = :tenant_9_schemaid; ?column? --------------------------------------------------------------------- t @@ -1177,9 +1177,9 @@ SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = :tenant_9_colocat -- (on workers) Make sure that dropping an empty tenant schema -- (via DROP OWNED BY) doesn't leave any dangling entries in --- pg_dist_tenant_schema and pg_dist_colocation. +-- pg_dist_schema and pg_dist_colocation. SELECT result FROM run_command_on_workers($$ - SELECT COUNT(*)=0 FROM pg_dist_tenant_schema + SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid = (SELECT schemaid FROM tenant_9_schemaid) $$); result @@ -1234,12 +1234,12 @@ CREATE TABLE tenant_10.tbl_1(a int, b text); CREATE TABLE tenant_10.tbl_2(a int, b text); DROP TABLE tenant_10.tbl_2; CREATE SCHEMA tenant_11; -SELECT schemaid AS tenant_10_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_10' \gset -SELECT colocationid AS tenant_10_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_10' \gset -SELECT schemaid AS tenant_11_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_11' \gset -SELECT colocationid AS tenant_11_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_11' \gset +SELECT schemaid AS tenant_10_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_10' \gset +SELECT colocationid AS tenant_10_colocationid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_10' \gset +SELECT schemaid AS tenant_11_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_11' \gset +SELECT colocationid AS tenant_11_colocationid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_11' \gset SELECT result FROM run_command_on_workers($$ - SELECT schemaid INTO tenant_10_schemaid FROM pg_dist_tenant_schema + SELECT schemaid INTO tenant_10_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_10' $$); result @@ -1249,7 +1249,7 @@ $$); (2 rows) SELECT result FROM run_command_on_workers($$ - SELECT schemaid INTO tenant_11_schemaid FROM pg_dist_tenant_schema + SELECT schemaid INTO tenant_11_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_11' $$); result @@ -1259,7 +1259,7 @@ $$); (2 rows) -- (on coordinator) Verify metadata for tenant schemas that are created via non-super-user. -SELECT COUNT(DISTINCT(schemaid))=2 FROM pg_dist_tenant_schema WHERE schemaid IN (:tenant_10_schemaid, :tenant_11_schemaid); +SELECT COUNT(DISTINCT(schemaid))=2 FROM pg_dist_schema WHERE schemaid IN (:tenant_10_schemaid, :tenant_11_schemaid); ?column? --------------------------------------------------------------------- t @@ -1273,7 +1273,7 @@ SELECT COUNT(DISTINCT(colocationid))=2 FROM pg_dist_colocation WHERE colocationi -- (on workers) Verify metadata for tenant schemas that are created via non-super-user. SELECT result FROM run_command_on_workers($$ - SELECT COUNT(DISTINCT(schemaid))=2 FROM pg_dist_tenant_schema + SELECT COUNT(DISTINCT(schemaid))=2 FROM pg_dist_schema WHERE schemaid IN (SELECT schemaid FROM tenant_10_schemaid UNION SELECT schemaid FROM tenant_11_schemaid) $$); result @@ -1298,8 +1298,8 @@ SET client_min_messages TO WARNING; DROP SCHEMA tenant_10, tenant_11 CASCADE; SET client_min_messages TO NOTICE; -- (on coordinator) Verify that dropping a tenant schema via non-super-user --- deletes the associated pg_dist_tenant_schema entry. -SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid IN (:tenant_10_schemaid, :tenant_11_schemaid); +-- deletes the associated pg_dist_schema entry. +SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid IN (:tenant_10_schemaid, :tenant_11_schemaid); ?column? --------------------------------------------------------------------- t @@ -1312,9 +1312,9 @@ SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid IN (:tenant_10_colo (1 row) -- (on workers) Verify that dropping a tenant schema via non-super-user --- deletes the associated pg_dist_tenant_schema entry. +-- deletes the associated pg_dist_schema entry. SELECT result FROM run_command_on_workers($$ - SELECT COUNT(*)=0 FROM pg_dist_tenant_schema + SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid IN (SELECT schemaid FROM tenant_10_schemaid UNION SELECT schemaid FROM tenant_11_schemaid) $$); result diff --git a/src/test/regress/expected/single_node.out b/src/test/regress/expected/single_node.out index 785cea2f8..4b6ea0837 100644 --- a/src/test/regress/expected/single_node.out +++ b/src/test/regress/expected/single_node.out @@ -160,8 +160,8 @@ ERROR: relation "single_node_nullkey_c1_90630532" is a shard relation SET citus.enable_schema_based_sharding TO ON; CREATE SCHEMA tenant_1; CREATE TABLE tenant_1.tbl_1 (a int); --- verify that we recorded tenant_1 in pg_dist_tenant_schema -SELECT COUNT(*)=1 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_1'; +-- verify that we recorded tenant_1 in pg_dist_schema +SELECT COUNT(*)=1 FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_1'; ?column? --------------------------------------------------------------------- t diff --git a/src/test/regress/expected/single_node_0.out b/src/test/regress/expected/single_node_0.out index 8ca701e40..4749b9b81 100644 --- a/src/test/regress/expected/single_node_0.out +++ b/src/test/regress/expected/single_node_0.out @@ -160,8 +160,8 @@ ERROR: relation "single_node_nullkey_c1_90630532" is a shard relation SET citus.enable_schema_based_sharding TO ON; CREATE SCHEMA tenant_1; CREATE TABLE tenant_1.tbl_1 (a int); --- verify that we recorded tenant_1 in pg_dist_tenant_schema -SELECT COUNT(*)=1 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_1'; +-- verify that we recorded tenant_1 in pg_dist_schema +SELECT COUNT(*)=1 FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_1'; ?column? --------------------------------------------------------------------- t diff --git a/src/test/regress/expected/upgrade_list_citus_objects.out b/src/test/regress/expected/upgrade_list_citus_objects.out index d5854bdeb..3442552ff 100644 --- a/src/test/regress/expected/upgrade_list_citus_objects.out +++ b/src/test/regress/expected/upgrade_list_citus_objects.out @@ -311,8 +311,8 @@ ORDER BY 1; table pg_dist_placement table pg_dist_poolinfo table pg_dist_rebalance_strategy + table pg_dist_schema table pg_dist_shard - table pg_dist_tenant_schema table pg_dist_transaction type citus.distribution_type type citus.shard_transfer_mode diff --git a/src/test/regress/expected/upgrade_schema_based_sharding_after.out b/src/test/regress/expected/upgrade_schema_based_sharding_after.out index 4ecb7cf12..52d501b16 100644 --- a/src/test/regress/expected/upgrade_schema_based_sharding_after.out +++ b/src/test/regress/expected/upgrade_schema_based_sharding_after.out @@ -1,7 +1,7 @@ ALTER SCHEMA "tenant\'_1" RENAME TO tenant_1; ALTER SCHEMA "tenant\'_2" RENAME TO tenant_2; -- verify that colocation id is set even for empty tenant -SELECT colocationid > 0 FROM pg_dist_tenant_schema +SELECT colocationid > 0 FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_1'; ?column? --------------------------------------------------------------------- @@ -10,7 +10,7 @@ WHERE schemaid::regnamespace::text = 'tenant_1'; -- verify the same on workers SELECT result FROM run_command_on_workers($$ - SELECT colocationid > 0 FROM pg_dist_tenant_schema + SELECT colocationid > 0 FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_1'; $$); result @@ -23,7 +23,7 @@ $$); SELECT colocationid = ( SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_2.test_table'::regclass ) -FROM pg_dist_tenant_schema +FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_2'; ?column? --------------------------------------------------------------------- @@ -35,7 +35,7 @@ SELECT result FROM run_command_on_workers($$ SELECT colocationid = ( SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_2.test_table'::regclass ) - FROM pg_dist_tenant_schema + FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_2'; $$); result @@ -61,7 +61,7 @@ SELECT colocationid = ( WHERE logicalrelid = 'tenant_1.tbl_1'::regclass AND partmethod = 'n' AND repmodel = 's' ) -FROM pg_dist_tenant_schema +FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_1'; ?column? --------------------------------------------------------------------- @@ -73,7 +73,7 @@ SELECT colocationid = ( WHERE logicalrelid = 'tenant_2.tbl_1'::regclass AND partmethod = 'n' AND repmodel = 's' ) -FROM pg_dist_tenant_schema +FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_2'; ?column? --------------------------------------------------------------------- @@ -87,7 +87,7 @@ ALTER SCHEMA tenant_2 RENAME TO "tenant\'_2"; SET citus.enable_schema_based_sharding TO ON; CREATE SCHEMA tenant_3; -- Show that we can create furher tenant schemas after pg upgrade. -SELECT COUNT(*)=1 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_3'; +SELECT COUNT(*)=1 FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_3'; ?column? --------------------------------------------------------------------- t diff --git a/src/test/regress/spec/isolation_citus_schema_distribute_undistribute.spec b/src/test/regress/spec/isolation_citus_schema_distribute_undistribute.spec index 24699c7cf..02a4cc829 100644 --- a/src/test/regress/spec/isolation_citus_schema_distribute_undistribute.spec +++ b/src/test/regress/spec/isolation_citus_schema_distribute_undistribute.spec @@ -41,7 +41,7 @@ step "s1-schema-undistribute" step "s1-verify-distributed-schema" { - SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_tenant_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; + SELECT logicalrelid, partmethod, partkey, (colocationid = (SELECT colocationid AS tenant_colocid FROM pg_dist_schema)) AS is_correct_colocid, repmodel, autoconverted FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant%' ORDER BY logicalrelid; } step "s1-commit" diff --git a/src/test/regress/sql/citus_schema_distribute_undistribute.sql b/src/test/regress/sql/citus_schema_distribute_undistribute.sql index f6b5ee32b..1008b90b2 100644 --- a/src/test/regress/sql/citus_schema_distribute_undistribute.sql +++ b/src/test/regress/sql/citus_schema_distribute_undistribute.sql @@ -199,11 +199,11 @@ SET role dummyregular; SELECT citus_schema_distribute('tenant1'); -- show the schema is a tenant schema now -SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset --- below query verifies the same colocationid in pg_dist_tenant_schema, pg_dist_colocation and all entries in pg_dist_partition at the same time +SELECT colocationid AS tenant1_colocid FROM pg_dist_schema schemaid \gset +-- below query verifies the same colocationid in pg_dist_schema, pg_dist_colocation and all entries in pg_dist_partition at the same time SELECT '$$' || ' SELECT colocationid = ' || :tenant1_colocid || - ' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' || + ' FROM pg_dist_schema JOIN pg_dist_colocation USING(colocationid)' || ' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''tenant1%'')' || '$$' AS verify_tenant_query \gset @@ -213,7 +213,7 @@ SELECT result FROM run_command_on_all_nodes(:verify_tenant_query); SELECT citus_schema_undistribute('tenant1'); -- show the schema is a regular schema -SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$); +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_schema $$); -- below query verifies the tenant colocationid is removed from both pg_dist_colocation and all entries in pg_dist_partition at the same time SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$); SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$); @@ -227,10 +227,10 @@ SET role dummysuper; SELECT citus_schema_distribute('tenant1'); -- show the schema is a tenant schema now -SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset +SELECT colocationid AS tenant1_colocid FROM pg_dist_schema schemaid \gset SELECT '$$' || ' SELECT colocationid = ' || :tenant1_colocid || - ' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' || + ' FROM pg_dist_schema JOIN pg_dist_colocation USING(colocationid)' || ' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''tenant1%'')' || '$$' AS verify_tenant_query \gset @@ -240,7 +240,7 @@ SELECT result FROM run_command_on_all_nodes(:verify_tenant_query); SELECT citus_schema_undistribute('tenant1'); -- show the schema is a regular schema -SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$); +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_schema $$); SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$); SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$); @@ -291,7 +291,7 @@ SELECT citus_schema_distribute('tenant1'); ROLLBACK; -- show the schema is a regular schema -SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$); +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_schema $$); SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$); SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$); @@ -304,10 +304,10 @@ SELECT citus_schema_distribute('tenant1'); COMMIT; -- show the schema is a tenant schema now -SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset +SELECT colocationid AS tenant1_colocid FROM pg_dist_schema schemaid \gset SELECT '$$' || ' SELECT colocationid = ' || :tenant1_colocid || - ' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' || + ' FROM pg_dist_schema JOIN pg_dist_colocation USING(colocationid)' || ' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''tenant1%'')' || '$$' AS verify_tenant_query \gset @@ -323,7 +323,7 @@ SET client_min_messages TO WARNING; SELECT citus_schema_undistribute('tenant1'); -- show the schema is a regular schema now -SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$); +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_schema $$); SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$); SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$); @@ -359,10 +359,10 @@ INSERT INTO "CiTuS.TeeN"."TeeNTabLE.1!?!" SELECT i, 'asd'::text FROM generate_se SELECT citus_schema_distribute('"CiTuS.TeeN"'); -- show the schema is a tenant schema now -SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset +SELECT colocationid AS tenant1_colocid FROM pg_dist_schema schemaid \gset SELECT '$$' || ' SELECT colocationid = ' || :tenant1_colocid || - ' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' || + ' FROM pg_dist_schema JOIN pg_dist_colocation USING(colocationid)' || ' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''"CiTuS.TeeN"%'')' || '$$' AS verify_tenant_query \gset @@ -372,7 +372,7 @@ SELECT result FROM run_command_on_all_nodes(:verify_tenant_query); SELECT citus_schema_undistribute('"CiTuS.TeeN"'); -- show the schema is a regular schema -SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$); +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_schema $$); SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$); SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$); @@ -394,10 +394,10 @@ SELECT undistribute_table('tenant1.single_shard_t'); SELECT citus_schema_distribute('tenant1'); -- show the schema is a tenant schema now -SELECT colocationid AS tenant1_colocid FROM pg_dist_tenant_schema schemaid \gset +SELECT colocationid AS tenant1_colocid FROM pg_dist_schema schemaid \gset SELECT '$$' || ' SELECT colocationid = ' || :tenant1_colocid || - ' FROM pg_dist_tenant_schema JOIN pg_dist_colocation USING(colocationid)' || + ' FROM pg_dist_schema JOIN pg_dist_colocation USING(colocationid)' || ' WHERE colocationid = ALL(SELECT colocationid FROM pg_dist_partition WHERE logicalrelid::text LIKE ''tenant1%'')' || '$$' AS verify_tenant_query \gset @@ -407,7 +407,7 @@ SELECT result FROM run_command_on_all_nodes(:verify_tenant_query); SELECT citus_schema_undistribute('tenant1'); -- show the schema is a regular schema now -SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$); +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_schema $$); SELECT result FROM run_command_on_all_nodes($$ SELECT COUNT(*)=0 FROM pg_dist_colocation FULL JOIN pg_dist_partition USING(colocationid) WHERE (logicalrelid::text LIKE 'tenant1.%' OR logicalrelid is NULL) AND colocationid > 0 $$); SELECT result FROM run_command_on_all_nodes($$ SELECT array_agg(logicalrelid ORDER BY logicalrelid) FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant1.%' AND colocationid > 0 $$); @@ -416,13 +416,13 @@ CREATE SCHEMA empty_tenant; SELECT citus_schema_distribute('empty_tenant'); -- show the schema is a tenant schema now -SELECT colocationid AS empty_tenant_colocid FROM pg_dist_tenant_schema schemaid \gset -SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace FROM pg_dist_colocation JOIN pg_dist_tenant_schema USING(colocationid) $$); +SELECT colocationid AS empty_tenant_colocid FROM pg_dist_schema schemaid \gset +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace FROM pg_dist_colocation JOIN pg_dist_schema USING(colocationid) $$); SELECT citus_schema_undistribute('empty_tenant'); -- show the schema is a regular schema now -SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_tenant_schema $$); +SELECT result FROM run_command_on_all_nodes($$ SELECT schemaid::regnamespace as schemaname FROM pg_dist_schema $$); SELECT '$$' || 'SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = ' || :empty_tenant_colocid || '$$' AS verify_empty_tenant_query \gset SELECT result FROM run_command_on_all_nodes(:verify_empty_tenant_query); diff --git a/src/test/regress/sql/multi_extension.sql b/src/test/regress/sql/multi_extension.sql index c71216ecd..aeef30938 100644 --- a/src/test/regress/sql/multi_extension.sql +++ b/src/test/regress/sql/multi_extension.sql @@ -938,7 +938,7 @@ CREATE TABLE tenant_schema.test(x int, y int); SELECT colocationid = ( SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_schema.test'::regclass ) -FROM pg_dist_tenant_schema +FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_schema'; -- and make sure that we can't remove the coordinator due to "test" @@ -983,7 +983,7 @@ DROP EXTENSION citus_columnar; SET citus.enable_schema_based_sharding TO ON; CREATE EXTENSION citus_columnar; -SELECT COUNT(*)=0 FROM pg_dist_tenant_schema +SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid IN ('columnar'::regnamespace, 'columnar_internal'::regnamespace); RESET citus.enable_schema_based_sharding; diff --git a/src/test/regress/sql/schema_based_sharding.sql b/src/test/regress/sql/schema_based_sharding.sql index ad20757ee..0417c0e69 100644 --- a/src/test/regress/sql/schema_based_sharding.sql +++ b/src/test/regress/sql/schema_based_sharding.sql @@ -28,8 +28,8 @@ CREATE TABLE regular_schema.test_table(a int, b text); SELECT create_distributed_table('regular_schema.test_table', 'a'); SET citus.enable_schema_based_sharding TO ON; --- show that regular_schema doesn't show up in pg_dist_tenant_schema -SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'regular_schema'; +-- show that regular_schema doesn't show up in pg_dist_schema +SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'regular_schema'; -- empty tenant CREATE SCHEMA "tenant\'_1"; @@ -73,12 +73,12 @@ ALTER TABLE regular_schema.test_table SET SCHEMA tenant_2; ALTER TABLE tenant_2.test_table SET SCHEMA tenant_3; -- (on coordinator) verify that colocation id is set for empty tenants too -SELECT colocationid > 0 FROM pg_dist_tenant_schema +SELECT colocationid > 0 FROM pg_dist_schema WHERE schemaid::regnamespace::text IN ('tenant_1', 'tenant_3'); -- (on workers) verify that colocation id is set for empty tenants too SELECT result FROM run_command_on_workers($$ - SELECT array_agg(colocationid > 0) FROM pg_dist_tenant_schema + SELECT array_agg(colocationid > 0) FROM pg_dist_schema WHERE schemaid::regnamespace::text IN ('tenant_1', 'tenant_3'); $$); @@ -92,7 +92,7 @@ WHERE logicalrelid = 'tenant_2.test_table'::regclass AND SELECT colocationid = ( SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_2.test_table'::regclass ) -FROM pg_dist_tenant_schema +FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_2'; -- (on workers) verify that colocation id is properly set for non-empty tenant schema @@ -100,7 +100,7 @@ SELECT result FROM run_command_on_workers($$ SELECT colocationid = ( SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_2.test_table'::regclass ) - FROM pg_dist_tenant_schema + FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_2'; $$); @@ -111,7 +111,7 @@ CREATE TABLE tenant_1.test_table(a int, b text); SELECT colocationid = ( SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_1.test_table'::regclass ) -FROM pg_dist_tenant_schema +FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_1'; -- (on workers) verify that colocation id is properly set for now-non-empty tenant schema @@ -119,12 +119,12 @@ SELECT result FROM run_command_on_workers($$ SELECT colocationid = ( SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_1.test_table'::regclass ) - FROM pg_dist_tenant_schema + FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_1'; $$); -- verify that tenant_1 and tenant_2 have different colocation ids -SELECT COUNT(DISTINCT(colocationid))=2 FROM pg_dist_tenant_schema +SELECT COUNT(DISTINCT(colocationid))=2 FROM pg_dist_schema WHERE schemaid::regnamespace::text IN ('tenant_1', 'tenant_2'); -- verify that we don't allow creating tenant tables via CREATE SCHEMA command @@ -322,7 +322,7 @@ SELECT COUNT(*) = 5 FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant_5.test_table_like_%' AND partmethod = 'n' AND repmodel = 's' AND colocationid = ( - SELECT colocationid FROM pg_dist_tenant_schema + SELECT colocationid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_5' ); @@ -347,11 +347,11 @@ CREATE TABLE tenant_5.tbl_2(a int, b text); CREATE SCHEMA "CiTuS.TeeN_108"; ALTER SCHEMA "CiTuS.TeeN_108" RENAME TO citus_teen_proper; -SELECT schemaid AS citus_teen_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'citus_teen_proper' \gset -SELECT colocationid AS citus_teen_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'citus_teen_proper' \gset +SELECT schemaid AS citus_teen_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'citus_teen_proper' \gset +SELECT colocationid AS citus_teen_colocationid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'citus_teen_proper' \gset SELECT result FROM run_command_on_workers($$ - SELECT schemaid INTO citus_teen_schemaid FROM pg_dist_tenant_schema + SELECT schemaid INTO citus_teen_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'citus_teen_proper' $$); @@ -361,7 +361,7 @@ SELECT :citus_teen_colocationid > 0; -- (on workers) verify that the same colocation id is used on workers too SELECT format( 'SELECT result FROM run_command_on_workers($$ - SELECT COUNT(*)=1 FROM pg_dist_tenant_schema + SELECT COUNT(*)=1 FROM pg_dist_schema WHERE schemaid::regnamespace::text = ''citus_teen_proper'' AND colocationid = %s; $$);', @@ -394,18 +394,18 @@ WHERE logicalrelid::regclass::text LIKE 'tenant_4.%'; SELECT COUNT(DISTINCT(colocationid))=1 FROM pg_dist_partition WHERE logicalrelid::regclass::text LIKE 'tenant_5.%'; -SELECT schemaid AS tenant_4_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_4' \gset -SELECT colocationid AS tenant_4_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_4' \gset +SELECT schemaid AS tenant_4_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_4' \gset +SELECT colocationid AS tenant_4_colocationid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_4' \gset SELECT result FROM run_command_on_workers($$ - SELECT schemaid INTO tenant_4_schemaid FROM pg_dist_tenant_schema + SELECT schemaid INTO tenant_4_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_4' $$); SET client_min_messages TO WARNING; -- Rename it to a name that contains a single quote to verify that we properly --- escape its name when sending the command to delete the pg_dist_tenant_schema +-- escape its name when sending the command to delete the pg_dist_schema -- entry on workers. ALTER SCHEMA tenant_4 RENAME TO "tenant\'_4"; @@ -414,22 +414,22 @@ DROP SCHEMA "tenant\'_4", "CiTuS.TeeN_108" CASCADE; SET client_min_messages TO NOTICE; -- (on coordinator) Verify that dropping a tenant schema deletes the associated --- pg_dist_tenant_schema entry and pg_dist_colocation too. -SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid = :tenant_4_schemaid; +-- pg_dist_schema entry and pg_dist_colocation too. +SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid = :tenant_4_schemaid; SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = :tenant_4_colocationid; -SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid = :citus_teen_schemaid; +SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid = :citus_teen_schemaid; SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = :citus_teen_colocationid; -- (on workers) Verify that dropping a tenant schema deletes the associated --- pg_dist_tenant_schema entry and pg_dist_colocation too. +-- pg_dist_schema entry and pg_dist_colocation too. SELECT result FROM run_command_on_workers($$ - SELECT COUNT(*)=0 FROM pg_dist_tenant_schema + SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid = (SELECT schemaid FROM tenant_4_schemaid) $$); SELECT result FROM run_command_on_workers($$ - SELECT COUNT(*)=0 FROM pg_dist_tenant_schema + SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid = (SELECT schemaid FROM citus_teen_schemaid) $$); @@ -467,12 +467,12 @@ SELECT create_distributed_table('regular_schema.null_shard_key_table_2', null); -- Show that we don't chose to colocate regular single-shard tables with -- tenant tables by default. -SELECT * FROM pg_dist_tenant_schema WHERE colocationid = ( +SELECT * FROM pg_dist_schema WHERE colocationid = ( SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'regular_schema.null_shard_key_table_2'::regclass ); -- save the colocation id used for tenant_5 -SELECT colocationid AS tenant_5_old_colocationid FROM pg_dist_tenant_schema +SELECT colocationid AS tenant_5_old_colocationid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_5' \gset -- drop all the tables that belong to tenant_5 and create a new one @@ -480,32 +480,32 @@ DROP TABLE tenant_5.tbl_1, tenant_5.tbl_2, tenant_5.tbl_3; CREATE TABLE tenant_5.tbl_4(a int, b text); -- (on coordinator) verify that tenant_5 is still associated with the same colocation id -SELECT colocationid = :tenant_5_old_colocationid FROM pg_dist_tenant_schema +SELECT colocationid = :tenant_5_old_colocationid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_5'; -- (on workers) verify that tenant_5 is still associated with the same colocation id SELECT format( 'SELECT result FROM run_command_on_workers($$ - SELECT colocationid = %s FROM pg_dist_tenant_schema + SELECT colocationid = %s FROM pg_dist_schema WHERE schemaid::regnamespace::text = ''tenant_5''; $$);', :tenant_5_old_colocationid) AS verify_workers_query \gset :verify_workers_query -SELECT schemaid AS tenant_1_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_1' \gset -SELECT colocationid AS tenant_1_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_1' \gset +SELECT schemaid AS tenant_1_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_1' \gset +SELECT colocationid AS tenant_1_colocationid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_1' \gset -SELECT schemaid AS tenant_2_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_2' \gset -SELECT colocationid AS tenant_2_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_2' \gset +SELECT schemaid AS tenant_2_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_2' \gset +SELECT colocationid AS tenant_2_colocationid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_2' \gset SELECT result FROM run_command_on_workers($$ - SELECT schemaid INTO tenant_1_schemaid FROM pg_dist_tenant_schema + SELECT schemaid INTO tenant_1_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_1' $$); SELECT result FROM run_command_on_workers($$ - SELECT schemaid INTO tenant_2_schemaid FROM pg_dist_tenant_schema + SELECT schemaid INTO tenant_2_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_2' $$); @@ -537,16 +537,16 @@ DROP ROLE test_non_super_user; SET client_min_messages TO NOTICE; -- (on coordinator) Verify that dropping a tenant schema always deletes --- the associated pg_dist_tenant_schema entry even if the the schema was +-- the associated pg_dist_schema entry even if the the schema was -- dropped while the GUC was set to off. -SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid IN (:tenant_1_schemaid, :tenant_2_schemaid); +SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid IN (:tenant_1_schemaid, :tenant_2_schemaid); SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid IN (:tenant_1_colocationid, :tenant_2_colocationid); -- (on workers) Verify that dropping a tenant schema always deletes --- the associated pg_dist_tenant_schema entry even if the the schema was +-- the associated pg_dist_schema entry even if the the schema was -- dropped while the GUC was set to off. SELECT result FROM run_command_on_workers($$ - SELECT COUNT(*)=0 FROM pg_dist_tenant_schema + SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid IN (SELECT schemaid FROM tenant_1_schemaid UNION SELECT schemaid FROM tenant_2_schemaid) $$); @@ -569,20 +569,20 @@ $$); SET citus.enable_schema_based_sharding TO ON; SET client_min_messages TO NOTICE; --- show that all schemaid values are unique and non-null in pg_dist_tenant_schema -SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid IS NULL; -SELECT (SELECT COUNT(*) FROM pg_dist_tenant_schema) = - (SELECT COUNT(DISTINCT(schemaid)) FROM pg_dist_tenant_schema); +-- show that all schemaid values are unique and non-null in pg_dist_schema +SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid IS NULL; +SELECT (SELECT COUNT(*) FROM pg_dist_schema) = + (SELECT COUNT(DISTINCT(schemaid)) FROM pg_dist_schema); --- show that all colocationid values are unique and non-null in pg_dist_tenant_schema -SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE colocationid IS NULL; -SELECT (SELECT COUNT(*) FROM pg_dist_tenant_schema) = - (SELECT COUNT(DISTINCT(colocationid)) FROM pg_dist_tenant_schema); +-- show that all colocationid values are unique and non-null in pg_dist_schema +SELECT COUNT(*)=0 FROM pg_dist_schema WHERE colocationid IS NULL; +SELECT (SELECT COUNT(*) FROM pg_dist_schema) = + (SELECT COUNT(DISTINCT(colocationid)) FROM pg_dist_schema); CREATE TABLE public.cannot_be_a_tenant_table(a int, b text); -- show that we don't consider public schema as a tenant schema -SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'public'; +SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'public'; DROP TABLE public.cannot_be_a_tenant_table; @@ -592,13 +592,13 @@ BEGIN; -- Show that we don't consider public schema as a tenant schema, -- even if it's recreated. - SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'public'; + SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'public'; ROLLBACK; CREATE TEMPORARY TABLE temp_table(a int, b text); -- show that we don't consider temporary schemas as tenant schemas -SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = '%pg_temp%'; +SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid::regnamespace::text = '%pg_temp%'; DROP TABLE temp_table; @@ -611,7 +611,7 @@ BEGIN; SELECT colocationid = ( SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_7.tbl_1'::regclass ) - FROM pg_dist_tenant_schema + FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_7'; -- make sure that both tables created in tenant_7 are colocated @@ -627,7 +627,7 @@ BEGIN; CREATE TABLE tenant_8.tbl_2(a int, b text); ROLLBACK; -SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_8'; +SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_8'; SELECT COUNT(*)=0 FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant_8.%'; -- Verify that citus.enable_schema_based_sharding and citus.use_citus_managed_tables @@ -675,27 +675,27 @@ CREATE TABLE tenant_5.tbl_5(a int, b text, FOREIGN KEY(a) REFERENCES tenant_7.tb CREATE SCHEMA tenant_9; -SELECT schemaid AS tenant_9_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_9' \gset -SELECT colocationid AS tenant_9_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_9' \gset +SELECT schemaid AS tenant_9_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_9' \gset +SELECT colocationid AS tenant_9_colocationid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_9' \gset SELECT result FROM run_command_on_workers($$ - SELECT schemaid INTO tenant_9_schemaid FROM pg_dist_tenant_schema + SELECT schemaid INTO tenant_9_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_9' $$); DROP SCHEMA tenant_9; -- (on coordinator) Make sure that dropping an empty tenant schema --- doesn't leave any dangling entries in pg_dist_tenant_schema and +-- doesn't leave any dangling entries in pg_dist_schema and -- pg_dist_colocation. -SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid = :tenant_9_schemaid; +SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid = :tenant_9_schemaid; SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = :tenant_9_colocationid; -- (on workers) Make sure that dropping an empty tenant schema --- doesn't leave any dangling entries in pg_dist_tenant_schema and +-- doesn't leave any dangling entries in pg_dist_schema and -- pg_dist_colocation. SELECT result FROM run_command_on_workers($$ - SELECT COUNT(*)=0 FROM pg_dist_tenant_schema + SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid = (SELECT schemaid FROM tenant_9_schemaid) $$); @@ -776,11 +776,11 @@ SET citus.shard_replication_factor TO 1; SET client_min_messages TO NOTICE; SET citus.enable_schema_based_sharding TO ON; -SELECT schemaid AS tenant_9_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_9' \gset -SELECT colocationid AS tenant_9_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_9' \gset +SELECT schemaid AS tenant_9_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_9' \gset +SELECT colocationid AS tenant_9_colocationid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_9' \gset SELECT result FROM run_command_on_workers($$ - SELECT schemaid INTO tenant_9_schemaid FROM pg_dist_tenant_schema + SELECT schemaid INTO tenant_9_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_9' $$); @@ -788,15 +788,15 @@ DROP OWNED BY test_other_super_user; -- (on coordinator) Make sure that dropping an empty tenant schema -- (via DROP OWNED BY) doesn't leave any dangling entries in --- pg_dist_tenant_schema and pg_dist_colocation. -SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid = :tenant_9_schemaid; +-- pg_dist_schema and pg_dist_colocation. +SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid = :tenant_9_schemaid; SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid = :tenant_9_colocationid; -- (on workers) Make sure that dropping an empty tenant schema -- (via DROP OWNED BY) doesn't leave any dangling entries in --- pg_dist_tenant_schema and pg_dist_colocation. +-- pg_dist_schema and pg_dist_colocation. SELECT result FROM run_command_on_workers($$ - SELECT COUNT(*)=0 FROM pg_dist_tenant_schema + SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid = (SELECT schemaid FROM tenant_9_schemaid) $$); @@ -841,29 +841,29 @@ DROP TABLE tenant_10.tbl_2; CREATE SCHEMA tenant_11; -SELECT schemaid AS tenant_10_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_10' \gset -SELECT colocationid AS tenant_10_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_10' \gset +SELECT schemaid AS tenant_10_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_10' \gset +SELECT colocationid AS tenant_10_colocationid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_10' \gset -SELECT schemaid AS tenant_11_schemaid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_11' \gset -SELECT colocationid AS tenant_11_colocationid FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_11' \gset +SELECT schemaid AS tenant_11_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_11' \gset +SELECT colocationid AS tenant_11_colocationid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_11' \gset SELECT result FROM run_command_on_workers($$ - SELECT schemaid INTO tenant_10_schemaid FROM pg_dist_tenant_schema + SELECT schemaid INTO tenant_10_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_10' $$); SELECT result FROM run_command_on_workers($$ - SELECT schemaid INTO tenant_11_schemaid FROM pg_dist_tenant_schema + SELECT schemaid INTO tenant_11_schemaid FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_11' $$); -- (on coordinator) Verify metadata for tenant schemas that are created via non-super-user. -SELECT COUNT(DISTINCT(schemaid))=2 FROM pg_dist_tenant_schema WHERE schemaid IN (:tenant_10_schemaid, :tenant_11_schemaid); +SELECT COUNT(DISTINCT(schemaid))=2 FROM pg_dist_schema WHERE schemaid IN (:tenant_10_schemaid, :tenant_11_schemaid); SELECT COUNT(DISTINCT(colocationid))=2 FROM pg_dist_colocation WHERE colocationid IN (:tenant_10_colocationid, :tenant_11_colocationid); -- (on workers) Verify metadata for tenant schemas that are created via non-super-user. SELECT result FROM run_command_on_workers($$ - SELECT COUNT(DISTINCT(schemaid))=2 FROM pg_dist_tenant_schema + SELECT COUNT(DISTINCT(schemaid))=2 FROM pg_dist_schema WHERE schemaid IN (SELECT schemaid FROM tenant_10_schemaid UNION SELECT schemaid FROM tenant_11_schemaid) $$); @@ -880,14 +880,14 @@ DROP SCHEMA tenant_10, tenant_11 CASCADE; SET client_min_messages TO NOTICE; -- (on coordinator) Verify that dropping a tenant schema via non-super-user --- deletes the associated pg_dist_tenant_schema entry. -SELECT COUNT(*)=0 FROM pg_dist_tenant_schema WHERE schemaid IN (:tenant_10_schemaid, :tenant_11_schemaid); +-- deletes the associated pg_dist_schema entry. +SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid IN (:tenant_10_schemaid, :tenant_11_schemaid); SELECT COUNT(*)=0 FROM pg_dist_colocation WHERE colocationid IN (:tenant_10_colocationid, :tenant_11_colocationid); -- (on workers) Verify that dropping a tenant schema via non-super-user --- deletes the associated pg_dist_tenant_schema entry. +-- deletes the associated pg_dist_schema entry. SELECT result FROM run_command_on_workers($$ - SELECT COUNT(*)=0 FROM pg_dist_tenant_schema + SELECT COUNT(*)=0 FROM pg_dist_schema WHERE schemaid IN (SELECT schemaid FROM tenant_10_schemaid UNION SELECT schemaid FROM tenant_11_schemaid) $$); diff --git a/src/test/regress/sql/single_node.sql b/src/test/regress/sql/single_node.sql index f74699b20..55f244b16 100644 --- a/src/test/regress/sql/single_node.sql +++ b/src/test/regress/sql/single_node.sql @@ -104,8 +104,8 @@ SET citus.enable_schema_based_sharding TO ON; CREATE SCHEMA tenant_1; CREATE TABLE tenant_1.tbl_1 (a int); --- verify that we recorded tenant_1 in pg_dist_tenant_schema -SELECT COUNT(*)=1 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_1'; +-- verify that we recorded tenant_1 in pg_dist_schema +SELECT COUNT(*)=1 FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_1'; -- verify that tenant_1.tbl_1 is recorded in pg_dist_partition, as a single-shard table SELECT COUNT(*)=1 FROM pg_dist_partition diff --git a/src/test/regress/sql/upgrade_schema_based_sharding_after.sql b/src/test/regress/sql/upgrade_schema_based_sharding_after.sql index 300d94574..cd8e521ec 100644 --- a/src/test/regress/sql/upgrade_schema_based_sharding_after.sql +++ b/src/test/regress/sql/upgrade_schema_based_sharding_after.sql @@ -2,12 +2,12 @@ ALTER SCHEMA "tenant\'_1" RENAME TO tenant_1; ALTER SCHEMA "tenant\'_2" RENAME TO tenant_2; -- verify that colocation id is set even for empty tenant -SELECT colocationid > 0 FROM pg_dist_tenant_schema +SELECT colocationid > 0 FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_1'; -- verify the same on workers SELECT result FROM run_command_on_workers($$ - SELECT colocationid > 0 FROM pg_dist_tenant_schema + SELECT colocationid > 0 FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_1'; $$); @@ -15,7 +15,7 @@ $$); SELECT colocationid = ( SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_2.test_table'::regclass ) -FROM pg_dist_tenant_schema +FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_2'; -- verify the same on workers @@ -23,7 +23,7 @@ SELECT result FROM run_command_on_workers($$ SELECT colocationid = ( SELECT colocationid FROM pg_dist_partition WHERE logicalrelid = 'tenant_2.test_table'::regclass ) - FROM pg_dist_tenant_schema + FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_2'; $$); @@ -41,7 +41,7 @@ SELECT colocationid = ( WHERE logicalrelid = 'tenant_1.tbl_1'::regclass AND partmethod = 'n' AND repmodel = 's' ) -FROM pg_dist_tenant_schema +FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_1'; SELECT colocationid = ( @@ -49,7 +49,7 @@ SELECT colocationid = ( WHERE logicalrelid = 'tenant_2.tbl_1'::regclass AND partmethod = 'n' AND repmodel = 's' ) -FROM pg_dist_tenant_schema +FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_2'; -- rollback the changes made on following schemas to make this test idempotent @@ -62,7 +62,7 @@ SET citus.enable_schema_based_sharding TO ON; CREATE SCHEMA tenant_3; -- Show that we can create furher tenant schemas after pg upgrade. -SELECT COUNT(*)=1 FROM pg_dist_tenant_schema WHERE schemaid::regnamespace::text = 'tenant_3'; +SELECT COUNT(*)=1 FROM pg_dist_schema WHERE schemaid::regnamespace::text = 'tenant_3'; -- drop the schema created in this test to this test idempotent DROP SCHEMA tenant_3 CASCADE; From 3cc7a4aa42afe304301f65e22fd85ef4dd4eed56 Mon Sep 17 00:00:00 2001 From: Naisila Puka <37271756+naisila@users.noreply.github.com> Date: Wed, 14 Jun 2023 15:32:38 +0300 Subject: [PATCH 095/118] Fix pg14-pg15 upgrade_distributed_triggers test (#6981) This test is only relevant for pg14-15 upgrade. However, the check on `upgrade_distributed_triggers_after` didn't take into consideration the case when we are doing pg15-16 upgrade. Hence, I added one more condition to the test: existence of `upgrade_distributed_triggers` schema which can only be created in pg14. --- .../regress/expected/upgrade_distributed_triggers_after.out | 4 ++-- .../regress/expected/upgrade_distributed_triggers_after_0.out | 4 ++-- src/test/regress/sql/upgrade_distributed_triggers_after.sql | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/regress/expected/upgrade_distributed_triggers_after.out b/src/test/regress/expected/upgrade_distributed_triggers_after.out index 7037b4c50..f36bf1749 100644 --- a/src/test/regress/expected/upgrade_distributed_triggers_after.out +++ b/src/test/regress/expected/upgrade_distributed_triggers_after.out @@ -9,9 +9,9 @@ -- this test is relevant only for pg14-15 upgrade -- SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int = 15 AS server_version_eq_15 +SELECT substring(:'server_version', '\d+')::int = 15 AND EXISTS (SELECT * FROM pg_namespace WHERE nspname = 'upgrade_distributed_triggers') AS is_14_15_pg_upgrade \gset -\if :server_version_eq_15 +\if :is_14_15_pg_upgrade \else \q \endif diff --git a/src/test/regress/expected/upgrade_distributed_triggers_after_0.out b/src/test/regress/expected/upgrade_distributed_triggers_after_0.out index 2b1f5cac1..c6a0d053a 100644 --- a/src/test/regress/expected/upgrade_distributed_triggers_after_0.out +++ b/src/test/regress/expected/upgrade_distributed_triggers_after_0.out @@ -9,8 +9,8 @@ -- this test is relevant only for pg14-15 upgrade -- SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int = 15 AS server_version_eq_15 +SELECT substring(:'server_version', '\d+')::int = 15 AND EXISTS (SELECT * FROM pg_namespace WHERE nspname = 'upgrade_distributed_triggers') AS is_14_15_pg_upgrade \gset -\if :server_version_eq_15 +\if :is_14_15_pg_upgrade \else \q diff --git a/src/test/regress/sql/upgrade_distributed_triggers_after.sql b/src/test/regress/sql/upgrade_distributed_triggers_after.sql index 681f1896b..181833594 100644 --- a/src/test/regress/sql/upgrade_distributed_triggers_after.sql +++ b/src/test/regress/sql/upgrade_distributed_triggers_after.sql @@ -10,9 +10,9 @@ -- SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int = 15 AS server_version_eq_15 +SELECT substring(:'server_version', '\d+')::int = 15 AND EXISTS (SELECT * FROM pg_namespace WHERE nspname = 'upgrade_distributed_triggers') AS is_14_15_pg_upgrade \gset -\if :server_version_eq_15 +\if :is_14_15_pg_upgrade \else \q \endif From 4f793abc4a72fd0cd7b0feb213b027a517562a14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emel=20=C5=9Eim=C5=9Fek?= Date: Wed, 14 Jun 2023 17:35:52 +0300 Subject: [PATCH 096/118] Turn on GUC_REPORT flag for search_path to enable reporting back the parameter value upon change. (#6983) DESCRIPTION: Turns on the GUC_REPORT flag for search_path. This results in postgres to report the parameter status back in addition to Command Complete packet. In response to the following command, > SET search_path TO client1; postgres sends back the following packets (shown in pseudo form): C (Command Complete) SET + **S (Parameter Status) search_path = client1** --- src/backend/distributed/shared_library_init.c | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/backend/distributed/shared_library_init.c b/src/backend/distributed/shared_library_init.c index 57bfb7197..a9b49e7c9 100644 --- a/src/backend/distributed/shared_library_init.c +++ b/src/backend/distributed/shared_library_init.c @@ -191,7 +191,7 @@ static void CitusCleanupConnectionsAtExit(int code, Datum arg); static void DecrementExternalClientBackendCounterAtExit(int code, Datum arg); static void CreateRequiredDirectories(void); static void RegisterCitusConfigVariables(void); -static void OverridePostgresConfigAssignHooks(void); +static void OverridePostgresConfigProperties(void); static bool ErrorIfNotASuitableDeadlockFactor(double *newval, void **extra, GucSource source); static bool WarnIfDeprecatedExecutorUsed(int *newval, void **extra, GucSource source); @@ -2587,16 +2587,17 @@ RegisterCitusConfigVariables(void) /* warn about config items in the citus namespace that are not registered above */ EmitWarningsOnPlaceholders("citus"); - OverridePostgresConfigAssignHooks(); + OverridePostgresConfigProperties(); } /* - * OverridePostgresConfigAssignHooks overrides GUC assign hooks where we want - * custom behaviour. + * OverridePostgresConfigProperties overrides GUC properties where we want + * custom behaviour. We should consider using Postgres function find_option + * in this function once it is exported by Postgres in a later release. */ static void -OverridePostgresConfigAssignHooks(void) +OverridePostgresConfigProperties(void) { struct config_generic **guc_vars = get_guc_variables(); int gucCount = GetNumConfigOptions(); @@ -2612,6 +2613,17 @@ OverridePostgresConfigAssignHooks(void) OldApplicationNameAssignHook = stringVar->assign_hook; stringVar->assign_hook = ApplicationNameAssignHook; } + + /* + * Turn on GUC_REPORT for search_path. GUC_REPORT provides that an S (Parameter Status) + * packet is appended after the C (Command Complete) packet sent from the server + * for SET command. S packet contains the new value of the parameter + * if its value has been changed. + */ + if (strcmp(var->name, "search_path") == 0) + { + var->flags |= GUC_REPORT; + } } } From 002a88ae7fbd1d4cb41c168b075ac3a6aeb368d6 Mon Sep 17 00:00:00 2001 From: Ahmet Gedemenli Date: Thu, 15 Jun 2023 13:13:45 +0300 Subject: [PATCH 097/118] Error for single shard table creation if replication factor > 1 (#7006) Error for single shard table creation if replication factor > 1 --- .../commands/create_distributed_table.c | 8 ++++++ .../regress/expected/citus_stat_tenants.out | 1 + .../insert_select_single_shard_table.out | 1 + ...n_citus_schema_distribute_undistribute.out | 28 +++++++++++++++++++ .../isolation_schema_based_sharding.out | 12 ++++---- src/test/regress/expected/multi_extension.out | 1 + .../regress/expected/multi_metadata_sync.out | 4 +-- .../expected/multi_metadata_sync_0.out | 4 +-- .../expected/query_single_shard_table.out | 5 ++++ .../expected/schema_based_sharding.out | 11 ++++++++ .../expected/single_shard_table_udfs.out | 1 + ..._citus_schema_distribute_undistribute.spec | 4 ++- .../spec/isolation_schema_based_sharding.spec | 5 ++-- src/test/regress/sql/citus_stat_tenants.sql | 1 + .../sql/insert_select_single_shard_table.sql | 1 + src/test/regress/sql/multi_extension.sql | 1 + src/test/regress/sql/multi_metadata_sync.sql | 3 +- .../regress/sql/query_single_shard_table.sql | 7 +++++ .../regress/sql/schema_based_sharding.sql | 9 ++++++ .../regress/sql/single_shard_table_udfs.sql | 1 + 20 files changed, 94 insertions(+), 14 deletions(-) diff --git a/src/backend/distributed/commands/create_distributed_table.c b/src/backend/distributed/commands/create_distributed_table.c index 9849da9f4..85331aea7 100644 --- a/src/backend/distributed/commands/create_distributed_table.c +++ b/src/backend/distributed/commands/create_distributed_table.c @@ -1086,6 +1086,14 @@ CreateCitusTable(Oid relationId, CitusTableType tableType, relation_close(relation, NoLock); + if (tableType == SINGLE_SHARD_DISTRIBUTED && ShardReplicationFactor > 1) + { + ereport(ERROR, (errmsg("could not create single shard table: " + "citus.shard_replication_factor is greater than 1"), + errhint("Consider setting citus.shard_replication_factor to 1 " + "and try again"))); + } + /* * EnsureTableNotDistributed errors out when relation is a citus table but * we don't want to ask user to first undistribute their citus local tables diff --git a/src/test/regress/expected/citus_stat_tenants.out b/src/test/regress/expected/citus_stat_tenants.out index 090f0e10e..e7ddadb71 100644 --- a/src/test/regress/expected/citus_stat_tenants.out +++ b/src/test/regress/expected/citus_stat_tenants.out @@ -888,6 +888,7 @@ SELECT citus_stat_tenants_reset(); (1 row) +SET citus.shard_replication_factor TO 1; CREATE TABLE dist_tbl_text_single_shard(a text, b int); select create_distributed_table('dist_tbl_text_single_shard', NULL); create_distributed_table diff --git a/src/test/regress/expected/insert_select_single_shard_table.out b/src/test/regress/expected/insert_select_single_shard_table.out index f61d6b549..8dbb1cf9a 100644 --- a/src/test/regress/expected/insert_select_single_shard_table.out +++ b/src/test/regress/expected/insert_select_single_shard_table.out @@ -2,6 +2,7 @@ CREATE SCHEMA insert_select_single_shard_table; SET search_path TO insert_select_single_shard_table; SET citus.next_shard_id TO 1820000; SET citus.shard_count TO 32; +SET citus.shard_replication_factor TO 1; SET client_min_messages TO NOTICE; CREATE TABLE nullkey_c1_t1(a int, b int); CREATE TABLE nullkey_c1_t2(a int, b int); diff --git a/src/test/regress/expected/isolation_citus_schema_distribute_undistribute.out b/src/test/regress/expected/isolation_citus_schema_distribute_undistribute.out index a392a0129..88737f262 100644 --- a/src/test/regress/expected/isolation_citus_schema_distribute_undistribute.out +++ b/src/test/regress/expected/isolation_citus_schema_distribute_undistribute.out @@ -8,6 +8,7 @@ citus_add_local_table_to_metadata step s1-begin: BEGIN; + SET citus.shard_replication_factor TO 1; step s1-schema-distribute: SELECT citus_schema_distribute('tenant1'); @@ -53,6 +54,7 @@ citus_schema_distribute step s1-begin: BEGIN; + SET citus.shard_replication_factor TO 1; step s1-schema-undistribute: SELECT citus_schema_undistribute('tenant1'); @@ -90,6 +92,7 @@ citus_add_local_table_to_metadata step s1-begin: BEGIN; + SET citus.shard_replication_factor TO 1; step s1-schema-distribute: SELECT citus_schema_distribute('tenant1'); @@ -137,6 +140,7 @@ citus_schema_distribute step s1-begin: BEGIN; + SET citus.shard_replication_factor TO 1; step s1-schema-undistribute: SELECT citus_schema_undistribute('tenant1'); @@ -174,6 +178,7 @@ citus_add_local_table_to_metadata step s1-begin: BEGIN; + SET citus.shard_replication_factor TO 1; step s1-schema-distribute: SELECT citus_schema_distribute('tenant1'); @@ -184,6 +189,7 @@ citus_schema_distribute (1 row) step s2-add-table: + SET citus.shard_replication_factor TO 1; CREATE TABLE tenant1.table4(id int PRIMARY KEY, name text, col bigint); step s1-commit: @@ -223,6 +229,7 @@ citus_schema_distribute step s1-begin: BEGIN; + SET citus.shard_replication_factor TO 1; step s1-schema-undistribute: SELECT citus_schema_undistribute('tenant1'); @@ -233,6 +240,7 @@ citus_schema_undistribute (1 row) step s2-add-table: + SET citus.shard_replication_factor TO 1; CREATE TABLE tenant1.table4(id int PRIMARY KEY, name text, col bigint); step s1-commit: @@ -261,6 +269,7 @@ citus_add_local_table_to_metadata step s1-begin: BEGIN; + SET citus.shard_replication_factor TO 1; step s1-schema-distribute: SELECT citus_schema_distribute('tenant1'); @@ -308,6 +317,7 @@ citus_schema_distribute step s1-begin: BEGIN; + SET citus.shard_replication_factor TO 1; step s1-schema-undistribute: SELECT citus_schema_undistribute('tenant1'); @@ -345,6 +355,7 @@ citus_add_local_table_to_metadata step s1-begin: BEGIN; + SET citus.shard_replication_factor TO 1; step s1-schema-distribute: SELECT citus_schema_distribute('tenant1'); @@ -393,6 +404,7 @@ citus_schema_distribute step s1-begin: BEGIN; + SET citus.shard_replication_factor TO 1; step s1-schema-undistribute: SELECT citus_schema_undistribute('tenant1'); @@ -431,6 +443,7 @@ citus_add_local_table_to_metadata step s1-begin: BEGIN; + SET citus.shard_replication_factor TO 1; step s1-schema-distribute: SELECT citus_schema_distribute('tenant1'); @@ -479,6 +492,7 @@ citus_schema_distribute step s1-begin: BEGIN; + SET citus.shard_replication_factor TO 1; step s1-schema-undistribute: SELECT citus_schema_undistribute('tenant1'); @@ -518,6 +532,7 @@ citus_add_local_table_to_metadata step s1-begin: BEGIN; + SET citus.shard_replication_factor TO 1; step s1-schema-distribute: SELECT citus_schema_distribute('tenant1'); @@ -566,6 +581,7 @@ citus_schema_distribute step s1-begin: BEGIN; + SET citus.shard_replication_factor TO 1; step s1-schema-undistribute: SELECT citus_schema_undistribute('tenant1'); @@ -603,6 +619,7 @@ citus_add_local_table_to_metadata step s1-begin: BEGIN; + SET citus.shard_replication_factor TO 1; step s1-schema-distribute: SELECT citus_schema_distribute('tenant1'); @@ -651,6 +668,7 @@ citus_schema_distribute step s1-begin: BEGIN; + SET citus.shard_replication_factor TO 1; step s1-schema-undistribute: SELECT citus_schema_undistribute('tenant1'); @@ -689,6 +707,7 @@ citus_add_local_table_to_metadata step s1-begin: BEGIN; + SET citus.shard_replication_factor TO 1; step s1-schema-distribute: SELECT citus_schema_distribute('tenant1'); @@ -737,6 +756,7 @@ citus_schema_distribute step s1-begin: BEGIN; + SET citus.shard_replication_factor TO 1; step s1-schema-undistribute: SELECT citus_schema_undistribute('tenant1'); @@ -778,6 +798,7 @@ step s2-create-unique-index: step s1-begin: BEGIN; + SET citus.shard_replication_factor TO 1; step s1-schema-distribute: SELECT citus_schema_distribute('tenant1'); @@ -829,6 +850,7 @@ citus_schema_distribute step s1-begin: BEGIN; + SET citus.shard_replication_factor TO 1; step s1-schema-undistribute: SELECT citus_schema_undistribute('tenant1'); @@ -867,6 +889,7 @@ citus_add_local_table_to_metadata step s1-begin: BEGIN; + SET citus.shard_replication_factor TO 1; step s1-schema-distribute: SELECT citus_schema_distribute('tenant1'); @@ -916,6 +939,7 @@ citus_schema_distribute step s1-begin: BEGIN; + SET citus.shard_replication_factor TO 1; step s1-schema-undistribute: SELECT citus_schema_undistribute('tenant1'); @@ -959,6 +983,7 @@ step s2-insert: step s1-begin: BEGIN; + SET citus.shard_replication_factor TO 1; step s1-schema-distribute: SELECT citus_schema_distribute('tenant1'); @@ -1011,6 +1036,7 @@ citus_schema_distribute step s1-begin: BEGIN; + SET citus.shard_replication_factor TO 1; step s1-schema-undistribute: SELECT citus_schema_undistribute('tenant1'); @@ -1053,6 +1079,7 @@ step s2-insert: step s1-begin: BEGIN; + SET citus.shard_replication_factor TO 1; step s1-schema-distribute: SELECT citus_schema_distribute('tenant1'); @@ -1105,6 +1132,7 @@ citus_schema_distribute step s1-begin: BEGIN; + SET citus.shard_replication_factor TO 1; step s1-schema-undistribute: SELECT citus_schema_undistribute('tenant1'); diff --git a/src/test/regress/expected/isolation_schema_based_sharding.out b/src/test/regress/expected/isolation_schema_based_sharding.out index 48d00bfa0..f089babf5 100644 --- a/src/test/regress/expected/isolation_schema_based_sharding.out +++ b/src/test/regress/expected/isolation_schema_based_sharding.out @@ -1,8 +1,8 @@ Parsed test spec with 2 sessions starting permutation: s1-begin s2-begin s1-tenant-1-create-table-1 s2-tenant-1-create-table-2 s1-commit s2-tenant-1-verify-colocation s2-commit -step s1-begin: BEGIN; -step s2-begin: BEGIN; +step s1-begin: BEGIN; SET citus.shard_replication_factor TO 1; +step s2-begin: BEGIN; SET citus.shard_replication_factor TO 1; step s1-tenant-1-create-table-1: CREATE TABLE tenant_1.tbl_1 (a int); step s2-tenant-1-create-table-2: CREATE TABLE tenant_1.tbl_2 (a int); step s1-commit: COMMIT; @@ -15,8 +15,8 @@ t step s2-commit: COMMIT; starting permutation: s1-begin s2-begin s1-tenant-4-create-table-1 s2-tenant-4-create-table-2 s1-commit s2-tenant-4-verify-colocation s2-commit -step s1-begin: BEGIN; -step s2-begin: BEGIN; +step s1-begin: BEGIN; SET citus.shard_replication_factor TO 1; +step s2-begin: BEGIN; SET citus.shard_replication_factor TO 1; step s1-tenant-4-create-table-1: CREATE TABLE tenant_4.tbl_1 (a int); step s2-tenant-4-create-table-2: CREATE TABLE tenant_4.tbl_2 (a int); step s1-commit: COMMIT; @@ -29,8 +29,8 @@ t step s2-commit: COMMIT; starting permutation: s1-begin s2-begin s1-tenant-2-create-table-1 s2-tenant-3-create-table-1 s1-commit s2-commit -step s1-begin: BEGIN; -step s2-begin: BEGIN; +step s1-begin: BEGIN; SET citus.shard_replication_factor TO 1; +step s2-begin: BEGIN; SET citus.shard_replication_factor TO 1; step s1-tenant-2-create-table-1: CREATE TABLE tenant_2.tbl_1 (a int); step s2-tenant-3-create-table-1: CREATE TABLE tenant_3.tbl_1 (a int); step s1-commit: COMMIT; diff --git a/src/test/regress/expected/multi_extension.out b/src/test/regress/expected/multi_extension.out index fd297ef85..54862798c 100644 --- a/src/test/regress/expected/multi_extension.out +++ b/src/test/regress/expected/multi_extension.out @@ -1333,6 +1333,7 @@ SELECT * FROM multi_extension.print_extension_changes(); -- Test downgrade to 11.3-1 from 12.0-1 ALTER EXTENSION citus UPDATE TO '12.0-1'; CREATE TABLE null_shard_key (x int, y int); +SET citus.shard_replication_factor TO 1; SELECT create_distributed_table('null_shard_key', null); create_distributed_table --------------------------------------------------------------------- diff --git a/src/test/regress/expected/multi_metadata_sync.out b/src/test/regress/expected/multi_metadata_sync.out index 168d28e8a..b74cdc179 100644 --- a/src/test/regress/expected/multi_metadata_sync.out +++ b/src/test/regress/expected/multi_metadata_sync.out @@ -118,7 +118,6 @@ SELECT create_distributed_table('mx_test_table', 'col_1'); (1 row) reset citus.shard_count; -reset citus.shard_replication_factor; -- Set the replication model of the test table to streaming replication so that it is -- considered as an MX table UPDATE pg_dist_partition SET repmodel='s' WHERE logicalrelid='mx_test_table'::regclass; @@ -131,6 +130,7 @@ SELECT create_distributed_table('single_shard_tbl', null); (1 row) INSERT INTO single_shard_tbl VALUES (1); +reset citus.shard_replication_factor; -- Show that the created MX table is and its sequences are included in the activate node snapshot SELECT unnest(activate_node_snapshot()) order by 1; unnest @@ -184,7 +184,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 2 UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 2 WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (2, 8, 1, 'integer'::regtype, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) - WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (3, 1, 2, 0, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) + WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (3, 1, 1, 0, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('database', ARRAY['regression']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('role', ARRAY['postgres']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('schema', ARRAY['public']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; diff --git a/src/test/regress/expected/multi_metadata_sync_0.out b/src/test/regress/expected/multi_metadata_sync_0.out index 87800915d..16c319d4e 100644 --- a/src/test/regress/expected/multi_metadata_sync_0.out +++ b/src/test/regress/expected/multi_metadata_sync_0.out @@ -118,7 +118,6 @@ SELECT create_distributed_table('mx_test_table', 'col_1'); (1 row) reset citus.shard_count; -reset citus.shard_replication_factor; -- Set the replication model of the test table to streaming replication so that it is -- considered as an MX table UPDATE pg_dist_partition SET repmodel='s' WHERE logicalrelid='mx_test_table'::regclass; @@ -131,6 +130,7 @@ SELECT create_distributed_table('single_shard_tbl', null); (1 row) INSERT INTO single_shard_tbl VALUES (1); +reset citus.shard_replication_factor; -- Show that the created MX table is and its sequences are included in the activate node snapshot SELECT unnest(activate_node_snapshot()) order by 1; unnest @@ -184,7 +184,7 @@ SELECT unnest(activate_node_snapshot()) order by 1; UPDATE pg_dist_node SET isactive = TRUE WHERE nodeid = 2 UPDATE pg_dist_node SET metadatasynced = TRUE WHERE nodeid = 2 WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (2, 8, 1, 'integer'::regtype, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) - WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (3, 1, 2, 0, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) + WITH colocation_group_data (colocationid, shardcount, replicationfactor, distributioncolumntype, distributioncolumncollationname, distributioncolumncollationschema) AS (VALUES (3, 1, 1, 0, NULL, NULL)) SELECT pg_catalog.citus_internal_add_colocation_metadata(colocationid, shardcount, replicationfactor, distributioncolumntype, coalesce(c.oid, 0)) FROM colocation_group_data d LEFT JOIN pg_collation c ON (d.distributioncolumncollationname = c.collname AND d.distributioncolumncollationschema::regnamespace = c.collnamespace) WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('database', ARRAY['regression']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('role', ARRAY['postgres']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; WITH distributed_object_data(typetext, objnames, objargs, distargumentindex, colocationid, force_delegation) AS (VALUES ('schema', ARRAY['public']::text[], ARRAY[]::text[], -1, 0, false)) SELECT citus_internal_add_object_metadata(typetext, objnames, objargs, distargumentindex::int, colocationid::int, force_delegation::bool) FROM distributed_object_data; diff --git a/src/test/regress/expected/query_single_shard_table.out b/src/test/regress/expected/query_single_shard_table.out index 68c178553..5716c570d 100644 --- a/src/test/regress/expected/query_single_shard_table.out +++ b/src/test/regress/expected/query_single_shard_table.out @@ -2,6 +2,7 @@ CREATE SCHEMA query_single_shard_table; SET search_path TO query_single_shard_table; SET citus.next_shard_id TO 1620000; SET citus.shard_count TO 32; +SET citus.shard_replication_factor TO 1; SET client_min_messages TO NOTICE; CREATE TABLE nullkey_c1_t1(a int, b int); CREATE TABLE nullkey_c1_t2(a int, b int); @@ -43,6 +44,7 @@ SELECT create_distributed_table('nullkey_c3_t1', null, colocate_with=>'none'); (1 row) INSERT INTO nullkey_c3_t1 SELECT i, i FROM generate_series(1, 8) i; +RESET citus.shard_replication_factor; CREATE TABLE reference_table(a int, b int); SELECT create_reference_table('reference_table'); create_reference_table @@ -81,6 +83,7 @@ INSERT INTO articles_hash VALUES ( 4, 4, 'altdorfer', 14551),( 5, 5, 'aruru', (28, 8, 'aerophyte', 5454),(29, 9, 'amateur', 9524), (42, 2, 'ausable', 15885),(43, 3, 'affixal', 12723), (49, 9, 'anyone', 2681),(50, 10, 'anjanette', 19519); +SET citus.shard_replication_factor TO 1; SELECT create_distributed_table('articles_hash', null, colocate_with=>'none'); NOTICE: Copying data from local table... NOTICE: copying the data has completed @@ -143,6 +146,7 @@ SELECT create_distributed_table('bigserial_test', null); (1 row) +RESET citus.shard_replication_factor; CREATE TABLE append_table (text_col text, a int); SELECT create_distributed_table('append_table', 'a', 'append'); create_distributed_table @@ -166,6 +170,7 @@ CALL public.create_range_partitioned_shards('range_table', '{"0","25"}','{"24"," INSERT INTO range_table VALUES (0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 50); \set users_table_data_file :abs_srcdir '/data/users_table.data' \set events_table_data_file :abs_srcdir '/data/events_table.data' +SET citus.shard_replication_factor TO 1; CREATE TABLE users_table (user_id int, time timestamp, value_1 int, value_2 int, value_3 float, value_4 bigint); SELECT create_distributed_table('users_table', null, colocate_with=>'none'); create_distributed_table diff --git a/src/test/regress/expected/schema_based_sharding.out b/src/test/regress/expected/schema_based_sharding.out index 6dba7227d..7af81966c 100644 --- a/src/test/regress/expected/schema_based_sharding.out +++ b/src/test/regress/expected/schema_based_sharding.out @@ -384,6 +384,12 @@ SELECT EXISTS( t (1 row) +-- errors out because shard replication factor > 1 +SET citus.shard_replication_factor TO 2; +CREATE TABLE tenant_4.tbl_3 AS SELECT 1 AS a, 'text' as b; +ERROR: could not create single shard table: citus.shard_replication_factor is greater than 1 +HINT: Consider setting citus.shard_replication_factor to 1 and try again +SET citus.shard_replication_factor TO 1; -- verify that we allow creating tenant tables by using CREATE TABLE AS / SELECT INTO commands CREATE TABLE tenant_4.tbl_3 AS SELECT 1 AS a, 'text' as b; NOTICE: Copying data from local table... @@ -1439,6 +1445,11 @@ SELECT pg_reload_conf(); \c - - - :master_port SET search_path TO regular_schema; CREATE TABLE type_sing(a INT); +-- errors out because shard_replication_factor = 2 +SELECT create_distributed_table('type_sing', NULL, colocate_with:='none'); +ERROR: could not create single shard table: citus.shard_replication_factor is greater than 1 +HINT: Consider setting citus.shard_replication_factor to 1 and try again +SET citus.shard_replication_factor TO 1; SELECT create_distributed_table('type_sing', NULL, colocate_with:='none'); create_distributed_table --------------------------------------------------------------------- diff --git a/src/test/regress/expected/single_shard_table_udfs.out b/src/test/regress/expected/single_shard_table_udfs.out index 5d3314070..3b73473b0 100644 --- a/src/test/regress/expected/single_shard_table_udfs.out +++ b/src/test/regress/expected/single_shard_table_udfs.out @@ -1066,6 +1066,7 @@ SELECT tablename, indexname FROM pg_indexes WHERE schemaname = 'null_dist_key_ud SET search_path TO null_dist_key_udfs; --test isolate_tenant_to_new_shard CREATE TABLE iso_tbl (a INT); +SET citus.shard_replication_factor TO 1; SELECT create_distributed_table('iso_tbl', NULL, colocate_with:='none'); create_distributed_table --------------------------------------------------------------------- diff --git a/src/test/regress/spec/isolation_citus_schema_distribute_undistribute.spec b/src/test/regress/spec/isolation_citus_schema_distribute_undistribute.spec index 02a4cc829..bb40918f7 100644 --- a/src/test/regress/spec/isolation_citus_schema_distribute_undistribute.spec +++ b/src/test/regress/spec/isolation_citus_schema_distribute_undistribute.spec @@ -1,7 +1,7 @@ setup { SELECT citus_set_coordinator_host('localhost', 57636); - + SET citus.shard_replication_factor TO 1; CREATE SCHEMA tenant1; CREATE TABLE tenant1.table1(id int PRIMARY KEY, name text, col bigint); INSERT INTO tenant1.table1 SELECT i, 'asd', i*1000 FROM generate_series(11, 20) i; @@ -27,6 +27,7 @@ session "s1" step "s1-begin" { BEGIN; + SET citus.shard_replication_factor TO 1; } step "s1-schema-distribute" @@ -63,6 +64,7 @@ step "s2-rename-schema" step "s2-add-table" { + SET citus.shard_replication_factor TO 1; CREATE TABLE tenant1.table4(id int PRIMARY KEY, name text, col bigint); } diff --git a/src/test/regress/spec/isolation_schema_based_sharding.spec b/src/test/regress/spec/isolation_schema_based_sharding.spec index 2f6180538..6f3fe9dc5 100644 --- a/src/test/regress/spec/isolation_schema_based_sharding.spec +++ b/src/test/regress/spec/isolation_schema_based_sharding.spec @@ -1,6 +1,7 @@ setup { SET citus.enable_schema_based_sharding TO ON; + SET citus.shard_replication_factor TO 1; CREATE SCHEMA tenant_1; CREATE SCHEMA tenant_2; CREATE SCHEMA tenant_3; @@ -16,7 +17,7 @@ teardown session "s1" -step "s1-begin" { BEGIN; } +step "s1-begin" { BEGIN; SET citus.shard_replication_factor TO 1;} step "s1-tenant-1-create-table-1" { CREATE TABLE tenant_1.tbl_1 (a int); } step "s1-tenant-4-create-table-1" { CREATE TABLE tenant_4.tbl_1 (a int); } step "s1-tenant-2-create-table-1" { CREATE TABLE tenant_2.tbl_1 (a int); } @@ -24,7 +25,7 @@ step "s1-commit" { COMMIT; } session "s2" -step "s2-begin" { BEGIN; } +step "s2-begin" { BEGIN; SET citus.shard_replication_factor TO 1;} step "s2-tenant-1-create-table-2" { CREATE TABLE tenant_1.tbl_2 (a int); } step "s2-tenant-4-create-table-2" { CREATE TABLE tenant_4.tbl_2 (a int); } step "s2-tenant-1-verify-colocation" { SELECT COUNT(DISTINCT(colocationid))=1 FROM pg_dist_partition WHERE logicalrelid::text LIKE 'tenant_1.%'; } diff --git a/src/test/regress/sql/citus_stat_tenants.sql b/src/test/regress/sql/citus_stat_tenants.sql index 2a6a20335..9160b3499 100644 --- a/src/test/regress/sql/citus_stat_tenants.sql +++ b/src/test/regress/sql/citus_stat_tenants.sql @@ -316,6 +316,7 @@ SELECT tenant_attribute, query_count_in_this_period FROM citus_stat_tenants ORDE -- single shard distributed table, which is not part of a tenant schema SELECT citus_stat_tenants_reset(); +SET citus.shard_replication_factor TO 1; CREATE TABLE dist_tbl_text_single_shard(a text, b int); select create_distributed_table('dist_tbl_text_single_shard', NULL); diff --git a/src/test/regress/sql/insert_select_single_shard_table.sql b/src/test/regress/sql/insert_select_single_shard_table.sql index 4d1e1a73c..fb080d206 100644 --- a/src/test/regress/sql/insert_select_single_shard_table.sql +++ b/src/test/regress/sql/insert_select_single_shard_table.sql @@ -3,6 +3,7 @@ SET search_path TO insert_select_single_shard_table; SET citus.next_shard_id TO 1820000; SET citus.shard_count TO 32; +SET citus.shard_replication_factor TO 1; SET client_min_messages TO NOTICE; diff --git a/src/test/regress/sql/multi_extension.sql b/src/test/regress/sql/multi_extension.sql index aeef30938..72d939867 100644 --- a/src/test/regress/sql/multi_extension.sql +++ b/src/test/regress/sql/multi_extension.sql @@ -595,6 +595,7 @@ SELECT * FROM multi_extension.print_extension_changes(); ALTER EXTENSION citus UPDATE TO '12.0-1'; CREATE TABLE null_shard_key (x int, y int); +SET citus.shard_replication_factor TO 1; SELECT create_distributed_table('null_shard_key', null); -- Show that we cannot downgrade to 11.3-1 becuase the cluster has a diff --git a/src/test/regress/sql/multi_metadata_sync.sql b/src/test/regress/sql/multi_metadata_sync.sql index c1a0a6a9b..0529e1e1d 100644 --- a/src/test/regress/sql/multi_metadata_sync.sql +++ b/src/test/regress/sql/multi_metadata_sync.sql @@ -56,7 +56,6 @@ set citus.shard_count to 8; set citus.shard_replication_factor to 1; SELECT create_distributed_table('mx_test_table', 'col_1'); reset citus.shard_count; -reset citus.shard_replication_factor; -- Set the replication model of the test table to streaming replication so that it is -- considered as an MX table @@ -67,6 +66,8 @@ CREATE TABLE single_shard_tbl(a int); SELECT create_distributed_table('single_shard_tbl', null); INSERT INTO single_shard_tbl VALUES (1); +reset citus.shard_replication_factor; + -- Show that the created MX table is and its sequences are included in the activate node snapshot SELECT unnest(activate_node_snapshot()) order by 1; diff --git a/src/test/regress/sql/query_single_shard_table.sql b/src/test/regress/sql/query_single_shard_table.sql index b6002f8b1..c77d5b1dd 100644 --- a/src/test/regress/sql/query_single_shard_table.sql +++ b/src/test/regress/sql/query_single_shard_table.sql @@ -3,6 +3,7 @@ SET search_path TO query_single_shard_table; SET citus.next_shard_id TO 1620000; SET citus.shard_count TO 32; +SET citus.shard_replication_factor TO 1; SET client_min_messages TO NOTICE; @@ -24,6 +25,8 @@ CREATE TABLE nullkey_c3_t1(a int, b int); SELECT create_distributed_table('nullkey_c3_t1', null, colocate_with=>'none'); INSERT INTO nullkey_c3_t1 SELECT i, i FROM generate_series(1, 8) i; +RESET citus.shard_replication_factor; + CREATE TABLE reference_table(a int, b int); SELECT create_reference_table('reference_table'); INSERT INTO reference_table SELECT i, i FROM generate_series(0, 5) i; @@ -53,6 +56,7 @@ INSERT INTO articles_hash VALUES ( 4, 4, 'altdorfer', 14551),( 5, 5, 'aruru', (42, 2, 'ausable', 15885),(43, 3, 'affixal', 12723), (49, 9, 'anyone', 2681),(50, 10, 'anjanette', 19519); +SET citus.shard_replication_factor TO 1; SELECT create_distributed_table('articles_hash', null, colocate_with=>'none'); CREATE TABLE raw_events_first (user_id int, time timestamp, value_1 int, value_2 int, value_3 float, value_4 bigint, UNIQUE(user_id, value_1)); @@ -80,6 +84,8 @@ SELECT create_reference_table('modify_fast_path_reference'); CREATE TABLE bigserial_test (x int, y int, z bigserial); SELECT create_distributed_table('bigserial_test', null); +RESET citus.shard_replication_factor; + CREATE TABLE append_table (text_col text, a int); SELECT create_distributed_table('append_table', 'a', 'append'); SELECT master_create_empty_shard('append_table') AS shardid1 \gset @@ -112,6 +118,7 @@ INSERT INTO range_table VALUES (0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), ( \set users_table_data_file :abs_srcdir '/data/users_table.data' \set events_table_data_file :abs_srcdir '/data/events_table.data' +SET citus.shard_replication_factor TO 1; CREATE TABLE users_table (user_id int, time timestamp, value_1 int, value_2 int, value_3 float, value_4 bigint); SELECT create_distributed_table('users_table', null, colocate_with=>'none'); \set client_side_copy_command '\\copy users_table FROM ' :'users_table_data_file' ' WITH CSV;' diff --git a/src/test/regress/sql/schema_based_sharding.sql b/src/test/regress/sql/schema_based_sharding.sql index 0417c0e69..e02c68dee 100644 --- a/src/test/regress/sql/schema_based_sharding.sql +++ b/src/test/regress/sql/schema_based_sharding.sql @@ -273,6 +273,10 @@ SELECT EXISTS( inhparent = 'tenant_4.parent_attach_test'::regclass ) AS is_partition; +-- errors out because shard replication factor > 1 +SET citus.shard_replication_factor TO 2; +CREATE TABLE tenant_4.tbl_3 AS SELECT 1 AS a, 'text' as b; +SET citus.shard_replication_factor TO 1; -- verify that we allow creating tenant tables by using CREATE TABLE AS / SELECT INTO commands CREATE TABLE tenant_4.tbl_3 AS SELECT 1 AS a, 'text' as b; CREATE TEMP TABLE IF NOT EXISTS tenant_4.tbl_4 AS SELECT 1 as a, 'text' as b; @@ -974,6 +978,11 @@ SELECT pg_reload_conf(); SET search_path TO regular_schema; CREATE TABLE type_sing(a INT); + +-- errors out because shard_replication_factor = 2 +SELECT create_distributed_table('type_sing', NULL, colocate_with:='none'); + +SET citus.shard_replication_factor TO 1; SELECT create_distributed_table('type_sing', NULL, colocate_with:='none'); SET citus.enable_schema_based_sharding TO ON; diff --git a/src/test/regress/sql/single_shard_table_udfs.sql b/src/test/regress/sql/single_shard_table_udfs.sql index 0e516e107..615e566db 100644 --- a/src/test/regress/sql/single_shard_table_udfs.sql +++ b/src/test/regress/sql/single_shard_table_udfs.sql @@ -508,6 +508,7 @@ SET search_path TO null_dist_key_udfs; --test isolate_tenant_to_new_shard CREATE TABLE iso_tbl (a INT); +SET citus.shard_replication_factor TO 1; SELECT create_distributed_table('iso_tbl', NULL, colocate_with:='none'); SELECT isolate_tenant_to_new_shard('iso_tbl', 5); From 5bf163a27d42b0813110fe05452903dfa6c3db43 Mon Sep 17 00:00:00 2001 From: Naisila Puka <37271756+naisila@users.noreply.github.com> Date: Thu, 15 Jun 2023 14:54:06 +0300 Subject: [PATCH 098/118] Remove PG13 from CI and Configure (#7002) DESCRIPTION: Drops PG13 Support This commit is the first phase of dropping PG13 support. It consists of the following: - Removes pg13 from CI tests Among other things, Citus upgrade tests should now use PG14. Earliest Citus version supporting PG14 is 10.2. We also pick 11.3 version for upgrade_pg_dist_cleanup tests. Therefore, we run the citus upgrade tests with versions 10.2 and 11.3. - Removes pg13 from configure script - Remove upgrade_columnar_metapage upgrade tests We populate first_row_number column of columnar.stripe table during citus 10.1-10.2 upgrade. Given that we start from citus 10.2.0, which is the oldest version supporting PG14, we don't have that upgrade path anymore. Hence we remove these tests. - Removes upgrade_pg_dist_object_test and upgrade_partition_constraints tests These upgrade tests require the citus old version to be less than 10.0. Given that we drop support for PG13, we run upgrade tests with PG14, which starts with 10.2. So we remove these upgrade tests. - Documents that upgrade_post_11 should upgrade from version less than 11 In this way we make sure we run citus_finalize_upgrade_to_citus11 script - Adds needed alternative output for upgrade_citus_finish_citus_upgrade Given that we use 11.3 as the citus old version as well, we add this alternative output because pg_catalog.citus_finish_citus_upgrade() makes sense if last_upgrade_major_version < 11. See below for reference: pg_catalog.citus_finish_citus_upgrade(): ... IF last_upgrade_major_version < 11 THEN PERFORM citus_finalize_upgrade_to_citus11(); performed_upgrade := true; END IF; IF NOT performed_upgrade THEN RAISE NOTICE 'already at the latest distributed schema version (%)', last_upgrade_version_string; RETURN; END IF; ... And that's it :) The second phase of dropping PG13 support will consist in removing all the PG13 specific compilation paths/tests in the Citus repo. Will be done soon. --- .circleci/config.yml | 145 ++---------------- configure | 2 +- configure.ac | 2 +- .../after_citus_upgrade_coord_schedule | 3 - .../before_citus_upgrade_coord_schedule | 3 - src/test/regress/bin/normalize.sed | 3 - src/test/regress/citus_tests/common.py | 1 - .../upgrade_citus_finish_citus_upgrade.out | 11 ++ .../upgrade_citus_finish_citus_upgrade_0.out | 41 +++++ .../upgrade_columnar_metapage_after.out | 127 --------------- .../upgrade_columnar_metapage_after_0.out | 13 -- .../upgrade_columnar_metapage_before.out | 31 ---- .../upgrade_columnar_metapage_before_0.out | 13 -- .../upgrade_partition_constraints_after.out | 33 ---- .../upgrade_partition_constraints_before.out | 44 ------ ...upgrade_partition_constraints_before_0.out | 13 -- .../upgrade_pg_dist_object_test_after.out | 90 ----------- .../upgrade_pg_dist_object_test_after_1.out | 51 ------ .../upgrade_pg_dist_object_test_before.out | 46 ------ .../upgrade_pg_dist_object_test_before_0.out | 13 -- .../expected/upgrade_post_11_after.out | 14 ++ ...fter_0.out => upgrade_post_11_after_0.out} | 10 +- .../expected/upgrade_post_11_before.out | 14 ++ ...ter_0.out => upgrade_post_11_before_0.out} | 10 +- .../mixed_after_citus_upgrade_schedule | 1 - .../mixed_before_citus_upgrade_schedule | 1 - .../upgrade_citus_finish_citus_upgrade.sql | 7 + .../sql/upgrade_columnar_metapage_after.sql | 78 ---------- .../sql/upgrade_columnar_metapage_before.sql | 26 ---- .../upgrade_partition_constraints_after.sql | 25 --- .../upgrade_partition_constraints_before.sql | 39 ----- .../sql/upgrade_pg_dist_object_test_after.sql | 28 ---- .../upgrade_pg_dist_object_test_before.sql | 35 ----- .../regress/sql/upgrade_post_11_after.sql | 10 ++ .../regress/sql/upgrade_post_11_before.sql | 9 ++ 35 files changed, 128 insertions(+), 864 deletions(-) create mode 100644 src/test/regress/expected/upgrade_citus_finish_citus_upgrade_0.out delete mode 100644 src/test/regress/expected/upgrade_columnar_metapage_after.out delete mode 100644 src/test/regress/expected/upgrade_columnar_metapage_after_0.out delete mode 100644 src/test/regress/expected/upgrade_columnar_metapage_before.out delete mode 100644 src/test/regress/expected/upgrade_columnar_metapage_before_0.out delete mode 100644 src/test/regress/expected/upgrade_partition_constraints_after.out delete mode 100644 src/test/regress/expected/upgrade_partition_constraints_before.out delete mode 100644 src/test/regress/expected/upgrade_partition_constraints_before_0.out delete mode 100644 src/test/regress/expected/upgrade_pg_dist_object_test_after.out delete mode 100644 src/test/regress/expected/upgrade_pg_dist_object_test_after_1.out delete mode 100644 src/test/regress/expected/upgrade_pg_dist_object_test_before.out delete mode 100644 src/test/regress/expected/upgrade_pg_dist_object_test_before_0.out rename src/test/regress/expected/{upgrade_partition_constraints_after_0.out => upgrade_post_11_after_0.out} (52%) rename src/test/regress/expected/{upgrade_pg_dist_object_test_after_0.out => upgrade_post_11_before_0.out} (52%) delete mode 100644 src/test/regress/sql/upgrade_columnar_metapage_after.sql delete mode 100644 src/test/regress/sql/upgrade_columnar_metapage_before.sql delete mode 100644 src/test/regress/sql/upgrade_partition_constraints_after.sql delete mode 100644 src/test/regress/sql/upgrade_partition_constraints_before.sql delete mode 100644 src/test/regress/sql/upgrade_pg_dist_object_test_after.sql delete mode 100644 src/test/regress/sql/upgrade_pg_dist_object_test_before.sql diff --git a/.circleci/config.yml b/.circleci/config.yml index d99398790..3b2b62cac 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,10 +6,7 @@ orbs: parameters: image_suffix: type: string - default: '-vf5dc39a' - pg13_version: - type: string - default: '13.11' + default: '-vbab548a' pg14_version: type: string default: '14.8' @@ -18,7 +15,7 @@ parameters: default: '15.3' upgrade_pg_versions: type: string - default: '13.11-14.8-15.3' + default: '14.8-15.3' style_checker_tools_version: type: string default: '0.8.18' @@ -565,7 +562,7 @@ jobs: check-merge-to-enterprise: docker: - - image: citus/extbuilder:<< pipeline.parameters.pg13_version >> + - image: citus/extbuilder:<< pipeline.parameters.pg14_version >> working_directory: /home/circleci/project steps: - checkout @@ -717,10 +714,6 @@ workflows: when: not: << pipeline.parameters.flaky_test >> jobs: - - build: - name: build-13 - pg_major: 13 - image_tag: '<< pipeline.parameters.pg13_version >>' - build: name: build-14 pg_major: 14 @@ -733,79 +726,6 @@ workflows: - check-style - check-sql-snapshots - - test-citus: &test-citus-13 - name: 'test-13_check-multi' - make: check-multi - pg_major: 13 - image_tag: '<< pipeline.parameters.pg13_version >>' - requires: [build-13] - - test-citus: - <<: *test-citus-13 - name: 'test-13_check-multi-1' - make: check-multi-1 - - test-citus: - <<: *test-citus-13 - name: 'test-13_check-mx' - make: check-multi-mx - - test-citus: - <<: *test-citus-13 - name: 'test-13_check-vanilla' - make: check-vanilla - - test-citus: - <<: *test-citus-13 - name: 'test-13_check-isolation' - make: check-isolation - - test-citus: - <<: *test-citus-13 - name: 'test-13_check-operations' - make: check-operations - - test-citus: - <<: *test-citus-13 - name: 'test-13_check-follower-cluster' - make: check-follower-cluster - - test-citus: - <<: *test-citus-13 - name: 'test-13_check-columnar' - make: check-columnar - - test-citus: - <<: *test-citus-13 - name: 'test-13_check-columnar-isolation' - make: check-columnar-isolation - - test-citus: - <<: *test-citus-13 - name: 'test-13_check-failure' - image: citus/failtester - make: check-failure - - test-citus: - <<: *test-citus-13 - name: 'test-13_check-enterprise' - make: check-enterprise - - test-citus: - <<: *test-citus-13 - name: 'test-13_check-enterprise-isolation' - make: check-enterprise-isolation - - test-citus: - <<: *test-citus-13 - name: 'test-13_check-enterprise-isolation-logicalrep-1' - make: check-enterprise-isolation-logicalrep-1 - - test-citus: - <<: *test-citus-13 - name: 'test-13_check-enterprise-isolation-logicalrep-2' - make: check-enterprise-isolation-logicalrep-2 - - test-citus: - <<: *test-citus-13 - name: 'test-13_check-enterprise-isolation-logicalrep-3' - make: check-enterprise-isolation-logicalrep-3 - - test-citus: - <<: *test-citus-13 - name: 'test-13_check-enterprise-failure' - image: citus/failtester - make: check-enterprise-failure - - test-citus: - <<: *test-citus-13 - name: 'test-13_check-split' - make: check-split - - test-citus: &test-citus-14 name: 'test-14_check-split' make: check-split @@ -952,12 +872,6 @@ workflows: image: citus/failtester make: check-failure - - test-pytest: - name: 'test-13_pytest' - pg_major: 13 - image_tag: '<< pipeline.parameters.pg13_version >>' - requires: [build-13] - - test-pytest: name: 'test-14_pytest' pg_major: 14 @@ -977,12 +891,6 @@ workflows: image_tag: '<< pipeline.parameters.pg15_version >>' requires: [build-15] - - test-arbitrary-configs: - name: 'test-13_check-arbitrary-configs' - pg_major: 13 - image_tag: '<< pipeline.parameters.pg13_version >>' - requires: [build-13] - - test-arbitrary-configs: name: 'test-14_check-arbitrary-configs' pg_major: 14 @@ -995,12 +903,6 @@ workflows: image_tag: '<< pipeline.parameters.pg15_version >>' requires: [build-15] - - test-query-generator: - name: 'test-13_check-query-generator' - pg_major: 13 - image_tag: '<< pipeline.parameters.pg13_version >>' - requires: [build-13] - - test-query-generator: name: 'test-14_check-query-generator' pg_major: 14 @@ -1013,13 +915,6 @@ workflows: image_tag: '<< pipeline.parameters.pg15_version >>' requires: [build-15] - - test-pg-upgrade: - name: 'test-13-14_check-pg-upgrade' - old_pg_major: 13 - new_pg_major: 14 - image_tag: '<< pipeline.parameters.upgrade_pg_versions >>' - requires: [build-13, build-14] - - test-pg-upgrade: name: 'test-14-15_check-pg-upgrade' old_pg_major: 14 @@ -1028,32 +923,13 @@ workflows: requires: [build-14, build-15] - test-citus-upgrade: - name: test-13_check-citus-upgrade - pg_major: 13 - image_tag: '<< pipeline.parameters.pg13_version >>' - requires: [build-13] + name: test-14_check-citus-upgrade + pg_major: 14 + image_tag: '<< pipeline.parameters.pg14_version >>' + requires: [build-14] - upload-coverage: requires: - - test-13_check-multi - - test-13_check-multi-1 - - test-13_check-mx - - test-13_check-vanilla - - test-13_check-isolation - - test-13_check-operations - - test-13_check-follower-cluster - - test-13_check-columnar - - test-13_check-columnar-isolation - - test-13_check-failure - - test-13_check-enterprise - - test-13_check-enterprise-isolation - - test-13_check-enterprise-isolation-logicalrep-1 - - test-13_check-enterprise-isolation-logicalrep-2 - - test-13_check-enterprise-isolation-logicalrep-3 - - test-13_check-enterprise-failure - - test-13_check-split - - test-13_check-arbitrary-configs - - test-13_check-query-generator - test-14_check-multi - test-14_check-multi-1 - test-14_check-mx @@ -1092,18 +968,17 @@ workflows: - test-15_check-split - test-15_check-arbitrary-configs - test-15_check-query-generator - - test-13-14_check-pg-upgrade - test-14-15_check-pg-upgrade - - test-13_check-citus-upgrade + - test-14_check-citus-upgrade - ch_benchmark: - requires: [build-13] + requires: [build-14] filters: branches: only: - /ch_benchmark\/.*/ # match with ch_benchmark/ prefix - tpcc_benchmark: - requires: [build-13] + requires: [build-14] filters: branches: only: diff --git a/configure b/configure index 73a841bee..9150c1ab2 100755 --- a/configure +++ b/configure @@ -2588,7 +2588,7 @@ fi if test "$with_pg_version_check" = no; then { $as_echo "$as_me:${as_lineno-$LINENO}: building against PostgreSQL $version_num (skipped compatibility check)" >&5 $as_echo "$as_me: building against PostgreSQL $version_num (skipped compatibility check)" >&6;} -elif test "$version_num" != '13' -a "$version_num" != '14' -a "$version_num" != '15'; then +elif test "$version_num" != '14' -a "$version_num" != '15'; then as_fn_error $? "Citus is not compatible with the detected PostgreSQL version ${version_num}." "$LINENO" 5 else { $as_echo "$as_me:${as_lineno-$LINENO}: building against PostgreSQL $version_num" >&5 diff --git a/configure.ac b/configure.ac index f44e9f177..078e14c42 100644 --- a/configure.ac +++ b/configure.ac @@ -80,7 +80,7 @@ AC_SUBST(with_pg_version_check) if test "$with_pg_version_check" = no; then AC_MSG_NOTICE([building against PostgreSQL $version_num (skipped compatibility check)]) -elif test "$version_num" != '13' -a "$version_num" != '14' -a "$version_num" != '15'; then +elif test "$version_num" != '14' -a "$version_num" != '15'; then AC_MSG_ERROR([Citus is not compatible with the detected PostgreSQL version ${version_num}.]) else AC_MSG_NOTICE([building against PostgreSQL $version_num]) diff --git a/src/test/regress/after_citus_upgrade_coord_schedule b/src/test/regress/after_citus_upgrade_coord_schedule index 6a2a5255a..f4f6bb29f 100644 --- a/src/test/regress/after_citus_upgrade_coord_schedule +++ b/src/test/regress/after_citus_upgrade_coord_schedule @@ -3,7 +3,4 @@ test: upgrade_citus_finish_citus_upgrade test: upgrade_pg_dist_cleanup_after test: upgrade_basic_after -test: upgrade_partition_constraints_after -test: upgrade_pg_dist_object_test_after -test: upgrade_columnar_metapage_after test: upgrade_post_11_after diff --git a/src/test/regress/before_citus_upgrade_coord_schedule b/src/test/regress/before_citus_upgrade_coord_schedule index 0e0eaa091..1195058d6 100644 --- a/src/test/regress/before_citus_upgrade_coord_schedule +++ b/src/test/regress/before_citus_upgrade_coord_schedule @@ -1,8 +1,5 @@ # this schedule is to be run on only coordinators test: upgrade_basic_before -test: upgrade_partition_constraints_before -test: upgrade_pg_dist_object_test_before -test: upgrade_columnar_metapage_before test: upgrade_pg_dist_cleanup_before test: upgrade_post_11_before diff --git a/src/test/regress/bin/normalize.sed b/src/test/regress/bin/normalize.sed index 16ad804cb..5b958b636 100644 --- a/src/test/regress/bin/normalize.sed +++ b/src/test/regress/bin/normalize.sed @@ -211,9 +211,6 @@ s/ERROR: cannot append to shardId [0-9]+/ERROR: cannot append to shardId xxxxx /local tables that are added to metadata automatically by citus, but not chained with reference tables via foreign keys might be automatically converted back to postgres tables$/d /Executing citus_add_local_table_to_metadata(.*) prevents this for the given relation, and all of the connected relations$/d -# normalize partitioned table shard constraint name errors for upgrade_partition_constraints_(before|after) -s/^(ERROR: child table is missing constraint "\w+)_([0-9])+"/\1_xxxxxx"/g - # normalize for distributed deadlock delay in isolation_metadata_sync_deadlock # isolation tester first detects a lock, but then deadlock detector cancels the # session. Sometimes happens that deadlock detector cancels the session before diff --git a/src/test/regress/citus_tests/common.py b/src/test/regress/citus_tests/common.py index 9cf18cc89..9ff30ebf9 100644 --- a/src/test/regress/citus_tests/common.py +++ b/src/test/regress/citus_tests/common.py @@ -90,7 +90,6 @@ def get_pg_major_version(): PG_MAJOR_VERSION = get_pg_major_version() OLDEST_SUPPORTED_CITUS_VERSION_MATRIX = { - 13: "9.5.0", 14: "10.2.0", 15: "11.1.5", } diff --git a/src/test/regress/expected/upgrade_citus_finish_citus_upgrade.out b/src/test/regress/expected/upgrade_citus_finish_citus_upgrade.out index bb80d9103..e3ca353c2 100644 --- a/src/test/regress/expected/upgrade_citus_finish_citus_upgrade.out +++ b/src/test/regress/expected/upgrade_citus_finish_citus_upgrade.out @@ -1,4 +1,15 @@ -- Citus upgrades are finished by calling a procedure +-- Note that pg_catalog.citus_finish_citus_upgrade() behaves differently +-- when last upgrade citus version is less than 11 +-- so we have two alternative outputs for this test +\set upgrade_test_old_citus_version `echo "$CITUS_OLD_VERSION"` +SELECT substring(:'upgrade_test_old_citus_version', 'v(\d+)\.\d+\.\d+')::int < 11 +AS upgrade_test_old_citus_version_lt_11_0; + upgrade_test_old_citus_version_lt_11_0 +--------------------------------------------------------------------- + t +(1 row) + -- this is a transactional procedure, so rollback should be fine BEGIN; CALL citus_finish_citus_upgrade(); diff --git a/src/test/regress/expected/upgrade_citus_finish_citus_upgrade_0.out b/src/test/regress/expected/upgrade_citus_finish_citus_upgrade_0.out new file mode 100644 index 000000000..3c8a9a25c --- /dev/null +++ b/src/test/regress/expected/upgrade_citus_finish_citus_upgrade_0.out @@ -0,0 +1,41 @@ +-- Citus upgrades are finished by calling a procedure +-- Note that pg_catalog.citus_finish_citus_upgrade() behaves differently +-- when last upgrade citus version is less than 11 +-- so we have two alternative outputs for this test +\set upgrade_test_old_citus_version `echo "$CITUS_OLD_VERSION"` +SELECT substring(:'upgrade_test_old_citus_version', 'v(\d+)\.\d+\.\d+')::int < 11 +AS upgrade_test_old_citus_version_lt_11_0; + upgrade_test_old_citus_version_lt_11_0 +--------------------------------------------------------------------- + f +(1 row) + +-- this is a transactional procedure, so rollback should be fine +BEGIN; + CALL citus_finish_citus_upgrade(); +NOTICE: already at the latest distributed schema version (11.3-1) +ROLLBACK; +-- do the actual job +CALL citus_finish_citus_upgrade(); +NOTICE: already at the latest distributed schema version (11.3-1) +-- show that the upgrade is successfull +SELECT metadata->>'last_upgrade_version' = extversion +FROM pg_dist_node_metadata, pg_extension WHERE extname = 'citus'; + ?column? +--------------------------------------------------------------------- + f +(1 row) + +-- idempotent, should be called multiple times +-- still, do not NOTICE the version as it changes per release +SET client_min_messages TO WARNING; +CALL citus_finish_citus_upgrade(); +-- we should be able to sync metadata in nontransactional way as well +SET citus.metadata_sync_mode TO 'nontransactional'; +SELECT start_metadata_sync_to_all_nodes(); + start_metadata_sync_to_all_nodes +--------------------------------------------------------------------- + t +(1 row) + +RESET citus.metadata_sync_mode; diff --git a/src/test/regress/expected/upgrade_columnar_metapage_after.out b/src/test/regress/expected/upgrade_columnar_metapage_after.out deleted file mode 100644 index 87e2424cb..000000000 --- a/src/test/regress/expected/upgrade_columnar_metapage_after.out +++ /dev/null @@ -1,127 +0,0 @@ -\set upgrade_test_old_citus_version `echo "$CITUS_OLD_VERSION"` -SELECT substring(:'upgrade_test_old_citus_version', 'v(\d+)\.\d+\.\d+')::int >= 10 AND - substring(:'upgrade_test_old_citus_version', 'v\d+\.(\d+)\.\d+')::int >= 0 -AS upgrade_test_old_citus_version_ge_10_0; - upgrade_test_old_citus_version_ge_10_0 ---------------------------------------------------------------------- - t -(1 row) - -\gset -\if :upgrade_test_old_citus_version_ge_10_0 -\else -\q -\endif --- it's not the best practice to define this here, but we don't want to include --- columnar_test_helpers in upgrade test schedule -CREATE OR REPLACE FUNCTION columnar_storage_info( - rel regclass, - version_major OUT int4, - version_minor OUT int4, - storage_id OUT int8, - reserved_stripe_id OUT int8, - reserved_row_number OUT int8, - reserved_offset OUT int8) -STRICT -LANGUAGE c AS 'citus', 'columnar_storage_info'; -CREATE VIEW columnar_table_stripe_info AS -SELECT columnar_table_storageids.relname relname, - columnar.stripe.stripe_num stripe_num, - columnar.stripe.row_count row_count, - columnar.stripe.first_row_number first_row_number -FROM columnar.stripe, -( - SELECT c.oid relid, c.relname relname, (columnar_storage_info(c.oid)).storage_id relstorageid - FROM pg_class c, pg_am a - WHERE c.relam = a.oid AND amname = 'columnar' -) columnar_table_storageids -WHERE relstorageid = columnar.stripe.storage_id; -SET search_path TO upgrade_columnar_metapage, public; --- show that first_row_number values are equal to MAX(row_count) * stripe_num + COLUMNAR_FIRST_ROW_NUMBER -SELECT * FROM columnar_table_stripe_info ORDER BY relname, stripe_num; - relname | stripe_num | row_count | first_row_number ---------------------------------------------------------------------- - columnar_table_1 | 1 | 150000 | 1 - columnar_table_1 | 2 | 10000 | 150001 - columnar_table_2 | 1 | 1000 | 1 - columnar_table_2 | 2 | 901 | 150001 - columnar_table_3 | 1 | 2 | 1 -(5 rows) - --- should work since we upgrade metapages when upgrading schema version -INSERT INTO columnar_table_1 VALUES (3); --- state of stripe metadata for columnar_table_1 after post-upgrade insert -SELECT * FROM columnar_table_stripe_info WHERE relname = 'columnar_table_1' ORDER BY stripe_num; - relname | stripe_num | row_count | first_row_number ---------------------------------------------------------------------- - columnar_table_1 | 1 | 150000 | 1 - columnar_table_1 | 2 | 10000 | 150001 - columnar_table_1 | 3 | 1 | 160001 -(3 rows) - --- show that all columnar relation's metapage's are upgraded to "2.0" -SELECT count(*)=0 -FROM (SELECT (columnar_storage_info(c.oid)).* t - FROM pg_class c, pg_am a - WHERE c.relam = a.oid AND amname = 'columnar') t -WHERE t.version_major != 2 and t.version_minor != 0; - ?column? ---------------------------------------------------------------------- - t -(1 row) - --- print metapage for two of the tables -SELECT version_major, version_minor, reserved_stripe_id, reserved_row_number - FROM columnar_storage_info('columnar_table_1'); - version_major | version_minor | reserved_stripe_id | reserved_row_number ---------------------------------------------------------------------- - 2 | 0 | 4 | 310001 -(1 row) - -SELECT version_major, version_minor, reserved_stripe_id, reserved_row_number - FROM columnar_storage_info('columnar_table_2'); - version_major | version_minor | reserved_stripe_id | reserved_row_number ---------------------------------------------------------------------- - 2 | 0 | 3 | 150902 -(1 row) - --- show that no_data_columnar_table also has metapage after upgrade -SELECT version_major, version_minor, reserved_stripe_id, reserved_row_number - FROM columnar_storage_info('no_data_columnar_table'); - version_major | version_minor | reserved_stripe_id | reserved_row_number ---------------------------------------------------------------------- - 2 | 0 | 1 | 1 -(1 row) - --- table is already upgraded, make sure that upgrade_columnar_metapage is no-op -SELECT columnar_internal.upgrade_columnar_storage(c.oid) -FROM pg_class c, pg_am a -WHERE c.relam = a.oid AND amname = 'columnar' and relname = 'columnar_table_2'; - upgrade_columnar_storage ---------------------------------------------------------------------- - -(1 row) - -SELECT version_major, version_minor, reserved_stripe_id, reserved_row_number - FROM columnar_storage_info('columnar_table_2'); - version_major | version_minor | reserved_stripe_id | reserved_row_number ---------------------------------------------------------------------- - 2 | 0 | 3 | 150902 -(1 row) - -VACUUM FULL columnar_table_2; --- print metapage and stripe metadata after post-upgrade vacuum full -SELECT version_major, version_minor, reserved_stripe_id, reserved_row_number - FROM columnar_storage_info('columnar_table_2'); - version_major | version_minor | reserved_stripe_id | reserved_row_number ---------------------------------------------------------------------- - 2 | 0 | 3 | 2001 -(1 row) - -SELECT * FROM columnar_table_stripe_info WHERE relname = 'columnar_table_2' ORDER BY stripe_num; - relname | stripe_num | row_count | first_row_number ---------------------------------------------------------------------- - columnar_table_2 | 1 | 1000 | 1 - columnar_table_2 | 2 | 901 | 1001 -(2 rows) - diff --git a/src/test/regress/expected/upgrade_columnar_metapage_after_0.out b/src/test/regress/expected/upgrade_columnar_metapage_after_0.out deleted file mode 100644 index e515fe47d..000000000 --- a/src/test/regress/expected/upgrade_columnar_metapage_after_0.out +++ /dev/null @@ -1,13 +0,0 @@ -\set upgrade_test_old_citus_version `echo "$CITUS_OLD_VERSION"` -SELECT substring(:'upgrade_test_old_citus_version', 'v(\d+)\.\d+\.\d+')::int >= 10 AND - substring(:'upgrade_test_old_citus_version', 'v\d+\.(\d+)\.\d+')::int >= 0 -AS upgrade_test_old_citus_version_ge_10_0; - upgrade_test_old_citus_version_ge_10_0 ---------------------------------------------------------------------- - f -(1 row) - -\gset -\if :upgrade_test_old_citus_version_ge_10_0 -\else -\q diff --git a/src/test/regress/expected/upgrade_columnar_metapage_before.out b/src/test/regress/expected/upgrade_columnar_metapage_before.out deleted file mode 100644 index 936ad7f10..000000000 --- a/src/test/regress/expected/upgrade_columnar_metapage_before.out +++ /dev/null @@ -1,31 +0,0 @@ -\set upgrade_test_old_citus_version `echo "$CITUS_OLD_VERSION"` -SELECT substring(:'upgrade_test_old_citus_version', 'v(\d+)\.\d+\.\d+')::int >= 10 AND - substring(:'upgrade_test_old_citus_version', 'v\d+\.(\d+)\.\d+')::int >= 0 -AS upgrade_test_old_citus_version_ge_10_0; - upgrade_test_old_citus_version_ge_10_0 ---------------------------------------------------------------------- - t -(1 row) - -\gset -\if :upgrade_test_old_citus_version_ge_10_0 -\else -\q -\endif -CREATE SCHEMA upgrade_columnar_metapage; -SET search_path TO upgrade_columnar_metapage, public; -CREATE TABLE columnar_table_1(a INT, b INT) USING columnar; -INSERT INTO columnar_table_1 SELECT i FROM generate_series(160001, 320000) i; -CREATE TABLE columnar_table_2(b INT) USING columnar; -SELECT alter_columnar_table_set('columnar_table_2', - chunk_group_row_limit => 1000, - stripe_row_limit => 1000); - alter_columnar_table_set ---------------------------------------------------------------------- - -(1 row) - -INSERT INTO columnar_table_2 SELECT i FROM generate_series(1600, 3500) i; -CREATE TABLE columnar_table_3(b INT) USING columnar; -INSERT INTO columnar_table_3 VALUES (1), (2); -CREATE TABLE no_data_columnar_table(a INT, b INT, c TEXT) USING columnar; diff --git a/src/test/regress/expected/upgrade_columnar_metapage_before_0.out b/src/test/regress/expected/upgrade_columnar_metapage_before_0.out deleted file mode 100644 index e515fe47d..000000000 --- a/src/test/regress/expected/upgrade_columnar_metapage_before_0.out +++ /dev/null @@ -1,13 +0,0 @@ -\set upgrade_test_old_citus_version `echo "$CITUS_OLD_VERSION"` -SELECT substring(:'upgrade_test_old_citus_version', 'v(\d+)\.\d+\.\d+')::int >= 10 AND - substring(:'upgrade_test_old_citus_version', 'v\d+\.(\d+)\.\d+')::int >= 0 -AS upgrade_test_old_citus_version_ge_10_0; - upgrade_test_old_citus_version_ge_10_0 ---------------------------------------------------------------------- - f -(1 row) - -\gset -\if :upgrade_test_old_citus_version_ge_10_0 -\else -\q diff --git a/src/test/regress/expected/upgrade_partition_constraints_after.out b/src/test/regress/expected/upgrade_partition_constraints_after.out deleted file mode 100644 index b8980fabc..000000000 --- a/src/test/regress/expected/upgrade_partition_constraints_after.out +++ /dev/null @@ -1,33 +0,0 @@ --- run this test only when old citus version is earlier than 10.0 -\set upgrade_test_old_citus_version `echo "$CITUS_OLD_VERSION"` -SELECT substring(:'upgrade_test_old_citus_version', 'v(\d+)\.\d+\.\d+')::int < 10 -AS upgrade_test_old_citus_version_lt_10_0; - upgrade_test_old_citus_version_lt_10_0 ---------------------------------------------------------------------- - t -(1 row) - -\gset -\if :upgrade_test_old_citus_version_lt_10_0 -\else -\q -\endif --- test cases for #3970 -SET search_path = test_3970; ---5. add a partition --- This command will fail as the child table has a wrong constraint name -CREATE TABLE part_table_p202009 PARTITION OF part_table FOR VALUES FROM ('2020-09-01 00:00:00') TO ('2020-10-01 00:00:00'); -ERROR: child table is missing constraint "ck_01234567890123456789012345678901234567890123_8478db72_xxxxxx" -CONTEXT: while executing command on localhost:xxxxx --- fix constraint names on partitioned table shards -SELECT fix_pre_citus10_partitioned_table_constraint_names('part_table'::regclass); - fix_pre_citus10_partitioned_table_constraint_names ---------------------------------------------------------------------- - -(1 row) - ---5. add a partition -CREATE TABLE part_table_p202009 PARTITION OF part_table FOR VALUES FROM ('2020-09-01 00:00:00') TO ('2020-10-01 00:00:00'); -RESET search_path; -DROP SCHEMA test_3970 CASCADE; -NOTICE: drop cascades to table test_3970.part_table diff --git a/src/test/regress/expected/upgrade_partition_constraints_before.out b/src/test/regress/expected/upgrade_partition_constraints_before.out deleted file mode 100644 index 7e589005a..000000000 --- a/src/test/regress/expected/upgrade_partition_constraints_before.out +++ /dev/null @@ -1,44 +0,0 @@ --- run this test only when old citus version is earlier than 10.0 -\set upgrade_test_old_citus_version `echo "$CITUS_OLD_VERSION"` -SELECT substring(:'upgrade_test_old_citus_version', 'v(\d+)\.\d+\.\d+')::int < 10 -AS upgrade_test_old_citus_version_lt_10_0; - upgrade_test_old_citus_version_lt_10_0 ---------------------------------------------------------------------- - t -(1 row) - -\gset -\if :upgrade_test_old_citus_version_lt_10_0 -\else -\q -\endif --- test cases for #3970 -CREATE SCHEMA test_3970; -SET search_path = test_3970; ---1. create a partitioned table -CREATE TABLE part_table ( - work_ymdt timestamp without time zone NOT NULL, - seq bigint NOT NULL, - my_seq bigint NOT NULL, - work_memo character varying(150), - CONSTRAINT work_memo_check CHECK ((octet_length((work_memo)::text) <= 150)) -) -PARTITION BY RANGE (work_ymdt); ---2. perform create_distributed_table -SELECT create_distributed_table('part_table', 'seq'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - ---3. add a partition -CREATE TABLE part_table_p202008 PARTITION OF part_table FOR VALUES FROM ('2020-08-01 00:00:00') TO ('2020-09-01 00:00:00'); ---4. add a check constraint -ALTER TABLE part_table ADD CONSTRAINT my_seq CHECK (my_seq > 0); ---5. add a partition --- This command will fail as the child table has a wrong constraint name -CREATE TABLE part_table_p202009 PARTITION OF part_table FOR VALUES FROM ('2020-09-01 00:00:00') TO ('2020-10-01 00:00:00'); -ERROR: child table is missing constraint "my_seq_xxxxxx" -CONTEXT: while executing command on localhost:xxxxx --- Add another constraint with a long name that will get truncated with a hash -ALTER TABLE part_table ADD CONSTRAINT ck_012345678901234567890123456789012345678901234567890123456789 CHECK (my_seq > 0); diff --git a/src/test/regress/expected/upgrade_partition_constraints_before_0.out b/src/test/regress/expected/upgrade_partition_constraints_before_0.out deleted file mode 100644 index 1654fa35c..000000000 --- a/src/test/regress/expected/upgrade_partition_constraints_before_0.out +++ /dev/null @@ -1,13 +0,0 @@ --- run this test only when old citus version is earlier than 10.0 -\set upgrade_test_old_citus_version `echo "$CITUS_OLD_VERSION"` -SELECT substring(:'upgrade_test_old_citus_version', 'v(\d+)\.\d+\.\d+')::int < 10 -AS upgrade_test_old_citus_version_lt_10_0; - upgrade_test_old_citus_version_lt_10_0 ---------------------------------------------------------------------- - f -(1 row) - -\gset -\if :upgrade_test_old_citus_version_lt_10_0 -\else -\q diff --git a/src/test/regress/expected/upgrade_pg_dist_object_test_after.out b/src/test/regress/expected/upgrade_pg_dist_object_test_after.out deleted file mode 100644 index 2490664fc..000000000 --- a/src/test/regress/expected/upgrade_pg_dist_object_test_after.out +++ /dev/null @@ -1,90 +0,0 @@ --- run this test only when old citus version is earlier than 10.0 -\set upgrade_test_old_citus_version `echo "$CITUS_OLD_VERSION"` -SELECT substring(:'upgrade_test_old_citus_version', 'v(\d+)\.\d+\.\d+')::int < 10 -AS upgrade_test_old_citus_version_lt_10_0; - upgrade_test_old_citus_version_lt_10_0 ---------------------------------------------------------------------- - t -(1 row) - -\gset -\if :upgrade_test_old_citus_version_lt_10_0 -\else -\q -\endif --- drop objects from previous test (uprade_basic_after.sql) for a clean test --- drop upgrade_basic schema and switch back to public schema -SET search_path to public; -DROP SCHEMA upgrade_basic CASCADE; -NOTICE: drop cascades to 7 other objects -DETAIL: drop cascades to table upgrade_basic.t -drop cascades to table upgrade_basic.tp -drop cascades to table upgrade_basic.t_ab -drop cascades to table upgrade_basic.t2 -drop cascades to table upgrade_basic.r -drop cascades to table upgrade_basic.tr -drop cascades to table upgrade_basic.t_range --- as we updated citus to available version, --- "isn" extension --- "new_schema" schema --- "public" schema --- "fooschema" schema --- "footype" type (under schema 'fooschema') - -- will now be marked as distributed - -- but, - -- "seg" extension - -- will not be marked as distributed --- see underlying objects -SELECT i.* FROM pg_catalog.pg_dist_object, pg_identify_object_as_address(classid, objid, objsubid) i ORDER BY 1, 2, 3; - type | object_names | object_args ---------------------------------------------------------------------- - collation | {post_11_upgrade,german_phonebook_unpropagated} | {} - database | {postgres} | {} - extension | {plpgsql} | {} - function | {post_11_upgrade,func_in_transaction_def} | {} - role | {postgres} | {} - schema | {fooschema} | {} - schema | {new_schema} | {} - schema | {post_11_upgrade} | {} - schema | {public} | {} - sequence | {post_11_upgrade,SC1} | {} - sequence | {post_11_upgrade,seq_bigint} | {} - sequence | {post_11_upgrade,unrelated_sequence} | {} - table | {fooschema,footable} | {} - table | {new_schema,another_dist_table} | {} - table | {post_11_upgrade,colocated_dist_table} | {} - table | {post_11_upgrade,colocated_partitioned_table} | {} - table | {post_11_upgrade,colocated_partitioned_table_2020_01_01} | {} - table | {post_11_upgrade,dist} | {} - table | {post_11_upgrade,employees} | {} - table | {post_11_upgrade,index_backed_rep_identity} | {} - table | {post_11_upgrade,part_table} | {} - table | {post_11_upgrade,part_table_p202008} | {} - table | {post_11_upgrade,part_table_p202009} | {} - table | {post_11_upgrade,reference_table} | {} - table | {post_11_upgrade,sensors} | {} - table | {post_11_upgrade,sensors_2020_01_01} | {} - table | {post_11_upgrade,sensors_news} | {} - table | {post_11_upgrade,sensors_old} | {} - table | {post_11_upgrade,sensors_parser} | {} - table | {post_11_upgrade,sensors_parser_a_partition} | {} - table | {post_11_upgrade,test} | {} - table | {post_11_upgrade,test_propagate_collate} | {} - table | {public,dist_table} | {} - text search configuration | {post_11_upgrade,partial_index_test_config} | {} - type | {fooschema.footype} | {} - type | {post_11_upgrade.my_type} | {} - type | {post_11_upgrade.my_type_for_view} | {} - view | {post_11_upgrade,depends_on_citus} | {} - view | {post_11_upgrade,depends_on_nothing_1} | {} - view | {post_11_upgrade,depends_on_nothing_2} | {} - view | {post_11_upgrade,depends_on_pg} | {} - view | {post_11_upgrade,depends_on_seq} | {} - view | {post_11_upgrade,non_dist_upgrade_multiple_dist_view} | {} - view | {post_11_upgrade,non_dist_upgrade_ref_view} | {} - view | {post_11_upgrade,non_dist_upgrade_ref_view_2} | {} - view | {post_11_upgrade,reporting_line} | {} - view | {post_11_upgrade,view_for_upgrade_test} | {} - view | {post_11_upgrade,view_for_upgrade_test_my_type} | {} - (48 rows) - diff --git a/src/test/regress/expected/upgrade_pg_dist_object_test_after_1.out b/src/test/regress/expected/upgrade_pg_dist_object_test_after_1.out deleted file mode 100644 index 61e3ef713..000000000 --- a/src/test/regress/expected/upgrade_pg_dist_object_test_after_1.out +++ /dev/null @@ -1,51 +0,0 @@ --- run this test only when old citus version is earlier than 10.0 -\set upgrade_test_old_citus_version `echo "$CITUS_OLD_VERSION"` -SELECT substring(:'upgrade_test_old_citus_version', 'v(\d+)\.\d+\.\d+')::int < 10 -AS upgrade_test_old_citus_version_lt_10_0; - upgrade_test_old_citus_version_lt_10_0 ---------------------------------------------------------------------- - t -(1 row) - -\gset -\if :upgrade_test_old_citus_version_lt_10_0 -\else -\q -\endif --- drop objects from previous test (uprade_basic_after.sql) for a clean test --- drop upgrade_basic schema and switch back to public schema -SET search_path to public; -DROP SCHEMA upgrade_basic CASCADE; -NOTICE: drop cascades to 7 other objects -DETAIL: drop cascades to table upgrade_basic.t -drop cascades to table upgrade_basic.tp -drop cascades to table upgrade_basic.t_ab -drop cascades to table upgrade_basic.t2 -drop cascades to table upgrade_basic.r -drop cascades to table upgrade_basic.tr -drop cascades to table upgrade_basic.t_range --- as we updated citus to available version, --- "isn" extension --- "new_schema" schema --- "public" schema --- "fooschema" schema --- "footype" type (under schema 'fooschema') - -- will now be marked as distributed - -- but, - -- "seg" extension - -- will not be marked as distributed --- see underlying objects -SELECT i.* FROM pg_catalog.pg_dist_object, pg_identify_object_as_address(classid, objid, objsubid) i ORDER BY 1, 2, 3; - type | object_names | object_args ---------------------------------------------------------------------- - database | {postgres} | {} - role | {postgres} | {} - schema | {fooschema} | {} - schema | {new_schema} | {} - schema | {public} | {} - table | {fooschema,footable} | {} - table | {new_schema,another_dist_table} | {} - table | {public,dist_table} | {} - type | {fooschema.footype} | {} - (9 rows) - diff --git a/src/test/regress/expected/upgrade_pg_dist_object_test_before.out b/src/test/regress/expected/upgrade_pg_dist_object_test_before.out deleted file mode 100644 index c5ae71852..000000000 --- a/src/test/regress/expected/upgrade_pg_dist_object_test_before.out +++ /dev/null @@ -1,46 +0,0 @@ --- run this test only when old citus version is earlier than 10.0 -\set upgrade_test_old_citus_version `echo "$CITUS_OLD_VERSION"` -SELECT substring(:'upgrade_test_old_citus_version', 'v(\d+)\.\d+\.\d+')::int < 10 -AS upgrade_test_old_citus_version_lt_10_0; - upgrade_test_old_citus_version_lt_10_0 ---------------------------------------------------------------------- - t -(1 row) - -\gset -\if :upgrade_test_old_citus_version_lt_10_0 -\else -\q -\endif --- schema propagation -- --- public schema -CREATE TABLE dist_table (a int); -SELECT create_reference_table('dist_table'); - create_reference_table ---------------------------------------------------------------------- - -(1 row) - --- custom schema -CREATE SCHEMA new_schema; -SET search_path to new_schema; -CREATE TABLE another_dist_table (a int); -SELECT create_reference_table('another_dist_table'); - create_reference_table ---------------------------------------------------------------------- - -(1 row) - --- another custom schema and a type --- create table that depends both on a type & schema here (actually type depends on the schema) --- here we test if schema is marked as distributed successfully. --- This is because tracking the dependencies will hit to the schema for two times -CREATE SCHEMA fooschema; -CREATE TYPE fooschema.footype AS (x int, y int); -CREATE TABLE fooschema.footable (f fooschema.footype); -SELECT create_reference_table('fooschema.footable'); - create_reference_table ---------------------------------------------------------------------- - -(1 row) - diff --git a/src/test/regress/expected/upgrade_pg_dist_object_test_before_0.out b/src/test/regress/expected/upgrade_pg_dist_object_test_before_0.out deleted file mode 100644 index 1654fa35c..000000000 --- a/src/test/regress/expected/upgrade_pg_dist_object_test_before_0.out +++ /dev/null @@ -1,13 +0,0 @@ --- run this test only when old citus version is earlier than 10.0 -\set upgrade_test_old_citus_version `echo "$CITUS_OLD_VERSION"` -SELECT substring(:'upgrade_test_old_citus_version', 'v(\d+)\.\d+\.\d+')::int < 10 -AS upgrade_test_old_citus_version_lt_10_0; - upgrade_test_old_citus_version_lt_10_0 ---------------------------------------------------------------------- - f -(1 row) - -\gset -\if :upgrade_test_old_citus_version_lt_10_0 -\else -\q diff --git a/src/test/regress/expected/upgrade_post_11_after.out b/src/test/regress/expected/upgrade_post_11_after.out index cf41da8e1..422bc846f 100644 --- a/src/test/regress/expected/upgrade_post_11_after.out +++ b/src/test/regress/expected/upgrade_post_11_after.out @@ -1,3 +1,17 @@ +-- run this test only when old citus version is earlier than 11.0 +\set upgrade_test_old_citus_version `echo "$CITUS_OLD_VERSION"` +SELECT substring(:'upgrade_test_old_citus_version', 'v(\d+)\.\d+\.\d+')::int < 11 +AS upgrade_test_old_citus_version_lt_11_0; + upgrade_test_old_citus_version_lt_11_0 +--------------------------------------------------------------------- + t +(1 row) + +\gset +\if :upgrade_test_old_citus_version_lt_11_0 +\else +\q +\endif SET search_path = post_11_upgrade; -- tables, views and their dependencies become objects with Citus 11+ SELECT pg_identify_object_as_address(classid, objid, objsubid) FROM pg_catalog.pg_dist_object WHERE objid IN ('post_11_upgrade'::regnamespace, 'post_11_upgrade.part_table'::regclass, 'post_11_upgrade.sensors'::regclass, 'post_11_upgrade.func_in_transaction_def'::regproc, 'post_11_upgrade.partial_index_test_config'::regconfig, 'post_11_upgrade.my_type'::regtype, 'post_11_upgrade.employees'::regclass, 'post_11_upgrade.view_for_upgrade_test'::regclass, 'post_11_upgrade.my_type_for_view'::regtype, 'post_11_upgrade.view_for_upgrade_test_my_type'::regclass, 'post_11_upgrade.non_dist_table_for_view'::regclass, 'post_11_upgrade.non_dist_upgrade_test_view'::regclass, 'post_11_upgrade.non_dist_upgrade_test_view_local_join'::regclass, 'post_11_upgrade.non_dist_upgrade_multiple_dist_view'::regclass, 'post_11_upgrade.non_dist_upgrade_ref_view'::regclass, 'post_11_upgrade.non_dist_upgrade_ref_view_2'::regclass, 'post_11_upgrade.reporting_line'::regclass, 'post_11_upgrade.v_test_1'::regclass, 'post_11_upgrade.v_test_2'::regclass, 'post_11_upgrade.owned_by_extension_table'::regclass, 'post_11_upgrade.materialized_view'::regclass, 'post_11_upgrade.owned_by_extension_view'::regclass, 'post_11_upgrade.local_type'::regtype, 'post_11_upgrade.non_dist_dist_table_for_view'::regclass, 'post_11_upgrade.depends_on_nothing_1'::regclass, 'post_11_upgrade.depends_on_nothing_2'::regclass, 'post_11_upgrade.depends_on_pg'::regclass, 'post_11_upgrade.depends_on_citus'::regclass, 'post_11_upgrade.depends_on_seq'::regclass, 'post_11_upgrade.depends_on_seq_and_no_support'::regclass) ORDER BY 1; diff --git a/src/test/regress/expected/upgrade_partition_constraints_after_0.out b/src/test/regress/expected/upgrade_post_11_after_0.out similarity index 52% rename from src/test/regress/expected/upgrade_partition_constraints_after_0.out rename to src/test/regress/expected/upgrade_post_11_after_0.out index 1654fa35c..7ddb3fe39 100644 --- a/src/test/regress/expected/upgrade_partition_constraints_after_0.out +++ b/src/test/regress/expected/upgrade_post_11_after_0.out @@ -1,13 +1,13 @@ --- run this test only when old citus version is earlier than 10.0 +-- run this test only when old citus version is earlier than 11.0 \set upgrade_test_old_citus_version `echo "$CITUS_OLD_VERSION"` -SELECT substring(:'upgrade_test_old_citus_version', 'v(\d+)\.\d+\.\d+')::int < 10 -AS upgrade_test_old_citus_version_lt_10_0; - upgrade_test_old_citus_version_lt_10_0 +SELECT substring(:'upgrade_test_old_citus_version', 'v(\d+)\.\d+\.\d+')::int < 11 +AS upgrade_test_old_citus_version_lt_11_0; + upgrade_test_old_citus_version_lt_11_0 --------------------------------------------------------------------- f (1 row) \gset -\if :upgrade_test_old_citus_version_lt_10_0 +\if :upgrade_test_old_citus_version_lt_11_0 \else \q diff --git a/src/test/regress/expected/upgrade_post_11_before.out b/src/test/regress/expected/upgrade_post_11_before.out index 05ff85578..695e5f743 100644 --- a/src/test/regress/expected/upgrade_post_11_before.out +++ b/src/test/regress/expected/upgrade_post_11_before.out @@ -1,3 +1,17 @@ +-- run this test only when old citus version is earlier than 11.0 +\set upgrade_test_old_citus_version `echo "$CITUS_OLD_VERSION"` +SELECT substring(:'upgrade_test_old_citus_version', 'v(\d+)\.\d+\.\d+')::int < 11 +AS upgrade_test_old_citus_version_lt_11_0; + upgrade_test_old_citus_version_lt_11_0 +--------------------------------------------------------------------- + t +(1 row) + +\gset +\if :upgrade_test_old_citus_version_lt_11_0 +\else +\q +\endif -- test cases for #3970 SET citus.shard_count TO 32; SET citus.shard_replication_factor TO 1; diff --git a/src/test/regress/expected/upgrade_pg_dist_object_test_after_0.out b/src/test/regress/expected/upgrade_post_11_before_0.out similarity index 52% rename from src/test/regress/expected/upgrade_pg_dist_object_test_after_0.out rename to src/test/regress/expected/upgrade_post_11_before_0.out index 1654fa35c..7ddb3fe39 100644 --- a/src/test/regress/expected/upgrade_pg_dist_object_test_after_0.out +++ b/src/test/regress/expected/upgrade_post_11_before_0.out @@ -1,13 +1,13 @@ --- run this test only when old citus version is earlier than 10.0 +-- run this test only when old citus version is earlier than 11.0 \set upgrade_test_old_citus_version `echo "$CITUS_OLD_VERSION"` -SELECT substring(:'upgrade_test_old_citus_version', 'v(\d+)\.\d+\.\d+')::int < 10 -AS upgrade_test_old_citus_version_lt_10_0; - upgrade_test_old_citus_version_lt_10_0 +SELECT substring(:'upgrade_test_old_citus_version', 'v(\d+)\.\d+\.\d+')::int < 11 +AS upgrade_test_old_citus_version_lt_11_0; + upgrade_test_old_citus_version_lt_11_0 --------------------------------------------------------------------- f (1 row) \gset -\if :upgrade_test_old_citus_version_lt_10_0 +\if :upgrade_test_old_citus_version_lt_11_0 \else \q diff --git a/src/test/regress/mixed_after_citus_upgrade_schedule b/src/test/regress/mixed_after_citus_upgrade_schedule index 223d7f349..9722b7317 100644 --- a/src/test/regress/mixed_after_citus_upgrade_schedule +++ b/src/test/regress/mixed_after_citus_upgrade_schedule @@ -1,2 +1 @@ test: upgrade_basic_after -test: upgrade_pg_dist_object_test_after diff --git a/src/test/regress/mixed_before_citus_upgrade_schedule b/src/test/regress/mixed_before_citus_upgrade_schedule index d7447b6df..7ed0eb5bf 100644 --- a/src/test/regress/mixed_before_citus_upgrade_schedule +++ b/src/test/regress/mixed_before_citus_upgrade_schedule @@ -1,2 +1 @@ test: upgrade_basic_before -test: upgrade_pg_dist_object_test_before diff --git a/src/test/regress/sql/upgrade_citus_finish_citus_upgrade.sql b/src/test/regress/sql/upgrade_citus_finish_citus_upgrade.sql index a326fb0a4..8d0405ea6 100644 --- a/src/test/regress/sql/upgrade_citus_finish_citus_upgrade.sql +++ b/src/test/regress/sql/upgrade_citus_finish_citus_upgrade.sql @@ -1,5 +1,12 @@ -- Citus upgrades are finished by calling a procedure +-- Note that pg_catalog.citus_finish_citus_upgrade() behaves differently +-- when last upgrade citus version is less than 11 +-- so we have two alternative outputs for this test +\set upgrade_test_old_citus_version `echo "$CITUS_OLD_VERSION"` +SELECT substring(:'upgrade_test_old_citus_version', 'v(\d+)\.\d+\.\d+')::int < 11 +AS upgrade_test_old_citus_version_lt_11_0; + -- this is a transactional procedure, so rollback should be fine BEGIN; CALL citus_finish_citus_upgrade(); diff --git a/src/test/regress/sql/upgrade_columnar_metapage_after.sql b/src/test/regress/sql/upgrade_columnar_metapage_after.sql deleted file mode 100644 index d015d0b0d..000000000 --- a/src/test/regress/sql/upgrade_columnar_metapage_after.sql +++ /dev/null @@ -1,78 +0,0 @@ -\set upgrade_test_old_citus_version `echo "$CITUS_OLD_VERSION"` -SELECT substring(:'upgrade_test_old_citus_version', 'v(\d+)\.\d+\.\d+')::int >= 10 AND - substring(:'upgrade_test_old_citus_version', 'v\d+\.(\d+)\.\d+')::int >= 0 -AS upgrade_test_old_citus_version_ge_10_0; -\gset -\if :upgrade_test_old_citus_version_ge_10_0 -\else -\q -\endif - --- it's not the best practice to define this here, but we don't want to include --- columnar_test_helpers in upgrade test schedule -CREATE OR REPLACE FUNCTION columnar_storage_info( - rel regclass, - version_major OUT int4, - version_minor OUT int4, - storage_id OUT int8, - reserved_stripe_id OUT int8, - reserved_row_number OUT int8, - reserved_offset OUT int8) -STRICT -LANGUAGE c AS 'citus', 'columnar_storage_info'; - -CREATE VIEW columnar_table_stripe_info AS -SELECT columnar_table_storageids.relname relname, - columnar.stripe.stripe_num stripe_num, - columnar.stripe.row_count row_count, - columnar.stripe.first_row_number first_row_number -FROM columnar.stripe, -( - SELECT c.oid relid, c.relname relname, (columnar_storage_info(c.oid)).storage_id relstorageid - FROM pg_class c, pg_am a - WHERE c.relam = a.oid AND amname = 'columnar' -) columnar_table_storageids -WHERE relstorageid = columnar.stripe.storage_id; - -SET search_path TO upgrade_columnar_metapage, public; - --- show that first_row_number values are equal to MAX(row_count) * stripe_num + COLUMNAR_FIRST_ROW_NUMBER -SELECT * FROM columnar_table_stripe_info ORDER BY relname, stripe_num; - --- should work since we upgrade metapages when upgrading schema version -INSERT INTO columnar_table_1 VALUES (3); - --- state of stripe metadata for columnar_table_1 after post-upgrade insert -SELECT * FROM columnar_table_stripe_info WHERE relname = 'columnar_table_1' ORDER BY stripe_num; - --- show that all columnar relation's metapage's are upgraded to "2.0" -SELECT count(*)=0 -FROM (SELECT (columnar_storage_info(c.oid)).* t - FROM pg_class c, pg_am a - WHERE c.relam = a.oid AND amname = 'columnar') t -WHERE t.version_major != 2 and t.version_minor != 0; - --- print metapage for two of the tables -SELECT version_major, version_minor, reserved_stripe_id, reserved_row_number - FROM columnar_storage_info('columnar_table_1'); -SELECT version_major, version_minor, reserved_stripe_id, reserved_row_number - FROM columnar_storage_info('columnar_table_2'); - --- show that no_data_columnar_table also has metapage after upgrade -SELECT version_major, version_minor, reserved_stripe_id, reserved_row_number - FROM columnar_storage_info('no_data_columnar_table'); - --- table is already upgraded, make sure that upgrade_columnar_metapage is no-op -SELECT columnar_internal.upgrade_columnar_storage(c.oid) -FROM pg_class c, pg_am a -WHERE c.relam = a.oid AND amname = 'columnar' and relname = 'columnar_table_2'; - -SELECT version_major, version_minor, reserved_stripe_id, reserved_row_number - FROM columnar_storage_info('columnar_table_2'); - -VACUUM FULL columnar_table_2; - --- print metapage and stripe metadata after post-upgrade vacuum full -SELECT version_major, version_minor, reserved_stripe_id, reserved_row_number - FROM columnar_storage_info('columnar_table_2'); -SELECT * FROM columnar_table_stripe_info WHERE relname = 'columnar_table_2' ORDER BY stripe_num; diff --git a/src/test/regress/sql/upgrade_columnar_metapage_before.sql b/src/test/regress/sql/upgrade_columnar_metapage_before.sql deleted file mode 100644 index 34e6d4fa4..000000000 --- a/src/test/regress/sql/upgrade_columnar_metapage_before.sql +++ /dev/null @@ -1,26 +0,0 @@ -\set upgrade_test_old_citus_version `echo "$CITUS_OLD_VERSION"` -SELECT substring(:'upgrade_test_old_citus_version', 'v(\d+)\.\d+\.\d+')::int >= 10 AND - substring(:'upgrade_test_old_citus_version', 'v\d+\.(\d+)\.\d+')::int >= 0 -AS upgrade_test_old_citus_version_ge_10_0; -\gset -\if :upgrade_test_old_citus_version_ge_10_0 -\else -\q -\endif - -CREATE SCHEMA upgrade_columnar_metapage; -SET search_path TO upgrade_columnar_metapage, public; - -CREATE TABLE columnar_table_1(a INT, b INT) USING columnar; -INSERT INTO columnar_table_1 SELECT i FROM generate_series(160001, 320000) i; - -CREATE TABLE columnar_table_2(b INT) USING columnar; -SELECT alter_columnar_table_set('columnar_table_2', - chunk_group_row_limit => 1000, - stripe_row_limit => 1000); -INSERT INTO columnar_table_2 SELECT i FROM generate_series(1600, 3500) i; - -CREATE TABLE columnar_table_3(b INT) USING columnar; -INSERT INTO columnar_table_3 VALUES (1), (2); - -CREATE TABLE no_data_columnar_table(a INT, b INT, c TEXT) USING columnar; diff --git a/src/test/regress/sql/upgrade_partition_constraints_after.sql b/src/test/regress/sql/upgrade_partition_constraints_after.sql deleted file mode 100644 index bf18d6ca4..000000000 --- a/src/test/regress/sql/upgrade_partition_constraints_after.sql +++ /dev/null @@ -1,25 +0,0 @@ --- run this test only when old citus version is earlier than 10.0 -\set upgrade_test_old_citus_version `echo "$CITUS_OLD_VERSION"` -SELECT substring(:'upgrade_test_old_citus_version', 'v(\d+)\.\d+\.\d+')::int < 10 -AS upgrade_test_old_citus_version_lt_10_0; -\gset -\if :upgrade_test_old_citus_version_lt_10_0 -\else -\q -\endif - --- test cases for #3970 -SET search_path = test_3970; - ---5. add a partition --- This command will fail as the child table has a wrong constraint name -CREATE TABLE part_table_p202009 PARTITION OF part_table FOR VALUES FROM ('2020-09-01 00:00:00') TO ('2020-10-01 00:00:00'); - --- fix constraint names on partitioned table shards -SELECT fix_pre_citus10_partitioned_table_constraint_names('part_table'::regclass); - ---5. add a partition -CREATE TABLE part_table_p202009 PARTITION OF part_table FOR VALUES FROM ('2020-09-01 00:00:00') TO ('2020-10-01 00:00:00'); - -RESET search_path; -DROP SCHEMA test_3970 CASCADE; diff --git a/src/test/regress/sql/upgrade_partition_constraints_before.sql b/src/test/regress/sql/upgrade_partition_constraints_before.sql deleted file mode 100644 index a64f8c0e8..000000000 --- a/src/test/regress/sql/upgrade_partition_constraints_before.sql +++ /dev/null @@ -1,39 +0,0 @@ --- run this test only when old citus version is earlier than 10.0 -\set upgrade_test_old_citus_version `echo "$CITUS_OLD_VERSION"` -SELECT substring(:'upgrade_test_old_citus_version', 'v(\d+)\.\d+\.\d+')::int < 10 -AS upgrade_test_old_citus_version_lt_10_0; -\gset -\if :upgrade_test_old_citus_version_lt_10_0 -\else -\q -\endif - --- test cases for #3970 -CREATE SCHEMA test_3970; -SET search_path = test_3970; - ---1. create a partitioned table -CREATE TABLE part_table ( - work_ymdt timestamp without time zone NOT NULL, - seq bigint NOT NULL, - my_seq bigint NOT NULL, - work_memo character varying(150), - CONSTRAINT work_memo_check CHECK ((octet_length((work_memo)::text) <= 150)) -) -PARTITION BY RANGE (work_ymdt); - ---2. perform create_distributed_table -SELECT create_distributed_table('part_table', 'seq'); - ---3. add a partition -CREATE TABLE part_table_p202008 PARTITION OF part_table FOR VALUES FROM ('2020-08-01 00:00:00') TO ('2020-09-01 00:00:00'); - ---4. add a check constraint -ALTER TABLE part_table ADD CONSTRAINT my_seq CHECK (my_seq > 0); - ---5. add a partition --- This command will fail as the child table has a wrong constraint name -CREATE TABLE part_table_p202009 PARTITION OF part_table FOR VALUES FROM ('2020-09-01 00:00:00') TO ('2020-10-01 00:00:00'); - --- Add another constraint with a long name that will get truncated with a hash -ALTER TABLE part_table ADD CONSTRAINT ck_012345678901234567890123456789012345678901234567890123456789 CHECK (my_seq > 0); diff --git a/src/test/regress/sql/upgrade_pg_dist_object_test_after.sql b/src/test/regress/sql/upgrade_pg_dist_object_test_after.sql deleted file mode 100644 index 49926f6fb..000000000 --- a/src/test/regress/sql/upgrade_pg_dist_object_test_after.sql +++ /dev/null @@ -1,28 +0,0 @@ --- run this test only when old citus version is earlier than 10.0 -\set upgrade_test_old_citus_version `echo "$CITUS_OLD_VERSION"` -SELECT substring(:'upgrade_test_old_citus_version', 'v(\d+)\.\d+\.\d+')::int < 10 -AS upgrade_test_old_citus_version_lt_10_0; -\gset -\if :upgrade_test_old_citus_version_lt_10_0 -\else -\q -\endif - --- drop objects from previous test (uprade_basic_after.sql) for a clean test --- drop upgrade_basic schema and switch back to public schema -SET search_path to public; -DROP SCHEMA upgrade_basic CASCADE; - --- as we updated citus to available version, --- "isn" extension --- "new_schema" schema --- "public" schema --- "fooschema" schema --- "footype" type (under schema 'fooschema') --- will now be marked as distributed --- but, --- "seg" extension --- will not be marked as distributed - --- see underlying objects -SELECT i.* FROM pg_catalog.pg_dist_object, pg_identify_object_as_address(classid, objid, objsubid) i ORDER BY 1, 2, 3; diff --git a/src/test/regress/sql/upgrade_pg_dist_object_test_before.sql b/src/test/regress/sql/upgrade_pg_dist_object_test_before.sql deleted file mode 100644 index 9b2cead88..000000000 --- a/src/test/regress/sql/upgrade_pg_dist_object_test_before.sql +++ /dev/null @@ -1,35 +0,0 @@ --- run this test only when old citus version is earlier than 10.0 -\set upgrade_test_old_citus_version `echo "$CITUS_OLD_VERSION"` -SELECT substring(:'upgrade_test_old_citus_version', 'v(\d+)\.\d+\.\d+')::int < 10 -AS upgrade_test_old_citus_version_lt_10_0; -\gset -\if :upgrade_test_old_citus_version_lt_10_0 -\else -\q -\endif - --- schema propagation -- - --- public schema -CREATE TABLE dist_table (a int); -SELECT create_reference_table('dist_table'); - --- custom schema -CREATE SCHEMA new_schema; - -SET search_path to new_schema; - -CREATE TABLE another_dist_table (a int); -SELECT create_reference_table('another_dist_table'); - --- another custom schema and a type - --- create table that depends both on a type & schema here (actually type depends on the schema) --- here we test if schema is marked as distributed successfully. --- This is because tracking the dependencies will hit to the schema for two times - -CREATE SCHEMA fooschema; -CREATE TYPE fooschema.footype AS (x int, y int); - -CREATE TABLE fooschema.footable (f fooschema.footype); -SELECT create_reference_table('fooschema.footable'); diff --git a/src/test/regress/sql/upgrade_post_11_after.sql b/src/test/regress/sql/upgrade_post_11_after.sql index 946c52ae2..ba9b12f3b 100644 --- a/src/test/regress/sql/upgrade_post_11_after.sql +++ b/src/test/regress/sql/upgrade_post_11_after.sql @@ -1,3 +1,13 @@ +-- run this test only when old citus version is earlier than 11.0 +\set upgrade_test_old_citus_version `echo "$CITUS_OLD_VERSION"` +SELECT substring(:'upgrade_test_old_citus_version', 'v(\d+)\.\d+\.\d+')::int < 11 +AS upgrade_test_old_citus_version_lt_11_0; +\gset +\if :upgrade_test_old_citus_version_lt_11_0 +\else +\q +\endif + SET search_path = post_11_upgrade; -- tables, views and their dependencies become objects with Citus 11+ diff --git a/src/test/regress/sql/upgrade_post_11_before.sql b/src/test/regress/sql/upgrade_post_11_before.sql index 9d17c78ac..617adc7b0 100644 --- a/src/test/regress/sql/upgrade_post_11_before.sql +++ b/src/test/regress/sql/upgrade_post_11_before.sql @@ -1,3 +1,12 @@ +-- run this test only when old citus version is earlier than 11.0 +\set upgrade_test_old_citus_version `echo "$CITUS_OLD_VERSION"` +SELECT substring(:'upgrade_test_old_citus_version', 'v(\d+)\.\d+\.\d+')::int < 11 +AS upgrade_test_old_citus_version_lt_11_0; +\gset +\if :upgrade_test_old_citus_version_lt_11_0 +\else +\q +\endif -- test cases for #3970 SET citus.shard_count TO 32; From 04f6868ed22e8366f7e4f8d464232c3af149aed4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20Ozan=20Akg=C3=BCl?= Date: Fri, 16 Jun 2023 14:21:58 +0300 Subject: [PATCH 099/118] Add citus_schemas view (#6979) DESCRIPTION: Adds citus_schemas view The citus_schemas view will be created in public schema if it exists, if not the view will be created in pg_catalog. Need to: - [x] Add tests - [x] Fix tests --- .../distributed/sql/citus--11.3-1--12.0-1.sql | 2 + .../sql/downgrades/citus--12.0-1--11.3-1.sql | 4 + .../sql/udfs/citus_schemas/12.0-1.sql | 42 ++++++++++ .../sql/udfs/citus_schemas/latest.sql | 42 ++++++++++ src/test/regress/expected/multi_extension.out | 20 +++-- .../expected/schema_based_sharding.out | 80 ++++++++++++++++++- .../expected/upgrade_list_citus_objects.out | 3 +- .../regress/sql/schema_based_sharding.sql | 68 +++++++++++++++- 8 files changed, 250 insertions(+), 11 deletions(-) create mode 100644 src/backend/distributed/sql/udfs/citus_schemas/12.0-1.sql create mode 100644 src/backend/distributed/sql/udfs/citus_schemas/latest.sql diff --git a/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql b/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql index 7f075ba14..688b79fe8 100644 --- a/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql +++ b/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql @@ -31,6 +31,8 @@ DROP FUNCTION citus_shard_sizes; #include "udfs/citus_tables/12.0-1.sql" #include "udfs/citus_shards/12.0-1.sql" +#include "udfs/citus_schemas/12.0-1.sql" + -- udfs used to include schema-based tenants in tenant monitoring #include "udfs/citus_stat_tenants_local/12.0-1.sql" diff --git a/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql b/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql index 251c22366..3acd60311 100644 --- a/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql +++ b/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql @@ -43,6 +43,10 @@ DROP FUNCTION pg_catalog.citus_internal_unregister_tenant_schema_globally(Oid, t #include "../udfs/citus_drop_trigger/10.2-1.sql" +-- citus_schemas might be created in either of the schemas +DROP VIEW IF EXISTS public.citus_schemas; +DROP VIEW IF EXISTS pg_catalog.citus_schemas; + DROP VIEW pg_catalog.citus_shards; DROP FUNCTION pg_catalog.citus_shard_sizes; #include "../udfs/citus_shard_sizes/10.0-1.sql" diff --git a/src/backend/distributed/sql/udfs/citus_schemas/12.0-1.sql b/src/backend/distributed/sql/udfs/citus_schemas/12.0-1.sql new file mode 100644 index 000000000..1760b5ab0 --- /dev/null +++ b/src/backend/distributed/sql/udfs/citus_schemas/12.0-1.sql @@ -0,0 +1,42 @@ +DO $$ +declare +citus_schemas_create_query text; +BEGIN +citus_schemas_create_query=$CSCQ$ + CREATE OR REPLACE VIEW %I.citus_schemas AS + SELECT + ts.schemaid::regnamespace AS schema_name, + ts.colocationid AS colocation_id, + CASE + WHEN pg_catalog.has_schema_privilege(CURRENT_USER, ts.schemaid::regnamespace, 'USAGE') + THEN pg_size_pretty(coalesce(schema_sizes.schema_size, 0)) + ELSE NULL + END AS schema_size, + pg_get_userbyid(n.nspowner) AS schema_owner + FROM + pg_dist_schema ts + JOIN + pg_namespace n ON (ts.schemaid = n.oid) + LEFT JOIN ( + SELECT c.relnamespace::regnamespace schema_id, SUM(size) AS schema_size + FROM citus_shard_sizes() css, pg_dist_shard ds, pg_class c + WHERE css.shard_id = ds.shardid AND ds.logicalrelid = c.oid + GROUP BY schema_id + ) schema_sizes ON schema_sizes.schema_id = ts.schemaid + ORDER BY + schema_name; +$CSCQ$; + +IF EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'public') THEN + EXECUTE format(citus_schemas_create_query, 'public'); + REVOKE ALL ON public.citus_schemas FROM public; + GRANT SELECT ON public.citus_schemas TO public; +ELSE + EXECUTE format(citus_schemas_create_query, 'citus'); + ALTER VIEW citus.citus_schemas SET SCHEMA pg_catalog; + REVOKE ALL ON pg_catalog.citus_schemas FROM public; + GRANT SELECT ON pg_catalog.citus_schemas TO public; +END IF; + +END; +$$; diff --git a/src/backend/distributed/sql/udfs/citus_schemas/latest.sql b/src/backend/distributed/sql/udfs/citus_schemas/latest.sql new file mode 100644 index 000000000..1760b5ab0 --- /dev/null +++ b/src/backend/distributed/sql/udfs/citus_schemas/latest.sql @@ -0,0 +1,42 @@ +DO $$ +declare +citus_schemas_create_query text; +BEGIN +citus_schemas_create_query=$CSCQ$ + CREATE OR REPLACE VIEW %I.citus_schemas AS + SELECT + ts.schemaid::regnamespace AS schema_name, + ts.colocationid AS colocation_id, + CASE + WHEN pg_catalog.has_schema_privilege(CURRENT_USER, ts.schemaid::regnamespace, 'USAGE') + THEN pg_size_pretty(coalesce(schema_sizes.schema_size, 0)) + ELSE NULL + END AS schema_size, + pg_get_userbyid(n.nspowner) AS schema_owner + FROM + pg_dist_schema ts + JOIN + pg_namespace n ON (ts.schemaid = n.oid) + LEFT JOIN ( + SELECT c.relnamespace::regnamespace schema_id, SUM(size) AS schema_size + FROM citus_shard_sizes() css, pg_dist_shard ds, pg_class c + WHERE css.shard_id = ds.shardid AND ds.logicalrelid = c.oid + GROUP BY schema_id + ) schema_sizes ON schema_sizes.schema_id = ts.schemaid + ORDER BY + schema_name; +$CSCQ$; + +IF EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'public') THEN + EXECUTE format(citus_schemas_create_query, 'public'); + REVOKE ALL ON public.citus_schemas FROM public; + GRANT SELECT ON public.citus_schemas TO public; +ELSE + EXECUTE format(citus_schemas_create_query, 'citus'); + ALTER VIEW citus.citus_schemas SET SCHEMA pg_catalog; + REVOKE ALL ON pg_catalog.citus_schemas FROM public; + GRANT SELECT ON pg_catalog.citus_schemas TO public; +END IF; + +END; +$$; diff --git a/src/test/regress/expected/multi_extension.out b/src/test/regress/expected/multi_extension.out index 54862798c..ca0c0514a 100644 --- a/src/test/regress/expected/multi_extension.out +++ b/src/test/regress/expected/multi_extension.out @@ -88,10 +88,11 @@ WHERE pgd.refclassid = 'pg_extension'::regclass AND pge.extname = 'citus' AND pgio.schema NOT IN ('pg_catalog', 'citus', 'citus_internal', 'test', 'columnar', 'columnar_internal') ORDER BY 1, 2; - type | identity + type | identity --------------------------------------------------------------------- + view | public.citus_schemas view | public.citus_tables -(1 row) +(2 rows) -- DROP EXTENSION pre-created by the regression suite DROP EXTENSION citus; @@ -1367,7 +1368,8 @@ SELECT * FROM multi_extension.print_extension_changes(); | function citus_schema_undistribute(regnamespace) void | function citus_stat_tenants_local_internal(boolean) SETOF record | table pg_dist_schema -(7 rows) + | view public.citus_schemas +(8 rows) DROP TABLE multi_extension.prev_objects, multi_extension.extension_diff; -- show running version @@ -1387,10 +1389,11 @@ WHERE pgd.refclassid = 'pg_extension'::regclass AND pge.extname = 'citus' AND pgio.schema NOT IN ('pg_catalog', 'citus', 'citus_internal', 'test', 'columnar', 'columnar_internal') ORDER BY 1, 2; - type | identity + type | identity --------------------------------------------------------------------- + view | public.citus_schemas view | public.citus_tables -(1 row) +(2 rows) -- see incompatible version errors out RESET citus.enable_version_checks; @@ -1498,10 +1501,11 @@ ALTER EXTENSION citus UPDATE; -- if cache is invalidated succesfull, this \d should work without any problem \d List of relations - Schema | Name | Type | Owner + Schema | Name | Type | Owner --------------------------------------------------------------------- - public | citus_tables | view | postgres -(1 row) + public | citus_schemas | view | postgres + public | citus_tables | view | postgres +(2 rows) \c - - - :master_port -- test https://github.com/citusdata/citus/issues/3409 diff --git a/src/test/regress/expected/schema_based_sharding.out b/src/test/regress/expected/schema_based_sharding.out index 7af81966c..951fc05e7 100644 --- a/src/test/regress/expected/schema_based_sharding.out +++ b/src/test/regress/expected/schema_based_sharding.out @@ -1474,8 +1474,86 @@ SELECT table_name, citus_table_type FROM citus_shards WHERE table_name::text LIK (2 rows) RESET citus.enable_schema_based_sharding; +-- test citus_schemas +SET citus.enable_schema_based_sharding TO ON; +CREATE USER citus_schema_role SUPERUSER; +SET ROLE citus_schema_role; +CREATE SCHEMA citus_sch1; +CREATE TABLE citus_sch1.tbl1(a INT); +CREATE TABLE citus_sch1.tbl2(a INT); +RESET ROLE; +CREATE SCHEMA citus_sch2; +CREATE TABLE citus_sch2.tbl1(a INT); +SET citus.enable_schema_based_sharding TO OFF; +INSERT INTO citus_sch1.tbl1 SELECT * FROM generate_series(1, 10000); +INSERT INTO citus_sch1.tbl2 SELECT * FROM generate_series(1, 5000); +INSERT INTO citus_sch2.tbl1 SELECT * FROM generate_series(1, 12000); +SELECT + cs.schema_name, + cs.colocation_id = ctc.colocation_id AS correct_colocation_id, + cs.schema_size = ctc.calculated_size AS correct_size, + cs.schema_owner +FROM public.citus_schemas cs +JOIN +( + SELECT + c.relnamespace, ct.colocation_id, + pg_size_pretty(sum(citus_total_relation_size(ct.table_name))) AS calculated_size + FROM public.citus_tables ct, pg_class c + WHERE ct.table_name::oid = c.oid + GROUP BY 1, 2 +) ctc ON cs.schema_name = ctc.relnamespace +WHERE cs.schema_name::text LIKE 'citus\_sch_' +ORDER BY cs.schema_name::text; + schema_name | correct_colocation_id | correct_size | schema_owner +--------------------------------------------------------------------- + citus_sch1 | t | t | citus_schema_role + citus_sch2 | t | t | postgres +(2 rows) + +-- test empty schema and empty tables +SET citus.enable_schema_based_sharding TO ON; +CREATE SCHEMA citus_empty_sch1; +CREATE SCHEMA citus_empty_sch2; +CREATE TABLE citus_empty_sch2.tbl1(a INT); +SET citus.enable_schema_based_sharding TO OFF; +SELECT schema_name, schema_size FROM public.citus_schemas +WHERE schema_name::text LIKE 'citus\_empty\_sch_' ORDER BY schema_name::text; + schema_name | schema_size +--------------------------------------------------------------------- + citus_empty_sch1 | 0 bytes + citus_empty_sch2 | 0 bytes +(2 rows) + +-- test with non-privileged role +CREATE USER citus_schema_nonpri; +SET ROLE citus_schema_nonpri; +SET client_min_messages TO ERROR; +SELECT schema_name, colocation_id > 0 AS colocation_id_visible, schema_size IS NOT NULL AS schema_size_visible, schema_owner +FROM public.citus_schemas WHERE schema_name::text LIKE 'citus\_sch_' ORDER BY schema_name::text; + schema_name | colocation_id_visible | schema_size_visible | schema_owner +--------------------------------------------------------------------- + citus_sch1 | t | f | citus_schema_role + citus_sch2 | t | f | postgres +(2 rows) + +RESET client_min_messages; +RESET ROLE; +-- test using citus_tables from workers +\c - - - :worker_1_port +SELECT schema_name, colocation_id > 0 AS colocation_id_visible, schema_size IS NOT NULL AS schema_size_visible, schema_owner +FROM public.citus_schemas WHERE schema_name::text LIKE 'citus\_sch_' ORDER BY schema_name::text; + schema_name | colocation_id_visible | schema_size_visible | schema_owner +--------------------------------------------------------------------- + citus_sch1 | t | t | citus_schema_role + citus_sch2 | t | t | postgres +(2 rows) + +\c - - - :master_port +SET search_path TO regular_schema; SET client_min_messages TO WARNING; -DROP SCHEMA regular_schema, tenant_3, tenant_5, tenant_7, tenant_6, type_sch CASCADE; +DROP SCHEMA regular_schema, tenant_3, tenant_5, tenant_7, tenant_6, type_sch, citus_sch1, citus_sch2, citus_empty_sch1, citus_empty_sch2 CASCADE; +DROP ROLE citus_schema_role, citus_schema_nonpri; SELECT citus_remove_node('localhost', :master_port); citus_remove_node --------------------------------------------------------------------- diff --git a/src/test/regress/expected/upgrade_list_citus_objects.out b/src/test/regress/expected/upgrade_list_citus_objects.out index 3442552ff..3e9698788 100644 --- a/src/test/regress/expected/upgrade_list_citus_objects.out +++ b/src/test/regress/expected/upgrade_list_citus_objects.out @@ -327,6 +327,7 @@ ORDER BY 1; view citus_dist_stat_activity view citus_lock_waits view citus_locks + view citus_schema.citus_schemas view citus_schema.citus_tables view citus_shard_indexes_on_worker view citus_shards @@ -337,5 +338,5 @@ ORDER BY 1; view citus_stat_tenants_local view pg_dist_shard_placement view time_partitions -(329 rows) +(330 rows) diff --git a/src/test/regress/sql/schema_based_sharding.sql b/src/test/regress/sql/schema_based_sharding.sql index e02c68dee..88ca77a40 100644 --- a/src/test/regress/sql/schema_based_sharding.sql +++ b/src/test/regress/sql/schema_based_sharding.sql @@ -994,7 +994,73 @@ SELECT table_name, citus_table_type FROM citus_shards WHERE table_name::text LIK RESET citus.enable_schema_based_sharding; +-- test citus_schemas +SET citus.enable_schema_based_sharding TO ON; +CREATE USER citus_schema_role SUPERUSER; +SET ROLE citus_schema_role; +CREATE SCHEMA citus_sch1; +CREATE TABLE citus_sch1.tbl1(a INT); +CREATE TABLE citus_sch1.tbl2(a INT); +RESET ROLE; + +CREATE SCHEMA citus_sch2; +CREATE TABLE citus_sch2.tbl1(a INT); +SET citus.enable_schema_based_sharding TO OFF; + +INSERT INTO citus_sch1.tbl1 SELECT * FROM generate_series(1, 10000); +INSERT INTO citus_sch1.tbl2 SELECT * FROM generate_series(1, 5000); + +INSERT INTO citus_sch2.tbl1 SELECT * FROM generate_series(1, 12000); + +SELECT + cs.schema_name, + cs.colocation_id = ctc.colocation_id AS correct_colocation_id, + cs.schema_size = ctc.calculated_size AS correct_size, + cs.schema_owner +FROM public.citus_schemas cs +JOIN +( + SELECT + c.relnamespace, ct.colocation_id, + pg_size_pretty(sum(citus_total_relation_size(ct.table_name))) AS calculated_size + FROM public.citus_tables ct, pg_class c + WHERE ct.table_name::oid = c.oid + GROUP BY 1, 2 +) ctc ON cs.schema_name = ctc.relnamespace +WHERE cs.schema_name::text LIKE 'citus\_sch_' +ORDER BY cs.schema_name::text; + +-- test empty schema and empty tables +SET citus.enable_schema_based_sharding TO ON; +CREATE SCHEMA citus_empty_sch1; + +CREATE SCHEMA citus_empty_sch2; +CREATE TABLE citus_empty_sch2.tbl1(a INT); +SET citus.enable_schema_based_sharding TO OFF; + +SELECT schema_name, schema_size FROM public.citus_schemas +WHERE schema_name::text LIKE 'citus\_empty\_sch_' ORDER BY schema_name::text; + +-- test with non-privileged role +CREATE USER citus_schema_nonpri; +SET ROLE citus_schema_nonpri; + +SET client_min_messages TO ERROR; +SELECT schema_name, colocation_id > 0 AS colocation_id_visible, schema_size IS NOT NULL AS schema_size_visible, schema_owner +FROM public.citus_schemas WHERE schema_name::text LIKE 'citus\_sch_' ORDER BY schema_name::text; + +RESET client_min_messages; +RESET ROLE; + +-- test using citus_tables from workers +\c - - - :worker_1_port +SELECT schema_name, colocation_id > 0 AS colocation_id_visible, schema_size IS NOT NULL AS schema_size_visible, schema_owner +FROM public.citus_schemas WHERE schema_name::text LIKE 'citus\_sch_' ORDER BY schema_name::text; +\c - - - :master_port +SET search_path TO regular_schema; + SET client_min_messages TO WARNING; -DROP SCHEMA regular_schema, tenant_3, tenant_5, tenant_7, tenant_6, type_sch CASCADE; +DROP SCHEMA regular_schema, tenant_3, tenant_5, tenant_7, tenant_6, type_sch, citus_sch1, citus_sch2, citus_empty_sch1, citus_empty_sch2 CASCADE; +DROP ROLE citus_schema_role, citus_schema_nonpri; SELECT citus_remove_node('localhost', :master_port); From 12a093b45623fa488cc610bf4ba44c875bd578ae Mon Sep 17 00:00:00 2001 From: Onur Tirtir Date: Fri, 16 Jun 2023 14:34:23 +0300 Subject: [PATCH 100/118] Allow using generated identity column based on int/smallint when creating a distributed table (#7008) Allow using generated identity column based on int/smallint when creating a distributed table so that applications that rely on those data types don't break. Inserting into / modifying such columns from workers is not allowed but it's better than not allowing such columns altogether. --- .../commands/create_distributed_table.c | 2 - src/backend/distributed/commands/table.c | 30 --- src/backend/distributed/shared_library_init.c | 39 ++++ src/include/distributed/commands.h | 1 - src/test/regress/citus_tests/run_test.py | 8 + .../expected/create_single_shard_table.out | 43 ++-- .../regress/expected/generated_identity.out | 185 +++++++++++++++--- .../expected/multi_sequence_default.out | 12 ++ .../regress/sql/create_single_shard_table.sql | 8 +- src/test/regress/sql/generated_identity.sql | 72 ++++++- 10 files changed, 318 insertions(+), 82 deletions(-) diff --git a/src/backend/distributed/commands/create_distributed_table.c b/src/backend/distributed/commands/create_distributed_table.c index 85331aea7..8810e6db9 100644 --- a/src/backend/distributed/commands/create_distributed_table.c +++ b/src/backend/distributed/commands/create_distributed_table.c @@ -1835,8 +1835,6 @@ EnsureRelationCanBeDistributed(Oid relationId, Var *distributionColumn, { Oid parentRelationId = InvalidOid; - ErrorIfTableHasUnsupportedIdentityColumn(relationId); - EnsureLocalTableEmptyIfNecessary(relationId, distributionMethod); /* user really wants triggers? */ diff --git a/src/backend/distributed/commands/table.c b/src/backend/distributed/commands/table.c index 4a1e69e14..24c4e49fb 100644 --- a/src/backend/distributed/commands/table.c +++ b/src/backend/distributed/commands/table.c @@ -4049,36 +4049,6 @@ MakeNameListFromRangeVar(const RangeVar *rel) } -/* - * ErrorIfTableHasUnsupportedIdentityColumn errors out if the given table has any identity column other than bigint identity column. - */ -void -ErrorIfTableHasUnsupportedIdentityColumn(Oid relationId) -{ - Relation relation = relation_open(relationId, AccessShareLock); - TupleDesc tupleDescriptor = RelationGetDescr(relation); - - for (int attributeIndex = 0; attributeIndex < tupleDescriptor->natts; - attributeIndex++) - { - Form_pg_attribute attributeForm = TupleDescAttr(tupleDescriptor, attributeIndex); - - if (attributeForm->attidentity && attributeForm->atttypid != INT8OID) - { - char *qualifiedRelationName = generate_qualified_relation_name(relationId); - ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg( - "cannot complete operation on %s with smallint/int identity column", - qualifiedRelationName), - errhint( - "Use bigint identity column instead."))); - } - } - - relation_close(relation, NoLock); -} - - /* * ErrorIfTableHasIdentityColumn errors out if the given table has identity column */ diff --git a/src/backend/distributed/shared_library_init.c b/src/backend/distributed/shared_library_init.c index a9b49e7c9..a67e4c878 100644 --- a/src/backend/distributed/shared_library_init.c +++ b/src/backend/distributed/shared_library_init.c @@ -185,6 +185,7 @@ static void CitusObjectAccessHook(ObjectAccessType access, Oid classId, Oid obje static void DoInitialCleanup(void); static void ResizeStackToMaximumDepth(void); static void multi_log_hook(ErrorData *edata); +static bool IsSequenceOverflowError(ErrorData *edata); static void RegisterConnectionCleanup(void); static void RegisterExternalClientBackendCounterDecrement(void); static void CitusCleanupConnectionsAtExit(int code, Datum arg); @@ -683,6 +684,15 @@ multi_log_hook(ErrorData *edata) * Show the user a meaningful error message when a backend is cancelled * by the distributed deadlock detection. Also reset the state for this, * since the next cancelation of the backend might have another reason. + * + * We also want to provide a useful hint for sequence overflow errors + * because they're likely to be caused by the way Citus handles smallint/int + * based sequences on worker nodes. Note that we add the hint without checking + * whether we're on a worker node or the sequence was used on a distributed + * table because catalog might not be available at this point. And given + * that this hint might be shown for regular Postgres tables too, we inject + * the hint only when EnableUnsupportedFeatureMessages is set to true. + * Otherwise, vanilla tests would fail. */ bool clearState = true; if (edata->elevel == ERROR && edata->sqlerrcode == ERRCODE_QUERY_CANCELED && @@ -700,6 +710,16 @@ multi_log_hook(ErrorData *edata) edata->message = pstrdup("canceling the transaction since it was " "involved in a distributed deadlock"); } + else if (EnableUnsupportedFeatureMessages && + IsSequenceOverflowError(edata)) + { + edata->detail = pstrdup("nextval(sequence) calls in worker nodes " + "are not supported for column defaults of " + "type int or smallint"); + edata->hint = pstrdup("If the command was issued from a worker node, " + "try issuing it from the coordinator node " + "instead."); + } if (original_emit_log_hook) { @@ -708,6 +728,25 @@ multi_log_hook(ErrorData *edata) } +/* + * IsSequenceOverflowError returns true if the given error is a sequence + * overflow error. + */ +static bool +IsSequenceOverflowError(ErrorData *edata) +{ + static const char *sequenceOverflowedMsgPrefix = + "nextval: reached maximum value of sequence"; + static const int sequenceOverflowedMsgPrefixLen = 42; + + return edata->elevel == ERROR && + edata->sqlerrcode == ERRCODE_SEQUENCE_GENERATOR_LIMIT_EXCEEDED && + edata->message != NULL && + strncmp(edata->message, sequenceOverflowedMsgPrefix, + sequenceOverflowedMsgPrefixLen) == 0; +} + + /* * StartupCitusBackend initializes per-backend infrastructure, and is called * the first time citus is used in a database. diff --git a/src/include/distributed/commands.h b/src/include/distributed/commands.h index 9a1c22c64..442e58058 100644 --- a/src/include/distributed/commands.h +++ b/src/include/distributed/commands.h @@ -591,7 +591,6 @@ extern bool ConstrTypeCitusCanDefaultName(ConstrType constrType); extern char * GetAlterColumnWithNextvalDefaultCmd(Oid sequenceOid, Oid relationId, char *colname, bool missingTableOk); -extern void ErrorIfTableHasUnsupportedIdentityColumn(Oid relationId); extern void ErrorIfTableHasIdentityColumn(Oid relationId); extern void ConvertNewTableIfNecessary(Node *createStmt); diff --git a/src/test/regress/citus_tests/run_test.py b/src/test/regress/citus_tests/run_test.py index 1ce2cfd9f..b41ba35cc 100755 --- a/src/test/regress/citus_tests/run_test.py +++ b/src/test/regress/citus_tests/run_test.py @@ -158,6 +158,14 @@ DEPS = { None, ["isolation_setup", "isolation_add_remove_node"] ), "schema_based_sharding": TestDeps("minimal_schedule"), + "multi_sequence_default": TestDeps( + None, + [ + "multi_test_helpers", + "multi_cluster_management", + "multi_table_ddl", + ], + ), } diff --git a/src/test/regress/expected/create_single_shard_table.out b/src/test/regress/expected/create_single_shard_table.out index 8d4756caf..dab58af25 100644 --- a/src/test/regress/expected/create_single_shard_table.out +++ b/src/test/regress/expected/create_single_shard_table.out @@ -1040,12 +1040,27 @@ CREATE TABLE identity_test ( c bigint GENERATED BY DEFAULT AS IDENTITY (START WITH 1000 INCREMENT BY 1000) ); SELECT create_distributed_table('identity_test', NULL, distribution_type=>null); -ERROR: cannot complete operation on create_single_shard_table.identity_test with smallint/int identity column -HINT: Use bigint identity column instead. + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO identity_test (a) VALUES (DEFAULT) RETURNING a; + a +--------------------------------------------------------------------- + 10 +(1 row) + +SELECT result FROM run_command_on_workers($$ + INSERT INTO create_single_shard_table.identity_test (a) VALUES (DEFAULT) +$$); + result +--------------------------------------------------------------------- + ERROR: nextval: reached maximum value of sequence "identity_test_a_seq" (2147483647) + ERROR: nextval: reached maximum value of sequence "identity_test_a_seq" (2147483647) +(2 rows) + DROP TABLE identity_test; --- Above failed because we don't support using a data type other than BIGINT --- for identity columns, so drop the table and create a new one with BIGINT --- identity columns. CREATE TABLE identity_test ( a bigint GENERATED BY DEFAULT AS IDENTITY (START WITH 10 INCREMENT BY 10), b bigint GENERATED ALWAYS AS IDENTITY (START WITH 100 INCREMENT BY 100), @@ -1131,7 +1146,7 @@ BEGIN; INSERT INTO referencing_table VALUES (1, 2); -- fails INSERT INTO referencing_table VALUES (2, 2); -ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730099" +ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730100" DETAIL: Key (a)=(2) is not present in table "referenced_table_xxxxxxx". CONTEXT: while executing command on localhost:xxxxx ROLLBACK; @@ -1177,7 +1192,7 @@ BEGIN; INSERT INTO referencing_table VALUES (1, 2); -- fails INSERT INTO referencing_table VALUES (2, 2); -ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730135" +ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730136" DETAIL: Key (a)=(2) is not present in table "referenced_table_xxxxxxx". CONTEXT: while executing command on localhost:xxxxx ROLLBACK; @@ -1295,8 +1310,8 @@ SELECT result, success FROM run_command_on_workers($$ $$); result | success --------------------------------------------------------------------- - ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730152" | f - ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730152" | f + ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730153" | f + ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730153" | f (2 rows) DROP TABLE referencing_table, referenced_table; @@ -1311,8 +1326,8 @@ SELECT create_distributed_table('self_fkey_test', NULL, distribution_type=>null) INSERT INTO self_fkey_test VALUES (1, 1); -- ok INSERT INTO self_fkey_test VALUES (2, 3); -- fails -ERROR: insert or update on table "self_fkey_test_1730153" violates foreign key constraint "self_fkey_test_b_fkey_1730153" -DETAIL: Key (b)=(3) is not present in table "self_fkey_test_1730153". +ERROR: insert or update on table "self_fkey_test_1730154" violates foreign key constraint "self_fkey_test_b_fkey_1730154" +DETAIL: Key (b)=(3) is not present in table "self_fkey_test_1730154". CONTEXT: while executing command on localhost:xxxxx -- similar foreign key tests but this time create the referencing table later on -- referencing table is a single-shard table @@ -1336,7 +1351,7 @@ BEGIN; INSERT INTO referencing_table VALUES (1, 2); -- fails INSERT INTO referencing_table VALUES (2, 2); -ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730155" +ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730156" DETAIL: Key (a)=(2) is not present in table "referenced_table_xxxxxxx". CONTEXT: while executing command on localhost:xxxxx ROLLBACK; @@ -1359,7 +1374,7 @@ BEGIN; INSERT INTO referencing_table VALUES (2, 1); -- fails INSERT INTO referencing_table VALUES (1, 2); -ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_b_fkey_1730157" +ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_b_fkey_1730158" DETAIL: Key (a, b)=(1, 2) is not present in table "referenced_table_xxxxxxx". CONTEXT: while executing command on localhost:xxxxx ROLLBACK; @@ -1466,7 +1481,7 @@ BEGIN; INSERT INTO referencing_table VALUES (1, 2); -- fails INSERT INTO referencing_table VALUES (2, 2); -ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730198" +ERROR: insert or update on table "referencing_table_xxxxxxx" violates foreign key constraint "referencing_table_a_fkey_1730199" DETAIL: Key (a)=(2) is not present in table "referenced_table_xxxxxxx". CONTEXT: while executing command on localhost:xxxxx ROLLBACK; diff --git a/src/test/regress/expected/generated_identity.out b/src/test/regress/expected/generated_identity.out index 865012af0..617a8fee6 100644 --- a/src/test/regress/expected/generated_identity.out +++ b/src/test/regress/expected/generated_identity.out @@ -16,44 +16,185 @@ SELECT 1 from citus_add_node('localhost', :master_port, groupId=>0); 1 (1 row) --- smallint identity column can not be distributed CREATE TABLE smallint_identity_column ( a smallint GENERATED BY DEFAULT AS IDENTITY ); -SELECT create_distributed_table('smallint_identity_column', 'a'); -ERROR: cannot complete operation on generated_identities.smallint_identity_column with smallint/int identity column -HINT: Use bigint identity column instead. -SELECT create_distributed_table_concurrently('smallint_identity_column', 'a'); -ERROR: cannot complete operation on generated_identities.smallint_identity_column with smallint/int identity column -HINT: Use bigint identity column instead. -SELECT create_reference_table('smallint_identity_column'); -ERROR: cannot complete operation on a table with identity column -SELECT citus_add_local_table_to_metadata('smallint_identity_column'); +CREATE VIEW verify_smallint_identity_column AS +SELECT attidentity, attgenerated FROM pg_attribute WHERE attrelid = 'smallint_identity_column'::regclass AND attname = 'a'; +BEGIN; + SELECT create_distributed_table('smallint_identity_column', 'a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + + SELECT * FROM verify_smallint_identity_column; + attidentity | attgenerated +--------------------------------------------------------------------- + d | +(1 row) + +ROLLBACK; +BEGIN; + SELECT create_reference_table('smallint_identity_column'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + + SELECT * FROM verify_smallint_identity_column; + attidentity | attgenerated +--------------------------------------------------------------------- + d | +(1 row) + +ROLLBACK; +BEGIN; + SELECT citus_add_local_table_to_metadata('smallint_identity_column'); citus_add_local_table_to_metadata --------------------------------------------------------------------- (1 row) -DROP TABLE smallint_identity_column; --- int identity column can not be distributed + SELECT * FROM verify_smallint_identity_column; + attidentity | attgenerated +--------------------------------------------------------------------- + d | +(1 row) + +ROLLBACK; +SELECT create_distributed_table_concurrently('smallint_identity_column', 'a'); + create_distributed_table_concurrently +--------------------------------------------------------------------- + +(1 row) + +SELECT * FROM verify_smallint_identity_column; + attidentity | attgenerated +--------------------------------------------------------------------- + d | +(1 row) + +SELECT result FROM run_command_on_workers('INSERT INTO generated_identities.smallint_identity_column (a) VALUES (DEFAULT);'); + result +--------------------------------------------------------------------- + ERROR: nextval: reached maximum value of sequence "smallint_identity_column_a_seq" (32767) + ERROR: nextval: reached maximum value of sequence "smallint_identity_column_a_seq" (32767) +(2 rows) + +DROP TABLE smallint_identity_column CASCADE; CREATE TABLE int_identity_column ( a int GENERATED BY DEFAULT AS IDENTITY ); -SELECT create_distributed_table('int_identity_column', 'a'); -ERROR: cannot complete operation on generated_identities.int_identity_column with smallint/int identity column -HINT: Use bigint identity column instead. -SELECT create_distributed_table_concurrently('int_identity_column', 'a'); -ERROR: cannot complete operation on generated_identities.int_identity_column with smallint/int identity column -HINT: Use bigint identity column instead. -SELECT create_reference_table('int_identity_column'); -ERROR: cannot complete operation on a table with identity column -SELECT citus_add_local_table_to_metadata('int_identity_column'); +CREATE VIEW verify_int_identity_column AS +SELECT attidentity, attgenerated FROM pg_attribute WHERE attrelid = 'int_identity_column'::regclass AND attname = 'a'; +BEGIN; + SELECT create_distributed_table('int_identity_column', 'a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + + SELECT * FROM verify_int_identity_column; + attidentity | attgenerated +--------------------------------------------------------------------- + d | +(1 row) + +ROLLBACK; +BEGIN; + SELECT create_reference_table('int_identity_column'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + + SELECT * FROM verify_int_identity_column; + attidentity | attgenerated +--------------------------------------------------------------------- + d | +(1 row) + +ROLLBACK; +BEGIN; + SELECT citus_add_local_table_to_metadata('int_identity_column'); citus_add_local_table_to_metadata --------------------------------------------------------------------- (1 row) -DROP TABLE int_identity_column; + SELECT * FROM verify_int_identity_column; + attidentity | attgenerated +--------------------------------------------------------------------- + d | +(1 row) + +ROLLBACK; +SELECT create_distributed_table_concurrently('int_identity_column', 'a'); + create_distributed_table_concurrently +--------------------------------------------------------------------- + +(1 row) + +SELECT * FROM verify_int_identity_column; + attidentity | attgenerated +--------------------------------------------------------------------- + d | +(1 row) + +SELECT result FROM run_command_on_workers('INSERT INTO generated_identities.int_identity_column (a) VALUES (DEFAULT);'); + result +--------------------------------------------------------------------- + ERROR: nextval: reached maximum value of sequence "int_identity_column_a_seq" (2147483647) + ERROR: nextval: reached maximum value of sequence "int_identity_column_a_seq" (2147483647) +(2 rows) + +DROP TABLE int_identity_column CASCADE; +CREATE TABLE reference_int_identity_column ( + a int GENERATED BY DEFAULT AS IDENTITY +); +SELECT create_reference_table('reference_int_identity_column'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO generated_identities.reference_int_identity_column (a) VALUES (DEFAULT) RETURNING a; + a +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT result FROM run_command_on_workers('INSERT INTO generated_identities.reference_int_identity_column (a) VALUES (DEFAULT);'); + result +--------------------------------------------------------------------- + ERROR: nextval: reached maximum value of sequence "reference_int_identity_column_a_seq" (2147483647) + ERROR: nextval: reached maximum value of sequence "reference_int_identity_column_a_seq" (2147483647) +(2 rows) + +CREATE TABLE citus_local_int_identity_column ( + a int GENERATED BY DEFAULT AS IDENTITY +); +SELECT citus_add_local_table_to_metadata('citus_local_int_identity_column'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO generated_identities.citus_local_int_identity_column (a) VALUES (DEFAULT) RETURNING a; + a +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT result FROM run_command_on_workers('INSERT INTO generated_identities.citus_local_int_identity_column (a) VALUES (DEFAULT);'); + result +--------------------------------------------------------------------- + ERROR: nextval: reached maximum value of sequence "citus_local_int_identity_column_a_seq" (2147483647) + ERROR: nextval: reached maximum value of sequence "citus_local_int_identity_column_a_seq" (2147483647) +(2 rows) + +DROP TABLE reference_int_identity_column, citus_local_int_identity_column; RESET citus.shard_replication_factor; CREATE TABLE bigint_identity_column ( a bigint GENERATED BY DEFAULT AS IDENTITY, diff --git a/src/test/regress/expected/multi_sequence_default.out b/src/test/regress/expected/multi_sequence_default.out index 57305befa..1cf2e806d 100644 --- a/src/test/regress/expected/multi_sequence_default.out +++ b/src/test/regress/expected/multi_sequence_default.out @@ -668,8 +668,12 @@ ERROR: nextval(sequence) calls in worker nodes are not supported for column def -- nextval from worker should fail for int and smallint sequences SELECT nextval('seq_12'); ERROR: nextval: reached maximum value of sequence "seq_12" (32767) +DETAIL: nextval(sequence) calls in worker nodes are not supported for column defaults of type int or smallint +HINT: If the command was issued from a worker node, try issuing it from the coordinator node instead. SELECT nextval('seq_13'); ERROR: nextval: reached maximum value of sequence "seq_13" (2147483647) +DETAIL: nextval(sequence) calls in worker nodes are not supported for column defaults of type int or smallint +HINT: If the command was issued from a worker node, try issuing it from the coordinator node instead. -- nextval from worker should work for bigint sequences SELECT nextval('seq_14'); nextval @@ -738,8 +742,12 @@ SELECT nextval('seq_12'); -- nextval from worker should fail for int and smallint sequences SELECT nextval('seq_13'); ERROR: nextval: reached maximum value of sequence "seq_13" (2147483647) +DETAIL: nextval(sequence) calls in worker nodes are not supported for column defaults of type int or smallint +HINT: If the command was issued from a worker node, try issuing it from the coordinator node instead. SELECT nextval('seq_14'); ERROR: nextval: reached maximum value of sequence "seq_14" (32767) +DETAIL: nextval(sequence) calls in worker nodes are not supported for column defaults of type int or smallint +HINT: If the command was issued from a worker node, try issuing it from the coordinator node instead. \c - - - :master_port SET citus.shard_replication_factor TO 1; SET search_path = sequence_default, public; @@ -804,8 +812,12 @@ SELECT nextval('seq_12'); -- nextval from worker should fail for int and smallint sequences SELECT nextval('seq_13'); ERROR: nextval: reached maximum value of sequence "seq_13" (2147483647) +DETAIL: nextval(sequence) calls in worker nodes are not supported for column defaults of type int or smallint +HINT: If the command was issued from a worker node, try issuing it from the coordinator node instead. SELECT nextval('seq_14'); ERROR: nextval: reached maximum value of sequence "seq_14" (32767) +DETAIL: nextval(sequence) calls in worker nodes are not supported for column defaults of type int or smallint +HINT: If the command was issued from a worker node, try issuing it from the coordinator node instead. \c - - - :master_port -- Show that sequence and its dependency schema will be propagated if a distributed -- table with default column is added diff --git a/src/test/regress/sql/create_single_shard_table.sql b/src/test/regress/sql/create_single_shard_table.sql index 0830aa7f8..00940778b 100644 --- a/src/test/regress/sql/create_single_shard_table.sql +++ b/src/test/regress/sql/create_single_shard_table.sql @@ -734,11 +734,13 @@ CREATE TABLE identity_test ( SELECT create_distributed_table('identity_test', NULL, distribution_type=>null); +INSERT INTO identity_test (a) VALUES (DEFAULT) RETURNING a; +SELECT result FROM run_command_on_workers($$ + INSERT INTO create_single_shard_table.identity_test (a) VALUES (DEFAULT) +$$); + DROP TABLE identity_test; --- Above failed because we don't support using a data type other than BIGINT --- for identity columns, so drop the table and create a new one with BIGINT --- identity columns. CREATE TABLE identity_test ( a bigint GENERATED BY DEFAULT AS IDENTITY (START WITH 10 INCREMENT BY 10), b bigint GENERATED ALWAYS AS IDENTITY (START WITH 100 INCREMENT BY 100), diff --git a/src/test/regress/sql/generated_identity.sql b/src/test/regress/sql/generated_identity.sql index c2980d0bd..40021f8d3 100644 --- a/src/test/regress/sql/generated_identity.sql +++ b/src/test/regress/sql/generated_identity.sql @@ -9,26 +9,78 @@ SET citus.shard_replication_factor TO 1; SELECT 1 from citus_add_node('localhost', :master_port, groupId=>0); --- smallint identity column can not be distributed CREATE TABLE smallint_identity_column ( a smallint GENERATED BY DEFAULT AS IDENTITY ); -SELECT create_distributed_table('smallint_identity_column', 'a'); + +CREATE VIEW verify_smallint_identity_column AS +SELECT attidentity, attgenerated FROM pg_attribute WHERE attrelid = 'smallint_identity_column'::regclass AND attname = 'a'; + +BEGIN; + SELECT create_distributed_table('smallint_identity_column', 'a'); + SELECT * FROM verify_smallint_identity_column; +ROLLBACK; + +BEGIN; + SELECT create_reference_table('smallint_identity_column'); + SELECT * FROM verify_smallint_identity_column; +ROLLBACK; + +BEGIN; + SELECT citus_add_local_table_to_metadata('smallint_identity_column'); + SELECT * FROM verify_smallint_identity_column; +ROLLBACK; + SELECT create_distributed_table_concurrently('smallint_identity_column', 'a'); -SELECT create_reference_table('smallint_identity_column'); -SELECT citus_add_local_table_to_metadata('smallint_identity_column'); +SELECT * FROM verify_smallint_identity_column; +SELECT result FROM run_command_on_workers('INSERT INTO generated_identities.smallint_identity_column (a) VALUES (DEFAULT);'); -DROP TABLE smallint_identity_column; +DROP TABLE smallint_identity_column CASCADE; --- int identity column can not be distributed CREATE TABLE int_identity_column ( a int GENERATED BY DEFAULT AS IDENTITY ); -SELECT create_distributed_table('int_identity_column', 'a'); + +CREATE VIEW verify_int_identity_column AS +SELECT attidentity, attgenerated FROM pg_attribute WHERE attrelid = 'int_identity_column'::regclass AND attname = 'a'; + +BEGIN; + SELECT create_distributed_table('int_identity_column', 'a'); + SELECT * FROM verify_int_identity_column; +ROLLBACK; + +BEGIN; + SELECT create_reference_table('int_identity_column'); + SELECT * FROM verify_int_identity_column; +ROLLBACK; + +BEGIN; + SELECT citus_add_local_table_to_metadata('int_identity_column'); + SELECT * FROM verify_int_identity_column; +ROLLBACK; + SELECT create_distributed_table_concurrently('int_identity_column', 'a'); -SELECT create_reference_table('int_identity_column'); -SELECT citus_add_local_table_to_metadata('int_identity_column'); -DROP TABLE int_identity_column; +SELECT * FROM verify_int_identity_column; +SELECT result FROM run_command_on_workers('INSERT INTO generated_identities.int_identity_column (a) VALUES (DEFAULT);'); + +DROP TABLE int_identity_column CASCADE; + +CREATE TABLE reference_int_identity_column ( + a int GENERATED BY DEFAULT AS IDENTITY +); +SELECT create_reference_table('reference_int_identity_column'); +INSERT INTO generated_identities.reference_int_identity_column (a) VALUES (DEFAULT) RETURNING a; +SELECT result FROM run_command_on_workers('INSERT INTO generated_identities.reference_int_identity_column (a) VALUES (DEFAULT);'); + +CREATE TABLE citus_local_int_identity_column ( + a int GENERATED BY DEFAULT AS IDENTITY +); +SELECT citus_add_local_table_to_metadata('citus_local_int_identity_column'); +INSERT INTO generated_identities.citus_local_int_identity_column (a) VALUES (DEFAULT) RETURNING a; +SELECT result FROM run_command_on_workers('INSERT INTO generated_identities.citus_local_int_identity_column (a) VALUES (DEFAULT);'); + +DROP TABLE reference_int_identity_column, citus_local_int_identity_column; + RESET citus.shard_replication_factor; From f4a90da8c8814115af5599b29b519ab9d1380c3a Mon Sep 17 00:00:00 2001 From: Pino de Candia <32303022+pinodeca@users.noreply.github.com> Date: Fri, 16 Jun 2023 08:34:11 -0500 Subject: [PATCH 101/118] Replace Slack heroku app with plain link in the Readme banner. (#6985) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6b6dc5664..ae67dadca 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ [![Latest Docs](https://img.shields.io/badge/docs-latest-brightgreen.svg)](https://docs.citusdata.com/) [![Stack Overflow](https://img.shields.io/badge/Stack%20Overflow-%20-545353?logo=Stack%20Overflow)](https://stackoverflow.com/questions/tagged/citus) -[![Slack Status](https://citus-slack.herokuapp.com/badge.svg)](https://citus-public.slack.com/) +[Slack](https://citus-public.slack.com/) [![Code Coverage](https://codecov.io/gh/citusdata/citus/branch/master/graph/badge.svg)](https://app.codecov.io/gh/citusdata/citus) [![Twitter](https://img.shields.io/twitter/follow/citusdata.svg?label=Follow%20@citusdata)](https://twitter.com/intent/follow?screen_name=citusdata) From 3adc1575d96545d09e9d3a9c6b5c88890e4b9a14 Mon Sep 17 00:00:00 2001 From: Marco Slot Date: Fri, 16 Jun 2023 15:54:37 +0200 Subject: [PATCH 102/118] Fix DROP CONSTRAINT in command string with other commands (#7012) Co-authored-by: Marco Slot --- src/backend/distributed/commands/table.c | 17 +++++++--- .../deparser/deparse_table_stmts.c | 32 +++++++++++++++++++ .../expected/create_single_shard_table.out | 19 ++++++++++- .../regress/sql/create_single_shard_table.sql | 16 +++++++++- 4 files changed, 78 insertions(+), 6 deletions(-) diff --git a/src/backend/distributed/commands/table.c b/src/backend/distributed/commands/table.c index 24c4e49fb..a5e997969 100644 --- a/src/backend/distributed/commands/table.c +++ b/src/backend/distributed/commands/table.c @@ -1382,6 +1382,16 @@ PreprocessAlterTableStmt(Node *node, const char *alterTableCommand, constraintName, missingOk); rightRelationId = GetReferencedTableId(foreignKeyId); } + + /* + * We support deparsing for DROP CONSTRAINT, but currently deparsing is only + * possible if all subcommands are supported. + */ + if (list_length(commandList) == 1 && + alterTableStatement->objtype == OBJECT_TABLE) + { + deparseAT = true; + } } else if (alterTableType == AT_AddColumn) { @@ -1589,11 +1599,10 @@ PreprocessAlterTableStmt(Node *node, const char *alterTableCommand, DDLJob *ddlJob = palloc0(sizeof(DDLJob)); ObjectAddressSet(ddlJob->targetObjectAddress, RelationRelationId, leftRelationId); - const char *sqlForTaskList = alterTableCommand; if (deparseAT) { newStmt->cmds = list_make1(newCmd); - sqlForTaskList = DeparseTreeNode((Node *) newStmt); + alterTableCommand = DeparseTreeNode((Node *) newStmt); } ddlJob->metadataSyncCommand = useInitialDDLCommandString ? alterTableCommand : NULL; @@ -1609,13 +1618,13 @@ PreprocessAlterTableStmt(Node *node, const char *alterTableCommand, { /* if foreign key or attaching partition index related, use specialized task list function ... */ ddlJob->taskList = InterShardDDLTaskList(leftRelationId, rightRelationId, - sqlForTaskList); + alterTableCommand); } } else { /* ... otherwise use standard DDL task list function */ - ddlJob->taskList = DDLTaskList(leftRelationId, sqlForTaskList); + ddlJob->taskList = DDLTaskList(leftRelationId, alterTableCommand); if (!propagateCommandToWorkers) { ddlJob->taskList = NIL; diff --git a/src/backend/distributed/deparser/deparse_table_stmts.c b/src/backend/distributed/deparser/deparse_table_stmts.c index 9a8cdedef..6e0dd3f06 100644 --- a/src/backend/distributed/deparser/deparse_table_stmts.c +++ b/src/backend/distributed/deparser/deparse_table_stmts.c @@ -31,6 +31,8 @@ static void AppendAlterTableStmt(StringInfo buf, AlterTableStmt *stmt); static void AppendAlterTableCmd(StringInfo buf, AlterTableCmd *alterTableCmd, AlterTableStmt *stmt); static void AppendAlterTableCmdAddColumn(StringInfo buf, AlterTableCmd *alterTableCmd); +static void AppendAlterTableCmdDropConstraint(StringInfo buf, + AlterTableCmd *alterTableCmd); char * DeparseAlterTableSchemaStmt(Node *node) @@ -411,6 +413,12 @@ AppendAlterTableCmd(StringInfo buf, AlterTableCmd *alterTableCmd, AlterTableStmt break; } + case AT_DropConstraint: + { + AppendAlterTableCmdDropConstraint(buf, alterTableCmd); + break; + } + case AT_AddConstraint: { Constraint *constraint = (Constraint *) alterTableCmd->def; @@ -488,3 +496,27 @@ AppendAlterTableCmdAddColumn(StringInfo buf, AlterTableCmd *alterTableCmd) appendStringInfo(buf, " COLLATE %s", identifier); } } + + +/* + * AppendAlterTableCmdDropConstraint builds and appends to the given buffer an + * AT_DropConstraint command from given AlterTableCmd object in the form + * DROP CONSTRAINT ... + */ +static void +AppendAlterTableCmdDropConstraint(StringInfo buf, AlterTableCmd *alterTableCmd) +{ + appendStringInfoString(buf, " DROP CONSTRAINT"); + + if (alterTableCmd->missing_ok) + { + appendStringInfoString(buf, " IF EXISTS"); + } + + appendStringInfo(buf, " %s", quote_identifier(alterTableCmd->name)); + + if (alterTableCmd->behavior == DROP_CASCADE) + { + appendStringInfoString(buf, " CASCADE"); + } +} diff --git a/src/test/regress/expected/create_single_shard_table.out b/src/test/regress/expected/create_single_shard_table.out index dab58af25..248f196ff 100644 --- a/src/test/regress/expected/create_single_shard_table.out +++ b/src/test/regress/expected/create_single_shard_table.out @@ -989,7 +989,24 @@ ALTER TABLE null_key_dist ADD CONSTRAINT fkey_add_test_4 FOREIGN KEY(a) ALTER TABLE null_key_dist DROP CONSTRAINT fkey_add_test_1; ALTER TABLE null_key_dist DROP CONSTRAINT fkey_add_test_2; ALTER TABLE null_key_dist DROP CONSTRAINT fkey_add_test_3; -ALTER TABLE null_key_dist DROP CONSTRAINT fkey_add_test_4; +-- mimic a conjoined django command +CREATE FUNCTION drop_fkey_4() + RETURNS void + LANGUAGE plpgsql +AS $function$ +BEGIN + EXECUTE $$ + SET CONSTRAINTS fkey_add_test_4 IMMEDIATE; + ALTER TABLE null_key_dist DROP CONSTRAINT fkey_add_test_4; + $$; +END +$function$; +SELECT drop_fkey_4(); + drop_fkey_4 +--------------------------------------------------------------------- + +(1 row) + ALTER TABLE "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" DROP CONSTRAINT fkey_to_dummy_dist; -- create a view that depends on the single-shard table CREATE VIEW public.v1 AS SELECT * FROM null_key_dist; diff --git a/src/test/regress/sql/create_single_shard_table.sql b/src/test/regress/sql/create_single_shard_table.sql index 00940778b..8df785429 100644 --- a/src/test/regress/sql/create_single_shard_table.sql +++ b/src/test/regress/sql/create_single_shard_table.sql @@ -677,7 +677,21 @@ ALTER TABLE null_key_dist ADD CONSTRAINT fkey_add_test_4 FOREIGN KEY(a) ALTER TABLE null_key_dist DROP CONSTRAINT fkey_add_test_1; ALTER TABLE null_key_dist DROP CONSTRAINT fkey_add_test_2; ALTER TABLE null_key_dist DROP CONSTRAINT fkey_add_test_3; -ALTER TABLE null_key_dist DROP CONSTRAINT fkey_add_test_4; + +-- mimic a conjoined django command +CREATE FUNCTION drop_fkey_4() + RETURNS void + LANGUAGE plpgsql +AS $function$ +BEGIN + EXECUTE $$ + SET CONSTRAINTS fkey_add_test_4 IMMEDIATE; + ALTER TABLE null_key_dist DROP CONSTRAINT fkey_add_test_4; + $$; +END +$function$; +SELECT drop_fkey_4(); + ALTER TABLE "NULL_!_dist_key"."nullKeyTable.1!?!9012345678901234567890123456789012345678901234567890123456789" DROP CONSTRAINT fkey_to_dummy_dist; -- create a view that depends on the single-shard table From ce2ba1d07eb7b8c8563e341445cc3414d474d687 Mon Sep 17 00:00:00 2001 From: Nils Dijk Date: Fri, 16 Jun 2023 16:06:22 +0200 Subject: [PATCH 103/118] Optimize QueryPushdownSqlTaskList on memory and cpu (#6945) While going over this piece of code (a long time ago) it was bothering to me we keep a bool array with the size of shardcount to iterate only over shards present in the list of non-pruned shards. Especially since we keep min/max of the set shards to optimize iteration. Postgres has the bitmapset datastructure which a) takes significantly less space, b) has iterator functions to only iterate over set bits, c) can efficiently skip long sequences of unset bits and d) stops quickly once the last set bit has been reached. I have been contemplating if it is worth to keep the minShardOffset because of readability and the efficient skipping of unset bits, however, I have decided to keep it -although less readable-, as there are known usecases where 100k+ shards are pruned to single digit shards. If these would end up at the end of `shardcount` a hotloop of zero checks on the first iteration _could_ cause a theoretical performance regression. All in all, this code is using less memory in all cases where it matters, and less cpu in most cases, while using more idiomatic datastructures for the task at hand. --- .../planner/multi_physical_planner.c | 52 ++++++------------- 1 file changed, 17 insertions(+), 35 deletions(-) diff --git a/src/backend/distributed/planner/multi_physical_planner.c b/src/backend/distributed/planner/multi_physical_planner.c index f0cb3122c..21befa6f2 100644 --- a/src/backend/distributed/planner/multi_physical_planner.c +++ b/src/backend/distributed/planner/multi_physical_planner.c @@ -2179,8 +2179,9 @@ QueryPushdownSqlTaskList(Query *query, uint64 jobId, { List *sqlTaskList = NIL; uint32 taskIdIndex = 1; /* 0 is reserved for invalid taskId */ - int shardCount = 0; - bool *taskRequiredForShardIndex = NULL; + int minShardOffset = INT_MAX; + int prevShardCount = 0; + Bitmapset *taskRequiredForShardIndex = NULL; /* error if shards are not co-partitioned */ ErrorIfUnsupportedShardDistribution(query); @@ -2194,10 +2195,6 @@ QueryPushdownSqlTaskList(Query *query, uint64 jobId, return NIL; } - /* defaults to be used if this is a reference table-only query */ - int minShardOffset = 0; - int maxShardOffset = 0; - RelationRestriction *relationRestriction = NULL; List *prunedShardList = NULL; @@ -2213,7 +2210,7 @@ QueryPushdownSqlTaskList(Query *query, uint64 jobId, } /* we expect distributed tables to have the same shard count */ - if (shardCount > 0 && shardCount != cacheEntry->shardIntervalArrayLength) + if (prevShardCount > 0 && prevShardCount != cacheEntry->shardIntervalArrayLength) { *planningError = DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, "shard counts of co-located tables do not " @@ -2221,16 +2218,7 @@ QueryPushdownSqlTaskList(Query *query, uint64 jobId, NULL, NULL); return NIL; } - - if (taskRequiredForShardIndex == NULL) - { - shardCount = cacheEntry->shardIntervalArrayLength; - taskRequiredForShardIndex = (bool *) palloc0(shardCount); - - /* there is a distributed table, find the shard range */ - minShardOffset = shardCount; - maxShardOffset = -1; - } + prevShardCount = cacheEntry->shardIntervalArrayLength; /* * For left joins we don't care about the shards pruned for the right hand side. @@ -2252,32 +2240,26 @@ QueryPushdownSqlTaskList(Query *query, uint64 jobId, { int shardIndex = shardInterval->shardIndex; - taskRequiredForShardIndex[shardIndex] = true; - + taskRequiredForShardIndex = + bms_add_member(taskRequiredForShardIndex, shardIndex); minShardOffset = Min(minShardOffset, shardIndex); - maxShardOffset = Max(maxShardOffset, shardIndex); } } /* - * To avoid iterating through all shards indexes we keep the minimum and maximum - * offsets of shards that were not pruned away. This optimisation is primarily - * relevant for queries on range-distributed tables that, due to range filters, - * prune to a small number of adjacent shards. + * We keep track of minShardOffset to skip over a potentially big amount of pruned + * shards. However, we need to start at minShardOffset - 1 to make sure we don't + * miss to first/min shard recorder as bms_next_member will return the first member + * added after shardOffset. Meaning minShardOffset would be the first member we + * expect. * - * In other cases, such as an OR condition on a hash-distributed table, we may - * still visit most or all shards even if some of them were pruned away. However, - * given that hash-distributed tables typically only have a few shards the - * iteration is still very fast. + * We don't have to keep track of maxShardOffset as the bitmapset will only have been + * allocated till the last shard we have added. Therefore, the iterator will quickly + * identify the end of the bitmapset. */ - for (int shardOffset = minShardOffset; shardOffset <= maxShardOffset; shardOffset++) + int shardOffset = minShardOffset - 1; + while ((shardOffset = bms_next_member(taskRequiredForShardIndex, shardOffset)) >= 0) { - if (taskRequiredForShardIndex != NULL && !taskRequiredForShardIndex[shardOffset]) - { - /* this shard index is pruned away for all relations */ - continue; - } - Task *subqueryTask = QueryPushdownTaskCreate(query, shardOffset, relationRestrictionContext, taskIdIndex, From fba5c8dd304c991b29c8877b406b2e119657452c Mon Sep 17 00:00:00 2001 From: aykut-bozkurt <51649454+aykut-bozkurt@users.noreply.github.com> Date: Mon, 19 Jun 2023 10:21:13 +0300 Subject: [PATCH 104/118] ALTER TABLE SET SCHEMA for single shard tables (#7004) Adds support for altering schema of single shard tables. We do that in 2 steps. 1. Undistribute the tenant table at `preprocess` step, 2. Distribute new schema if it is a distributed schema after DDLs are propagated. DESCRIPTION: Adds support for altering a table's schema to/from distributed schemas. --- .../distributed/commands/alter_table.c | 65 +++++++- ..._table_operation_for_connected_relations.c | 3 +- .../distributed/commands/foreign_constraint.c | 4 + .../commands/schema_based_sharding.c | 106 ++++++------ src/backend/distributed/commands/table.c | 108 ++++++++++++- .../distributed/commands/utility_hook.c | 15 +- .../distributed/utils/colocation_utils.c | 5 +- src/include/distributed/commands.h | 26 ++- src/include/distributed/metadata_utility.h | 7 + .../citus_schema_distribute_undistribute.out | 16 +- .../expected/schema_based_sharding.out | 152 ++++++++++++++++-- .../regress/sql/schema_based_sharding.sql | 98 ++++++++++- 12 files changed, 508 insertions(+), 97 deletions(-) diff --git a/src/backend/distributed/commands/alter_table.c b/src/backend/distributed/commands/alter_table.c index d574e997a..5fa670ab2 100644 --- a/src/backend/distributed/commands/alter_table.c +++ b/src/backend/distributed/commands/alter_table.c @@ -248,7 +248,8 @@ undistribute_table(PG_FUNCTION_ARGS) TableConversionParameters params = { .relationId = relationId, - .cascadeViaForeignKeys = cascadeViaForeignKeys + .cascadeViaForeignKeys = cascadeViaForeignKeys, + .bypassTenantCheck = false }; UndistributeTable(¶ms); @@ -429,6 +430,55 @@ UndistributeTables(List *relationIdList) } +/* + * EnsureUndistributeTenantTableSafe ensures that it is safe to undistribute a tenant table. + */ +void +EnsureUndistributeTenantTableSafe(Oid relationId, const char *operationName) +{ + Oid schemaId = get_rel_namespace(relationId); + Assert(IsTenantSchema(schemaId)); + + /* We only allow undistribute while altering schema */ + if (strcmp(operationName, TenantOperationNames[TENANT_SET_SCHEMA]) != 0) + { + ErrorIfTenantTable(relationId, operationName); + } + + char *tableName = get_rel_name(relationId); + char *schemaName = get_namespace_name(schemaId); + + /* + * Partition table cannot be undistributed. Otherwise, its parent table would still + * be a tenant table whereas partition table would be a local table. + */ + if (PartitionTable(relationId)) + { + ereport(ERROR, (errmsg("%s is not allowed for partition table %s in distributed " + "schema %s", operationName, tableName, schemaName), + errdetail("partition table should be under the same distributed " + "schema as its parent and be a tenant table."))); + } + + /* + * When table is referenced by or referencing to a table in the same tenant + * schema, we should disallow undistributing the table since we do not allow + * foreign keys from/to Citus local or Postgres local table to/from distributed + * schema. + */ + List *fkeyCommandsWithSingleShardTables = + GetFKeyCreationCommandsRelationInvolvedWithTableType( + relationId, INCLUDE_SINGLE_SHARD_TABLES); + if (fkeyCommandsWithSingleShardTables != NIL) + { + ereport(ERROR, (errmsg("%s is not allowed for table %s in distributed schema %s", + operationName, tableName, schemaName), + errdetail("distributed schemas cannot have foreign keys from/to " + "local tables or different schema"))); + } +} + + /* * UndistributeTable undistributes the given table. It uses ConvertTable function to * create a new local table and move everything to that table. @@ -449,7 +499,13 @@ UndistributeTable(TableConversionParameters *params) "because the table is not distributed"))); } - ErrorIfTenantTable(params->relationId, "undistribute_table"); + Oid schemaId = get_rel_namespace(params->relationId); + if (!params->bypassTenantCheck && IsTenantSchema(schemaId) && + IsCitusTableType(params->relationId, SINGLE_SHARD_DISTRIBUTED)) + { + EnsureUndistributeTenantTableSafe(params->relationId, + TenantOperationNames[TENANT_UNDISTRIBUTE_TABLE]); + } if (!params->cascadeViaForeignKeys) { @@ -506,7 +562,7 @@ AlterDistributedTable(TableConversionParameters *params) "is not distributed"))); } - ErrorIfTenantTable(params->relationId, "alter_distributed_table"); + ErrorIfTenantTable(params->relationId, TenantOperationNames[TENANT_ALTER_TABLE]); ErrorIfColocateWithTenantTable(params->colocateWith); EnsureTableNotForeign(params->relationId); @@ -1267,7 +1323,8 @@ ErrorIfColocateWithTenantTable(char *colocateWith) { text *colocateWithTableNameText = cstring_to_text(colocateWith); Oid colocateWithTableId = ResolveRelationId(colocateWithTableNameText, false); - ErrorIfTenantTable(colocateWithTableId, "colocate_with"); + ErrorIfTenantTable(colocateWithTableId, + TenantOperationNames[TENANT_COLOCATE_WITH]); } } diff --git a/src/backend/distributed/commands/cascade_table_operation_for_connected_relations.c b/src/backend/distributed/commands/cascade_table_operation_for_connected_relations.c index 1c01028d3..1102a3a51 100644 --- a/src/backend/distributed/commands/cascade_table_operation_for_connected_relations.c +++ b/src/backend/distributed/commands/cascade_table_operation_for_connected_relations.c @@ -468,7 +468,8 @@ ExecuteCascadeOperationForRelationIdList(List *relationIdList, { TableConversionParameters params = { .relationId = relationId, - .cascadeViaForeignKeys = cascadeViaForeignKeys + .cascadeViaForeignKeys = cascadeViaForeignKeys, + .bypassTenantCheck = false }; UndistributeTable(¶ms); } diff --git a/src/backend/distributed/commands/foreign_constraint.c b/src/backend/distributed/commands/foreign_constraint.c index b48b6c54a..40ccb0ddf 100644 --- a/src/backend/distributed/commands/foreign_constraint.c +++ b/src/backend/distributed/commands/foreign_constraint.c @@ -1356,6 +1356,10 @@ IsTableTypeIncluded(Oid relationId, int flags) { return (flags & INCLUDE_LOCAL_TABLES) != 0; } + else if (IsCitusTableType(relationId, SINGLE_SHARD_DISTRIBUTED)) + { + return (flags & INCLUDE_SINGLE_SHARD_TABLES) != 0; + } else if (IsCitusTableType(relationId, DISTRIBUTED_TABLE)) { return (flags & INCLUDE_DISTRIBUTED_TABLES) != 0; diff --git a/src/backend/distributed/commands/schema_based_sharding.c b/src/backend/distributed/commands/schema_based_sharding.c index 91bae0943..b717cb5ae 100644 --- a/src/backend/distributed/commands/schema_based_sharding.c +++ b/src/backend/distributed/commands/schema_based_sharding.c @@ -40,6 +40,14 @@ static void EnsureSchemaExist(Oid schemaId); /* controlled via citus.enable_schema_based_sharding GUC */ bool EnableSchemaBasedSharding = false; +const char *TenantOperationNames[TOTAL_TENANT_OPERATION] = { + "undistribute_table", + "alter_distributed_table", + "colocate_with", + "update_distributed_table_colocation", + "set schema", +}; + PG_FUNCTION_INFO_V1(citus_internal_unregister_tenant_schema_globally); PG_FUNCTION_INFO_V1(citus_schema_distribute); @@ -374,12 +382,7 @@ SchemaGetNonShardTableIdList(Oid schemaId) * - Schema name is in the allowed-list, * - Schema does not depend on an extension (created by extension), * - No extension depends on the schema (CREATE EXTENSION SCHEMA ), - * - Current user should be the owner of tables under the schema, - * - Table kinds are supported, - * - Referencing and referenced foreign keys for the tables under the schema are - * supported, - * - Tables under the schema are not owned by an extension, - * - Only Citus local and Postgres local tables exist under the schema. + * - Some checks for the table for being a valid tenant table. */ static void EnsureSchemaCanBeDistributed(Oid schemaId, List *schemaTableIdList) @@ -409,39 +412,55 @@ EnsureSchemaCanBeDistributed(Oid schemaId, List *schemaTableIdList) Oid relationId = InvalidOid; foreach_oid(relationId, schemaTableIdList) { - /* Ensure table owner */ - EnsureTableOwner(relationId); + EnsureTenantTable(relationId, "citus_schema_distribute"); + } +} - /* Check relation kind */ - EnsureTableKindSupportedForTenantSchema(relationId); - /* Check foreign keys */ - EnsureFKeysForTenantTable(relationId); +/* + * EnsureTenantTable ensures the table can be a valid tenant table. + * - Current user should be the owner of table, + * - Table kind is supported, + * - Referencing and referenced foreign keys for the table are supported, + * - Table is not owned by an extension, + * - Table should be Citus local or Postgres local table. + */ +void +EnsureTenantTable(Oid relationId, char *operationName) +{ + /* Ensure table owner */ + EnsureTableOwner(relationId); - /* Check table not owned by an extension */ - ObjectAddress *tableAddress = palloc0(sizeof(ObjectAddress)); - ObjectAddressSet(*tableAddress, RelationRelationId, relationId); - if (IsAnyObjectAddressOwnedByExtension(list_make1(tableAddress), NULL)) - { - char *tableName = get_namespace_name(schemaId); - ereport(ERROR, (errmsg("schema cannot be distributed since it has " - "table %s which is owned by an extension", - tableName))); - } + /* Check relation kind */ + EnsureTableKindSupportedForTenantSchema(relationId); - /* Postgres local tables are allowed */ - if (!IsCitusTable(relationId)) - { - continue; - } + /* Check foreign keys */ + EnsureFKeysForTenantTable(relationId); - /* Only Citus local tables, amongst Citus table types, are allowed */ - if (!IsCitusTableType(relationId, CITUS_LOCAL_TABLE)) - { - ereport(ERROR, (errmsg("schema already has distributed tables"), - errhint("Undistribute distributed tables under " - "the schema before distributing the schema."))); - } + /* Check table not owned by an extension */ + ObjectAddress *tableAddress = palloc0(sizeof(ObjectAddress)); + ObjectAddressSet(*tableAddress, RelationRelationId, relationId); + if (IsAnyObjectAddressOwnedByExtension(list_make1(tableAddress), NULL)) + { + Oid schemaId = get_rel_namespace(relationId); + char *tableName = get_namespace_name(schemaId); + ereport(ERROR, (errmsg("schema cannot be distributed since it has " + "table %s which is owned by an extension", + tableName))); + } + + /* Postgres local tables are allowed */ + if (!IsCitusTable(relationId)) + { + return; + } + + /* Only Citus local tables, amongst Citus table types, are allowed */ + if (!IsCitusTableType(relationId, CITUS_LOCAL_TABLE)) + { + ereport(ERROR, (errmsg("distributed schema cannot have distributed tables"), + errhint("Undistribute distributed tables before " + "'%s'.", operationName))); } } @@ -743,7 +762,7 @@ citus_schema_undistribute(PG_FUNCTION_ARGS) * if the given relation is a tenant table. */ void -ErrorIfTenantTable(Oid relationId, char *operationName) +ErrorIfTenantTable(Oid relationId, const char *operationName) { if (IsTenantSchema(get_rel_namespace(relationId))) { @@ -753,20 +772,3 @@ ErrorIfTenantTable(Oid relationId, char *operationName) operationName))); } } - - -/* - * ErrorIfTenantSchema errors out with the given operation name, - * if the given schema is a tenant schema. - */ -void -ErrorIfTenantSchema(Oid nspOid, char *operationName) -{ - if (IsTenantSchema(nspOid)) - { - ereport(ERROR, (errmsg( - "%s is not allowed for %s because it is a distributed schema", - get_namespace_name(nspOid), - operationName))); - } -} diff --git a/src/backend/distributed/commands/table.c b/src/backend/distributed/commands/table.c index a5e997969..4ea28c71d 100644 --- a/src/backend/distributed/commands/table.c +++ b/src/backend/distributed/commands/table.c @@ -41,6 +41,7 @@ #include "distributed/resource_lock.h" #include "distributed/version_compat.h" #include "distributed/worker_shard_visibility.h" +#include "distributed/tenant_schema_metadata.h" #include "foreign/foreign.h" #include "lib/stringinfo.h" #include "nodes/parsenodes.h" @@ -2310,9 +2311,52 @@ PreprocessAlterTableSchemaStmt(Node *node, const char *queryString, return NIL; } - ErrorIfTenantTable(relationId, "ALTER TABLE SET SCHEMA"); - ErrorIfTenantSchema(get_namespace_oid(stmt->newschema, false), - "ALTER TABLE SET SCHEMA"); + Oid oldSchemaId = get_rel_namespace(relationId); + Oid newSchemaId = get_namespace_oid(stmt->newschema, stmt->missing_ok); + if (!OidIsValid(oldSchemaId) || !OidIsValid(newSchemaId)) + { + return NIL; + } + + /* Do nothing if new schema is the same as old schema */ + if (newSchemaId == oldSchemaId) + { + return NIL; + } + + /* Undistribute table if its old schema is a tenant schema */ + if (IsTenantSchema(oldSchemaId) && IsCoordinator()) + { + EnsureUndistributeTenantTableSafe(relationId, + TenantOperationNames[TENANT_SET_SCHEMA]); + + char *oldSchemaName = get_namespace_name(oldSchemaId); + char *tableName = stmt->relation->relname; + ereport(NOTICE, (errmsg("undistributing table %s in distributed schema %s " + "before altering its schema", tableName, oldSchemaName))); + + /* Undistribute tenant table by suppressing weird notices */ + TableConversionParameters params = { + .relationId = relationId, + .cascadeViaForeignKeys = false, + .bypassTenantCheck = true, + .suppressNoticeMessages = true, + }; + UndistributeTable(¶ms); + + /* relation id changes after undistribute_table */ + relationId = get_relname_relid(tableName, oldSchemaId); + + /* + * After undistribution, the table could be Citus table or Postgres table. + * If it is Postgres table, do not propagate the `ALTER TABLE SET SCHEMA` + * command to workers. + */ + if (!IsCitusTable(relationId)) + { + return NIL; + } + } DDLJob *ddlJob = palloc0(sizeof(DDLJob)); QualifyTreeNode((Node *) stmt); @@ -4166,3 +4210,61 @@ ConvertNewTableIfNecessary(Node *createStmt) CreateCitusLocalTable(createdRelationId, cascade, autoConverted); } } + + +/* + * ConvertToTenantTableIfNecessary converts given relation to a tenant table if its + * schema changed to a distributed schema. + */ +void +ConvertToTenantTableIfNecessary(AlterObjectSchemaStmt *stmt) +{ + Assert(stmt->objectType == OBJECT_TABLE || stmt->objectType == OBJECT_FOREIGN_TABLE); + + if (!IsCoordinator()) + { + return; + } + + /* + * We will let Postgres deal with missing_ok + */ + List *tableAddresses = GetObjectAddressListFromParseTree((Node *) stmt, true, true); + + /* the code-path only supports a single object */ + Assert(list_length(tableAddresses) == 1); + + /* We have already asserted that we have exactly 1 address in the addresses. */ + ObjectAddress *tableAddress = linitial(tableAddresses); + char relKind = get_rel_relkind(tableAddress->objectId); + if (relKind == RELKIND_SEQUENCE || relKind == RELKIND_VIEW) + { + return; + } + + Oid relationId = tableAddress->objectId; + Oid schemaId = get_namespace_oid(stmt->newschema, stmt->missing_ok); + if (!OidIsValid(schemaId)) + { + return; + } + + /* + * Make table a tenant table when its schema actually changed. When its schema + * is not changed as in `ALTER TABLE SET SCHEMA `, we detect + * that by seeing the table is still a single shard table. (i.e. not undistributed + * at `preprocess` step) + */ + if (!IsCitusTableType(relationId, SINGLE_SHARD_DISTRIBUTED) && + IsTenantSchema(schemaId)) + { + EnsureTenantTable(relationId, "ALTER TABLE SET SCHEMA"); + + char *schemaName = get_namespace_name(schemaId); + char *tableName = stmt->relation->relname; + ereport(NOTICE, (errmsg("converting table %s to a tenant table in distributed " + "schema %s", tableName, schemaName))); + + CreateTenantSchemaTable(relationId); + } +} diff --git a/src/backend/distributed/commands/utility_hook.c b/src/backend/distributed/commands/utility_hook.c index 6393fdd71..ad2f5de1b 100644 --- a/src/backend/distributed/commands/utility_hook.c +++ b/src/backend/distributed/commands/utility_hook.c @@ -370,6 +370,18 @@ multi_ProcessUtility(PlannedStmt *pstmt, ConvertNewTableIfNecessary(createStmt); } + + if (context == PROCESS_UTILITY_TOPLEVEL && + IsA(parsetree, AlterObjectSchemaStmt)) + { + AlterObjectSchemaStmt *alterSchemaStmt = castNode(AlterObjectSchemaStmt, + parsetree); + if (alterSchemaStmt->objectType == OBJECT_TABLE || + alterSchemaStmt->objectType == OBJECT_FOREIGN_TABLE) + { + ConvertToTenantTableIfNecessary(alterSchemaStmt); + } + } } UtilityHookLevel--; @@ -999,7 +1011,8 @@ UndistributeDisconnectedCitusLocalTables(void) TableConversionParameters params = { .relationId = citusLocalTableId, .cascadeViaForeignKeys = true, - .suppressNoticeMessages = true + .suppressNoticeMessages = true, + .bypassTenantCheck = false }; UndistributeTable(¶ms); } diff --git a/src/backend/distributed/utils/colocation_utils.c b/src/backend/distributed/utils/colocation_utils.c index dba791ba4..62a13af3a 100644 --- a/src/backend/distributed/utils/colocation_utils.c +++ b/src/backend/distributed/utils/colocation_utils.c @@ -116,7 +116,7 @@ update_distributed_table_colocation(PG_FUNCTION_ARGS) text *colocateWithTableNameText = PG_GETARG_TEXT_P(1); EnsureTableOwner(targetRelationId); - ErrorIfTenantTable(targetRelationId, "update_distributed_table_colocation"); + ErrorIfTenantTable(targetRelationId, TenantOperationNames[TENANT_UPDATE_COLOCATION]); char *colocateWithTableName = text_to_cstring(colocateWithTableNameText); if (IsColocateWithNone(colocateWithTableName)) @@ -127,7 +127,8 @@ update_distributed_table_colocation(PG_FUNCTION_ARGS) else { Oid colocateWithTableId = ResolveRelationId(colocateWithTableNameText, false); - ErrorIfTenantTable(colocateWithTableId, "colocate_with"); + ErrorIfTenantTable(colocateWithTableId, + TenantOperationNames[TENANT_COLOCATE_WITH]); EnsureTableOwner(colocateWithTableId); MarkTablesColocated(colocateWithTableId, targetRelationId); } diff --git a/src/include/distributed/commands.h b/src/include/distributed/commands.h index 442e58058..a013f3977 100644 --- a/src/include/distributed/commands.h +++ b/src/include/distributed/commands.h @@ -118,7 +118,7 @@ typedef enum ExtractForeignKeyConstraintsMode /* exclude the self-referencing foreign keys */ EXCLUDE_SELF_REFERENCES = 1 << 2, - /* any combination of the 4 flags below is supported */ + /* any combination of the 5 flags below is supported */ /* include foreign keys when the other table is a distributed table*/ INCLUDE_DISTRIBUTED_TABLES = 1 << 3, @@ -131,9 +131,13 @@ typedef enum ExtractForeignKeyConstraintsMode /* include foreign keys when the other table is a Postgres local table*/ INCLUDE_LOCAL_TABLES = 1 << 6, + /* include foreign keys when the other table is a single shard table*/ + INCLUDE_SINGLE_SHARD_TABLES = 1 << 7, + /* include foreign keys regardless of the other table's type */ INCLUDE_ALL_TABLE_TYPES = INCLUDE_DISTRIBUTED_TABLES | INCLUDE_REFERENCE_TABLES | - INCLUDE_CITUS_LOCAL_TABLES | INCLUDE_LOCAL_TABLES + INCLUDE_CITUS_LOCAL_TABLES | INCLUDE_LOCAL_TABLES | + INCLUDE_SINGLE_SHARD_TABLES } ExtractForeignKeyConstraintMode; @@ -155,6 +159,19 @@ typedef enum SearchForeignKeyColumnFlags /* callers can also pass union of above flags */ } SearchForeignKeyColumnFlags; + +typedef enum TenantOperation +{ + TENANT_UNDISTRIBUTE_TABLE = 0, + TENANT_ALTER_TABLE, + TENANT_COLOCATE_WITH, + TENANT_UPDATE_COLOCATION, + TENANT_SET_SCHEMA, +} TenantOperation; + +#define TOTAL_TENANT_OPERATION 5 +extern const char *TenantOperationNames[TOTAL_TENANT_OPERATION]; + /* begin.c - forward declarations */ extern void SaveBeginCommandProperties(TransactionStmt *transactionStmt); @@ -593,6 +610,7 @@ extern char * GetAlterColumnWithNextvalDefaultCmd(Oid sequenceOid, Oid relationI extern void ErrorIfTableHasIdentityColumn(Oid relationId); extern void ConvertNewTableIfNecessary(Node *createStmt); +extern void ConvertToTenantTableIfNecessary(AlterObjectSchemaStmt *alterObjectSchemaStmt); /* text_search.c - forward declarations */ extern List * GetCreateTextSearchConfigStatements(const ObjectAddress *address); @@ -792,11 +810,11 @@ extern void UpdateAutoConvertedForConnectedRelations(List *relationId, bool extern bool ShouldUseSchemaBasedSharding(char *schemaName); extern bool ShouldCreateTenantSchemaTable(Oid relationId); extern bool IsTenantSchema(Oid schemaId); +extern void EnsureTenantTable(Oid relationId, char *operationName); extern void ErrorIfIllegalPartitioningInTenantSchema(Oid parentRelationId, Oid partitionRelationId); extern void CreateTenantSchemaTable(Oid relationId); -extern void ErrorIfTenantTable(Oid relationId, char *operationName); -extern void ErrorIfTenantSchema(Oid nspOid, char *operationName); +extern void ErrorIfTenantTable(Oid relationId, const char *operationName); extern uint32 CreateTenantSchemaColocationId(void); #endif /*CITUS_COMMANDS_H */ diff --git a/src/include/distributed/metadata_utility.h b/src/include/distributed/metadata_utility.h index 473d105e8..6536e89bc 100644 --- a/src/include/distributed/metadata_utility.h +++ b/src/include/distributed/metadata_utility.h @@ -172,6 +172,12 @@ typedef struct TableConversionParameters * messages that we explicitly issue */ bool suppressNoticeMessages; + + /* + * bypassTenantCheck skips tenant table checks to allow some internal + * operations which are normally disallowed + */ + bool bypassTenantCheck; } TableConversionParameters; typedef struct TableConversionReturn @@ -363,6 +369,7 @@ extern void CreateDistributedTable(Oid relationId, char *distributionColumnName, bool shardCountIsStrict, char *colocateWithTableName); extern void CreateReferenceTable(Oid relationId); extern void CreateTruncateTrigger(Oid relationId); +extern void EnsureUndistributeTenantTableSafe(Oid relationId, const char *operationName); extern TableConversionReturn * UndistributeTable(TableConversionParameters *params); extern void UndistributeTables(List *relationIdList); diff --git a/src/test/regress/expected/citus_schema_distribute_undistribute.out b/src/test/regress/expected/citus_schema_distribute_undistribute.out index 8e6803b6f..ae08b6c6a 100644 --- a/src/test/regress/expected/citus_schema_distribute_undistribute.out +++ b/src/test/regress/expected/citus_schema_distribute_undistribute.out @@ -482,8 +482,8 @@ SELECT create_distributed_table('tenant1.dist', 'id'); (1 row) SELECT citus_schema_distribute('tenant1'); -ERROR: schema already has distributed tables -HINT: Undistribute distributed tables under the schema before distributing the schema. +ERROR: distributed schema cannot have distributed tables +HINT: Undistribute distributed tables before 'citus_schema_distribute'. SELECT undistribute_table('tenant1.dist'); undistribute_table --------------------------------------------------------------------- @@ -510,8 +510,8 @@ SELECT create_reference_table('tenant1.ref2'); (1 row) SELECT citus_schema_distribute('tenant1'); -ERROR: schema already has distributed tables -HINT: Undistribute distributed tables under the schema before distributing the schema. +ERROR: distributed schema cannot have distributed tables +HINT: Undistribute distributed tables before 'citus_schema_distribute'. SELECT undistribute_table('tenant1.ref2'); undistribute_table --------------------------------------------------------------------- @@ -766,8 +766,8 @@ SELECT create_distributed_table('tenant1.new_dist', 'id'); (1 row) SELECT citus_schema_distribute('tenant1'); -ERROR: schema already has distributed tables -HINT: Undistribute distributed tables under the schema before distributing the schema. +ERROR: distributed schema cannot have distributed tables +HINT: Undistribute distributed tables before 'citus_schema_distribute'. SELECT undistribute_table('tenant1.new_dist'); undistribute_table --------------------------------------------------------------------- @@ -795,8 +795,8 @@ SELECT create_distributed_table('tenant1.single_shard_t', NULL); (1 row) SELECT citus_schema_distribute('tenant1'); -ERROR: schema already has distributed tables -HINT: Undistribute distributed tables under the schema before distributing the schema. +ERROR: distributed schema cannot have distributed tables +HINT: Undistribute distributed tables before 'citus_schema_distribute'. SELECT undistribute_table('tenant1.single_shard_t'); undistribute_table --------------------------------------------------------------------- diff --git a/src/test/regress/expected/schema_based_sharding.out b/src/test/regress/expected/schema_based_sharding.out index 951fc05e7..b041708cf 100644 --- a/src/test/regress/expected/schema_based_sharding.out +++ b/src/test/regress/expected/schema_based_sharding.out @@ -80,24 +80,148 @@ ERROR: tenant_2.test_table is not allowed for update_distributed_table_colocati -- verify we also don't allow colocate_with a tenant table SELECT update_distributed_table_colocation('regular_schema.test_table', colocate_with => 'tenant_2.test_table'); ERROR: tenant_2.test_table is not allowed for colocate_with because it belongs to a distributed schema --- verify we don't allow undistribute_table for tenant tables -SELECT undistribute_table('tenant_2.test_table'); -ERROR: tenant_2.test_table is not allowed for undistribute_table because it belongs to a distributed schema +-- verify we do not allow undistribute_table for tenant tables +CREATE TABLE tenant_2.undist_table(id int); +SELECT undistribute_table('tenant_2.undist_table'); +ERROR: tenant_2.undist_table is not allowed for undistribute_table because it belongs to a distributed schema -- verify we don't allow alter_distributed_table for tenant tables SELECT alter_distributed_table('tenant_2.test_table', colocate_with => 'none'); ERROR: tenant_2.test_table is not allowed for alter_distributed_table because it belongs to a distributed schema -- verify we also don't allow colocate_with a tenant table SELECT alter_distributed_table('regular_schema.test_table', colocate_with => 'tenant_2.test_table'); ERROR: tenant_2.test_table is not allowed for colocate_with because it belongs to a distributed schema --- verify we don't allow ALTER TABLE SET SCHEMA for tenant tables -ALTER TABLE tenant_2.test_table SET SCHEMA regular_schema; -ERROR: tenant_2.test_table is not allowed for ALTER TABLE SET SCHEMA because it belongs to a distributed schema --- verify we don't allow ALTER TABLE SET SCHEMA for tenant schemas -ALTER TABLE regular_schema.test_table SET SCHEMA tenant_2; -ERROR: tenant_2 is not allowed for ALTER TABLE SET SCHEMA because it is a distributed schema --- the same, from tenant schema to tenant schema -ALTER TABLE tenant_2.test_table SET SCHEMA tenant_3; -ERROR: tenant_2.test_table is not allowed for ALTER TABLE SET SCHEMA because it belongs to a distributed schema +-- verify we can set tenant table's schema to regular schema +CREATE TABLE tenant_2.test_table2(id int); +ALTER TABLE tenant_2.test_table2 SET SCHEMA regular_schema; +NOTICE: undistributing table test_table2 in distributed schema tenant_2 before altering its schema +-- verify that regular_schema.test_table2 does not exist in pg_dist_partition +SELECT COUNT(*)=0 FROM pg_dist_partition +WHERE logicalrelid = 'regular_schema.test_table2'::regclass AND + partmethod = 'n' AND repmodel = 's' AND colocationid > 0; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- verify that tenant_2.test_table2 does not exist +SELECT * FROM tenant_2.test_table2; +ERROR: relation "tenant_2.test_table2" does not exist +-- verify we can set regular table's schema to distributed schema +CREATE TABLE regular_schema.test_table3(id int); +ALTER TABLE regular_schema.test_table3 SET SCHEMA tenant_2; +NOTICE: converting table test_table3 to a tenant table in distributed schema tenant_2 +-- verify that tenant_2.test_table3 is recorded in pg_dist_partition as a single-shard table. +SELECT COUNT(*)=1 FROM pg_dist_partition +WHERE logicalrelid = 'tenant_2.test_table3'::regclass AND + partmethod = 'n' AND repmodel = 's' AND colocationid > 0; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- verify that regular_schema.test_table3 does not exist +SELECT * FROM regular_schema.test_table3; +ERROR: relation "regular_schema.test_table3" does not exist +-- verify we can set tenant table's schema to another distributed schema +CREATE TABLE tenant_2.test_table4(id int); +ALTER TABLE tenant_2.test_table4 SET SCHEMA tenant_3; +NOTICE: undistributing table test_table4 in distributed schema tenant_2 before altering its schema +NOTICE: converting table test_table4 to a tenant table in distributed schema tenant_3 +-- verify that tenant_3.test_table4 is recorded in pg_dist_partition as a single-shard table. +SELECT COUNT(*)=1 FROM pg_dist_partition +WHERE logicalrelid = 'tenant_3.test_table4'::regclass AND + partmethod = 'n' AND repmodel = 's' AND colocationid > 0; + ?column? +--------------------------------------------------------------------- + t +(1 row) + +-- verify that tenant_2.test_table4 does not exist +SELECT * FROM tenant_2.test_table4; +ERROR: relation "tenant_2.test_table4" does not exist +-- verify that we can put a local table in regular schema into distributed schema +CREATE TABLE regular_schema.pg_local_tbl(id int); +ALTER TABLE regular_schema.pg_local_tbl SET SCHEMA tenant_2; +NOTICE: converting table pg_local_tbl to a tenant table in distributed schema tenant_2 +-- verify that we can put a Citus local table in regular schema into distributed schema +CREATE TABLE regular_schema.citus_local_tbl(id int); +SELECT citus_add_local_table_to_metadata('regular_schema.citus_local_tbl'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +ALTER TABLE regular_schema.citus_local_tbl SET SCHEMA tenant_2; +NOTICE: converting table citus_local_tbl to a tenant table in distributed schema tenant_2 +-- verify that we do not allow a hash distributed table in regular schema into distributed schema +CREATE TABLE regular_schema.hash_dist_tbl(id int); +SELECT create_distributed_table('regular_schema.hash_dist_tbl', 'id'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +ALTER TABLE regular_schema.hash_dist_tbl SET SCHEMA tenant_2; +ERROR: distributed schema cannot have distributed tables +HINT: Undistribute distributed tables before 'ALTER TABLE SET SCHEMA'. +-- verify that we do not allow a reference table in regular schema into distributed schema +CREATE TABLE regular_schema.ref_tbl(id int PRIMARY KEY); +SELECT create_reference_table('regular_schema.ref_tbl'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +ALTER TABLE regular_schema.ref_tbl SET SCHEMA tenant_2; +ERROR: distributed schema cannot have distributed tables +HINT: Undistribute distributed tables before 'ALTER TABLE SET SCHEMA'. +-- verify that we can put a table in tenant schema into regular schema +CREATE TABLE tenant_2.tenant_tbl(id int); +ALTER TABLE tenant_2.tenant_tbl SET SCHEMA regular_schema; +NOTICE: undistributing table tenant_tbl in distributed schema tenant_2 before altering its schema +-- verify that we can put a table in tenant schema into another tenant schema +CREATE TABLE tenant_2.tenant_tbl2(id int); +ALTER TABLE tenant_2.tenant_tbl2 SET SCHEMA tenant_3; +NOTICE: undistributing table tenant_tbl2 in distributed schema tenant_2 before altering its schema +NOTICE: converting table tenant_tbl2 to a tenant table in distributed schema tenant_3 +-- verify that we do not allow a local table in regular schema into distributed schema if it has foreign key to a non-reference table in another schema +CREATE TABLE regular_schema.pg_local_tbl1(id int PRIMARY KEY); +CREATE TABLE regular_schema.pg_local_tbl2(id int REFERENCES regular_schema.pg_local_tbl1(id)); +ALTER TABLE regular_schema.pg_local_tbl2 SET SCHEMA tenant_2; +ERROR: foreign keys from distributed schemas can only point to the same distributed schema or reference tables in regular schemas +DETAIL: "tenant_2.pg_local_tbl2" references "regular_schema.pg_local_tbl1" via foreign key constraint "pg_local_tbl2_id_fkey" +-- verify that we allow a local table in regular schema into distributed schema if it has foreign key to a reference table in another schema +CREATE TABLE regular_schema.pg_local_tbl3(id int REFERENCES regular_schema.ref_tbl(id)); +ALTER TABLE regular_schema.pg_local_tbl3 SET SCHEMA tenant_2; +NOTICE: converting table pg_local_tbl3 to a tenant table in distributed schema tenant_2 +-- verify that we do not allow a table in tenant schema into regular schema if it has foreign key to/from another table in the same schema +CREATE TABLE tenant_2.tenant_tbl1(id int PRIMARY KEY); +CREATE TABLE tenant_2.tenant_tbl2(id int REFERENCES tenant_2.tenant_tbl1(id)); +ALTER TABLE tenant_2.tenant_tbl1 SET SCHEMA regular_schema; +ERROR: set schema is not allowed for table tenant_tbl1 in distributed schema tenant_2 +DETAIL: distributed schemas cannot have foreign keys from/to local tables or different schema +ALTER TABLE tenant_2.tenant_tbl2 SET SCHEMA regular_schema; +ERROR: set schema is not allowed for table tenant_tbl2 in distributed schema tenant_2 +DETAIL: distributed schemas cannot have foreign keys from/to local tables or different schema +-- verify that we do not allow a table in distributed schema into another distributed schema if it has foreign key to/from another table in the same schema +CREATE TABLE tenant_2.tenant_tbl3(id int PRIMARY KEY); +CREATE TABLE tenant_2.tenant_tbl4(id int REFERENCES tenant_2.tenant_tbl3(id)); +ALTER TABLE tenant_2.tenant_tbl3 SET SCHEMA tenant_3; +ERROR: set schema is not allowed for table tenant_tbl3 in distributed schema tenant_2 +DETAIL: distributed schemas cannot have foreign keys from/to local tables or different schema +ALTER TABLE tenant_2.tenant_tbl4 SET SCHEMA tenant_3; +ERROR: set schema is not allowed for table tenant_tbl4 in distributed schema tenant_2 +DETAIL: distributed schemas cannot have foreign keys from/to local tables or different schema +-- alter set non-existent schema +ALTER TABLE tenant_2.test_table SET SCHEMA ghost_schema; +ERROR: schema "ghost_schema" does not exist +ALTER TABLE IF EXISTS tenant_2.test_table SET SCHEMA ghost_schema; +ERROR: schema "ghost_schema" does not exist +-- alter set non-existent table +ALTER TABLE tenant_2.ghost_table SET SCHEMA ghost_schema; +ERROR: relation "tenant_2.ghost_table" does not exist +ALTER TABLE IF EXISTS tenant_2.ghost_table SET SCHEMA ghost_schema; +NOTICE: relation "ghost_table" does not exist, skipping -- (on coordinator) verify that colocation id is set for empty tenants too SELECT colocationid > 0 FROM pg_dist_schema WHERE schemaid::regnamespace::text IN ('tenant_1', 'tenant_3'); @@ -265,8 +389,8 @@ SELECT EXISTS( (1 row) INSERT INTO tenant_4.another_partitioned_table VALUES (1, 'a'); -ERROR: insert or update on table "another_partitioned_table_child_1920040" violates foreign key constraint "another_partitioned_table_a_fkey_1920039" -DETAIL: Key (a)=(1) is not present in table "partitioned_table_1920037". +ERROR: insert or update on table "another_partitioned_table_child_1920090" violates foreign key constraint "another_partitioned_table_a_fkey_1920089" +DETAIL: Key (a)=(1) is not present in table "partitioned_table_1920087". CONTEXT: while executing command on localhost:xxxxx INSERT INTO tenant_4.partitioned_table VALUES (1, 'a'); INSERT INTO tenant_4.another_partitioned_table VALUES (1, 'a'); diff --git a/src/test/regress/sql/schema_based_sharding.sql b/src/test/regress/sql/schema_based_sharding.sql index 88ca77a40..7b16ba904 100644 --- a/src/test/regress/sql/schema_based_sharding.sql +++ b/src/test/regress/sql/schema_based_sharding.sql @@ -59,18 +59,100 @@ SELECT citus_add_local_table_to_metadata('tenant_2.test_table'); SELECT update_distributed_table_colocation('tenant_2.test_table', colocate_with => 'none'); -- verify we also don't allow colocate_with a tenant table SELECT update_distributed_table_colocation('regular_schema.test_table', colocate_with => 'tenant_2.test_table'); --- verify we don't allow undistribute_table for tenant tables -SELECT undistribute_table('tenant_2.test_table'); + +-- verify we do not allow undistribute_table for tenant tables +CREATE TABLE tenant_2.undist_table(id int); +SELECT undistribute_table('tenant_2.undist_table'); + -- verify we don't allow alter_distributed_table for tenant tables SELECT alter_distributed_table('tenant_2.test_table', colocate_with => 'none'); -- verify we also don't allow colocate_with a tenant table SELECT alter_distributed_table('regular_schema.test_table', colocate_with => 'tenant_2.test_table'); --- verify we don't allow ALTER TABLE SET SCHEMA for tenant tables -ALTER TABLE tenant_2.test_table SET SCHEMA regular_schema; --- verify we don't allow ALTER TABLE SET SCHEMA for tenant schemas -ALTER TABLE regular_schema.test_table SET SCHEMA tenant_2; --- the same, from tenant schema to tenant schema -ALTER TABLE tenant_2.test_table SET SCHEMA tenant_3; + +-- verify we can set tenant table's schema to regular schema +CREATE TABLE tenant_2.test_table2(id int); +ALTER TABLE tenant_2.test_table2 SET SCHEMA regular_schema; +-- verify that regular_schema.test_table2 does not exist in pg_dist_partition +SELECT COUNT(*)=0 FROM pg_dist_partition +WHERE logicalrelid = 'regular_schema.test_table2'::regclass AND + partmethod = 'n' AND repmodel = 's' AND colocationid > 0; +-- verify that tenant_2.test_table2 does not exist +SELECT * FROM tenant_2.test_table2; + +-- verify we can set regular table's schema to distributed schema +CREATE TABLE regular_schema.test_table3(id int); +ALTER TABLE regular_schema.test_table3 SET SCHEMA tenant_2; +-- verify that tenant_2.test_table3 is recorded in pg_dist_partition as a single-shard table. +SELECT COUNT(*)=1 FROM pg_dist_partition +WHERE logicalrelid = 'tenant_2.test_table3'::regclass AND + partmethod = 'n' AND repmodel = 's' AND colocationid > 0; +-- verify that regular_schema.test_table3 does not exist +SELECT * FROM regular_schema.test_table3; + +-- verify we can set tenant table's schema to another distributed schema +CREATE TABLE tenant_2.test_table4(id int); +ALTER TABLE tenant_2.test_table4 SET SCHEMA tenant_3; +-- verify that tenant_3.test_table4 is recorded in pg_dist_partition as a single-shard table. +SELECT COUNT(*)=1 FROM pg_dist_partition +WHERE logicalrelid = 'tenant_3.test_table4'::regclass AND + partmethod = 'n' AND repmodel = 's' AND colocationid > 0; +-- verify that tenant_2.test_table4 does not exist +SELECT * FROM tenant_2.test_table4; + +-- verify that we can put a local table in regular schema into distributed schema +CREATE TABLE regular_schema.pg_local_tbl(id int); +ALTER TABLE regular_schema.pg_local_tbl SET SCHEMA tenant_2; + +-- verify that we can put a Citus local table in regular schema into distributed schema +CREATE TABLE regular_schema.citus_local_tbl(id int); +SELECT citus_add_local_table_to_metadata('regular_schema.citus_local_tbl'); +ALTER TABLE regular_schema.citus_local_tbl SET SCHEMA tenant_2; + +-- verify that we do not allow a hash distributed table in regular schema into distributed schema +CREATE TABLE regular_schema.hash_dist_tbl(id int); +SELECT create_distributed_table('regular_schema.hash_dist_tbl', 'id'); +ALTER TABLE regular_schema.hash_dist_tbl SET SCHEMA tenant_2; + +-- verify that we do not allow a reference table in regular schema into distributed schema +CREATE TABLE regular_schema.ref_tbl(id int PRIMARY KEY); +SELECT create_reference_table('regular_schema.ref_tbl'); +ALTER TABLE regular_schema.ref_tbl SET SCHEMA tenant_2; + +-- verify that we can put a table in tenant schema into regular schema +CREATE TABLE tenant_2.tenant_tbl(id int); +ALTER TABLE tenant_2.tenant_tbl SET SCHEMA regular_schema; + +-- verify that we can put a table in tenant schema into another tenant schema +CREATE TABLE tenant_2.tenant_tbl2(id int); +ALTER TABLE tenant_2.tenant_tbl2 SET SCHEMA tenant_3; + +-- verify that we do not allow a local table in regular schema into distributed schema if it has foreign key to a non-reference table in another schema +CREATE TABLE regular_schema.pg_local_tbl1(id int PRIMARY KEY); +CREATE TABLE regular_schema.pg_local_tbl2(id int REFERENCES regular_schema.pg_local_tbl1(id)); +ALTER TABLE regular_schema.pg_local_tbl2 SET SCHEMA tenant_2; + +-- verify that we allow a local table in regular schema into distributed schema if it has foreign key to a reference table in another schema +CREATE TABLE regular_schema.pg_local_tbl3(id int REFERENCES regular_schema.ref_tbl(id)); +ALTER TABLE regular_schema.pg_local_tbl3 SET SCHEMA tenant_2; + +-- verify that we do not allow a table in tenant schema into regular schema if it has foreign key to/from another table in the same schema +CREATE TABLE tenant_2.tenant_tbl1(id int PRIMARY KEY); +CREATE TABLE tenant_2.tenant_tbl2(id int REFERENCES tenant_2.tenant_tbl1(id)); +ALTER TABLE tenant_2.tenant_tbl1 SET SCHEMA regular_schema; +ALTER TABLE tenant_2.tenant_tbl2 SET SCHEMA regular_schema; + +-- verify that we do not allow a table in distributed schema into another distributed schema if it has foreign key to/from another table in the same schema +CREATE TABLE tenant_2.tenant_tbl3(id int PRIMARY KEY); +CREATE TABLE tenant_2.tenant_tbl4(id int REFERENCES tenant_2.tenant_tbl3(id)); +ALTER TABLE tenant_2.tenant_tbl3 SET SCHEMA tenant_3; +ALTER TABLE tenant_2.tenant_tbl4 SET SCHEMA tenant_3; + +-- alter set non-existent schema +ALTER TABLE tenant_2.test_table SET SCHEMA ghost_schema; +ALTER TABLE IF EXISTS tenant_2.test_table SET SCHEMA ghost_schema; +-- alter set non-existent table +ALTER TABLE tenant_2.ghost_table SET SCHEMA ghost_schema; +ALTER TABLE IF EXISTS tenant_2.ghost_table SET SCHEMA ghost_schema; -- (on coordinator) verify that colocation id is set for empty tenants too SELECT colocationid > 0 FROM pg_dist_schema From d71ad4b65ac4d47ce5efcbd97189343dff6e423d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20Ozan=20Akg=C3=BCl?= Date: Mon, 19 Jun 2023 12:39:41 +0300 Subject: [PATCH 105/118] Add Publication Tests for Tenant Schema Tables (#7011) This PR adds schema based sharding tests to publication.sql file --- src/test/regress/expected/publication.out | 43 +++++++++++++++++---- src/test/regress/expected/publication_0.out | 24 ++++++++---- src/test/regress/sql/publication.sql | 33 ++++++++++++++-- 3 files changed, 81 insertions(+), 19 deletions(-) diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out index 7fc75637d..702d23f1f 100644 --- a/src/test/regress/expected/publication.out +++ b/src/test/regress/expected/publication.out @@ -90,13 +90,18 @@ SELECT DISTINCT c FROM ( SELECT worker_create_or_replace_object('CREATE PUBLICATION pubtables_orig WITH (publish_via_partition_root = ''false'', publish = ''insert, truncate'')'); (1 row) --- distribute a table, creating a mixed publication +-- distribute a table and create a tenant schema, creating a mixed publication SELECT create_distributed_table('test','x', colocate_with := 'none'); create_distributed_table --------------------------------------------------------------------- (1 row) +SET citus.enable_schema_based_sharding TO ON; +CREATE SCHEMA citus_schema_1; +CREATE TABLE citus_schema_1.test (x int primary key, y int, "column-1" int, doc xml); +SET citus.enable_schema_based_sharding TO OFF; +ALTER PUBLICATION pubtables_orig ADD TABLE citus_schema_1.test; -- some generic operations ALTER PUBLICATION pubtables_orig RENAME TO pubtables; ALTER PUBLICATION pubtables SET (publish = 'insert, update, delete'); @@ -108,7 +113,11 @@ ERROR: relation "notexist" does not exist -- operations with a distributed table ALTER PUBLICATION pubtables DROP TABLE test; ALTER PUBLICATION pubtables ADD TABLE test; -ALTER PUBLICATION pubtables SET TABLE test, "test-pubs", "publication-1"."test-pubs"; +ALTER PUBLICATION pubtables SET TABLE test, "test-pubs", "publication-1"."test-pubs", citus_schema_1.test; +-- operations with a tenant schema table +ALTER PUBLICATION pubtables DROP TABLE citus_schema_1.test; +ALTER PUBLICATION pubtables ADD TABLE citus_schema_1.test; +ALTER PUBLICATION pubtables SET TABLE test, "test-pubs", "publication-1"."test-pubs", citus_schema_1.test; -- operations with a local table in a mixed publication ALTER PUBLICATION pubtables DROP TABLE "test-pubs"; ALTER PUBLICATION pubtables ADD TABLE "test-pubs"; @@ -124,9 +133,9 @@ SELECT DISTINCT c FROM ( FROM run_command_on_workers($$ SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' AND c LIKE '%pubtables%' ORDER BY 1) s$$) ORDER BY c) s; - c + c --------------------------------------------------------------------- - SELECT worker_create_or_replace_object('CREATE PUBLICATION pubtables FOR TABLE publication.test, TABLE publication."test-pubs" WITH (publish_via_partition_root = ''false'', publish = ''insert, update, delete'')'); + SELECT worker_create_or_replace_object('CREATE PUBLICATION pubtables FOR TABLE publication.test, TABLE citus_schema_1.test, TABLE publication."test-pubs" WITH (publish_via_partition_root = ''false'', publish = ''insert, update, delete'')'); (1 row) -- operations with a strangely named distributed table in a mixed publication @@ -134,7 +143,7 @@ ALTER PUBLICATION pubtables DROP TABLE "test-pubs"; ALTER PUBLICATION pubtables ADD TABLE "test-pubs"; -- create a publication with distributed and local tables DROP PUBLICATION pubtables; -CREATE PUBLICATION pubtables FOR TABLE test, "test-pubs", "publication-1"."test-pubs"; +CREATE PUBLICATION pubtables FOR TABLE test, "test-pubs", "publication-1"."test-pubs", citus_schema_1.test; -- change distributed tables SELECT alter_distributed_table('test', shard_count := 5, cascade_to_colocated := true); NOTICE: creating a new table for publication.test @@ -194,9 +203,9 @@ SELECT DISTINCT c FROM ( FROM run_command_on_workers($$ SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' AND c LIKE '%pubtables%' ORDER BY 1) s$$) ORDER BY c) s; - c + c --------------------------------------------------------------------- - SELECT worker_create_or_replace_object('CREATE PUBLICATION pubtables FOR TABLE publication.test, TABLE publication."test-pubs" WITH (publish_via_partition_root = ''false'', publish = ''insert, update, delete, truncate'')'); + SELECT worker_create_or_replace_object('CREATE PUBLICATION pubtables FOR TABLE citus_schema_1.test, TABLE publication.test, TABLE publication."test-pubs" WITH (publish_via_partition_root = ''false'', publish = ''insert, update, delete, truncate'')'); (1 row) -- partitioned table @@ -257,10 +266,11 @@ SELECT substring(:'server_version', '\d+')::int >= 15 AS server_version_ge_15 SET client_min_messages TO ERROR; DROP SCHEMA publication CASCADE; DROP SCHEMA "publication-1" CASCADE; +DROP SCHEMA citus_schema_1 CASCADE; \q \endif -- recreate a mixed publication -CREATE PUBLICATION pubtables FOR TABLE test, "publication-1"."test-pubs"; +CREATE PUBLICATION pubtables FOR TABLE test, "publication-1"."test-pubs", citus_schema_1.test; -- operations on an existing distributed table ALTER PUBLICATION pubtables DROP TABLE test; ALTER PUBLICATION pubtables ADD TABLE test (y); @@ -277,6 +287,22 @@ SELECT DISTINCT c FROM ( SELECT worker_create_or_replace_object('CREATE PUBLICATION pubtables FOR TABLE publication.test WHERE (CASE test.x WHEN 5 THEN true ELSE false END) WITH (publish_via_partition_root = ''false'', publish = ''insert, update, delete, truncate'')'); (1 row) +-- operations on an existing tenant schema table +ALTER PUBLICATION pubtables ADD TABLE citus_schema_1.test (y); +ALTER PUBLICATION pubtables DROP TABLE citus_schema_1.test; +ALTER PUBLICATION pubtables SET TABLE citus_schema_1.test WHERE (doc IS DOCUMENT); +ALTER PUBLICATION pubtables SET TABLE citus_schema_1.test WHERE (xmlexists('//foo[text() = ''bar'']' PASSING BY VALUE doc)); +ALTER PUBLICATION pubtables SET TABLE citus_schema_1.test WHERE (CASE x WHEN 5 THEN true ELSE false END); +SELECT DISTINCT c FROM ( + SELECT unnest(result::text[]) c + FROM run_command_on_workers($$ + SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' AND c LIKE '%pubtables%' ORDER BY 1) s$$) + ORDER BY c) s; + c +--------------------------------------------------------------------- + SELECT worker_create_or_replace_object('CREATE PUBLICATION pubtables FOR TABLE citus_schema_1.test WHERE (CASE test.x WHEN 5 THEN true ELSE false END) WITH (publish_via_partition_root = ''false'', publish = ''insert, update, delete, truncate'')'); +(1 row) + ALTER PUBLICATION pubtables SET TABLE test ("column-1", x) WHERE (x > "column-1"), "publication-1"."test-pubs"; -- operations on a local table ALTER PUBLICATION pubtables DROP TABLE "publication-1"."test-pubs"; @@ -363,3 +389,4 @@ DROP PUBLICATION pubpartitioned; SET client_min_messages TO ERROR; DROP SCHEMA publication CASCADE; DROP SCHEMA "publication-1" CASCADE; +DROP SCHEMA citus_schema_1 CASCADE; diff --git a/src/test/regress/expected/publication_0.out b/src/test/regress/expected/publication_0.out index 02978ff65..14fa94d17 100644 --- a/src/test/regress/expected/publication_0.out +++ b/src/test/regress/expected/publication_0.out @@ -90,13 +90,18 @@ SELECT DISTINCT c FROM ( SELECT worker_create_or_replace_object('CREATE PUBLICATION pubtables_orig WITH (publish_via_partition_root = ''false'', publish = ''insert, truncate'')'); (1 row) --- distribute a table, creating a mixed publication +-- distribute a table and create a tenant schema, creating a mixed publication SELECT create_distributed_table('test','x', colocate_with := 'none'); create_distributed_table --------------------------------------------------------------------- (1 row) +SET citus.enable_schema_based_sharding TO ON; +CREATE SCHEMA citus_schema_1; +CREATE TABLE citus_schema_1.test (x int primary key, y int, "column-1" int, doc xml); +SET citus.enable_schema_based_sharding TO OFF; +ALTER PUBLICATION pubtables_orig ADD TABLE citus_schema_1.test; -- some generic operations ALTER PUBLICATION pubtables_orig RENAME TO pubtables; ALTER PUBLICATION pubtables SET (publish = 'insert, update, delete'); @@ -108,7 +113,11 @@ ERROR: relation "notexist" does not exist -- operations with a distributed table ALTER PUBLICATION pubtables DROP TABLE test; ALTER PUBLICATION pubtables ADD TABLE test; -ALTER PUBLICATION pubtables SET TABLE test, "test-pubs", "publication-1"."test-pubs"; +ALTER PUBLICATION pubtables SET TABLE test, "test-pubs", "publication-1"."test-pubs", citus_schema_1.test; +-- operations with a tenant schema table +ALTER PUBLICATION pubtables DROP TABLE citus_schema_1.test; +ALTER PUBLICATION pubtables ADD TABLE citus_schema_1.test; +ALTER PUBLICATION pubtables SET TABLE test, "test-pubs", "publication-1"."test-pubs", citus_schema_1.test; -- operations with a local table in a mixed publication ALTER PUBLICATION pubtables DROP TABLE "test-pubs"; ALTER PUBLICATION pubtables ADD TABLE "test-pubs"; @@ -124,9 +133,9 @@ SELECT DISTINCT c FROM ( FROM run_command_on_workers($$ SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' AND c LIKE '%pubtables%' ORDER BY 1) s$$) ORDER BY c) s; - c + c --------------------------------------------------------------------- - SELECT worker_create_or_replace_object('CREATE PUBLICATION pubtables FOR TABLE publication.test, publication."test-pubs" WITH (publish_via_partition_root = ''false'', publish = ''insert, update, delete'')'); + SELECT worker_create_or_replace_object('CREATE PUBLICATION pubtables FOR TABLE publication.test, citus_schema_1.test, publication."test-pubs" WITH (publish_via_partition_root = ''false'', publish = ''insert, update, delete'')'); (1 row) -- operations with a strangely named distributed table in a mixed publication @@ -134,7 +143,7 @@ ALTER PUBLICATION pubtables DROP TABLE "test-pubs"; ALTER PUBLICATION pubtables ADD TABLE "test-pubs"; -- create a publication with distributed and local tables DROP PUBLICATION pubtables; -CREATE PUBLICATION pubtables FOR TABLE test, "test-pubs", "publication-1"."test-pubs"; +CREATE PUBLICATION pubtables FOR TABLE test, "test-pubs", "publication-1"."test-pubs", citus_schema_1.test; -- change distributed tables SELECT alter_distributed_table('test', shard_count := 5, cascade_to_colocated := true); NOTICE: creating a new table for publication.test @@ -194,9 +203,9 @@ SELECT DISTINCT c FROM ( FROM run_command_on_workers($$ SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' AND c LIKE '%pubtables%' ORDER BY 1) s$$) ORDER BY c) s; - c + c --------------------------------------------------------------------- - SELECT worker_create_or_replace_object('CREATE PUBLICATION pubtables FOR TABLE publication.test, publication."test-pubs" WITH (publish_via_partition_root = ''false'', publish = ''insert, update, delete, truncate'')'); + SELECT worker_create_or_replace_object('CREATE PUBLICATION pubtables FOR TABLE citus_schema_1.test, publication.test, publication."test-pubs" WITH (publish_via_partition_root = ''false'', publish = ''insert, update, delete, truncate'')'); (1 row) -- partitioned table @@ -257,4 +266,5 @@ SELECT substring(:'server_version', '\d+')::int >= 15 AS server_version_ge_15 SET client_min_messages TO ERROR; DROP SCHEMA publication CASCADE; DROP SCHEMA "publication-1" CASCADE; +DROP SCHEMA citus_schema_1 CASCADE; \q diff --git a/src/test/regress/sql/publication.sql b/src/test/regress/sql/publication.sql index 488c0408c..8bd2ea923 100644 --- a/src/test/regress/sql/publication.sql +++ b/src/test/regress/sql/publication.sql @@ -84,8 +84,13 @@ SELECT DISTINCT c FROM ( SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' AND c LIKE '%pubtables%' ORDER BY 1) s$$) ORDER BY c) s; --- distribute a table, creating a mixed publication +-- distribute a table and create a tenant schema, creating a mixed publication SELECT create_distributed_table('test','x', colocate_with := 'none'); +SET citus.enable_schema_based_sharding TO ON; +CREATE SCHEMA citus_schema_1; +CREATE TABLE citus_schema_1.test (x int primary key, y int, "column-1" int, doc xml); +SET citus.enable_schema_based_sharding TO OFF; +ALTER PUBLICATION pubtables_orig ADD TABLE citus_schema_1.test; -- some generic operations ALTER PUBLICATION pubtables_orig RENAME TO pubtables; @@ -97,7 +102,12 @@ ALTER PUBLICATION pubtables ADD TABLE notexist; -- operations with a distributed table ALTER PUBLICATION pubtables DROP TABLE test; ALTER PUBLICATION pubtables ADD TABLE test; -ALTER PUBLICATION pubtables SET TABLE test, "test-pubs", "publication-1"."test-pubs"; +ALTER PUBLICATION pubtables SET TABLE test, "test-pubs", "publication-1"."test-pubs", citus_schema_1.test; + +-- operations with a tenant schema table +ALTER PUBLICATION pubtables DROP TABLE citus_schema_1.test; +ALTER PUBLICATION pubtables ADD TABLE citus_schema_1.test; +ALTER PUBLICATION pubtables SET TABLE test, "test-pubs", "publication-1"."test-pubs", citus_schema_1.test; -- operations with a local table in a mixed publication ALTER PUBLICATION pubtables DROP TABLE "test-pubs"; @@ -118,7 +128,7 @@ ALTER PUBLICATION pubtables ADD TABLE "test-pubs"; -- create a publication with distributed and local tables DROP PUBLICATION pubtables; -CREATE PUBLICATION pubtables FOR TABLE test, "test-pubs", "publication-1"."test-pubs"; +CREATE PUBLICATION pubtables FOR TABLE test, "test-pubs", "publication-1"."test-pubs", citus_schema_1.test; -- change distributed tables SELECT alter_distributed_table('test', shard_count := 5, cascade_to_colocated := true); @@ -184,11 +194,12 @@ SELECT substring(:'server_version', '\d+')::int >= 15 AS server_version_ge_15 SET client_min_messages TO ERROR; DROP SCHEMA publication CASCADE; DROP SCHEMA "publication-1" CASCADE; +DROP SCHEMA citus_schema_1 CASCADE; \q \endif -- recreate a mixed publication -CREATE PUBLICATION pubtables FOR TABLE test, "publication-1"."test-pubs"; +CREATE PUBLICATION pubtables FOR TABLE test, "publication-1"."test-pubs", citus_schema_1.test; -- operations on an existing distributed table ALTER PUBLICATION pubtables DROP TABLE test; @@ -197,6 +208,19 @@ ALTER PUBLICATION pubtables SET TABLE test WHERE (doc IS DOCUMENT); ALTER PUBLICATION pubtables SET TABLE test WHERE (xmlexists('//foo[text() = ''bar'']' PASSING BY VALUE doc)); ALTER PUBLICATION pubtables SET TABLE test WHERE (CASE x WHEN 5 THEN true ELSE false END); +SELECT DISTINCT c FROM ( + SELECT unnest(result::text[]) c + FROM run_command_on_workers($$ + SELECT array_agg(c) FROM (SELECT c FROM unnest(activate_node_snapshot()) c WHERE c LIKE '%CREATE PUBLICATION%' AND c LIKE '%pubtables%' ORDER BY 1) s$$) + ORDER BY c) s; + +-- operations on an existing tenant schema table +ALTER PUBLICATION pubtables ADD TABLE citus_schema_1.test (y); +ALTER PUBLICATION pubtables DROP TABLE citus_schema_1.test; +ALTER PUBLICATION pubtables SET TABLE citus_schema_1.test WHERE (doc IS DOCUMENT); +ALTER PUBLICATION pubtables SET TABLE citus_schema_1.test WHERE (xmlexists('//foo[text() = ''bar'']' PASSING BY VALUE doc)); +ALTER PUBLICATION pubtables SET TABLE citus_schema_1.test WHERE (CASE x WHEN 5 THEN true ELSE false END); + SELECT DISTINCT c FROM ( SELECT unnest(result::text[]) c FROM run_command_on_workers($$ @@ -260,3 +284,4 @@ DROP PUBLICATION pubpartitioned; SET client_min_messages TO ERROR; DROP SCHEMA publication CASCADE; DROP SCHEMA "publication-1" CASCADE; +DROP SCHEMA citus_schema_1 CASCADE; From c10cb50aa9f6a0849b3be2e96245a0c87ae5f7ee Mon Sep 17 00:00:00 2001 From: Xin Li <59580070+xin-hedera@users.noreply.github.com> Date: Mon, 19 Jun 2023 09:49:05 -0500 Subject: [PATCH 106/118] Support custom cast from / to timestamptz in time partition management UDFs (#6923) This is to implement custom cast of table partition column type from / to `timestamptz` in time partition management UDFs, as proposed in ticket #6454 The general idea is for a time partition column with type other than `date`, `timestamp`, or `timestamptz`, users can provide custom bidirectional cast between the column type and `timestamptz`, the UDFs then will be able to create and drop time partitions for such tables. Fixes #6454 --------- Signed-off-by: Xin Li Co-authored-by: Marco Slot Co-authored-by: Ahmet Gedemenli --- .../distributed/sql/citus--11.3-1--12.0-1.sql | 3 + .../sql/downgrades/citus--12.0-1--11.3-1.sql | 3 + .../udfs/drop_old_time_partitions/12.0-1.sql | 68 +++++ .../udfs/drop_old_time_partitions/latest.sql | 34 ++- .../12.0-1.sql | 239 ++++++++++++++++++ .../latest.sql | 66 +++-- .../regress/expected/multi_partitioning.out | 46 +++- src/test/regress/sql/multi_partitioning.sql | 34 +++ 8 files changed, 458 insertions(+), 35 deletions(-) create mode 100644 src/backend/distributed/sql/udfs/drop_old_time_partitions/12.0-1.sql create mode 100644 src/backend/distributed/sql/udfs/get_missing_time_partition_ranges/12.0-1.sql diff --git a/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql b/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql index 688b79fe8..998ffc2be 100644 --- a/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql +++ b/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql @@ -39,3 +39,6 @@ DROP FUNCTION citus_shard_sizes; -- udfs to convert a regular/tenant schema to a tenant/regular schema #include "udfs/citus_schema_distribute/12.0-1.sql" #include "udfs/citus_schema_undistribute/12.0-1.sql" + +#include "udfs/drop_old_time_partitions/12.0-1.sql" +#include "udfs/get_missing_time_partition_ranges/12.0-1.sql" diff --git a/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql b/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql index 3acd60311..1adb4cb72 100644 --- a/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql +++ b/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql @@ -73,3 +73,6 @@ DROP FUNCTION pg_catalog.citus_stat_tenants_local_internal( OUT DOUBLE PRECISION, OUT BIGINT); #include "../udfs/citus_stat_tenants_local/11.3-1.sql" + +#include "../udfs/drop_old_time_partitions/10.2-1.sql" +#include "../udfs/get_missing_time_partition_ranges/10.2-1.sql" diff --git a/src/backend/distributed/sql/udfs/drop_old_time_partitions/12.0-1.sql b/src/backend/distributed/sql/udfs/drop_old_time_partitions/12.0-1.sql new file mode 100644 index 000000000..a4bcd45a0 --- /dev/null +++ b/src/backend/distributed/sql/udfs/drop_old_time_partitions/12.0-1.sql @@ -0,0 +1,68 @@ +CREATE OR REPLACE PROCEDURE pg_catalog.drop_old_time_partitions( + table_name regclass, + older_than timestamptz) +LANGUAGE plpgsql +AS $$ +DECLARE + -- properties of the partitioned table + number_of_partition_columns int; + partition_column_index int; + partition_column_type regtype; + + -- used to support dynamic type casting between the partition column type and timestamptz + custom_cast text; + is_partition_column_castable boolean; + older_partitions_query text; + + r record; +BEGIN + -- check whether the table is time partitioned table, if not error out + SELECT partnatts, partattrs[0] + INTO number_of_partition_columns, partition_column_index + FROM pg_catalog.pg_partitioned_table + WHERE partrelid = table_name; + + IF NOT FOUND THEN + RAISE '% is not partitioned', table_name::text; + ELSIF number_of_partition_columns <> 1 THEN + RAISE 'partitioned tables with multiple partition columns are not supported'; + END IF; + + -- get datatype here to check interval-table type + SELECT atttypid + INTO partition_column_type + FROM pg_attribute + WHERE attrelid = table_name::oid + AND attnum = partition_column_index; + + -- we currently only support partitioning by date, timestamp, and timestamptz + custom_cast = ''; + IF partition_column_type <> 'date'::regtype + AND partition_column_type <> 'timestamp'::regtype + AND partition_column_type <> 'timestamptz'::regtype THEN + SELECT EXISTS(SELECT OID FROM pg_cast WHERE castsource = partition_column_type AND casttarget = 'timestamptz'::regtype) AND + EXISTS(SELECT OID FROM pg_cast WHERE castsource = 'timestamptz'::regtype AND casttarget = partition_column_type) + INTO is_partition_column_castable; + IF not is_partition_column_castable THEN + RAISE 'type of the partition column of the table % must be date, timestamp or timestamptz', table_name; + END IF; + custom_cast = format('::%s', partition_column_type); + END IF; + + older_partitions_query = format('SELECT partition, nspname AS schema_name, relname AS table_name, from_value, to_value + FROM pg_catalog.time_partitions, pg_catalog.pg_class c, pg_catalog.pg_namespace n + WHERE parent_table = $1 AND partition = c.oid AND c.relnamespace = n.oid + AND to_value IS NOT NULL + AND to_value%1$s::timestamptz <= $2 + ORDER BY to_value%1$s::timestamptz', custom_cast); + FOR r IN EXECUTE older_partitions_query USING table_name, older_than + LOOP + RAISE NOTICE 'dropping % with start time % and end time %', r.partition, r.from_value, r.to_value; + EXECUTE format('DROP TABLE %I.%I', r.schema_name, r.table_name); + END LOOP; +END; +$$; +COMMENT ON PROCEDURE pg_catalog.drop_old_time_partitions( + table_name regclass, + older_than timestamptz) +IS 'drop old partitions of a time-partitioned table'; diff --git a/src/backend/distributed/sql/udfs/drop_old_time_partitions/latest.sql b/src/backend/distributed/sql/udfs/drop_old_time_partitions/latest.sql index c735d9f67..a4bcd45a0 100644 --- a/src/backend/distributed/sql/udfs/drop_old_time_partitions/latest.sql +++ b/src/backend/distributed/sql/udfs/drop_old_time_partitions/latest.sql @@ -1,6 +1,6 @@ CREATE OR REPLACE PROCEDURE pg_catalog.drop_old_time_partitions( - table_name regclass, - older_than timestamptz) + table_name regclass, + older_than timestamptz) LANGUAGE plpgsql AS $$ DECLARE @@ -9,6 +9,11 @@ DECLARE partition_column_index int; partition_column_type regtype; + -- used to support dynamic type casting between the partition column type and timestamptz + custom_cast text; + is_partition_column_castable boolean; + older_partitions_query text; + r record; BEGIN -- check whether the table is time partitioned table, if not error out @@ -31,19 +36,26 @@ BEGIN AND attnum = partition_column_index; -- we currently only support partitioning by date, timestamp, and timestamptz + custom_cast = ''; IF partition_column_type <> 'date'::regtype AND partition_column_type <> 'timestamp'::regtype AND partition_column_type <> 'timestamptz'::regtype THEN + SELECT EXISTS(SELECT OID FROM pg_cast WHERE castsource = partition_column_type AND casttarget = 'timestamptz'::regtype) AND + EXISTS(SELECT OID FROM pg_cast WHERE castsource = 'timestamptz'::regtype AND casttarget = partition_column_type) + INTO is_partition_column_castable; + IF not is_partition_column_castable THEN RAISE 'type of the partition column of the table % must be date, timestamp or timestamptz', table_name; + END IF; + custom_cast = format('::%s', partition_column_type); END IF; - FOR r IN - SELECT partition, nspname AS schema_name, relname AS table_name, from_value, to_value - FROM pg_catalog.time_partitions, pg_catalog.pg_class c, pg_catalog.pg_namespace n - WHERE parent_table = table_name AND partition = c.oid AND c.relnamespace = n.oid - AND to_value IS NOT NULL - AND to_value::timestamptz <= older_than - ORDER BY to_value::timestamptz + older_partitions_query = format('SELECT partition, nspname AS schema_name, relname AS table_name, from_value, to_value + FROM pg_catalog.time_partitions, pg_catalog.pg_class c, pg_catalog.pg_namespace n + WHERE parent_table = $1 AND partition = c.oid AND c.relnamespace = n.oid + AND to_value IS NOT NULL + AND to_value%1$s::timestamptz <= $2 + ORDER BY to_value%1$s::timestamptz', custom_cast); + FOR r IN EXECUTE older_partitions_query USING table_name, older_than LOOP RAISE NOTICE 'dropping % with start time % and end time %', r.partition, r.from_value, r.to_value; EXECUTE format('DROP TABLE %I.%I', r.schema_name, r.table_name); @@ -51,6 +63,6 @@ BEGIN END; $$; COMMENT ON PROCEDURE pg_catalog.drop_old_time_partitions( - table_name regclass, - older_than timestamptz) + table_name regclass, + older_than timestamptz) IS 'drop old partitions of a time-partitioned table'; diff --git a/src/backend/distributed/sql/udfs/get_missing_time_partition_ranges/12.0-1.sql b/src/backend/distributed/sql/udfs/get_missing_time_partition_ranges/12.0-1.sql new file mode 100644 index 000000000..4cdca999a --- /dev/null +++ b/src/backend/distributed/sql/udfs/get_missing_time_partition_ranges/12.0-1.sql @@ -0,0 +1,239 @@ +CREATE OR REPLACE FUNCTION pg_catalog.get_missing_time_partition_ranges( + table_name regclass, + partition_interval INTERVAL, + to_value timestamptz, + from_value timestamptz DEFAULT now()) +returns table( + partition_name text, + range_from_value text, + range_to_value text) +LANGUAGE plpgsql +AS $$ +DECLARE + -- properties of the partitioned table + table_name_text text; + table_schema_text text; + number_of_partition_columns int; + partition_column_index int; + partition_column_type regtype; + + -- used for generating time ranges + current_range_from_value timestamptz := NULL; + current_range_to_value timestamptz := NULL; + current_range_from_value_text text; + current_range_to_value_text text; + + -- used to check whether there are misaligned (manually created) partitions + manual_partition regclass; + manual_partition_from_value_text text; + manual_partition_to_value_text text; + + -- used for partition naming + partition_name_format text; + max_table_name_length int := current_setting('max_identifier_length'); + + -- used to determine whether the partition_interval is a day multiple + is_day_multiple boolean; + + -- used to support dynamic type casting between the partition column type and timestamptz + custom_cast text; + is_partition_column_castable boolean; + partition regclass; + partition_covers_query text; + partition_exist_query text; +BEGIN + -- check whether the table is time partitioned table, if not error out + SELECT relname, nspname, partnatts, partattrs[0] + INTO table_name_text, table_schema_text, number_of_partition_columns, partition_column_index + FROM pg_catalog.pg_partitioned_table, pg_catalog.pg_class c, pg_catalog.pg_namespace n + WHERE partrelid = c.oid AND c.oid = table_name + AND c.relnamespace = n.oid; + IF NOT FOUND THEN + RAISE '% is not partitioned', table_name; + ELSIF number_of_partition_columns <> 1 THEN + RAISE 'partitioned tables with multiple partition columns are not supported'; + END IF; + + -- to not to have partitions to be created in parallel + EXECUTE format('LOCK TABLE %I.%I IN SHARE UPDATE EXCLUSIVE MODE', table_schema_text, table_name_text); + + -- get datatype here to check interval-table type alignment and generate range values in the right data format + SELECT atttypid + INTO partition_column_type + FROM pg_attribute + WHERE attrelid = table_name::oid + AND attnum = partition_column_index; + + -- we currently only support partitioning by date, timestamp, and timestamptz + custom_cast = ''; + IF partition_column_type <> 'date'::regtype + AND partition_column_type <> 'timestamp'::regtype + AND partition_column_type <> 'timestamptz'::regtype THEN + SELECT EXISTS(SELECT OID FROM pg_cast WHERE castsource = partition_column_type AND casttarget = 'timestamptz'::regtype) AND + EXISTS(SELECT OID FROM pg_cast WHERE castsource = 'timestamptz'::regtype AND casttarget = partition_column_type) + INTO is_partition_column_castable; + IF not is_partition_column_castable THEN + RAISE 'type of the partition column of the table % must be date, timestamp or timestamptz', table_name; + END IF; + custom_cast = format('::%s', partition_column_type); + END IF; + + IF partition_column_type = 'date'::regtype AND partition_interval IS NOT NULL THEN + SELECT date_trunc('day', partition_interval) = partition_interval + INTO is_day_multiple; + + IF NOT is_day_multiple THEN + RAISE 'partition interval of date partitioned table must be day or multiple days'; + END IF; + END IF; + + -- If no partition exists, truncate from_value to find intuitive initial value. + -- If any partition exist, use the initial partition as the pivot partition. + -- tp.to_value and tp.from_value are equal to '', if default partition exists. + EXECUTE format('SELECT tp.from_value%1$s::timestamptz, tp.to_value%1$s::timestamptz + FROM pg_catalog.time_partitions tp + WHERE parent_table = $1 AND tp.to_value <> '' AND tp.from_value <> '' + ORDER BY tp.from_value%1$s::timestamptz ASC + LIMIT 1', custom_cast) + INTO current_range_from_value, current_range_to_value + USING table_name; + + IF current_range_from_value is NULL THEN + -- Decide on the current_range_from_value of the initial partition according to interval of the table. + -- Since we will create all other partitions by adding intervals, truncating given start time will provide + -- more intuitive interval ranges, instead of starting from from_value directly. + IF partition_interval < INTERVAL '1 hour' THEN + current_range_from_value = date_trunc('minute', from_value); + ELSIF partition_interval < INTERVAL '1 day' THEN + current_range_from_value = date_trunc('hour', from_value); + ELSIF partition_interval < INTERVAL '1 week' THEN + current_range_from_value = date_trunc('day', from_value); + ELSIF partition_interval < INTERVAL '1 month' THEN + current_range_from_value = date_trunc('week', from_value); + ELSIF partition_interval = INTERVAL '3 months' THEN + current_range_from_value = date_trunc('quarter', from_value); + ELSIF partition_interval < INTERVAL '1 year' THEN + current_range_from_value = date_trunc('month', from_value); + ELSE + current_range_from_value = date_trunc('year', from_value); + END IF; + + current_range_to_value := current_range_from_value + partition_interval; + ELSE + -- if from_value is newer than pivot's from value, go forward, else go backward + IF from_value >= current_range_from_value THEN + WHILE current_range_from_value < from_value LOOP + current_range_from_value := current_range_from_value + partition_interval; + END LOOP; + ELSE + WHILE current_range_from_value > from_value LOOP + current_range_from_value := current_range_from_value - partition_interval; + END LOOP; + END IF; + current_range_to_value := current_range_from_value + partition_interval; + END IF; + + -- reuse pg_partman naming scheme for back-and-forth migration + IF partition_interval = INTERVAL '3 months' THEN + -- include quarter in partition name + partition_name_format = 'YYYY"q"Q'; + ELSIF partition_interval = INTERVAL '1 week' THEN + -- include week number in partition name + partition_name_format := 'IYYY"w"IW'; + ELSE + -- always start with the year + partition_name_format := 'YYYY'; + + IF partition_interval < INTERVAL '1 year' THEN + -- include month in partition name + partition_name_format := partition_name_format || '_MM'; + END IF; + + IF partition_interval < INTERVAL '1 month' THEN + -- include day of month in partition name + partition_name_format := partition_name_format || '_DD'; + END IF; + + IF partition_interval < INTERVAL '1 day' THEN + -- include time of day in partition name + partition_name_format := partition_name_format || '_HH24MI'; + END IF; + + IF partition_interval < INTERVAL '1 minute' THEN + -- include seconds in time of day in partition name + partition_name_format := partition_name_format || 'SS'; + END IF; + END IF; + + partition_exist_query = format('SELECT partition FROM pg_catalog.time_partitions tp + WHERE tp.from_value%1$s::timestamptz = $1 AND tp.to_value%1$s::timestamptz = $2 AND parent_table = $3', + custom_cast); + partition_covers_query = format('SELECT partition, tp.from_value, tp.to_value + FROM pg_catalog.time_partitions tp + WHERE + (($1 >= tp.from_value%1$s::timestamptz AND $1 < tp.to_value%1$s::timestamptz) OR + ($2 > tp.from_value%1$s::timestamptz AND $2 < tp.to_value%1$s::timestamptz)) AND + parent_table = $3', + custom_cast); + + WHILE current_range_from_value < to_value LOOP + -- Check whether partition with given range has already been created + -- Since partition interval can be given with different types, we are converting + -- all variables to timestamptz to make sure that we are comparing same type of parameters + EXECUTE partition_exist_query into partition using current_range_from_value, current_range_to_value, table_name; + + IF partition is not NULL THEN + current_range_from_value := current_range_to_value; + current_range_to_value := current_range_to_value + partition_interval; + CONTINUE; + END IF; + + -- Check whether any other partition covers from_value or to_value + -- That means some partitions doesn't align with the initial partition. + -- In other words, gap(s) exist between partitions which is not multiple of intervals. + EXECUTE partition_covers_query + INTO manual_partition, manual_partition_from_value_text, manual_partition_to_value_text + using current_range_from_value, current_range_to_value, table_name; + + IF manual_partition is not NULL THEN + RAISE 'partition % with the range from % to % does not align with the initial partition given the partition interval', + manual_partition::text, + manual_partition_from_value_text, + manual_partition_to_value_text + USING HINT = 'Only use partitions of the same size, without gaps between partitions.'; + END IF; + + IF partition_column_type = 'date'::regtype THEN + SELECT current_range_from_value::date::text INTO current_range_from_value_text; + SELECT current_range_to_value::date::text INTO current_range_to_value_text; + ELSIF partition_column_type = 'timestamp without time zone'::regtype THEN + SELECT current_range_from_value::timestamp::text INTO current_range_from_value_text; + SELECT current_range_to_value::timestamp::text INTO current_range_to_value_text; + ELSIF partition_column_type = 'timestamp with time zone'::regtype THEN + SELECT current_range_from_value::timestamptz::text INTO current_range_from_value_text; + SELECT current_range_to_value::timestamptz::text INTO current_range_to_value_text; + ELSE + EXECUTE format('SELECT $1%s::text', custom_cast) INTO current_range_from_value_text using current_range_from_value; + EXECUTE format('SELECT $1%s::text', custom_cast) INTO current_range_to_value_text using current_range_to_value; + END IF; + + -- use range values within the name of partition to have unique partition names + RETURN QUERY + SELECT + substring(table_name_text, 0, max_table_name_length - length(to_char(current_range_from_value, partition_name_format)) - 1) || '_p' || + to_char(current_range_from_value, partition_name_format), + current_range_from_value_text, + current_range_to_value_text; + + current_range_from_value := current_range_to_value; + current_range_to_value := current_range_to_value + partition_interval; + END LOOP; + RETURN; +END; +$$; +COMMENT ON FUNCTION pg_catalog.get_missing_time_partition_ranges( + table_name regclass, + partition_interval INTERVAL, + to_value timestamptz, + from_value timestamptz) +IS 'get missing partitions ranges for table within the range using the given interval'; diff --git a/src/backend/distributed/sql/udfs/get_missing_time_partition_ranges/latest.sql b/src/backend/distributed/sql/udfs/get_missing_time_partition_ranges/latest.sql index 214f03383..4cdca999a 100644 --- a/src/backend/distributed/sql/udfs/get_missing_time_partition_ranges/latest.sql +++ b/src/backend/distributed/sql/udfs/get_missing_time_partition_ranges/latest.sql @@ -34,6 +34,13 @@ DECLARE -- used to determine whether the partition_interval is a day multiple is_day_multiple boolean; + + -- used to support dynamic type casting between the partition column type and timestamptz + custom_cast text; + is_partition_column_castable boolean; + partition regclass; + partition_covers_query text; + partition_exist_query text; BEGIN -- check whether the table is time partitioned table, if not error out SELECT relname, nspname, partnatts, partattrs[0] @@ -58,10 +65,17 @@ BEGIN AND attnum = partition_column_index; -- we currently only support partitioning by date, timestamp, and timestamptz + custom_cast = ''; IF partition_column_type <> 'date'::regtype AND partition_column_type <> 'timestamp'::regtype - AND partition_column_type <> 'timestamptz'::regtype THEN + AND partition_column_type <> 'timestamptz'::regtype THEN + SELECT EXISTS(SELECT OID FROM pg_cast WHERE castsource = partition_column_type AND casttarget = 'timestamptz'::regtype) AND + EXISTS(SELECT OID FROM pg_cast WHERE castsource = 'timestamptz'::regtype AND casttarget = partition_column_type) + INTO is_partition_column_castable; + IF not is_partition_column_castable THEN RAISE 'type of the partition column of the table % must be date, timestamp or timestamptz', table_name; + END IF; + custom_cast = format('::%s', partition_column_type); END IF; IF partition_column_type = 'date'::regtype AND partition_interval IS NOT NULL THEN @@ -76,14 +90,15 @@ BEGIN -- If no partition exists, truncate from_value to find intuitive initial value. -- If any partition exist, use the initial partition as the pivot partition. -- tp.to_value and tp.from_value are equal to '', if default partition exists. - SELECT tp.from_value::timestamptz, tp.to_value::timestamptz + EXECUTE format('SELECT tp.from_value%1$s::timestamptz, tp.to_value%1$s::timestamptz + FROM pg_catalog.time_partitions tp + WHERE parent_table = $1 AND tp.to_value <> '' AND tp.from_value <> '' + ORDER BY tp.from_value%1$s::timestamptz ASC + LIMIT 1', custom_cast) INTO current_range_from_value, current_range_to_value - FROM pg_catalog.time_partitions tp - WHERE parent_table = table_name AND tp.to_value <> '' AND tp.from_value <> '' - ORDER BY tp.from_value::timestamptz ASC - LIMIT 1; + USING table_name; - IF NOT FOUND THEN + IF current_range_from_value is NULL THEN -- Decide on the current_range_from_value of the initial partition according to interval of the table. -- Since we will create all other partitions by adding intervals, truncating given start time will provide -- more intuitive interval ranges, instead of starting from from_value directly. @@ -150,16 +165,24 @@ BEGIN END IF; END IF; + partition_exist_query = format('SELECT partition FROM pg_catalog.time_partitions tp + WHERE tp.from_value%1$s::timestamptz = $1 AND tp.to_value%1$s::timestamptz = $2 AND parent_table = $3', + custom_cast); + partition_covers_query = format('SELECT partition, tp.from_value, tp.to_value + FROM pg_catalog.time_partitions tp + WHERE + (($1 >= tp.from_value%1$s::timestamptz AND $1 < tp.to_value%1$s::timestamptz) OR + ($2 > tp.from_value%1$s::timestamptz AND $2 < tp.to_value%1$s::timestamptz)) AND + parent_table = $3', + custom_cast); + WHILE current_range_from_value < to_value LOOP -- Check whether partition with given range has already been created -- Since partition interval can be given with different types, we are converting -- all variables to timestamptz to make sure that we are comparing same type of parameters - PERFORM * FROM pg_catalog.time_partitions tp - WHERE - tp.from_value::timestamptz = current_range_from_value::timestamptz AND - tp.to_value::timestamptz = current_range_to_value::timestamptz AND - parent_table = table_name; - IF found THEN + EXECUTE partition_exist_query into partition using current_range_from_value, current_range_to_value, table_name; + + IF partition is not NULL THEN current_range_from_value := current_range_to_value; current_range_to_value := current_range_to_value + partition_interval; CONTINUE; @@ -168,20 +191,16 @@ BEGIN -- Check whether any other partition covers from_value or to_value -- That means some partitions doesn't align with the initial partition. -- In other words, gap(s) exist between partitions which is not multiple of intervals. - SELECT partition, tp.from_value::text, tp.to_value::text + EXECUTE partition_covers_query INTO manual_partition, manual_partition_from_value_text, manual_partition_to_value_text - FROM pg_catalog.time_partitions tp - WHERE - ((current_range_from_value::timestamptz >= tp.from_value::timestamptz AND current_range_from_value < tp.to_value::timestamptz) OR - (current_range_to_value::timestamptz > tp.from_value::timestamptz AND current_range_to_value::timestamptz < tp.to_value::timestamptz)) AND - parent_table = table_name; + using current_range_from_value, current_range_to_value, table_name; - IF found THEN + IF manual_partition is not NULL THEN RAISE 'partition % with the range from % to % does not align with the initial partition given the partition interval', manual_partition::text, manual_partition_from_value_text, manual_partition_to_value_text - USING HINT = 'Only use partitions of the same size, without gaps between partitions.'; + USING HINT = 'Only use partitions of the same size, without gaps between partitions.'; END IF; IF partition_column_type = 'date'::regtype THEN @@ -194,7 +213,8 @@ BEGIN SELECT current_range_from_value::timestamptz::text INTO current_range_from_value_text; SELECT current_range_to_value::timestamptz::text INTO current_range_to_value_text; ELSE - RAISE 'type of the partition column of the table % must be date, timestamp or timestamptz', table_name; + EXECUTE format('SELECT $1%s::text', custom_cast) INTO current_range_from_value_text using current_range_from_value; + EXECUTE format('SELECT $1%s::text', custom_cast) INTO current_range_to_value_text using current_range_to_value; END IF; -- use range values within the name of partition to have unique partition names @@ -212,7 +232,7 @@ BEGIN END; $$; COMMENT ON FUNCTION pg_catalog.get_missing_time_partition_ranges( - table_name regclass, + table_name regclass, partition_interval INTERVAL, to_value timestamptz, from_value timestamptz) diff --git a/src/test/regress/expected/multi_partitioning.out b/src/test/regress/expected/multi_partitioning.out index 47139614d..c0a21d4d5 100644 --- a/src/test/regress/expected/multi_partitioning.out +++ b/src/test/regress/expected/multi_partitioning.out @@ -4059,6 +4059,47 @@ BEGIN; ROLLBACK; set client_min_messages to notice; +-- 7) test with bigint partition column +CREATE FUNCTION nanos_to_timestamptz(nanos bigint) RETURNS timestamptz LANGUAGE plpgsql AS +$$ +DECLARE + value timestamptz; +BEGIN + select to_timestamp(nanos * 1.0 / 1000000000) into value; + return value; +END; +$$; +CREATE CAST (bigint AS timestamptz) WITH FUNCTION nanos_to_timestamptz(bigint); +CREATE FUNCTION timestamptz_to_nanos(ts timestamptz) RETURNS bigint LANGUAGE plpgsql AS +$$ +DECLARE + value bigint; +BEGIN + select extract(epoch from ts) * 1000000000 into value; + return value; +END; +$$; +CREATE CAST (timestamptz AS bigint) WITH FUNCTION timestamptz_to_nanos(timestamptz); +CREATE TABLE bigint_partitioned_table (timestamp bigint, description text) partition by range (timestamp); +BEGIN; + SELECT create_time_partitions('bigint_partitioned_table', INTERVAL '1 month', '2023-05-01', '2023-01-1'); + create_time_partitions +--------------------------------------------------------------------- + t +(1 row) + + SELECT * FROM time_partitions WHERE parent_table = 'bigint_partitioned_table'::regclass ORDER BY 3; + parent_table | partition_column | partition | from_value | to_value | access_method +--------------------------------------------------------------------- + bigint_partitioned_table | timestamp | bigint_partitioned_table_p2023_01 | 1672560000000000000 | 1675238400000000000 | heap + bigint_partitioned_table | timestamp | bigint_partitioned_table_p2023_02 | 1675238400000000000 | 1677657600000000000 | heap + bigint_partitioned_table | timestamp | bigint_partitioned_table_p2023_03 | 1677657600000000000 | 1680332400000000000 | heap + bigint_partitioned_table | timestamp | bigint_partitioned_table_p2023_04 | 1680332400000000000 | 1682924400000000000 | heap +(4 rows) + +ROLLBACK; +DROP CAST (bigint AS timestamptz); +DROP CAST (timestamptz AS bigint); -- c) test drop_old_time_partitions -- 1) test with date partitioned table CREATE TABLE date_partitioned_table_to_exp (event_date date, event int) partition by range (event_date); @@ -4359,10 +4400,13 @@ SELECT a, b FROM stxdinp GROUP BY 1, 2; (10 rows) DROP SCHEMA partitioning_schema CASCADE; -NOTICE: drop cascades to 5 other objects +NOTICE: drop cascades to 8 other objects DETAIL: drop cascades to table "schema-test" drop cascades to table another_distributed_table drop cascades to table distributed_parent_table +drop cascades to function nanos_to_timestamptz(bigint) +drop cascades to function timestamptz_to_nanos(timestamp with time zone) +drop cascades to table bigint_partitioned_table drop cascades to table part_table_with_very_long_name drop cascades to table stxdinp RESET search_path; diff --git a/src/test/regress/sql/multi_partitioning.sql b/src/test/regress/sql/multi_partitioning.sql index 6fbd92638..39f7e8316 100644 --- a/src/test/regress/sql/multi_partitioning.sql +++ b/src/test/regress/sql/multi_partitioning.sql @@ -1839,6 +1839,40 @@ BEGIN; SELECT * FROM time_partitions WHERE parent_table = 'date_partitioned_citus_local_table'::regclass ORDER BY 3; ROLLBACK; set client_min_messages to notice; + +-- 7) test with bigint partition column +CREATE FUNCTION nanos_to_timestamptz(nanos bigint) RETURNS timestamptz LANGUAGE plpgsql AS +$$ +DECLARE + value timestamptz; +BEGIN + select to_timestamp(nanos * 1.0 / 1000000000) into value; + return value; +END; +$$; +CREATE CAST (bigint AS timestamptz) WITH FUNCTION nanos_to_timestamptz(bigint); + +CREATE FUNCTION timestamptz_to_nanos(ts timestamptz) RETURNS bigint LANGUAGE plpgsql AS +$$ +DECLARE + value bigint; +BEGIN + select extract(epoch from ts) * 1000000000 into value; + return value; +END; +$$; +CREATE CAST (timestamptz AS bigint) WITH FUNCTION timestamptz_to_nanos(timestamptz); + +CREATE TABLE bigint_partitioned_table (timestamp bigint, description text) partition by range (timestamp); + +BEGIN; + SELECT create_time_partitions('bigint_partitioned_table', INTERVAL '1 month', '2023-05-01', '2023-01-1'); + SELECT * FROM time_partitions WHERE parent_table = 'bigint_partitioned_table'::regclass ORDER BY 3; +ROLLBACK; + +DROP CAST (bigint AS timestamptz); +DROP CAST (timestamptz AS bigint); + -- c) test drop_old_time_partitions -- 1) test with date partitioned table CREATE TABLE date_partitioned_table_to_exp (event_date date, event int) partition by range (event_date); From 58da8771aa5a17a0c083855c574488a57eb65045 Mon Sep 17 00:00:00 2001 From: Teja Mupparti Date: Tue, 16 May 2023 16:45:06 -0700 Subject: [PATCH 107/118] This pull request introduces support for nonroutable merge commands in the following scenarios: 1) For distributed tables that are not colocated. 2) When joining on a non-distribution column for colocated tables. 3) When merging into a distributed table using reference or citus-local tables as the data source. This is accomplished primarily through the implementation of the following two strategies. Repartition: Plan the source query independently, execute the results into intermediate files, and repartition the files to co-locate them with the merge-target table. Subsequently, compile a final merge query on the target table using the intermediate results as the data source. Pull-to-coordinator: Execute the plan that requires evaluation at the coordinator, run the query on the coordinator, and redistribute the resulting rows to ensure colocation with the target shards. Direct the MERGE SQL operation to the worker nodes' target shards, using the intermediate files colocated with the data as the data source. --- src/backend/distributed/commands/multi_copy.c | 44 +- .../distributed/executor/adaptive_executor.c | 30 +- .../distributed/executor/citus_custom_scan.c | 42 +- .../distributed_intermediate_results.c | 12 + .../executor/insert_select_executor.c | 9 +- .../distributed/executor/merge_executor.c | 337 +++++ .../executor/multi_server_executor.c | 6 + .../executor/repartition_executor.c | 23 +- .../distributed/planner/distributed_planner.c | 13 +- .../planner/insert_select_planner.c | 3 +- .../planner/local_distributed_join_planner.c | 14 +- .../distributed/planner/merge_planner.c | 1286 ++++++++++++----- .../distributed/planner/multi_explain.c | 67 +- .../distributed/planner/multi_join_order.c | 4 +- .../planner/multi_router_planner.c | 43 +- .../distributed/planner/recursive_planning.c | 3 +- src/include/distributed/citus_custom_scan.h | 1 + src/include/distributed/commands/multi_copy.h | 6 + .../distributed/insert_select_executor.h | 3 +- .../distributed/insert_select_planner.h | 1 + .../local_distributed_join_planner.h | 1 + src/include/distributed/merge_executor.h | 17 + src/include/distributed/merge_planner.h | 16 +- src/include/distributed/multi_executor.h | 8 + src/include/distributed/multi_join_order.h | 2 + .../distributed/multi_physical_planner.h | 7 + .../distributed/multi_router_planner.h | 1 + .../distributed/multi_server_executor.h | 3 +- src/include/distributed/recursive_planning.h | 1 + .../distributed/repartition_executor.h | 2 +- src/test/regress/bin/normalize.sed | 2 + src/test/regress/citus_tests/config.py | 1 + src/test/regress/expected/merge.out | 908 ++++++++++-- src/test/regress/expected/merge_arbitrary.out | 48 + .../expected/merge_arbitrary_create.out | 74 + .../expected/merge_partition_tables.out | 230 +++ .../expected/merge_partition_tables_0.out | 6 + .../regress/expected/merge_repartition1.out | 1245 ++++++++++++++++ .../regress/expected/merge_repartition1_0.out | 6 + .../regress/expected/merge_repartition2.out | 212 +++ .../regress/expected/merge_repartition2_0.out | 6 + src/test/regress/expected/pg15.out | 11 +- src/test/regress/expected/pgmerge.out | 8 + src/test/regress/multi_schedule | 5 +- src/test/regress/sql/merge.sql | 489 ++++++- src/test/regress/sql/merge_arbitrary.sql | 45 + .../regress/sql/merge_arbitrary_create.sql | 54 + .../regress/sql/merge_partition_tables.sql | 164 +++ src/test/regress/sql/merge_repartition1.sql | 515 +++++++ src/test/regress/sql/merge_repartition2.sql | 139 ++ src/test/regress/sql/pg15.sql | 2 +- src/test/regress/sql/pgmerge.sql | 4 + 52 files changed, 5554 insertions(+), 625 deletions(-) create mode 100644 src/backend/distributed/executor/merge_executor.c create mode 100644 src/include/distributed/merge_executor.h create mode 100644 src/test/regress/expected/merge_partition_tables.out create mode 100644 src/test/regress/expected/merge_partition_tables_0.out create mode 100644 src/test/regress/expected/merge_repartition1.out create mode 100644 src/test/regress/expected/merge_repartition1_0.out create mode 100644 src/test/regress/expected/merge_repartition2.out create mode 100644 src/test/regress/expected/merge_repartition2_0.out create mode 100644 src/test/regress/sql/merge_partition_tables.sql create mode 100644 src/test/regress/sql/merge_repartition1.sql create mode 100644 src/test/regress/sql/merge_repartition2.sql diff --git a/src/backend/distributed/commands/multi_copy.c b/src/backend/distributed/commands/multi_copy.c index 8e92fd7a8..f8e6378d4 100644 --- a/src/backend/distributed/commands/multi_copy.c +++ b/src/backend/distributed/commands/multi_copy.c @@ -2128,12 +2128,36 @@ CitusCopyDestReceiverStartup(DestReceiver *dest, int operation, int columnCount = inputTupleDescriptor->natts; Oid *finalTypeArray = palloc0(columnCount * sizeof(Oid)); - copyDest->columnCoercionPaths = - ColumnCoercionPaths(destTupleDescriptor, inputTupleDescriptor, - tableId, columnNameList, finalTypeArray); - - copyDest->columnOutputFunctions = - TypeOutputFunctions(columnCount, finalTypeArray, copyOutState->binary); + /* + * To ensure the proper co-location and distribution of the target table, + * the entire process of repartitioning intermediate files requires the + * destReceiver to be created on the target rather than the source. + * + * Within this specific code path, it is assumed that the employed model + * is for insert-select. Consequently, it validates the column types of + * destTupleDescriptor(target) during the intermediate result generation + * process. However, this approach varies significantly for MERGE operations, + * where the source tuple(s) can have arbitrary types and are not required to + * align with the target column names. + * + * Despite this minor setback, a significant portion of the code responsible + * for repartitioning intermediate files can be reused for the MERGE + * operation. By leveraging the ability to perform actual coercion during + * the writing process to the target table, we can bypass this specific route. + */ + if (copyDest->skipCoercions) + { + copyDest->columnOutputFunctions = + ColumnOutputFunctions(inputTupleDescriptor, copyOutState->binary); + } + else + { + copyDest->columnCoercionPaths = + ColumnCoercionPaths(destTupleDescriptor, inputTupleDescriptor, + tableId, columnNameList, finalTypeArray); + copyDest->columnOutputFunctions = + TypeOutputFunctions(columnCount, finalTypeArray, copyOutState->binary); + } } /* wrap the column names as Values */ @@ -2597,9 +2621,11 @@ ShardIdForTuple(CitusCopyDestReceiver *copyDest, Datum *columnValues, bool *colu /* find the partition column value */ partitionColumnValue = columnValues[partitionColumnIndex]; - - /* annoyingly this is evaluated twice, but at least we don't crash! */ - partitionColumnValue = CoerceColumnValue(partitionColumnValue, coercePath); + if (!copyDest->skipCoercions) + { + /* annoyingly this is evaluated twice, but at least we don't crash! */ + partitionColumnValue = CoerceColumnValue(partitionColumnValue, coercePath); + } } /* diff --git a/src/backend/distributed/executor/adaptive_executor.c b/src/backend/distributed/executor/adaptive_executor.c index d88cdbea8..0c710909b 100644 --- a/src/backend/distributed/executor/adaptive_executor.c +++ b/src/backend/distributed/executor/adaptive_executor.c @@ -1015,6 +1015,32 @@ ExecuteTaskListOutsideTransaction(RowModifyLevel modLevel, List *taskList, } +/* + * ExecuteTaskListIntoTupleDestWithParam is a proxy to ExecuteTaskListExtended() which uses + * bind params from executor state, and with defaults for some of the arguments. + */ +uint64 +ExecuteTaskListIntoTupleDestWithParam(RowModifyLevel modLevel, List *taskList, + TupleDestination *tupleDest, + bool expectResults, + ParamListInfo paramListInfo) +{ + int targetPoolSize = MaxAdaptiveExecutorPoolSize; + bool localExecutionSupported = true; + ExecutionParams *executionParams = CreateBasicExecutionParams( + modLevel, taskList, targetPoolSize, localExecutionSupported + ); + + executionParams->xactProperties = DecideTransactionPropertiesForTaskList( + modLevel, taskList, false); + executionParams->expectResults = expectResults; + executionParams->tupleDestination = tupleDest; + executionParams->paramListInfo = paramListInfo; + + return ExecuteTaskListExtended(executionParams); +} + + /* * ExecuteTaskListIntoTupleDest is a proxy to ExecuteTaskListExtended() with defaults * for some of the arguments. @@ -1052,7 +1078,6 @@ ExecuteTaskListExtended(ExecutionParams *executionParams) return 0; } - ParamListInfo paramListInfo = NULL; uint64 locallyProcessedRows = 0; TupleDestination *defaultTupleDest = executionParams->tupleDestination; @@ -1065,7 +1090,7 @@ ExecuteTaskListExtended(ExecutionParams *executionParams) DistributedExecution *execution = CreateDistributedExecution( executionParams->modLevel, executionParams->taskList, - paramListInfo, executionParams->targetPoolSize, + executionParams->paramListInfo, executionParams->targetPoolSize, defaultTupleDest, &executionParams->xactProperties, executionParams->jobIdList, executionParams->localExecutionSupported); @@ -1117,6 +1142,7 @@ CreateBasicExecutionParams(RowModifyLevel modLevel, executionParams->expectResults = false; executionParams->isUtilityCommand = false; executionParams->jobIdList = NIL; + executionParams->paramListInfo = NULL; return executionParams; } diff --git a/src/backend/distributed/executor/citus_custom_scan.c b/src/backend/distributed/executor/citus_custom_scan.c index 3dc1f5068..a2fbb1f59 100644 --- a/src/backend/distributed/executor/citus_custom_scan.c +++ b/src/backend/distributed/executor/citus_custom_scan.c @@ -27,6 +27,8 @@ #include "distributed/listutils.h" #include "distributed/local_executor.h" #include "distributed/local_plan_cache.h" +#include "distributed/merge_executor.h" +#include "distributed/merge_planner.h" #include "distributed/multi_executor.h" #include "distributed/multi_server_executor.h" #include "distributed/multi_router_planner.h" @@ -53,6 +55,7 @@ extern AllowedDistributionColumn AllowedDistributionColumnValue; static Node * AdaptiveExecutorCreateScan(CustomScan *scan); static Node * NonPushableInsertSelectCreateScan(CustomScan *scan); static Node * DelayedErrorCreateScan(CustomScan *scan); +static Node * NonPushableMergeCommandCreateScan(CustomScan *scan); /* functions that are common to different scans */ static void CitusBeginScan(CustomScanState *node, EState *estate, int eflags); @@ -88,6 +91,11 @@ CustomScanMethods DelayedErrorCustomScanMethods = { DelayedErrorCreateScan }; +CustomScanMethods NonPushableMergeCommandCustomScanMethods = { + "Citus MERGE INTO ...", + NonPushableMergeCommandCreateScan +}; + /* * Define executor methods for the different executor types. @@ -111,6 +119,16 @@ static CustomExecMethods NonPushableInsertSelectCustomExecMethods = { }; +static CustomExecMethods NonPushableMergeCommandCustomExecMethods = { + .CustomName = "NonPushableMergeCommandScan", + .BeginCustomScan = CitusBeginScan, + .ExecCustomScan = NonPushableMergeCommandExecScan, + .EndCustomScan = CitusEndScan, + .ReScanCustomScan = CitusReScan, + .ExplainCustomScan = NonPushableMergeCommandExplainScan +}; + + /* * IsCitusCustomState returns if a given PlanState node is a CitusCustomState node. */ @@ -124,7 +142,8 @@ IsCitusCustomState(PlanState *planState) CustomScanState *css = castNode(CustomScanState, planState); if (css->methods == &AdaptiveExecutorCustomExecMethods || - css->methods == &NonPushableInsertSelectCustomExecMethods) + css->methods == &NonPushableInsertSelectCustomExecMethods || + css->methods == &NonPushableMergeCommandCustomExecMethods) { return true; } @@ -142,6 +161,7 @@ RegisterCitusCustomScanMethods(void) RegisterCustomScanMethods(&AdaptiveExecutorCustomScanMethods); RegisterCustomScanMethods(&NonPushableInsertSelectCustomScanMethods); RegisterCustomScanMethods(&DelayedErrorCustomScanMethods); + RegisterCustomScanMethods(&NonPushableMergeCommandCustomScanMethods); } @@ -723,6 +743,26 @@ DelayedErrorCreateScan(CustomScan *scan) } +/* + * NonPushableMergeCommandCreateScan creates the scan state for executing + * MERGE INTO ... into a distributed table with repartition of source rows. + */ +static Node * +NonPushableMergeCommandCreateScan(CustomScan *scan) +{ + CitusScanState *scanState = palloc0(sizeof(CitusScanState)); + + scanState->executorType = MULTI_EXECUTOR_NON_PUSHABLE_MERGE_QUERY; + scanState->customScanState.ss.ps.type = T_CustomScanState; + scanState->distributedPlan = GetDistributedPlan(scan); + scanState->customScanState.methods = &NonPushableMergeCommandCustomExecMethods; + scanState->finishedPreScan = false; + scanState->finishedRemoteScan = false; + + return (Node *) scanState; +} + + /* * CitusEndScan is used to clean up tuple store of the given custom scan state. */ diff --git a/src/backend/distributed/executor/distributed_intermediate_results.c b/src/backend/distributed/executor/distributed_intermediate_results.c index e7a5830e6..c10303e18 100644 --- a/src/backend/distributed/executor/distributed_intermediate_results.c +++ b/src/backend/distributed/executor/distributed_intermediate_results.c @@ -610,6 +610,18 @@ QueryStringForFragmentsTransfer(NodeToNodeFragmentsTransfer *fragmentsTransfer) StringInfo fragmentNamesArrayString = makeStringInfo(); int fragmentCount = 0; NodePair *nodePair = &fragmentsTransfer->nodes; + uint32 sourceNodeId = nodePair->sourceNodeId; + + /* + * If the placement is dummy, for example, queries that generate + * intermediate results at the coordinator that need to be redistributed + * to worker nodes, we need the local id. + */ + if (sourceNodeId == LOCAL_NODE_ID) + { + nodePair->sourceNodeId = GetLocalNodeId(); + } + WorkerNode *sourceNode = LookupNodeByNodeIdOrError(nodePair->sourceNodeId); appendStringInfoString(fragmentNamesArrayString, "ARRAY["); diff --git a/src/backend/distributed/executor/insert_select_executor.c b/src/backend/distributed/executor/insert_select_executor.c index 58312ba19..4a15289e6 100644 --- a/src/backend/distributed/executor/insert_select_executor.c +++ b/src/backend/distributed/executor/insert_select_executor.c @@ -20,6 +20,7 @@ #include "distributed/insert_select_planner.h" #include "distributed/intermediate_results.h" #include "distributed/local_executor.h" +#include "distributed/merge_planner.h" #include "distributed/multi_executor.h" #include "distributed/multi_partitioning_utils.h" #include "distributed/multi_physical_planner.h" @@ -63,8 +64,6 @@ static HTAB * ExecutePlanIntoColocatedIntermediateResults(Oid targetRelationId, PlannedStmt *selectPlan, EState *executorState, char *intermediateResultIdPrefix); -static List * BuildColumnNameListFromTargetList(Oid targetRelationId, - List *insertTargetList); static int PartitionColumnIndexFromColumnList(Oid relationId, List *columnNameList); static void WrapTaskListForProjection(List *taskList, List *projectedTargetEntries); @@ -374,7 +373,7 @@ ExecutePlanIntoRelation(Oid targetRelationId, List *insertTargetList, * BuildColumnNameListForCopyStatement build the column name list given the insert * target list. */ -static List * +List * BuildColumnNameListFromTargetList(Oid targetRelationId, List *insertTargetList) { List *columnNameList = NIL; @@ -424,13 +423,13 @@ PartitionColumnIndexFromColumnList(Oid relationId, List *columnNameList) * given target list. */ int -DistributionColumnIndex(List *insertTargetList, Var *partitionColumn) +DistributionColumnIndex(List *insertTargetList, Var *distributionColumn) { TargetEntry *insertTargetEntry = NULL; int targetEntryIndex = 0; foreach_ptr(insertTargetEntry, insertTargetList) { - if (insertTargetEntry->resno == partitionColumn->varattno) + if (insertTargetEntry->resno == distributionColumn->varattno) { return targetEntryIndex; } diff --git a/src/backend/distributed/executor/merge_executor.c b/src/backend/distributed/executor/merge_executor.c new file mode 100644 index 000000000..f501497c0 --- /dev/null +++ b/src/backend/distributed/executor/merge_executor.c @@ -0,0 +1,337 @@ +/*------------------------------------------------------------------------- + * + * merge_executor.c + * + * Executor logic for MERGE SQL statement. + * + * Copyright (c) Citus Data, Inc. + *------------------------------------------------------------------------- + */ + +#include "postgres.h" +#include "miscadmin.h" + +#include "distributed/distributed_execution_locks.h" +#include "distributed/insert_select_executor.h" +#include "distributed/intermediate_results.h" +#include "distributed/listutils.h" +#include "distributed/merge_executor.h" +#include "distributed/merge_planner.h" +#include "distributed/multi_executor.h" +#include "distributed/multi_partitioning_utils.h" +#include "distributed/multi_router_planner.h" +#include "distributed/repartition_executor.h" +#include "distributed/subplan_execution.h" + +#include "nodes/execnodes.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" + +static void ExecuteSourceAtWorkerAndRepartition(CitusScanState *scanState); +static void ExecuteSourceAtCoordAndRedistribution(CitusScanState *scanState); +static HTAB * ExecuteMergeSourcePlanIntoColocatedIntermediateResults(Oid targetRelationId, + Query *mergeQuery, + List * + sourceTargetList, + PlannedStmt * + sourcePlan, + EState *executorState, + char * + intermediateResultIdPrefix, + int + partitionColumnIndex); + + +/* + * NonPushableMergeCommandExecScan performs an MERGE INTO distributed_table + * USING (source-query) ... command. This can be done either by aggregating + * task results at the coordinator and repartitioning the results, or by + * repartitioning task results and directly transferring data between nodes. + */ +TupleTableSlot * +NonPushableMergeCommandExecScan(CustomScanState *node) +{ + CitusScanState *scanState = (CitusScanState *) node; + DistributedPlan *distributedPlan = scanState->distributedPlan; + + if (!scanState->finishedRemoteScan) + { + switch (distributedPlan->modifyWithSelectMethod) + { + case MODIFY_WITH_SELECT_REPARTITION: + { + ExecuteSourceAtWorkerAndRepartition(scanState); + break; + } + + case MODIFY_WITH_SELECT_VIA_COORDINATOR: + { + ExecuteSourceAtCoordAndRedistribution(scanState); + break; + } + + default: + { + ereport(ERROR, (errmsg("Unexpected MERGE execution method(%d)", + distributedPlan->modifyWithSelectMethod))); + } + } + + scanState->finishedRemoteScan = true; + } + + TupleTableSlot *resultSlot = ReturnTupleFromTuplestore(scanState); + + return resultSlot; +} + + +/* + * ExecuteSourceAtWorkerAndRepartition Executes the Citus distributed plan, including any + * sub-plans, and captures the results in intermediate files. Subsequently, redistributes + * the result files to ensure colocation with the target, and directs the MERGE SQL + * operation to the target shards on the worker nodes, utilizing the colocated + * intermediate files as the data source. + */ +static void +ExecuteSourceAtWorkerAndRepartition(CitusScanState *scanState) +{ + DistributedPlan *distributedPlan = scanState->distributedPlan; + Query *mergeQuery = + copyObject(distributedPlan->modifyQueryViaCoordinatorOrRepartition); + RangeTblEntry *targetRte = ExtractResultRelationRTE(mergeQuery); + RangeTblEntry *sourceRte = ExtractMergeSourceRangeTableEntry(mergeQuery); + Oid targetRelationId = targetRte->relid; + bool hasReturning = distributedPlan->expectResults; + Query *sourceQuery = sourceRte->subquery; + PlannedStmt *sourcePlan = + copyObject(distributedPlan->selectPlanForModifyViaCoordinatorOrRepartition); + EState *executorState = ScanStateGetExecutorState(scanState); + + /* + * If we are dealing with partitioned table, we also need to lock its + * partitions. Here we only lock targetRelation, we acquire necessary + * locks on source tables during execution of those source queries. + */ + if (PartitionedTable(targetRelationId)) + { + LockPartitionRelations(targetRelationId, RowExclusiveLock); + } + + bool randomAccess = true; + bool interTransactions = false; + DistributedPlan *distSourcePlan = + GetDistributedPlan((CustomScan *) sourcePlan->planTree); + Job *distSourceJob = distSourcePlan->workerJob; + List *distSourceTaskList = distSourceJob->taskList; + bool binaryFormat = + CanUseBinaryCopyFormatForTargetList(sourceQuery->targetList); + + ereport(DEBUG1, (errmsg("Executing subplans of the source query and " + "storing the results at the respective node(s)"))); + + ExecuteSubPlans(distSourcePlan); + + /* + * We have a separate directory for each transaction, so choosing + * the same result prefix won't cause filename conflicts. Results + * directory name also includes node id and database id, so we don't + * need to include them in the filename. We include job id here for + * the case "MERGE USING " is executed recursively. + */ + StringInfo distResultPrefixString = makeStringInfo(); + appendStringInfo(distResultPrefixString, + "repartitioned_results_" UINT64_FORMAT, + distSourceJob->jobId); + char *distResultPrefix = distResultPrefixString->data; + CitusTableCacheEntry *targetRelation = GetCitusTableCacheEntry(targetRelationId); + + ereport(DEBUG1, (errmsg("Redistributing source result rows across nodes"))); + + /* + * partitionColumnIndex determines the column in the selectTaskList to + * use for (re)partitioning of the source result, which will colocate + * the result data with the target. + */ + int partitionColumnIndex = distributedPlan->sourceResultRepartitionColumnIndex; + + /* + * Below call partitions the results using shard ranges and partition method of + * targetRelation, and then colocates the result files with shards. These + * transfers are done by calls to fetch_intermediate_results() between nodes. + */ + List **redistributedResults = + RedistributeTaskListResults(distResultPrefix, + distSourceTaskList, partitionColumnIndex, + targetRelation, binaryFormat); + + ereport(DEBUG1, (errmsg("Executing final MERGE on workers using " + "intermediate results"))); + + /* + * At this point source query has been executed on workers and results + * have been fetched in such a way that they are colocated with corresponding + * target shard(s). Create and execute a list of tasks of form + * MERGE INTO ... USING SELECT * FROM read_intermediate_results(...); + */ + List *taskList = + GenerateTaskListWithRedistributedResults(mergeQuery, + targetRelation, + redistributedResults, + binaryFormat); + + scanState->tuplestorestate = + tuplestore_begin_heap(randomAccess, interTransactions, work_mem); + ParamListInfo paramListInfo = executorState->es_param_list_info; + TupleDesc tupleDescriptor = ScanStateGetTupleDescriptor(scanState); + TupleDestination *tupleDest = + CreateTupleStoreTupleDest(scanState->tuplestorestate, + tupleDescriptor); + uint64 rowsMerged = + ExecuteTaskListIntoTupleDestWithParam(ROW_MODIFY_NONCOMMUTATIVE, taskList, + tupleDest, + hasReturning, + paramListInfo); + executorState->es_processed = rowsMerged; +} + + +/* + * ExecuteSourceAtCoordAndRedistribution Executes the plan that necessitates evaluation + * at the coordinator and redistributes the resulting rows to intermediate files, + * ensuring colocation with the target shards. Directs the MERGE SQL operation to the + * target shards on the worker nodes, utilizing the colocated intermediate files as the + * data source. + */ +void +ExecuteSourceAtCoordAndRedistribution(CitusScanState *scanState) +{ + EState *executorState = ScanStateGetExecutorState(scanState); + DistributedPlan *distributedPlan = scanState->distributedPlan; + Query *mergeQuery = + copyObject(distributedPlan->modifyQueryViaCoordinatorOrRepartition); + RangeTblEntry *targetRte = ExtractResultRelationRTE(mergeQuery); + RangeTblEntry *sourceRte = ExtractMergeSourceRangeTableEntry(mergeQuery); + Query *sourceQuery = sourceRte->subquery; + Oid targetRelationId = targetRte->relid; + PlannedStmt *sourcePlan = + copyObject(distributedPlan->selectPlanForModifyViaCoordinatorOrRepartition); + char *intermediateResultIdPrefix = distributedPlan->intermediateResultIdPrefix; + bool hasReturning = distributedPlan->expectResults; + int partitionColumnIndex = distributedPlan->sourceResultRepartitionColumnIndex; + + /* + * If we are dealing with partitioned table, we also need to lock its + * partitions. Here we only lock targetRelation, we acquire necessary + * locks on source tables during execution of those source queries. + */ + if (PartitionedTable(targetRelationId)) + { + LockPartitionRelations(targetRelationId, RowExclusiveLock); + } + + ereport(DEBUG1, (errmsg("Collect source query results on coordinator"))); + + List *prunedTaskList = NIL; + HTAB *shardStateHash = + ExecuteMergeSourcePlanIntoColocatedIntermediateResults( + targetRelationId, + mergeQuery, + sourceQuery->targetList, + sourcePlan, + executorState, + intermediateResultIdPrefix, + partitionColumnIndex); + + ereport(DEBUG1, (errmsg("Create a MERGE task list that needs to be routed"))); + + /* generate tasks for the .. phase */ + List *taskList = + GenerateTaskListWithColocatedIntermediateResults(targetRelationId, mergeQuery, + intermediateResultIdPrefix); + + /* + * We cannot actually execute MERGE INTO ... tasks that read from + * intermediate results that weren't created because no rows were + * written to them. Prune those tasks out by only including tasks + * on shards with connections. + */ + Task *task = NULL; + foreach_ptr(task, taskList) + { + uint64 shardId = task->anchorShardId; + bool shardModified = false; + + hash_search(shardStateHash, &shardId, HASH_FIND, &shardModified); + if (shardModified) + { + prunedTaskList = lappend(prunedTaskList, task); + } + } + + if (prunedTaskList == NIL) + { + /* No task to execute */ + return; + } + + ereport(DEBUG1, (errmsg("Execute MERGE task list"))); + bool randomAccess = true; + bool interTransactions = false; + Assert(scanState->tuplestorestate == NULL); + scanState->tuplestorestate = tuplestore_begin_heap(randomAccess, interTransactions, + work_mem); + TupleDesc tupleDescriptor = ScanStateGetTupleDescriptor(scanState); + ParamListInfo paramListInfo = executorState->es_param_list_info; + TupleDestination *tupleDest = + CreateTupleStoreTupleDest(scanState->tuplestorestate, tupleDescriptor); + uint64 rowsMerged = + ExecuteTaskListIntoTupleDestWithParam(ROW_MODIFY_NONCOMMUTATIVE, + prunedTaskList, + tupleDest, + hasReturning, + paramListInfo); + executorState->es_processed = rowsMerged; +} + + +/* + * ExecuteMergeSourcePlanIntoColocatedIntermediateResults Executes the given PlannedStmt + * and inserts tuples into a set of intermediate results that are colocated with the + * target table for further processing MERGE INTO. It also returns the hash of shard + * states that were used to insert tuplesinto the target relation. + */ +static HTAB * +ExecuteMergeSourcePlanIntoColocatedIntermediateResults(Oid targetRelationId, + Query *mergeQuery, + List *sourceTargetList, + PlannedStmt *sourcePlan, + EState *executorState, + char *intermediateResultIdPrefix, + int partitionColumnIndex) +{ + ParamListInfo paramListInfo = executorState->es_param_list_info; + + /* Get column name list and partition column index for the target table */ + List *columnNameList = + BuildColumnNameListFromTargetList(targetRelationId, sourceTargetList); + + /* set up a DestReceiver that copies into the intermediate file */ + const bool publishableData = false; + CitusCopyDestReceiver *copyDest = CreateCitusCopyDestReceiver(targetRelationId, + columnNameList, + partitionColumnIndex, + executorState, + intermediateResultIdPrefix, + publishableData); + + /* We can skip when writing to intermediate files */ + copyDest->skipCoercions = true; + + ExecutePlanIntoDestReceiver(sourcePlan, paramListInfo, (DestReceiver *) copyDest); + + executorState->es_processed = copyDest->tuplesSent; + XactModificationLevel = XACT_MODIFICATION_DATA; + + return copyDest->shardStateHash; +} diff --git a/src/backend/distributed/executor/multi_server_executor.c b/src/backend/distributed/executor/multi_server_executor.c index 07f3d6856..ac144c350 100644 --- a/src/backend/distributed/executor/multi_server_executor.c +++ b/src/backend/distributed/executor/multi_server_executor.c @@ -24,6 +24,7 @@ #include "distributed/multi_executor.h" #include "distributed/multi_physical_planner.h" #include "distributed/multi_server_executor.h" +#include "distributed/multi_router_planner.h" #include "distributed/coordinator_protocol.h" #include "distributed/subplan_execution.h" #include "distributed/tuple_destination.h" @@ -49,6 +50,11 @@ JobExecutorType(DistributedPlan *distributedPlan) if (distributedPlan->modifyQueryViaCoordinatorOrRepartition != NULL) { + if (IsMergeQuery(distributedPlan->modifyQueryViaCoordinatorOrRepartition)) + { + return MULTI_EXECUTOR_NON_PUSHABLE_MERGE_QUERY; + } + /* * We go through * MULTI_EXECUTOR_NON_PUSHABLE_INSERT_SELECT because diff --git a/src/backend/distributed/executor/repartition_executor.c b/src/backend/distributed/executor/repartition_executor.c index b35527b99..af4f0ac7e 100644 --- a/src/backend/distributed/executor/repartition_executor.c +++ b/src/backend/distributed/executor/repartition_executor.c @@ -120,7 +120,7 @@ GenerateTaskListWithColocatedIntermediateResults(Oid targetRelationId, */ Query *modifyWithResultQuery = copyObject(modifyQueryViaCoordinatorOrRepartition); RangeTblEntry *insertRte = ExtractResultRelationRTE(modifyWithResultQuery); - RangeTblEntry *selectRte = ExtractSelectRangeTableEntry(modifyWithResultQuery); + RangeTblEntry *selectRte = ExtractSourceResultRangeTableEntry(modifyWithResultQuery); CitusTableCacheEntry *targetCacheEntry = GetCitusTableCacheEntry(targetRelationId); int shardCount = targetCacheEntry->shardIntervalArrayLength; @@ -139,11 +139,18 @@ GenerateTaskListWithColocatedIntermediateResults(Oid targetRelationId, /* during COPY, the shard ID is appended to the result name */ appendStringInfo(resultId, "%s_" UINT64_FORMAT, resultIdPrefix, shardId); + /* + * For MERGE SQL, use the USING clause list, the main query target list + * is NULL + */ + List *targetList = IsMergeQuery(modifyQueryViaCoordinatorOrRepartition) ? + selectRte->subquery->targetList : + modifyQueryViaCoordinatorOrRepartition->targetList; + /* generate the query on the intermediate result */ - Query *resultSelectQuery = BuildSubPlanResultQuery( - modifyQueryViaCoordinatorOrRepartition->targetList, - columnAliasList, - resultId->data); + Query *resultSelectQuery = BuildSubPlanResultQuery(targetList, + columnAliasList, + resultId->data); /* put the intermediate result query in the INSERT..SELECT */ selectRte->subquery = resultSelectQuery; @@ -214,8 +221,6 @@ GenerateTaskListWithRedistributedResults(Query *modifyQueryViaCoordinatorOrRepar */ Query *modifyResultQuery = copyObject(modifyQueryViaCoordinatorOrRepartition); RangeTblEntry *insertRte = ExtractResultRelationRTE(modifyResultQuery); - RangeTblEntry *selectRte = ExtractSelectRangeTableEntry(modifyResultQuery); - List *selectTargetList = selectRte->subquery->targetList; Oid targetRelationId = targetRelation->relationId; int shardCount = targetRelation->shardIntervalArrayLength; @@ -223,6 +228,10 @@ GenerateTaskListWithRedistributedResults(Query *modifyQueryViaCoordinatorOrRepar uint32 taskIdIndex = 1; uint64 jobId = INVALID_JOB_ID; + RangeTblEntry *selectRte = + ExtractSourceResultRangeTableEntry(modifyResultQuery); + List *selectTargetList = selectRte->subquery->targetList; + for (shardOffset = 0; shardOffset < shardCount; shardOffset++) { ShardInterval *targetShardInterval = diff --git a/src/backend/distributed/planner/distributed_planner.c b/src/backend/distributed/planner/distributed_planner.c index e53259b77..3b6a8f9f7 100644 --- a/src/backend/distributed/planner/distributed_planner.c +++ b/src/backend/distributed/planner/distributed_planner.c @@ -925,6 +925,10 @@ GetRouterPlanType(Query *query, Query *originalQuery, bool hasUnresolvedParams) } else if (IsMergeQuery(originalQuery)) { + if (hasUnresolvedParams) + { + return REPLAN_WITH_BOUND_PARAMETERS; + } return MERGE_QUERY; } else @@ -990,7 +994,8 @@ CreateDistributedPlan(uint64 planId, bool allowRecursivePlanning, Query *origina case MERGE_QUERY: { distributedPlan = - CreateMergePlan(originalQuery, query, plannerRestrictionContext); + CreateMergePlan(planId, originalQuery, query, plannerRestrictionContext, + boundParams); break; } @@ -1377,6 +1382,12 @@ FinalizePlan(PlannedStmt *localPlan, DistributedPlan *distributedPlan) break; } + case MULTI_EXECUTOR_NON_PUSHABLE_MERGE_QUERY: + { + customScan->methods = &NonPushableMergeCommandCustomScanMethods; + break; + } + default: { customScan->methods = &DelayedErrorCustomScanMethods; diff --git a/src/backend/distributed/planner/insert_select_planner.c b/src/backend/distributed/planner/insert_select_planner.c index a44db5c28..84e76c6d4 100644 --- a/src/backend/distributed/planner/insert_select_planner.c +++ b/src/backend/distributed/planner/insert_select_planner.c @@ -86,7 +86,6 @@ static DeferredErrorMessage * InsertPartitionColumnMatchesSelect(Query *query, static DistributedPlan * CreateNonPushableInsertSelectPlan(uint64 planId, Query *parse, ParamListInfo boundParams); static DeferredErrorMessage * NonPushableInsertSelectSupported(Query *insertSelectQuery); -static Query * WrapSubquery(Query *subquery); static void RelabelTargetEntryList(List *selectTargetList, List *insertTargetList); static List * AddInsertSelectCasts(List *insertTargetList, List *selectTargetList, Oid targetRelationId); @@ -1477,7 +1476,7 @@ InsertSelectResultIdPrefix(uint64 planId) * WrapSubquery wraps the given query as a subquery in a newly constructed * "SELECT * FROM (...subquery...) citus_insert_select_subquery" query. */ -static Query * +Query * WrapSubquery(Query *subquery) { ParseState *pstate = make_parsestate(NULL); diff --git a/src/backend/distributed/planner/local_distributed_join_planner.c b/src/backend/distributed/planner/local_distributed_join_planner.c index 449b96195..2c6a63de1 100644 --- a/src/backend/distributed/planner/local_distributed_join_planner.c +++ b/src/backend/distributed/planner/local_distributed_join_planner.c @@ -485,6 +485,8 @@ RequiredAttrNumbersForRelation(RangeTblEntry *rangeTableEntry, PlannerInfo *plannerInfo = relationRestriction->plannerInfo; + int rteIndex = relationRestriction->index; + /* * Here we used the query from plannerInfo because it has the optimizations * so that it doesn't have unnecessary columns. The original query doesn't have @@ -492,8 +494,18 @@ RequiredAttrNumbersForRelation(RangeTblEntry *rangeTableEntry, * 'required' attributes. */ Query *queryToProcess = plannerInfo->parse; - int rteIndex = relationRestriction->index; + return RequiredAttrNumbersForRelationInternal(queryToProcess, rteIndex); +} + + +/* + * RequiredAttrNumbersForRelationInternal returns the required attribute numbers + * for the input range-table-index in the query parameter. + */ +List * +RequiredAttrNumbersForRelationInternal(Query *queryToProcess, int rteIndex) +{ List *allVarsInQuery = pull_vars_of_level((Node *) queryToProcess, 0); List *requiredAttrNumbers = NIL; diff --git a/src/backend/distributed/planner/merge_planner.c b/src/backend/distributed/planner/merge_planner.c index 86163e131..13d0b84d6 100644 --- a/src/backend/distributed/planner/merge_planner.c +++ b/src/backend/distributed/planner/merge_planner.c @@ -13,92 +13,350 @@ #include "postgres.h" #include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" #include "optimizer/optimizer.h" #include "parser/parsetree.h" +#include "tcop/tcopprot.h" #include "utils/lsyscache.h" #include "distributed/citus_clauses.h" +#include "distributed/citus_custom_scan.h" +#include "distributed/insert_select_planner.h" #include "distributed/listutils.h" +#include "distributed/local_distributed_join_planner.h" #include "distributed/merge_planner.h" #include "distributed/multi_logical_optimizer.h" #include "distributed/multi_router_planner.h" #include "distributed/pg_dist_node_metadata.h" #include "distributed/pg_version_constants.h" #include "distributed/query_pushdown_planning.h" +#include "distributed/query_colocation_checker.h" +#include "distributed/repartition_executor.h" +#include "distributed/shared_library_init.h" #if PG_VERSION_NUM >= PG_VERSION_15 -static DeferredErrorMessage * ErrorIfDistTablesNotColocated(Query *parse, - List * - distTablesList, - PlannerRestrictionContext - * - plannerRestrictionContext); -static DeferredErrorMessage * ErrorIfMergeHasUnsupportedTables(Oid targetRelationId, - Query *parse, - List *rangeTableList, - PlannerRestrictionContext * - restrictionContext); +static int SourceResultPartitionColumnIndex(Query *mergeQuery, + List *sourceTargetList, + CitusTableCacheEntry *targetRelation); +static Var * ValidateAndReturnVarIfSupported(Node *entryExpr); +static void ErrorIfMergeQueryQualAndTargetListNotSupported(Oid targetRelationId, + Query *originalQuery); +static void ErrorIfMergeNotSupported(Query *query, Oid targetRelationId, + List *rangeTableList); +static void ErrorIfMergeHasUnsupportedTables(Oid targetRelationId, List *rangeTableList); static bool IsDistributionColumnInMergeSource(Expr *columnExpression, Query *query, bool skipOuterVars); -static DeferredErrorMessage * InsertDistributionColumnMatchesSource(Oid targetRelationId, - Query *query); +static DeferredErrorMessage * DeferErrorIfRoutableMergeNotSupported(Query *query, + List *rangeTableList, + PlannerRestrictionContext + * + plannerRestrictionContext); static DeferredErrorMessage * MergeQualAndTargetListFunctionsSupported(Oid resultRelationId, - FromExpr *joinTree, + Query *query, Node *quals, List *targetList, CmdType commandType); +static DistributedPlan * CreateRouterMergePlan(Oid targetRelationId, Query *originalQuery, + Query *query, + List *rangeTableList, + PlannerRestrictionContext * + plannerRestrictionContext); +static void ErrorIfRepartitionMergeNotSupported(Oid targetRelationId, Query *mergeQuery, + Query *sourceQuery); +static void ConvertSourceRTEIntoSubquery(Query *mergeQuery, + RangeTblEntry *sourceRte, + PlannerRestrictionContext * + plannerRestrictionContext); +static void ConvertSubqueryRTEIntoSubquery(Query *mergeQuery, RangeTblEntry *sourceRte); +static void ConvertCteRTEIntoSubquery(Query *mergeQuery, RangeTblEntry *sourceRte); +static void ConvertRelationRTEIntoSubquery(Query *mergeQuery, + RangeTblEntry *sourceRte, + PlannerRestrictionContext * + plannerRestrictionContext); +static void ErrorIfUnsupportedCTEs(Query *query); +static void ContainsUnsupportedCTEs(Query *query); +static bool MergeQueryCTEWalker(Node *node, void *context); +static DistributedPlan * CreateNonPushableMergePlan(Oid targetRelationId, uint64 planId, + Query *originalQuery, + Query *query, + PlannerRestrictionContext * + plannerRestrictionContext, + ParamListInfo boundParams); +static char * MergeCommandResultIdPrefix(uint64 planId); + +#endif + /* - * ErrorIfDistTablesNotColocated Checks to see if - * - * - There are a minimum of two distributed tables (source and a target). - * - All the distributed tables are indeed colocated. - * - * If any of the conditions are not met, it raises an exception. + * CreateMergePlan + * 1) Check for conditions that are not supported in MERGE command. + * 2) Try to create a pushable plan + * - Check for conditions suitable for a routable plan, if not found, + * raise deferred error + * 3) Try to create repartition and redistribution plan + * - Check for conditions that prevent repartition strategy, if found, + * raise an exception and quit. */ -static DeferredErrorMessage * -ErrorIfDistTablesNotColocated(Query *parse, List *distTablesList, - PlannerRestrictionContext * - plannerRestrictionContext) +DistributedPlan * +CreateMergePlan(uint64 planId, Query *originalQuery, Query *query, + PlannerRestrictionContext *plannerRestrictionContext, + ParamListInfo boundParams) { - /* All MERGE tables must be distributed */ - if (list_length(distTablesList) < 2) + /* function is void for pre-15 versions of Postgres */ + #if PG_VERSION_NUM < PG_VERSION_15 + + ereport(ERROR, (errmsg("MERGE is not supported in pre-15 Postgres versions"))); + + #else + + Oid targetRelationId = ModifyQueryResultRelationId(originalQuery); + + /* + * Step 1: Look for definitive error conditions applicable to both Routable + * and Repartition strategies. + */ + List *rangeTableList = ExtractRangeTableEntryList(originalQuery); + ErrorIfMergeNotSupported(originalQuery, targetRelationId, rangeTableList); + + /* Step 2: Try pushable merge plan */ + DistributedPlan *distributedPlan = + CreateRouterMergePlan(targetRelationId, originalQuery, query, + rangeTableList, plannerRestrictionContext); + + /* Step 3: If the routing plan failed, try for repartition strategy */ + if (distributedPlan->planningError != NULL) { - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - "For MERGE command, both the source and target " - "must be distributed", NULL, NULL); + RaiseDeferredError(distributedPlan->planningError, DEBUG1); + + /* If MERGE is not routable, try repartitioning */ + distributedPlan = + CreateNonPushableMergePlan(targetRelationId, planId, + originalQuery, query, + plannerRestrictionContext, + boundParams); } - /* All distributed tables must be colocated */ - if (!AllDistributedRelationsInRTEListColocated(distTablesList)) + return distributedPlan; + + #endif +} + + +#if PG_VERSION_NUM >= PG_VERSION_15 + +/* + * CreateRouterMergePlan attempts to create a pushable plan for the given MERGE + * SQL statement. If the planning fails, the ->planningError is set to a description + * of the failure. + */ +static DistributedPlan * +CreateRouterMergePlan(Oid targetRelationId, Query *originalQuery, Query *query, + List *rangeTableList, + PlannerRestrictionContext *plannerRestrictionContext) +{ + DistributedPlan *distributedPlan = CitusMakeNode(DistributedPlan); + + Assert(originalQuery->commandType == CMD_MERGE); + Assert(OidIsValid(targetRelationId)); + + distributedPlan->planningError = DeferErrorIfRoutableMergeNotSupported(originalQuery, + rangeTableList, + plannerRestrictionContext); + if (distributedPlan->planningError != NULL) { - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - "For MERGE command, all the distributed tables " - "must be colocated", NULL, NULL); + return distributedPlan; } - return NULL; + Var *insertVar = + FetchAndValidateInsertVarIfExists(targetRelationId, originalQuery); + if (insertVar && + !IsDistributionColumnInMergeSource((Expr *) insertVar, originalQuery, true)) + { + ereport(ERROR, (errmsg("MERGE INSERT must use the source table " + "distribution column value"))); + } + + Job *job = RouterJob(originalQuery, plannerRestrictionContext, + &distributedPlan->planningError); + + if (distributedPlan->planningError != NULL) + { + return distributedPlan; + } + + ereport(DEBUG1, (errmsg("Creating MERGE router plan"))); + + distributedPlan->workerJob = job; + distributedPlan->targetRelationId = targetRelationId; + distributedPlan->modLevel = RowModifyLevelForQuery(query); + + /* There is no coordinator query for MERGE */ + distributedPlan->combineQuery = NULL; + + /* MERGE doesn't support RETURNING clause */ + distributedPlan->expectResults = false; + distributedPlan->fastPathRouterPlan = + plannerRestrictionContext->fastPathRestrictionContext->fastPathRouterQuery; + + return distributedPlan; +} + + +/* + * CreateNonPushableMergePlan comes into effect if the router planning fails + * and incorporates two planning strategies. + * + * ExecuteSourceAtWorkerAndRepartition(): Plan the source query independently, + * execute the results into intermediate files, and repartition the files to + * co-locate them with the merge-target table. Subsequently, compile a final + * merge query on the target table using the intermediate results as the data + * source. + * + * ExecuteSourceAtCoordAndRedistribution(): Execute the plan that requires + * evaluation at the coordinator, run the query on the coordinator, and + * redistribute the resulting rows to ensure colocation with the target shards. + * Direct the MERGE SQL operation to the worker nodes' target shards, using the + * intermediate files colocated with the data as the data source. + */ +static DistributedPlan * +CreateNonPushableMergePlan(Oid targetRelationId, uint64 planId, Query *originalQuery, + Query *query, + PlannerRestrictionContext *plannerRestrictionContext, + ParamListInfo boundParams) +{ + Query *mergeQuery = copyObject(originalQuery); + RangeTblEntry *sourceRte = ExtractMergeSourceRangeTableEntry(mergeQuery); + DistributedPlan *distributedPlan = CitusMakeNode(DistributedPlan); + + ereport(DEBUG1, (errmsg("Creating MERGE repartition plan"))); + ConvertSourceRTEIntoSubquery(mergeQuery, sourceRte, plannerRestrictionContext); + Query *sourceQuery = sourceRte->subquery; + + ErrorIfRepartitionMergeNotSupported(targetRelationId, mergeQuery, sourceQuery); + + CitusTableCacheEntry *targetRelation = GetCitusTableCacheEntry(targetRelationId); + + /* + * Get the index of the column in the source query that will be utilized + * to repartition the source rows, ensuring colocation with the target + */ + distributedPlan->sourceResultRepartitionColumnIndex = + SourceResultPartitionColumnIndex(mergeQuery, + sourceQuery->targetList, + targetRelation); + + /* + * Make a copy of the source query, since following code scribbles it + * but we need to keep the original for EXPLAIN. + */ + Query *sourceQueryCopy = copyObject(sourceQuery); + + /* plan the subquery, this may be another distributed query */ + int cursorOptions = CURSOR_OPT_PARALLEL_OK; + PlannedStmt *sourceRowsPlan = pg_plan_query(sourceQueryCopy, NULL, cursorOptions, + boundParams); + bool repartitioned = IsRedistributablePlan(sourceRowsPlan->planTree) && + IsSupportedRedistributionTarget(targetRelationId); + + /* If plan is distributed, no work at the coordinator */ + if (repartitioned) + { + distributedPlan->modifyWithSelectMethod = MODIFY_WITH_SELECT_REPARTITION; + } + else + { + distributedPlan->modifyWithSelectMethod = MODIFY_WITH_SELECT_VIA_COORDINATOR; + } + + /* There is no coordinator query for MERGE */ + distributedPlan->combineQuery = NULL; + + /* MERGE doesn't support RETURNING clause */ + distributedPlan->expectResults = false; + + distributedPlan->modLevel = RowModifyLevelForQuery(mergeQuery); + distributedPlan->targetRelationId = targetRelationId; + distributedPlan->intermediateResultIdPrefix = MergeCommandResultIdPrefix(planId); + distributedPlan->modifyQueryViaCoordinatorOrRepartition = mergeQuery; + distributedPlan->selectPlanForModifyViaCoordinatorOrRepartition = sourceRowsPlan; + distributedPlan->fastPathRouterPlan = + plannerRestrictionContext->fastPathRestrictionContext->fastPathRouterQuery; + + return distributedPlan; +} + + +/* + * ContainsUnsupportedCTEs checks the CTE if it's modifying or recursive CTE, if true, + * raises an exception. + */ +static void +ContainsUnsupportedCTEs(Query *query) +{ + if (query->hasModifyingCTE) + { + ereport(ERROR, (errmsg("CTEs with modifying actions are not yet " + "supported in MERGE"))); + } + + if (query->hasRecursive) + { + ereport(ERROR, (errmsg("Recursive CTEs are not yet " + "supported in MERGE"))); + } +} + + +/* + * MergeQueryCTEWalker descends into the MERGE query to check for any subqueries + */ +static bool +MergeQueryCTEWalker(Node *node, void *context) +{ + if (node == NULL) + { + return false; + } + + if (IsA(node, Query)) + { + Query *query = (Query *) node; + + ContainsUnsupportedCTEs(query); + + query_tree_walker(query, MergeQueryCTEWalker, NULL, 0); + + /* we're done, no need to recurse anymore for this query */ + return false; + } + + return expression_tree_walker(node, MergeQueryCTEWalker, context); +} + + +/* + * ErrorIfUnsupportedCTEs checks for unsupported CTEs, such as, modifying and recursive + */ +static void +ErrorIfUnsupportedCTEs(Query *query) +{ + ContainsUnsupportedCTEs(query); + query_tree_walker(query, MergeQueryCTEWalker, NULL, 0); } /* * ErrorIfMergeHasUnsupportedTables checks if all the tables(target, source or any CTE * present) in the MERGE command are local i.e. a combination of Citus local and Non-Citus - * tables (regular Postgres tables), or distributed tables with some restrictions, please - * see header of routine ErrorIfDistTablesNotColocated for details, raises an exception - * for all other combinations. + * tables (regular Postgres tables), or distributed tables with some restrictions + * raises an exception for all other combinations. */ -static DeferredErrorMessage * -ErrorIfMergeHasUnsupportedTables(Oid targetRelationId, Query *parse, List *rangeTableList, - PlannerRestrictionContext *restrictionContext) +static void +ErrorIfMergeHasUnsupportedTables(Oid targetRelationId, List *rangeTableList) { - List *distTablesList = NIL; - bool foundLocalTables = false; - bool foundReferenceTables = false; - RangeTblEntry *rangeTableEntry = NULL; foreach_ptr(rangeTableEntry, rangeTableList) { @@ -133,17 +391,18 @@ ErrorIfMergeHasUnsupportedTables(Oid targetRelationId, Query *parse, List *range case RTE_NAMEDTUPLESTORE: case RTE_RESULT: { - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - "MERGE command is not supported with " - "Tuplestores and results", - NULL, NULL); + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("MERGE command is not supported with " + "Tuplestores and results"))); + break; } default: { - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - "MERGE command: Unrecognized range table entry.", - NULL, NULL); + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg( + "MERGE command: Unrecognized range table entry(%d) ", + rangeTableEntry->rtekind))); } } @@ -163,12 +422,10 @@ ErrorIfMergeHasUnsupportedTables(Oid targetRelationId, Query *parse, List *range if (relationId == targetRelationId) { /* Usually we don't reach this exception as the Postgres parser catches it */ - StringInfo errorMessage = makeStringInfo(); - appendStringInfo(errorMessage, "MERGE command is not allowed on " - "relation type(relkind:%c)", - rangeTableEntry->relkind); - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - errorMessage->data, NULL, NULL); + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("MERGE command is not allowed on " + "relation type(relkind:%c)", + rangeTableEntry->relkind))); } break; } @@ -183,100 +440,58 @@ ErrorIfMergeHasUnsupportedTables(Oid targetRelationId, Query *parse, List *range default: { - StringInfo errorMessage = makeStringInfo(); - appendStringInfo(errorMessage, "Unexpected table type(relkind:%c) " - "in MERGE command", - rangeTableEntry->relkind); - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - errorMessage->data, NULL, NULL); + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Unexpected table type(relkind:%c) " + "in MERGE command", rangeTableEntry->relkind))); } } /* - * For now, save all distributed tables, later (below) we will - * check for supported combination(s). + * Check for unsupported distributed tables */ - if (IsCitusTableType(relationId, DISTRIBUTED_TABLE)) + if (extern_IsColumnarTableAmTable(relationId) && + relationId == targetRelationId) + { + /* Columnar tables are not supported */ + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Columnar table as target is " + "not allowed in MERGE command"))); + } + else if (IsCitusTableType(relationId, DISTRIBUTED_TABLE)) { /* Append/Range distributed tables are not supported */ if (IsCitusTableType(relationId, APPEND_DISTRIBUTED) || IsCitusTableType(relationId, RANGE_DISTRIBUTED)) { - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - "For MERGE command, all the distributed tables " - "must be colocated, for append/range distribution, " - "colocation is not supported", NULL, - "Consider using hash distribution instead"); + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("For MERGE command, append/range distribution " + "table is not supported yet"))); } - - distTablesList = lappend(distTablesList, rangeTableEntry); } - else if (IsCitusTableType(relationId, REFERENCE_TABLE)) + else if (IsCitusTableType(relationId, REFERENCE_TABLE) && + relationId == targetRelationId) { /* Reference table as a target is not allowed */ - if (relationId == targetRelationId) - { - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - "Reference table as target " - "is not allowed in " - "MERGE command", NULL, NULL); - } - - foundReferenceTables = true; + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Reference table as target is " + "not allowed in MERGE command"))); } else if (IsCitusTableType(relationId, CITUS_LOCAL_TABLE)) { - /* Citus local tables */ - foundLocalTables = true; + /* + * All the tables are local/reference, supported as long as + * coordinator is in the metadata. + */ + if (FindCoordinatorNodeId() == -1) + { + ereport(ERROR, (errmsg("Coordinator node is not in " + "the metadata"), + errhint("To ensure that the distributed planner " + "planner the Citus table, please consider " + "configuring a coordinator node"))); + } } - else if (!IsCitusTable(relationId)) - { - /* Regular Postgres table */ - foundLocalTables = true; - } - - /* Any other Citus table type missing ? */ } - - /* Ensure all tables are indeed local (or a combination of reference and local) */ - if (list_length(distTablesList) == 0) - { - /* - * All the tables are local/reference, supported as long as - * coordinator is in the metadata. - */ - if (FindCoordinatorNodeId() == -1) - { - elog(ERROR, "Coordinator node is not in the metadata. TODO better meesage"); - } - - /* All the tables are local/reference, supported */ - return NULL; - } - - if (foundLocalTables) - { - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - "MERGE command is not supported with " - "combination of distributed/local tables yet", - NULL, NULL); - } - - if (foundReferenceTables) - { - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - "MERGE command is not supported with " - "combination of distributed/reference yet", - NULL, - "If target is distributed, source " - "must be distributed and co-located"); - } - - - /* Ensure all distributed tables are indeed co-located */ - return ErrorIfDistTablesNotColocated(parse, - distTablesList, - restrictionContext); } @@ -320,118 +535,21 @@ IsDistributionColumnInMergeSource(Expr *columnExpression, Query *query, bool } -/* - * InsertDistributionColumnMatchesSource check to see if MERGE is inserting a - * value into the target which is not from the source table, if so, it - * raises an exception. - * Note: Inserting random values other than the joined column values will - * result in unexpected behaviour of rows ending up in incorrect shards, to - * prevent such mishaps, we disallow such inserts here. - */ -static DeferredErrorMessage * -InsertDistributionColumnMatchesSource(Oid targetRelationId, Query *query) -{ - Assert(IsMergeQuery(query)); - - if (!IsCitusTableType(targetRelationId, DISTRIBUTED_TABLE)) - { - return NULL; - } - - if (!HasDistributionKey(targetRelationId)) - { - return NULL; - } - - bool foundDistributionColumn = false; - MergeAction *action = NULL; - foreach_ptr(action, query->mergeActionList) - { - /* Skip MATCHED clause as INSERTS are not allowed in it*/ - if (action->matched) - { - continue; - } - - /* NOT MATCHED can have either INSERT or DO NOTHING */ - if (action->commandType == CMD_NOTHING) - { - return NULL; - } - - if (action->targetList == NIL) - { - /* INSERT DEFAULT VALUES is not allowed */ - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - "cannot perform MERGE INSERT with DEFAULTS", - NULL, NULL); - } - - Assert(action->commandType == CMD_INSERT); - Var *targetKey = PartitionColumn(targetRelationId, 1); - - TargetEntry *targetEntry = NULL; - foreach_ptr(targetEntry, action->targetList) - { - AttrNumber originalAttrNo = targetEntry->resno; - - /* skip processing of target table non-partition columns */ - if (originalAttrNo != targetKey->varattno) - { - continue; - } - - foundDistributionColumn = true; - - if (IsA(targetEntry->expr, Var)) - { - if (IsDistributionColumnInMergeSource(targetEntry->expr, query, true)) - { - return NULL; - } - else - { - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - "MERGE INSERT must use the source table " - "distribution column value", - NULL, NULL); - } - } - else - { - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - "MERGE INSERT must refer a source column " - "for distribution column ", - NULL, NULL); - } - } - - if (!foundDistributionColumn) - { - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - "MERGE INSERT must have distribution column as value", - NULL, NULL); - } - } - - return NULL; -} - - /* * MergeQualAndTargetListFunctionsSupported Checks WHEN/ON clause actions to see what functions * are allowed, if we are updating distribution column, etc. */ static DeferredErrorMessage * -MergeQualAndTargetListFunctionsSupported(Oid resultRelationId, FromExpr *joinTree, +MergeQualAndTargetListFunctionsSupported(Oid resultRelationId, Query *query, Node *quals, List *targetList, CmdType commandType) { - uint32 rangeTableId = 1; + uint32 targetRangeTableIndex = query->resultRelation; + FromExpr *joinTree = query->jointree; Var *distributionColumn = NULL; if (IsCitusTable(resultRelationId) && HasDistributionKey(resultRelationId)) { - distributionColumn = PartitionColumn(resultRelationId, rangeTableId); + distributionColumn = PartitionColumn(resultRelationId, targetRangeTableIndex); } ListCell *targetEntryCell = NULL; @@ -554,27 +672,367 @@ MergeQualAndTargetListFunctionsSupported(Oid resultRelationId, FromExpr *joinTre } -#endif +/* + * RepartitionMergeSupported checks if certain conditions cannot accommodate the + * strategy of repartition and redistribution of source rows, the routine will verify + * them and subsequently raises an exception. + */ +static void +ErrorIfRepartitionMergeNotSupported(Oid targetRelationId, Query *mergeQuery, + Query *sourceQuery) +{ + if (!IsCitusTableType(targetRelationId, DISTRIBUTED_TABLE)) + { + ereport(ERROR, + (errmsg("MERGE involving repartition of rows " + "is supported only if the target is distributed"))); + } + + RTEListProperties *queryRteListProperties = GetRTEListPropertiesForQuery(mergeQuery); + if (queryRteListProperties->hasPostgresLocalTable) + { + ereport(ERROR, (errmsg("MERGE INTO an distributed table from " + "Postgres table is not yet supported"))); + } + + queryRteListProperties = GetRTEListPropertiesForQuery(sourceQuery); + if (!queryRteListProperties->hasCitusTable) + { + ereport(ERROR, (errmsg("To MERGE into a distributed table, source must " + "be Citus table(s)"))); + } + + /* + * Sub-queries and CTEs are not allowed in actions and ON clause + */ + if (FindNodeMatchingCheckFunction((Node *) mergeQuery->jointree->quals, + IsNodeSubquery)) + { + ereport(ERROR, + (errmsg("Sub-queries and CTEs are not allowed in ON clause for MERGE " + "with repartitioning"), + errhint("Consider making the source and target colocated " + "and joined on the distribution column to make it a " + "routable query"))); + } + + MergeAction *action = NULL; + foreach_ptr(action, mergeQuery->mergeActionList) + { + if (FindNodeMatchingCheckFunction((Node *) action, IsNodeSubquery)) + { + ereport(ERROR, + (errmsg("Sub-queries and CTEs are not allowed in actions for MERGE " + "with repartitioning"), + errhint("Consider making the source and target colocated " + "and joined on the distribution column to make it a " + "routable query"))); + } + } +} /* - * MergeQuerySupported does check for a MERGE command in the query, if it finds - * one, it will verify the below criteria - * - Supported tables and combinations in ErrorIfMergeHasUnsupportedTables - * - Distributed tables requirements in ErrorIfDistTablesNotColocated - * - Checks target-lists and functions-in-quals in TargetlistAndFunctionsSupported + * ConvertCteRTEIntoSubquery takes a RTE_CTE and converts it into a RTE_SUBQUERY. */ -DeferredErrorMessage * -MergeQuerySupported(Oid resultRelationId, Query *originalQuery, bool multiShardQuery, - PlannerRestrictionContext *plannerRestrictionContext) +static void +ConvertCteRTEIntoSubquery(Query *mergeQuery, RangeTblEntry *sourceRte) { - /* function is void for pre-15 versions of Postgres */ - #if PG_VERSION_NUM < PG_VERSION_15 + CommonTableExpr *sourceCte = NULL; + CommonTableExpr *candidateCte = NULL; + List *cteList = NIL; + + /* + * Presently, CTEs are only permitted within the USING clause, and thus, + * we search for the corresponding one + */ + foreach_ptr(candidateCte, mergeQuery->cteList) + { + if (strcmp(candidateCte->ctename, sourceRte->ctename) == 0) + { + /* The source CTE that will be converted to a subquery */ + sourceCte = candidateCte; + } + else + { + /* + * Save any other CTEs that are referenced, either directly + * or indirectly, in the source CTE. + */ + cteList = lappend(cteList, candidateCte); + } + } + + Assert(sourceCte); + + Query *cteQuery = (Query *) copyObject(sourceCte->ctequery); + + sourceRte->rtekind = RTE_SUBQUERY; + + /* + * As we are delinking the CTE from main query, we have to walk through the + * tree and decrement the ctelevelsup, but by wrapping a subquery, we avoid + * adjusting the ctelevelsup in RTE's + */ + sourceRte->subquery = WrapSubquery(cteQuery); + + /* Copy the rest of the CTEs(if any) and remove them from main query */ + sourceRte->subquery->cteList = copyObject(cteList); + mergeQuery->cteList = NIL; + + /* Zero out CTE-specific fields */ + sourceRte->security_barrier = false; + sourceRte->ctename = NULL; + sourceRte->ctelevelsup = 0; + sourceRte->self_reference = false; + sourceRte->coltypes = NIL; + sourceRte->coltypmods = NIL; + sourceRte->colcollations = NIL; +} + + +/* + * ConvertRelationRTEIntoSubquery takes a RTE_RELATION and converts it into a RTE_SUBQUERY, + * which is basically a SELECT * FROM the relation. + */ +static void +ConvertRelationRTEIntoSubquery(Query *mergeQuery, RangeTblEntry *sourceRte, + PlannerRestrictionContext *plannerRestrictionContext) +{ + Query *sourceResultsQuery = makeNode(Query); + RangeTblRef *newRangeTableRef = makeNode(RangeTblRef); + List *requiredAttributes = NIL; + + RelationRestriction *relationRestriction = + RelationRestrictionForRelation(sourceRte, plannerRestrictionContext); + if (relationRestriction) + { + requiredAttributes = + RequiredAttrNumbersForRelationInternal(mergeQuery, + relationRestriction->index); + } + + sourceResultsQuery->commandType = CMD_SELECT; + + /* we copy the input rteRelation to preserve the rteIdentity */ + RangeTblEntry *newRangeTableEntry = copyObject(sourceRte); + sourceResultsQuery->rtable = list_make1(newRangeTableEntry); + + /* set the FROM expression to the subquery */ + newRangeTableRef->rtindex = SINGLE_RTE_INDEX; + sourceResultsQuery->jointree = makeFromExpr(list_make1(newRangeTableRef), NULL); + sourceResultsQuery->targetList = + CreateAllTargetListForRelation(sourceRte->relid, requiredAttributes); + List *restrictionList = + GetRestrictInfoListForRelation(sourceRte, plannerRestrictionContext); + List *copyRestrictionList = copyObject(restrictionList); + Expr *andedBoundExpressions = make_ands_explicit(copyRestrictionList); + sourceResultsQuery->jointree->quals = (Node *) andedBoundExpressions; + + /* + * Originally the quals were pointing to the RTE and its varno + * was pointing to its index in rtable. However now we converted the RTE + * to a subquery and the quals should be pointing to that subquery, which + * is the only RTE in its rtable, hence we update the varnos so that they + * point to the subquery RTE. + * Originally: rtable: [rte1, current_rte, rte3...] + * Now: rtable: [rte1, subquery[current_rte], rte3...] --subquery[current_rte] refers to its rtable. + */ + Node *quals = sourceResultsQuery->jointree->quals; + UpdateVarNosInNode(quals, SINGLE_RTE_INDEX); + + /* replace the function with the constructed subquery */ + sourceRte->rtekind = RTE_SUBQUERY; + sourceRte->subquery = sourceResultsQuery; + sourceRte->inh = false; +} + + +/* + * ConvertSubqueryRTEIntoSubquery takes a RTE_SUBQUERY and wraps it into a new + * subquery, which eliminates any resjunk columns and adjusts the CTE levelsup. + * In addition, if the subquery happens to be a SET operation, such as, + * (SELECT * from a UNION SELECT * FROM b), it reorders, adds casts and + * prepares a single taget list + */ +static void +ConvertSubqueryRTEIntoSubquery(Query *mergeQuery, RangeTblEntry *sourceRte) +{ + sourceRte->subquery = WrapSubquery(sourceRte->subquery); + + if (list_length(mergeQuery->cteList) > 0) + { + /* copy CTEs from the MERGE ... INTO statement into source subquery */ + sourceRte->subquery->cteList = copyObject(mergeQuery->cteList); + sourceRte->subquery->hasModifyingCTE = mergeQuery->hasModifyingCTE; + mergeQuery->cteList = NIL; + } +} + + +/* + * ConvertSourceRTEIntoSubquery converts MERGE's source RTE into a subquery, + * whose result rows are repartitioned during runtime. + */ +static void +ConvertSourceRTEIntoSubquery(Query *mergeQuery, RangeTblEntry *sourceRte, + PlannerRestrictionContext *plannerRestrictionContext) +{ + switch (sourceRte->rtekind) + { + case RTE_SUBQUERY: + { + ConvertSubqueryRTEIntoSubquery(mergeQuery, sourceRte); + return; + } + + case RTE_RELATION: + { + ConvertRelationRTEIntoSubquery(mergeQuery, + sourceRte, plannerRestrictionContext); + return; + } + + case RTE_CTE: + { + ConvertCteRTEIntoSubquery(mergeQuery, sourceRte); + return; + } + + default: + { + ereport(ERROR, (errmsg("Currently, Citus only supports " + "table, subquery, and CTEs as " + "valid sources for the MERGE " + "operation"))); + } + } +} + + +/* + * ErrorIfMergeNotSupported Checks for conditions that are not supported in either + * the routable or repartition strategies. It checks for + * - Supported table types and their combinations + * - Check the target lists and quals of both the query and merge actions + * - Supported CTEs + */ +static void +ErrorIfMergeNotSupported(Query *query, Oid targetRelationId, List *rangeTableList) +{ + ErrorIfMergeHasUnsupportedTables(targetRelationId, rangeTableList); + ErrorIfMergeQueryQualAndTargetListNotSupported(targetRelationId, query); + ErrorIfUnsupportedCTEs(query); +} + + +/* + * DeferErrorIfRoutableMergeNotSupported Checks for conditions that prevent pushable planning, if + * found, raises a deferred error, which then continues to try repartitioning strategy. + */ +static DeferredErrorMessage * +DeferErrorIfRoutableMergeNotSupported(Query *query, List *rangeTableList, + PlannerRestrictionContext *plannerRestrictionContext) +{ + List *distTablesList = NIL; + List *refTablesList = NIL; + List *localTablesList = NIL; + RangeTblEntry *rangeTableEntry = NULL; + + foreach_ptr(rangeTableEntry, rangeTableList) + { + Oid relationId = rangeTableEntry->relid; + + if (IsCitusTableType(relationId, DISTRIBUTED_TABLE)) + { + distTablesList = lappend(distTablesList, rangeTableEntry); + } + else if (IsCitusTableType(relationId, REFERENCE_TABLE)) + { + refTablesList = lappend(refTablesList, rangeTableEntry); + } + else if (IsCitusTableType(relationId, CITUS_LOCAL_TABLE)) + { + localTablesList = lappend(localTablesList, rangeTableEntry); + } + } + + if (list_length(distTablesList) > 0 && list_length(refTablesList) > 0) + { + ereport(DEBUG1, (errmsg( + "A mix of distributed and reference table, try repartitioning"))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "A mix of distributed and reference table, " + "routable query is not possible", NULL, NULL); + } + + if (list_length(distTablesList) > 0 && list_length(localTablesList) > 0) + { + ereport(DEBUG1, (errmsg( + "A mix of distributed and local table, try repartitioning"))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "A mix of distributed and citus-local table, " + "routable query is not possible", NULL, NULL); + } + + /* + * If all tables are either local or reference tables, no need to proceed further down + * as the below checks are applicable for distributed tables only + */ + if (list_length(distTablesList) == 0) + { + return NULL; + } + + /* Only one distributed table is involved in the MERGE */ + if (list_length(distTablesList) == 1) + { + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "There is only one distributed table, merge is not " + "pushable, try repartitioning", NULL, NULL); + } + + /* Ensure all distributed tables are indeed co-located */ + if (!AllDistributedRelationsInRTEListColocated(distTablesList)) + { + ereport(DEBUG1, (errmsg("Distributed tables are not co-located, try " + "repartitioning"))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "For MERGE command, all the distributed tables " + "must be colocated", NULL, NULL); + } + + DeferredErrorMessage *deferredError = + DeferErrorIfUnsupportedSubqueryPushdown(query, + plannerRestrictionContext); + if (deferredError) + { + ereport(DEBUG1, (errmsg("Sub-query is not pushable, try repartitioning"))); + return deferredError; + } + + if (HasDangerousJoinUsing(query->rtable, (Node *) query->jointree)) + { + ereport(DEBUG1, (errmsg( + "Query has ambigious joins, merge is not pushable, try repartitioning"))); + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "a join with USING causes an internal naming " + "conflict, use ON instead", NULL, NULL); + } return NULL; +} - #else +/* + * ErrorIfMergeQueryQualAndTargetListNotSupported does check for a MERGE command in the query, if it finds + * one, it will verify the below criteria + * - Distributed tables co-location requirements + * - Checks target-lists and functions-in-quals in TargetlistAndFunctionsSupported + */ +static void +ErrorIfMergeQueryQualAndTargetListNotSupported(Oid targetRelationId, Query *originalQuery) +{ /* * TODO: For now, we are adding an exception where any volatile or stable * functions are not allowed in the MERGE query, but this will become too @@ -585,42 +1043,20 @@ MergeQuerySupported(Oid resultRelationId, Query *originalQuery, bool multiShardQ */ if (contain_mutable_functions((Node *) originalQuery)) { - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - "non-IMMUTABLE functions are not yet supported " - "in MERGE sql with distributed tables ", - NULL, NULL); + ereport(ERROR, (errmsg("non-IMMUTABLE functions are not yet " + "supported in MERGE sql with distributed tables"))); } - List *rangeTableList = ExtractRangeTableEntryList(originalQuery); - - /* - * Fast path queries cannot have merge command, and we prevent the remaining here. - * In Citus we have limited support for MERGE, it's allowed only if all - * the tables(target, source or any CTE) tables are are local i.e. a - * combination of Citus local and Non-Citus tables (regular Postgres tables) - * or distributed tables with some restrictions, please see header of routine - * ErrorIfDistTablesNotColocated for details. - */ DeferredErrorMessage *deferredError = - ErrorIfMergeHasUnsupportedTables(resultRelationId, - originalQuery, - rangeTableList, - plannerRestrictionContext); - if (deferredError) - { - /* MERGE's unsupported combination, raise the exception */ - RaiseDeferredError(deferredError, ERROR); - } + MergeQualAndTargetListFunctionsSupported(targetRelationId, + originalQuery, + originalQuery->jointree->quals, + originalQuery->targetList, + originalQuery->commandType); - deferredError = MergeQualAndTargetListFunctionsSupported(resultRelationId, - originalQuery->jointree, - originalQuery->jointree-> - quals, - originalQuery->targetList, - originalQuery->commandType); if (deferredError) { - return deferredError; + RaiseDeferredError(deferredError, ERROR); } /* @@ -631,8 +1067,8 @@ MergeQuerySupported(Oid resultRelationId, Query *originalQuery, bool multiShardQ foreach_ptr(action, originalQuery->mergeActionList) { Assert(originalQuery->returningList == NULL); - deferredError = MergeQualAndTargetListFunctionsSupported(resultRelationId, - originalQuery->jointree, + deferredError = MergeQualAndTargetListFunctionsSupported(targetRelationId, + originalQuery, action->qual, action->targetList, action->commandType); @@ -642,86 +1078,284 @@ MergeQuerySupported(Oid resultRelationId, Query *originalQuery, bool multiShardQ RaiseDeferredError(deferredError, ERROR); } } +} - deferredError = - InsertDistributionColumnMatchesSource(resultRelationId, originalQuery); - if (deferredError) + +/* + * MergeCommandResultIdPrefix returns the prefix to use for intermediate results of + * an MERGE INTO ... USING source-query results via the coordinator. + */ +static char * +MergeCommandResultIdPrefix(uint64 planId) +{ + StringInfo resultIdPrefix = makeStringInfo(); + appendStringInfo(resultIdPrefix, "merge_into_" UINT64_FORMAT, planId); + return resultIdPrefix->data; +} + + +/* + * ValidateAndReturnVarIfSupported Checks for valid expressions of type Var, and + * returns the Var if it finds one, for everything else, raises an exception. + */ +static Var * +ValidateAndReturnVarIfSupported(Node *entryExpr) +{ + if (!IsA(entryExpr, Var)) { - /* MERGE's unsupported scenario, raise the exception */ - RaiseDeferredError(deferredError, ERROR); + ereport(ERROR, (errmsg("MERGE INSERT is using unsupported expression type " + "for distribution column"), + errdetail("Inserting arbitrary values that don't correspond " + "to the joined column values can lead to unpredictable " + "outcomes where rows are incorrectly distributed " + "among different shards"))); } - if (multiShardQuery) + /* Found a Var inserting into target's distribution column */ + return (Var *) entryExpr; +} + + +/* + * SourceResultPartitionColumnIndex collects all Join conditions from the + * ON clause and verifies if there is a join, either left or right, with + * the distribution column of the given target. Once a match is found, it + * returns the index of that match in the source's target list. + */ +static int +SourceResultPartitionColumnIndex(Query *mergeQuery, List *sourceTargetList, + CitusTableCacheEntry *targetRelation) +{ + if (IsCitusTableType(targetRelation->relationId, SINGLE_SHARD_DISTRIBUTED)) { - deferredError = - DeferErrorIfUnsupportedSubqueryPushdown(originalQuery, - plannerRestrictionContext); - if (deferredError) + ereport(ERROR, (errmsg("MERGE operation on non-colocated " + "distributed table(s) without a shard " + "key is not yet supported"))); + } + + /* Get all the Join conditions from the ON clause */ + List *mergeJoinConditionList = WhereClauseList(mergeQuery->jointree); + Var *targetColumn = targetRelation->partitionColumn; + Var *sourceRepartitionVar = NULL; + + OpExpr *validJoinClause = + SinglePartitionJoinClause(list_make1(targetColumn), mergeJoinConditionList); + if (!validJoinClause) + { + ereport(ERROR, (errmsg("The required join operation is missing between " + "the target's distribution column and any " + "expression originating from the source. The " + "issue may arise from either a non-equi-join or " + "a mismatch in the datatypes of the columns being " + "joined."), + errdetail("Without a equi-join condition on the target's " + "distribution column, the source rows " + "cannot be efficiently redistributed, and " + "the NOT-MATCHED condition cannot be evaluated " + "unambiguously. This can result in incorrect or " + "unexpected results when attempting to merge " + "tables in a distributed setting"))); + } + + /* both are verified in SinglePartitionJoinClause to not be NULL, assert is to guard */ + Var *leftColumn = LeftColumnOrNULL(validJoinClause); + Var *rightColumn = RightColumnOrNULL(validJoinClause); + + Assert(leftColumn != NULL); + Assert(rightColumn != NULL); + + if (equal(targetColumn, leftColumn)) + { + sourceRepartitionVar = rightColumn; + } + else if (equal(targetColumn, rightColumn)) + { + sourceRepartitionVar = leftColumn; + } + + /* Either we find an insert-action or it's not relevant for certain class of tables */ + Var *insertVar = + FetchAndValidateInsertVarIfExists(targetRelation->relationId, mergeQuery); + if (insertVar) + { + /* INSERT action, must choose joining column for inserted value */ + bool joinedOnInsertColumn = + JoinOnColumns(list_make1(targetColumn), insertVar, mergeJoinConditionList); + if (joinedOnInsertColumn) { - return deferredError; + sourceRepartitionVar = insertVar; + } + else + { + ereport(ERROR, (errmsg("MERGE INSERT must use the " + "source's joining column for " + "target's distribution column"))); } } - if (HasDangerousJoinUsing(originalQuery->rtable, (Node *) originalQuery->jointree)) + Assert(sourceRepartitionVar); + + int sourceResultRepartitionColumnIndex = + DistributionColumnIndex(sourceTargetList, sourceRepartitionVar); + + if (sourceResultRepartitionColumnIndex == -1) { - return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, - "a join with USING causes an internal naming " - "conflict, use ON instead", NULL, NULL); + ereport(ERROR, + (errmsg("Unexpected column index of the source list"))); + } + else + { + ereport(DEBUG1, (errmsg("Using column - index:%d from the source list " + "to redistribute", sourceResultRepartitionColumnIndex))); } - return NULL; + return sourceResultRepartitionColumnIndex; +} + + +#endif + + +/* + * ExtractMergeSourceRangeTableEntry returns the range table entry of source + * table or source query in USING clause. + */ +RangeTblEntry * +ExtractMergeSourceRangeTableEntry(Query *query) +{ + /* function is void for pre-15 versions of Postgres */ + #if PG_VERSION_NUM < PG_VERSION_15 + + ereport(ERROR, (errmsg("MERGE is not supported in pre-15 Postgres versions"))); + + #else + + Assert(IsMergeQuery(query)); + + List *fromList = query->jointree->fromlist; + + /* We should have only one RTE(MergeStmt->sourceRelation) in the from-list */ + if (list_length(fromList) != 1) + { + ereport(ERROR, (errmsg("Unexpected source list in MERGE sql USING clause"))); + } + + RangeTblRef *reference = linitial(fromList); + + /* + * The planner sometimes generates JoinExprs internally; these can + * have rtindex = 0 if there are no join alias variables referencing + * such joins. + */ + if (reference->rtindex == 0) + { + ereport(ERROR, (errmsg("Source is not an explicit query"), + errhint("Source query is a Join expression, " + "try converting into a query as SELECT * " + "FROM (..Join..)"))); + } + + Assert(reference->rtindex >= 1); + RangeTblEntry *subqueryRte = rt_fetch(reference->rtindex, query->rtable); + + return subqueryRte; #endif } /* - * CreateMergePlan attempts to create a plan for the given MERGE SQL - * statement. If planning fails ->planningError is set to a description - * of the failure. + * FetchAndValidateInsertVarIfExists checks to see if MERGE is inserting a + * value into the target which is not from the source table, if so, it + * raises an exception. The return value is the Var that's being inserted + * into the target's distribution column, If no INSERT action exist, it + * simply returns a NULL. + * Note: Inserting random values other than the joined column values will + * result in unexpected behaviour of rows ending up in incorrect shards, to + * prevent such mishaps, we disallow such inserts here. */ -DistributedPlan * -CreateMergePlan(Query *originalQuery, Query *query, - PlannerRestrictionContext *plannerRestrictionContext) +Var * +FetchAndValidateInsertVarIfExists(Oid targetRelationId, Query *query) { - DistributedPlan *distributedPlan = CitusMakeNode(DistributedPlan); - bool multiShardQuery = false; - Oid targetRelationId = ModifyQueryResultRelationId(originalQuery); + /* function is void for pre-15 versions of Postgres */ + #if PG_VERSION_NUM < PG_VERSION_15 - Assert(originalQuery->commandType == CMD_MERGE); - Assert(OidIsValid(targetRelationId)); + ereport(ERROR, (errmsg("MERGE is not supported in pre-15 Postgres versions"))); - distributedPlan->targetRelationId = targetRelationId; - distributedPlan->modLevel = RowModifyLevelForQuery(query); - distributedPlan->planningError = MergeQuerySupported(targetRelationId, - originalQuery, - multiShardQuery, - plannerRestrictionContext); + #else - if (distributedPlan->planningError != NULL) + Assert(IsMergeQuery(query)); + + if (!IsCitusTableType(targetRelationId, DISTRIBUTED_TABLE)) { - return distributedPlan; + return NULL; } - Job *job = RouterJob(originalQuery, plannerRestrictionContext, - &distributedPlan->planningError); - - if (distributedPlan->planningError != NULL) + if (!HasDistributionKey(targetRelationId)) { - return distributedPlan; + return NULL; } - ereport(DEBUG1, (errmsg("Creating MERGE router plan"))); + bool foundDistributionColumn = false; + MergeAction *action = NULL; + uint32 targetRangeTableIndex = query->resultRelation; + foreach_ptr(action, query->mergeActionList) + { + /* Skip MATCHED clause as INSERTS are not allowed in it */ + if (action->matched) + { + continue; + } - distributedPlan->workerJob = job; - distributedPlan->combineQuery = NULL; + /* NOT MATCHED can have either INSERT or DO NOTHING */ + if (action->commandType == CMD_NOTHING) + { + return NULL; + } - /* MERGE doesn't support RETURNING clause */ - distributedPlan->expectResults = false; - distributedPlan->fastPathRouterPlan = - plannerRestrictionContext->fastPathRestrictionContext->fastPathRouterQuery; + if (action->targetList == NIL) + { + /* INSERT DEFAULT VALUES is not allowed */ + ereport(ERROR, (errmsg("cannot perform MERGE INSERT with DEFAULTS"), + errdetail("Inserting arbitrary values that don't correspond " + "to the joined column values can lead to " + "unpredictable outcomes where rows are " + "incorrectly distributed among different " + "shards"))); + } - return distributedPlan; + Assert(action->commandType == CMD_INSERT); + Var *targetDistributionKey = + PartitionColumn(targetRelationId, targetRangeTableIndex); + + TargetEntry *targetEntry = NULL; + foreach_ptr(targetEntry, action->targetList) + { + AttrNumber originalAttrNo = targetEntry->resno; + + /* skip processing of target table non-distribution columns */ + if (originalAttrNo != targetDistributionKey->varattno) + { + continue; + } + + foundDistributionColumn = true; + + Node *insertExpr = + strip_implicit_coercions((Node *) copyObject(targetEntry->expr)); + return ValidateAndReturnVarIfSupported(insertExpr); + } + + if (!foundDistributionColumn) + { + ereport(ERROR, + (errmsg("MERGE INSERT must have distribution column as value"))); + } + } + + return NULL; + + #endif } diff --git a/src/backend/distributed/planner/multi_explain.c b/src/backend/distributed/planner/multi_explain.c index 248117904..1cc3d4102 100644 --- a/src/backend/distributed/planner/multi_explain.c +++ b/src/backend/distributed/planner/multi_explain.c @@ -33,6 +33,7 @@ #include "distributed/insert_select_planner.h" #include "distributed/insert_select_executor.h" #include "distributed/listutils.h" +#include "distributed/merge_planner.h" #include "distributed/multi_executor.h" #include "distributed/multi_explain.h" #include "distributed/multi_logical_optimizer.h" @@ -244,9 +245,8 @@ NonPushableInsertSelectExplainScan(CustomScanState *node, List *ancestors, */ Query *queryCopy = copyObject(selectRte->subquery); - bool repartition = distributedPlan->modifyWithSelectMethod == - MODIFY_WITH_SELECT_REPARTITION; - + bool repartition = + distributedPlan->modifyWithSelectMethod == MODIFY_WITH_SELECT_REPARTITION; if (es->analyze) { @@ -282,6 +282,67 @@ NonPushableInsertSelectExplainScan(CustomScanState *node, List *ancestors, } +/* + * NonPushableMergeSqlExplainScan is a custom scan explain callback function + * which is used to print explain information of a Citus plan for MERGE INTO + * distributed_table USING (source query/table), where source can be any query + * whose results are repartitioned to colocated with the target table. + */ +void +NonPushableMergeCommandExplainScan(CustomScanState *node, List *ancestors, + struct ExplainState *es) +{ + CitusScanState *scanState = (CitusScanState *) node; + DistributedPlan *distributedPlan = scanState->distributedPlan; + Query *mergeQuery = distributedPlan->modifyQueryViaCoordinatorOrRepartition; + RangeTblEntry *sourceRte = ExtractMergeSourceRangeTableEntry(mergeQuery); + + /* + * Create a copy because ExplainOneQuery can modify the query, and later + * executions of prepared statements might require it. See + * https://github.com/citusdata/citus/issues/3947 for what can happen. + */ + Query *sourceQueryCopy = copyObject(sourceRte->subquery); + bool repartition = + distributedPlan->modifyWithSelectMethod == MODIFY_WITH_SELECT_REPARTITION; + + if (es->analyze) + { + ereport(ERROR, (errmsg("EXPLAIN ANALYZE is currently not supported for " + "MERGE INTO ... commands with repartitioning"))); + } + + Oid targetRelationId = ModifyQueryResultRelationId(mergeQuery); + StringInfo mergeMethodMessage = makeStringInfo(); + appendStringInfo(mergeMethodMessage, + "MERGE INTO %s method", get_rel_name(targetRelationId)); + + if (repartition) + { + ExplainPropertyText(mergeMethodMessage->data, "repartition", es); + } + else + { + ExplainPropertyText(mergeMethodMessage->data, "pull to coordinator", es); + } + + ExplainOpenGroup("Source Query", "Source Query", false, es); + + /* explain the MERGE source query */ + IntoClause *into = NULL; + ParamListInfo params = NULL; + + /* + * With PG14, we need to provide a string here, for now we put an empty + * string, which is valid according to postgres. + */ + char *queryString = pstrdup(""); + ExplainOneQuery(sourceQueryCopy, 0, into, es, queryString, params, NULL); + + ExplainCloseGroup("Source Query", "Source Query", false, es); +} + + /* * ExplainSubPlans generates EXPLAIN output for subplans for CTEs * and complex subqueries. Because the planning for these queries diff --git a/src/backend/distributed/planner/multi_join_order.c b/src/backend/distributed/planner/multi_join_order.c index 0fff79ed8..79007b70d 100644 --- a/src/backend/distributed/planner/multi_join_order.c +++ b/src/backend/distributed/planner/multi_join_order.c @@ -81,8 +81,6 @@ static JoinOrderNode * CartesianProductReferenceJoin(JoinOrderNode *joinNode, JoinType joinType); static JoinOrderNode * LocalJoin(JoinOrderNode *joinNode, TableEntry *candidateTable, List *applicableJoinClauses, JoinType joinType); -static bool JoinOnColumns(List *currentPartitionColumnList, Var *candidatePartitionColumn, - List *joinClauseList); static JoinOrderNode * SinglePartitionJoin(JoinOrderNode *joinNode, TableEntry *candidateTable, List *applicableJoinClauses, @@ -212,7 +210,7 @@ ExtractLeftMostRangeTableIndex(Node *node, int *rangeTableIndex) /* * JoinOnColumns determines whether two columns are joined by a given join clause list. */ -static bool +bool JoinOnColumns(List *currentPartitionColumnList, Var *candidateColumn, List *joinClauseList) { diff --git a/src/backend/distributed/planner/multi_router_planner.c b/src/backend/distributed/planner/multi_router_planner.c index 87ab1277f..41ae916ad 100644 --- a/src/backend/distributed/planner/multi_router_planner.c +++ b/src/backend/distributed/planner/multi_router_planner.c @@ -388,6 +388,26 @@ AddPartitionKeyNotNullFilterToSelect(Query *subqery) } +/* + * ExtractSourceResultRangeTableEntry Generic wrapper for modification commands that + * utilizes results as input, based on an source query. + */ +RangeTblEntry * +ExtractSourceResultRangeTableEntry(Query *query) +{ + if (IsMergeQuery(query)) + { + return ExtractMergeSourceRangeTableEntry(query); + } + else if (CheckInsertSelectQuery(query)) + { + return ExtractSelectRangeTableEntry(query); + } + + return NULL; +} + + /* * ExtractSelectRangeTableEntry returns the range table entry of the subquery. * Note that the function expects and asserts that the input query be @@ -1863,19 +1883,7 @@ RouterJob(Query *originalQuery, PlannerRestrictionContext *plannerRestrictionCon if (*planningError) { - /* - * For MERGE, we do _not_ plan any other router job than the MERGE job itself, - * let's not continue further down the lane in distributed planning, simply - * bail out. - */ - if (IsMergeQuery(originalQuery)) - { - RaiseDeferredError(*planningError, ERROR); - } - else - { - return NULL; - } + return NULL; } Job *job = CreateJob(originalQuery); @@ -2366,14 +2374,7 @@ PlanRouterQuery(Query *originalQuery, Assert(UpdateOrDeleteOrMergeQuery(originalQuery)); - if (IsMergeQuery(originalQuery)) - { - targetRelationId = ModifyQueryResultRelationId(originalQuery); - planningError = MergeQuerySupported(targetRelationId, originalQuery, - isMultiShardQuery, - plannerRestrictionContext); - } - else + if (!IsMergeQuery(originalQuery)) { planningError = ModifyQuerySupported(originalQuery, originalQuery, isMultiShardQuery, diff --git a/src/backend/distributed/planner/recursive_planning.c b/src/backend/distributed/planner/recursive_planning.c index 936b17364..f582fd9df 100644 --- a/src/backend/distributed/planner/recursive_planning.c +++ b/src/backend/distributed/planner/recursive_planning.c @@ -188,7 +188,6 @@ static Query * BuildReadIntermediateResultsQuery(List *targetEntryList, List *columnAliasList, Const *resultIdConst, Oid functionOid, bool useBinaryCopyFormat); -static void UpdateVarNosInNode(Node *node, Index newVarNo); static Query * CreateOuterSubquery(RangeTblEntry *rangeTableEntry, List *outerSubqueryTargetList); static List * GenerateRequiredColNamesFromTargetList(List *targetList); @@ -1891,7 +1890,7 @@ GenerateRequiredColNamesFromTargetList(List *targetList) * UpdateVarNosInNode iterates the Vars in the * given node and updates the varno's as the newVarNo. */ -static void +void UpdateVarNosInNode(Node *node, Index newVarNo) { List *varList = pull_var_clause(node, PVC_RECURSE_AGGREGATES | diff --git a/src/include/distributed/citus_custom_scan.h b/src/include/distributed/citus_custom_scan.h index f31138ac2..a3da4958c 100644 --- a/src/include/distributed/citus_custom_scan.h +++ b/src/include/distributed/citus_custom_scan.h @@ -34,6 +34,7 @@ typedef struct CitusScanState extern CustomScanMethods AdaptiveExecutorCustomScanMethods; extern CustomScanMethods NonPushableInsertSelectCustomScanMethods; extern CustomScanMethods DelayedErrorCustomScanMethods; +extern CustomScanMethods NonPushableMergeCommandCustomScanMethods; extern void RegisterCitusCustomScanMethods(void); diff --git a/src/include/distributed/commands/multi_copy.h b/src/include/distributed/commands/multi_copy.h index 689725e70..70f93cfb9 100644 --- a/src/include/distributed/commands/multi_copy.h +++ b/src/include/distributed/commands/multi_copy.h @@ -152,6 +152,12 @@ typedef struct CitusCopyDestReceiver * upfront. */ uint64 appendShardId; + + /* + * When copying to intermediate files, we can skip coercions and run them + * when merging into the target tables. + */ + bool skipCoercions; } CitusCopyDestReceiver; diff --git a/src/include/distributed/insert_select_executor.h b/src/include/distributed/insert_select_executor.h index 1b08f5a94..fd8282014 100644 --- a/src/include/distributed/insert_select_executor.h +++ b/src/include/distributed/insert_select_executor.h @@ -18,6 +18,7 @@ extern TupleTableSlot * NonPushableInsertSelectExecScan(CustomScanState *node); - +extern List * BuildColumnNameListFromTargetList(Oid targetRelationId, + List *insertTargetList); #endif /* INSERT_SELECT_EXECUTOR_H */ diff --git a/src/include/distributed/insert_select_planner.h b/src/include/distributed/insert_select_planner.h index 74b8a0708..771d1d60f 100644 --- a/src/include/distributed/insert_select_planner.h +++ b/src/include/distributed/insert_select_planner.h @@ -44,6 +44,7 @@ extern DistributedPlan * CreateInsertSelectIntoLocalTablePlan(uint64 planId, plannerRestrictionContext); extern char * InsertSelectResultIdPrefix(uint64 planId); extern bool PlanningInsertSelect(void); +extern Query * WrapSubquery(Query *subquery); #endif /* INSERT_SELECT_PLANNER_H */ diff --git a/src/include/distributed/local_distributed_join_planner.h b/src/include/distributed/local_distributed_join_planner.h index f2108f603..dfb45f149 100644 --- a/src/include/distributed/local_distributed_join_planner.h +++ b/src/include/distributed/local_distributed_join_planner.h @@ -33,5 +33,6 @@ extern void RecursivelyPlanLocalTableJoins(Query *query, extern List * RequiredAttrNumbersForRelation(RangeTblEntry *relationRte, PlannerRestrictionContext * plannerRestrictionContext); +extern List * RequiredAttrNumbersForRelationInternal(Query *queryToProcess, int rteIndex); #endif /* LOCAL_DISTRIBUTED_JOIN_PLANNER_H */ diff --git a/src/include/distributed/merge_executor.h b/src/include/distributed/merge_executor.h new file mode 100644 index 000000000..0bc31ab74 --- /dev/null +++ b/src/include/distributed/merge_executor.h @@ -0,0 +1,17 @@ +/*------------------------------------------------------------------------- + * + * merge_executor.h + * + * Declarations for public functions and types related to executing + * MERGE INTO ... SQL commands. + * + * Copyright (c) Citus Data, Inc. + * + *------------------------------------------------------------------------- + */ +#ifndef MERGE_EXECUTOR_H +#define MERGE_EXECUTOR_H + +extern TupleTableSlot * NonPushableMergeCommandExecScan(CustomScanState *node); + +#endif /* MERGE_EXECUTOR_H */ diff --git a/src/include/distributed/merge_planner.h b/src/include/distributed/merge_planner.h index b4ec1852f..1548dae6a 100644 --- a/src/include/distributed/merge_planner.h +++ b/src/include/distributed/merge_planner.h @@ -19,16 +19,18 @@ #include "distributed/errormessage.h" #include "distributed/multi_physical_planner.h" -extern DeferredErrorMessage * MergeQuerySupported(Oid resultRelationId, - Query *originalQuery, - bool multiShardQuery, - PlannerRestrictionContext * - plannerRestrictionContext); -extern DistributedPlan * CreateMergePlan(Query *originalQuery, Query *query, +extern DistributedPlan * CreateMergePlan(uint64 planId, Query *originalQuery, + Query *query, PlannerRestrictionContext * - plannerRestrictionContext); + plannerRestrictionContext, + ParamListInfo boundParams); extern bool IsLocalTableModification(Oid targetRelationId, Query *query, uint64 shardId, RTEListProperties *rteProperties); +extern void NonPushableMergeCommandExplainScan(CustomScanState *node, List *ancestors, + struct ExplainState *es); +extern Var * FetchAndValidateInsertVarIfExists(Oid targetRelationId, Query *query); +extern RangeTblEntry * ExtractMergeSourceRangeTableEntry(Query *query); + #endif /* MERGE_PLANNER_H */ diff --git a/src/include/distributed/multi_executor.h b/src/include/distributed/multi_executor.h index 4e7f13601..5ae010d87 100644 --- a/src/include/distributed/multi_executor.h +++ b/src/include/distributed/multi_executor.h @@ -114,6 +114,9 @@ typedef struct ExecutionParams /* isUtilityCommand is true if the current execution is for a utility * command such as a DDL command.*/ bool isUtilityCommand; + + /* pass bind parameters to the distributed executor for parameterized plans */ + ParamListInfo paramListInfo; } ExecutionParams; ExecutionParams * CreateBasicExecutionParams(RowModifyLevel modLevel, @@ -122,6 +125,11 @@ ExecutionParams * CreateBasicExecutionParams(RowModifyLevel modLevel, bool localExecutionSupported); extern uint64 ExecuteTaskListExtended(ExecutionParams *executionParams); +extern uint64 ExecuteTaskListIntoTupleDestWithParam(RowModifyLevel modLevel, + List *taskList, + TupleDestination *tupleDest, + bool expectResults, + ParamListInfo paramListInfo); extern uint64 ExecuteTaskListIntoTupleDest(RowModifyLevel modLevel, List *taskList, TupleDestination *tupleDest, bool expectResults); diff --git a/src/include/distributed/multi_join_order.h b/src/include/distributed/multi_join_order.h index 92d1edaf2..4e4ba1dd2 100644 --- a/src/include/distributed/multi_join_order.h +++ b/src/include/distributed/multi_join_order.h @@ -108,6 +108,8 @@ extern Var * DistPartitionKey(Oid relationId); extern Var * DistPartitionKeyOrError(Oid relationId); extern char PartitionMethod(Oid relationId); extern char TableReplicationModel(Oid relationId); +extern bool JoinOnColumns(List *currentPartitionColumnList, Var *candidatePartitionColumn, + List *joinClauseList); #endif /* MULTI_JOIN_ORDER_H */ diff --git a/src/include/distributed/multi_physical_planner.h b/src/include/distributed/multi_physical_planner.h index c457918db..b7acc0574 100644 --- a/src/include/distributed/multi_physical_planner.h +++ b/src/include/distributed/multi_physical_planner.h @@ -463,6 +463,13 @@ typedef struct DistributedPlan * or if prepared statement parameters prevented successful planning. */ DeferredErrorMessage *planningError; + + /* + * When performing query execution scenarios that require repartitioning + * the source rows, this field stores the index of the column in the list + * of source rows to be repartitioned for colocation with the target. + */ + int sourceResultRepartitionColumnIndex; } DistributedPlan; diff --git a/src/include/distributed/multi_router_planner.h b/src/include/distributed/multi_router_planner.h index a255fd520..160cf6605 100644 --- a/src/include/distributed/multi_router_planner.h +++ b/src/include/distributed/multi_router_planner.h @@ -118,5 +118,6 @@ extern Job * RouterJob(Query *originalQuery, PlannerRestrictionContext *plannerRestrictionContext, DeferredErrorMessage **planningError); extern bool ContainsOnlyLocalTables(RTEListProperties *rteProperties); +extern RangeTblEntry * ExtractSourceResultRangeTableEntry(Query *query); #endif /* MULTI_ROUTER_PLANNER_H */ diff --git a/src/include/distributed/multi_server_executor.h b/src/include/distributed/multi_server_executor.h index 56dd6d808..f49ef60c2 100644 --- a/src/include/distributed/multi_server_executor.h +++ b/src/include/distributed/multi_server_executor.h @@ -29,7 +29,8 @@ typedef enum { MULTI_EXECUTOR_INVALID_FIRST = 0, MULTI_EXECUTOR_ADAPTIVE = 1, - MULTI_EXECUTOR_NON_PUSHABLE_INSERT_SELECT = 2 + MULTI_EXECUTOR_NON_PUSHABLE_INSERT_SELECT = 2, + MULTI_EXECUTOR_NON_PUSHABLE_MERGE_QUERY = 3 } MultiExecutorType; diff --git a/src/include/distributed/recursive_planning.h b/src/include/distributed/recursive_planning.h index e849d7158..8943443aa 100644 --- a/src/include/distributed/recursive_planning.h +++ b/src/include/distributed/recursive_planning.h @@ -46,6 +46,7 @@ extern void ReplaceRTERelationWithRteSubquery(RangeTblEntry *rangeTableEntry, extern bool IsRecursivelyPlannableRelation(RangeTblEntry *rangeTableEntry); extern bool IsRelationLocalTableOrMatView(Oid relationId); extern bool ContainsReferencesToOuterQuery(Query *query); +extern void UpdateVarNosInNode(Node *node, Index newVarNo); #endif /* RECURSIVE_PLANNING_H */ diff --git a/src/include/distributed/repartition_executor.h b/src/include/distributed/repartition_executor.h index 98173b828..de4ad122a 100644 --- a/src/include/distributed/repartition_executor.h +++ b/src/include/distributed/repartition_executor.h @@ -15,7 +15,7 @@ extern bool EnableRepartitionedInsertSelect; -extern int DistributionColumnIndex(List *insertTargetList, Var *partitionColumn); +extern int DistributionColumnIndex(List *insertTargetList, Var *distributionColumn); extern List * GenerateTaskListWithColocatedIntermediateResults(Oid targetRelationId, Query * modifyQueryViaCoordinatorOrRepartition, diff --git a/src/test/regress/bin/normalize.sed b/src/test/regress/bin/normalize.sed index 5b958b636..0370f4e98 100644 --- a/src/test/regress/bin/normalize.sed +++ b/src/test/regress/bin/normalize.sed @@ -159,6 +159,8 @@ s/Subplan [0-9]+\_/Subplan XXX\_/g # Plan numbers in insert select s/read_intermediate_result\('insert_select_[0-9]+_/read_intermediate_result('insert_select_XXX_/g +# Plan numbers in merge into +s/read_intermediate_result\('merge_into_[0-9]+_/read_intermediate_result('merge_into_XXX_/g # ignore job id in repartitioned insert/select s/repartitioned_results_[0-9]+/repartitioned_results_xxxxx/g diff --git a/src/test/regress/citus_tests/config.py b/src/test/regress/citus_tests/config.py index 16b18d1e7..560806962 100644 --- a/src/test/regress/citus_tests/config.py +++ b/src/test/regress/citus_tests/config.py @@ -220,6 +220,7 @@ class AllSingleShardTableDefaultConfig(CitusDefaultClusterConfig): # "dist_query_single_shard" table acts differently when the table # has a single shard. This is explained with a comment in the test. "nested_execution", + "merge_arbitrary", ] diff --git a/src/test/regress/expected/merge.out b/src/test/regress/expected/merge.out index 85d6daab6..882a22091 100644 --- a/src/test/regress/expected/merge.out +++ b/src/test/regress/expected/merge.out @@ -20,6 +20,14 @@ SET citus.next_shard_id TO 4000000; SET citus.explain_all_tasks TO true; SET citus.shard_replication_factor TO 1; SET citus.max_adaptive_executor_pool_size TO 1; +SET client_min_messages = warning; +SELECT 1 FROM master_add_node('localhost', :master_port, groupid => 0); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +RESET client_min_messages; CREATE TABLE source ( order_id INT, @@ -199,7 +207,7 @@ HINT: To remove the local data, run: SELECT truncate_local_data_after_distribut (1 row) -SELECT create_distributed_table('source', 'customer_id'); +SELECT create_distributed_table('source', 'customer_id', colocate_with=>'target'); NOTICE: Copying data from local table... NOTICE: copying the data has completed DETAIL: The local data in the table is no longer visible, but is still on disk. @@ -433,7 +441,7 @@ HINT: To remove the local data, run: SELECT truncate_local_data_after_distribut (1 row) -SELECT create_distributed_table('s1', 'id'); +SELECT create_distributed_table('s1', 'id', colocate_with=>'t1'); NOTICE: Copying data from local table... NOTICE: copying the data has completed DETAIL: The local data in the table is no longer visible, but is still on disk. @@ -643,7 +651,7 @@ HINT: To remove the local data, run: SELECT truncate_local_data_after_distribut (1 row) -SELECT create_distributed_table('s2', 'id'); +SELECT create_distributed_table('s2', 'id', colocate_with => 't2'); NOTICE: Copying data from local table... NOTICE: copying the data has completed DETAIL: The local data in the table is no longer visible, but is still on disk. @@ -1397,7 +1405,7 @@ HINT: To remove the local data, run: SELECT truncate_local_data_after_distribut (1 row) -SELECT create_distributed_table('source_cj1', 'sid1'); +SELECT create_distributed_table('source_cj1', 'sid1', colocate_with => 'target_cj'); NOTICE: Copying data from local table... NOTICE: copying the data has completed DETAIL: The local data in the table is no longer visible, but is still on disk. @@ -1407,7 +1415,7 @@ HINT: To remove the local data, run: SELECT truncate_local_data_after_distribut (1 row) -SELECT create_distributed_table('source_cj2', 'sid2'); +SELECT create_distributed_table('source_cj2', 'sid2', colocate_with => 'target_cj'); NOTICE: Copying data from local table... NOTICE: copying the data has completed DETAIL: The local data in the table is no longer visible, but is still on disk. @@ -1418,19 +1426,13 @@ HINT: To remove the local data, run: SELECT truncate_local_data_after_distribut (1 row) BEGIN; -SET citus.log_remote_commands to true; MERGE INTO target_cj t -USING source_cj1 s1 INNER JOIN source_cj2 s2 ON sid1 = sid2 +USING (SELECT * FROM source_cj1 s1 INNER JOIN source_cj2 s2 ON sid1 = sid2) s ON t.tid = sid1 AND t.tid = 2 WHEN MATCHED THEN UPDATE SET src = src2 WHEN NOT MATCHED THEN DO NOTHING; -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 MERGE INTO merge_schema.target_cj_xxxxxxx t USING (merge_schema.source_cj1_xxxxxxx s1 JOIN merge_schema.source_cj2_xxxxxxx s2 ON ((s1.sid1 OPERATOR(pg_catalog.=) s2.sid2))) ON ((t.tid OPERATOR(pg_catalog.=) s1.sid1) AND (t.tid OPERATOR(pg_catalog.=) 2)) WHEN MATCHED THEN UPDATE SET src = s2.src2 WHEN NOT MATCHED THEN DO NOTHING -DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -SET citus.log_remote_commands to false; SELECT * FROM target_cj ORDER BY 1; tid | src | val --------------------------------------------------------------------- @@ -1444,8 +1446,8 @@ ROLLBACK; BEGIN; -- try accessing columns from either side of the source join MERGE INTO target_cj t -USING source_cj1 s2 - INNER JOIN source_cj2 s1 ON sid1 = sid2 AND val1 = 10 +USING (SELECT * FROM source_cj1 s2 + INNER JOIN source_cj2 s1 ON sid1 = sid2 AND val1 = 10) s ON t.tid = sid1 AND t.tid = 2 WHEN MATCHED THEN UPDATE SET src = src1, val = val2 @@ -1513,7 +1515,7 @@ SELECT * FROM target_cj ORDER BY 1; ROLLBACK; -- Test PREPARE -PREPARE foo(int) AS +PREPARE merge_prepare(int) AS MERGE INTO target_cj target USING (SELECT * FROM source_cj1) sub ON target.tid = sub.sid1 AND target.tid = $1 @@ -1531,11 +1533,11 @@ SELECT * FROM target_cj ORDER BY 1; (4 rows) BEGIN; -EXECUTE foo(2); -EXECUTE foo(2); -EXECUTE foo(2); -EXECUTE foo(2); -EXECUTE foo(2); +EXECUTE merge_prepare(2); +EXECUTE merge_prepare(2); +EXECUTE merge_prepare(2); +EXECUTE merge_prepare(2); +EXECUTE merge_prepare(2); SELECT * FROM target_cj ORDER BY 1; tid | src | val --------------------------------------------------------------------- @@ -1549,12 +1551,7 @@ ROLLBACK; BEGIN; SET citus.log_remote_commands to true; SET client_min_messages TO DEBUG1; -EXECUTE foo(2); -DEBUG: -DEBUG: -DEBUG: -DEBUG: -DEBUG: Creating MERGE router plan +EXECUTE merge_prepare(2); DEBUG: DEBUG: Creating MERGE router plan NOTICE: issuing BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;SELECT assign_distributed_transaction_id(xx, xx, 'xxxxxxx'); @@ -1562,7 +1559,7 @@ DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx NOTICE: issuing MERGE INTO merge_schema.target_cj_xxxxxxx target USING (SELECT source_cj1.sid1, source_cj1.src1, source_cj1.val1 FROM merge_schema.source_cj1_xxxxxxx source_cj1) sub ON ((target.tid OPERATOR(pg_catalog.=) sub.sid1) AND (target.tid OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = sub.val1 WHEN NOT MATCHED THEN DO NOTHING DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx RESET client_min_messages; -EXECUTE foo(2); +EXECUTE merge_prepare(2); NOTICE: issuing MERGE INTO merge_schema.target_cj_xxxxxxx target USING (SELECT source_cj1.sid1, source_cj1.src1, source_cj1.val1 FROM merge_schema.source_cj1_xxxxxxx source_cj1) sub ON ((target.tid OPERATOR(pg_catalog.=) sub.sid1) AND (target.tid OPERATOR(pg_catalog.=) $1)) WHEN MATCHED THEN UPDATE SET val = sub.val1 WHEN NOT MATCHED THEN DO NOTHING DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx SET citus.log_remote_commands to false; @@ -1602,7 +1599,7 @@ HINT: To remove the local data, run: SELECT truncate_local_data_after_distribut (1 row) -SELECT create_distributed_table('citus_source', 'id'); +SELECT create_distributed_table('citus_source', 'id', colocate_with => 'citus_target'); NOTICE: Copying data from local table... NOTICE: copying the data has completed DETAIL: The local data in the table is no longer visible, but is still on disk. @@ -2519,9 +2516,495 @@ WHERE pg_result.t1 IS NULL OR local_ref.t1 IS NULL; 0 (1 row) +-- Now make target as distributed, keep reference as source +TRUNCATE reftarget_local; +TRUNCATE refsource_ref; +INSERT INTO reftarget_local VALUES(1, 0); +INSERT INTO reftarget_local VALUES(3, 100); +INSERT INTO refsource_ref VALUES(1, 1); +INSERT INTO refsource_ref VALUES(2, 2); +INSERT INTO refsource_ref VALUES(3, 3); +SELECT create_distributed_table('reftarget_local', 't1'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_schema.reftarget_local$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +MERGE INTO reftarget_local +USING (SELECT * FROM refsource_ref UNION SELECT * FROM refsource_ref) AS foo ON reftarget_local.t1 = foo.s1 +WHEN MATCHED AND reftarget_local.t2 = 100 THEN + DELETE +WHEN MATCHED THEN + UPDATE SET t2 = t2 + 100 +WHEN NOT MATCHED THEN + INSERT VALUES(foo.s1); +SELECT * INTO dist_reftarget FROM reftarget_local ORDER BY 1, 2; +-- Should be equal +SELECT c.*, p.* +FROM dist_reftarget c, pg_result p +WHERE c.t1 = p.t1 +ORDER BY 1,2; + t1 | t2 | t1 | t2 +--------------------------------------------------------------------- + 1 | 100 | 1 | 100 + 2 | | 2 | +(2 rows) + +-- Must return zero rows +SELECT count(*) +FROM pg_result FULL OUTER JOIN dist_reftarget ON pg_result.t1 = dist_reftarget.t1 +WHERE pg_result.t1 IS NULL OR dist_reftarget.t1 IS NULL; + count +--------------------------------------------------------------------- + 0 +(1 row) + +-- +-- Distributed (target), Reference(source) +-- +CREATE TABLE demo_distributed(id1 int, val1 int); +CREATE TABLE demo_source_table(id2 int, val2 int); +CREATE FUNCTION setup_demo_data() RETURNS VOID AS $$ +INSERT INTO demo_distributed VALUES(1, 100); +INSERT INTO demo_distributed VALUES(7, 100); +INSERT INTO demo_distributed VALUES(15, 100); +INSERT INTO demo_distributed VALUES(100, 0); +INSERT INTO demo_distributed VALUES(300, 100); +INSERT INTO demo_distributed VALUES(400, 0); + +INSERT INTO demo_source_table VALUES(1, 77); +INSERT INTO demo_source_table VALUES(15, 77); +INSERT INTO demo_source_table VALUES(75, 77); +INSERT INTO demo_source_table VALUES(100, 77); +INSERT INTO demo_source_table VALUES(300, 77); +INSERT INTO demo_source_table VALUES(400, 77); +INSERT INTO demo_source_table VALUES(500, 77); +$$ +LANGUAGE SQL; +CREATE FUNCTION merge_demo_data() RETURNS VOID AS $$ +MERGE INTO demo_distributed t +USING demo_source_table s ON s.id2 = t.id1 +WHEN MATCHED AND t.val1= 0 THEN + DELETE +WHEN MATCHED THEN + UPDATE SET val1 = val1 + s.val2 +WHEN NOT MATCHED THEN + INSERT VALUES(s.id2, s.val2); +$$ +LANGUAGE SQL; +SELECT setup_demo_data(); + setup_demo_data +--------------------------------------------------------------------- + +(1 row) + +SELECT merge_demo_data(); + merge_demo_data +--------------------------------------------------------------------- + +(1 row) + +SELECT * INTO pg_demo_result FROM demo_distributed ORDER BY 1, 2; +TRUNCATE demo_distributed; +TRUNCATE demo_source_table; +SELECT create_distributed_table('demo_distributed', 'id1'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_reference_table('demo_source_table'); + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +SELECT setup_demo_data(); + setup_demo_data +--------------------------------------------------------------------- + +(1 row) + +SELECT merge_demo_data(); + merge_demo_data +--------------------------------------------------------------------- + +(1 row) + +SELECT * INTO dist_demo_result FROM demo_distributed ORDER BY 1, 2; +-- Should be equal +SELECT c.*, p.* +FROM dist_demo_result c, pg_demo_result p +WHERE c.id1 = p.id1 +ORDER BY 1,2; + id1 | val1 | id1 | val1 +--------------------------------------------------------------------- + 1 | 177 | 1 | 177 + 7 | 100 | 7 | 100 + 15 | 177 | 15 | 177 + 75 | 77 | 75 | 77 + 300 | 177 | 300 | 177 + 500 | 77 | 500 | 77 +(6 rows) + +-- Must return zero rows +SELECT count(*) +FROM pg_demo_result p FULL OUTER JOIN dist_demo_result d ON p.id1 = d.id1 +WHERE p.id1 IS NULL OR d.id1 IS NULL; + count +--------------------------------------------------------------------- + 0 +(1 row) + +-- Now convert source as distributed, but non-colocated with target +DROP TABLE pg_demo_result, dist_demo_result; +SELECT undistribute_table('demo_distributed'); +NOTICE: creating a new table for merge_schema.demo_distributed +NOTICE: moving the data of merge_schema.demo_distributed +NOTICE: dropping the old merge_schema.demo_distributed +NOTICE: renaming the new table to merge_schema.demo_distributed + undistribute_table +--------------------------------------------------------------------- + +(1 row) + +SELECT undistribute_table('demo_source_table'); +NOTICE: creating a new table for merge_schema.demo_source_table +NOTICE: moving the data of merge_schema.demo_source_table +NOTICE: dropping the old merge_schema.demo_source_table +NOTICE: renaming the new table to merge_schema.demo_source_table + undistribute_table +--------------------------------------------------------------------- + +(1 row) + +CREATE OR REPLACE FUNCTION merge_demo_data() RETURNS VOID AS $$ +MERGE INTO demo_distributed t +USING (SELECT id2,val2 FROM demo_source_table UNION SELECT val2,id2 FROM demo_source_table) AS s +ON t.id1 = s.id2 +WHEN MATCHED THEN + UPDATE SET val1 = val1 + 1; +$$ +LANGUAGE SQL; +TRUNCATE demo_distributed; +TRUNCATE demo_source_table; +SELECT setup_demo_data(); + setup_demo_data +--------------------------------------------------------------------- + +(1 row) + +SELECT merge_demo_data(); + merge_demo_data +--------------------------------------------------------------------- + +(1 row) + +SELECT * INTO pg_demo_result FROM demo_distributed ORDER BY 1, 2; +SELECT create_distributed_table('demo_distributed', 'id1'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_schema.demo_distributed$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('demo_source_table', 'id2', colocate_with=>'none'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_schema.demo_source_table$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +TRUNCATE demo_distributed; +TRUNCATE demo_source_table; +SELECT setup_demo_data(); + setup_demo_data +--------------------------------------------------------------------- + +(1 row) + +SELECT merge_demo_data(); + merge_demo_data +--------------------------------------------------------------------- + +(1 row) + +SELECT * INTO dist_demo_result FROM demo_distributed ORDER BY 1, 2; +-- Should be equal +SELECT c.*, p.* +FROM dist_demo_result c, pg_demo_result p +WHERE c.id1 = p.id1 +ORDER BY 1,2; + id1 | val1 | id1 | val1 +--------------------------------------------------------------------- + 1 | 101 | 1 | 101 + 7 | 100 | 7 | 100 + 15 | 101 | 15 | 101 + 100 | 1 | 100 | 1 + 300 | 101 | 300 | 101 + 400 | 1 | 400 | 1 +(6 rows) + +-- Must return zero rows +SELECT count(*) +FROM pg_demo_result p FULL OUTER JOIN dist_demo_result d ON p.id1 = d.id1 +WHERE p.id1 IS NULL OR d.id1 IS NULL; + count +--------------------------------------------------------------------- + 0 +(1 row) + +-- Test with LIMIT +CREATE OR REPLACE FUNCTION merge_demo_data() RETURNS VOID AS $$ +MERGE INTO demo_distributed t +USING (SELECT 999 as s3, demo_source_table.* FROM (SELECT * FROM demo_source_table ORDER BY 1 LIMIT 3) as foo LEFT JOIN demo_source_table USING(id2)) AS s +ON t.id1 = s.id2 +WHEN MATCHED THEN + UPDATE SET val1 = s3 +WHEN NOT MATCHED THEN + INSERT VALUES(id2, s3); +$$ +LANGUAGE SQL; +DROP TABLE pg_demo_result, dist_demo_result; +SELECT undistribute_table('demo_distributed'); +NOTICE: creating a new table for merge_schema.demo_distributed +NOTICE: moving the data of merge_schema.demo_distributed +NOTICE: dropping the old merge_schema.demo_distributed +NOTICE: renaming the new table to merge_schema.demo_distributed + undistribute_table +--------------------------------------------------------------------- + +(1 row) + +SELECT undistribute_table('demo_source_table'); +NOTICE: creating a new table for merge_schema.demo_source_table +NOTICE: moving the data of merge_schema.demo_source_table +NOTICE: dropping the old merge_schema.demo_source_table +NOTICE: renaming the new table to merge_schema.demo_source_table + undistribute_table +--------------------------------------------------------------------- + +(1 row) + +TRUNCATE demo_distributed; +TRUNCATE demo_source_table; +SELECT setup_demo_data(); + setup_demo_data +--------------------------------------------------------------------- + +(1 row) + +SELECT merge_demo_data(); + merge_demo_data +--------------------------------------------------------------------- + +(1 row) + +SELECT * INTO pg_demo_result FROM demo_distributed ORDER BY 1, 2; +SELECT create_distributed_table('demo_distributed', 'id1'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_schema.demo_distributed$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('demo_source_table', 'id2', colocate_with=>'none'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_schema.demo_source_table$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +TRUNCATE demo_distributed; +TRUNCATE demo_source_table; +SELECT setup_demo_data(); + setup_demo_data +--------------------------------------------------------------------- + +(1 row) + +SELECT merge_demo_data(); + merge_demo_data +--------------------------------------------------------------------- + +(1 row) + +SELECT * INTO dist_demo_result FROM demo_distributed ORDER BY 1, 2; +-- Should be equal +SELECT c.*, p.* +FROM dist_demo_result c, pg_demo_result p +WHERE c.id1 = p.id1 +ORDER BY 1,2; + id1 | val1 | id1 | val1 +--------------------------------------------------------------------- + 1 | 999 | 1 | 999 + 7 | 100 | 7 | 100 + 15 | 999 | 15 | 999 + 75 | 999 | 75 | 999 + 100 | 0 | 100 | 0 + 300 | 100 | 300 | 100 + 400 | 0 | 400 | 0 +(7 rows) + +-- Must return zero rows +SELECT count(*) +FROM pg_demo_result p FULL OUTER JOIN dist_demo_result d ON p.id1 = d.id1 +WHERE p.id1 IS NULL OR d.id1 IS NULL; + count +--------------------------------------------------------------------- + 0 +(1 row) + +-- Test explain with repartition +SET citus.explain_all_tasks TO false; +EXPLAIN (COSTS OFF) +MERGE INTO demo_distributed t +USING (SELECT 999 as s3, demo_source_table.* FROM (SELECT * FROM demo_source_table ORDER BY 1 LIMIT 3) as foo LEFT JOIN demo_source_table USING(id2)) AS s +ON t.id1 = s.id2 +WHEN MATCHED THEN + UPDATE SET val1 = s3 +WHEN NOT MATCHED THEN + INSERT VALUES(id2, s3); + QUERY PLAN +--------------------------------------------------------------------- + Custom Scan (Citus MERGE INTO ...) + MERGE INTO demo_distributed method: pull to coordinator + -> Custom Scan (Citus Adaptive) + -> Distributed Subplan XXX_1 + -> Limit + -> Sort + Sort Key: remote_scan.id2 + -> Custom Scan (Citus Adaptive) + Task Count: 4 + Tasks Shown: One of 4 + -> Task + Node: host=localhost port=xxxxx dbname=regression + -> Limit + -> Sort + Sort Key: id2 + -> Seq Scan on demo_source_table_4000135 demo_source_table + -> Distributed Subplan XXX_2 + -> Custom Scan (Citus Adaptive) + Task Count: 4 + Tasks Shown: One of 4 + -> Task + Node: host=localhost port=xxxxx dbname=regression + -> Seq Scan on demo_source_table_4000135 demo_source_table + Task Count: 1 + Tasks Shown: All + -> Task + Node: host=localhost port=xxxxx dbname=regression + -> Merge Left Join + Merge Cond: (intermediate_result.id2 = intermediate_result_1.id2) + -> Sort + Sort Key: intermediate_result.id2 + -> Function Scan on read_intermediate_result intermediate_result + -> Sort + Sort Key: intermediate_result_1.id2 + -> Function Scan on read_intermediate_result intermediate_result_1 +(35 rows) + +-- Test multiple join conditions on distribution column +MERGE INTO demo_distributed t +USING (SELECT id2+1 as key, id2+3 as key2 FROM demo_source_table) s +ON t.id1 = s.key2 ANd t.id1 = s.key +WHEN NOT MATCHED THEN + INSERT VALUES(s.key2, 333); +MERGE INTO demo_distributed t +USING (SELECT id2+1 as key, id2+2 as key2 FROM demo_source_table) s +ON t.id1 = s.key2 AND t.id1 = s.key +WHEN NOT MATCHED THEN + DO NOTHING; +MERGE INTO demo_distributed t +USING (SELECT id2+1 as key, id2+3 as key2 FROM demo_source_table) s +ON t.val1 = s.key2 AND t.id1 = s.key AND t.id1 = s.key2 +WHEN NOT MATCHED THEN + INSERT VALUES(s.key2, 444); +-- Test aggregate functions in source-query +SELECT COUNT(*) FROM demo_distributed where val1 = 150; + count +--------------------------------------------------------------------- + 0 +(1 row) + +SELECT COUNT(*) FROM demo_distributed where id1 = 2; + count +--------------------------------------------------------------------- + 0 +(1 row) + +-- One row with Key=7 updated in demo_distributed to 150 +MERGE INTO demo_distributed t +USING (SELECT count(DISTINCT id2)::int4 as key FROM demo_source_table GROUP BY val2) s +ON t.id1 = s.key +WHEN NOT MATCHED THEN INSERT VALUES(s.key, 1) +WHEN MATCHED THEN UPDATE SET val1 = 150; +-- Seven rows with Key=2 inserted in demo_distributed +MERGE INTO demo_distributed t +USING (SELECT (count(DISTINCT val2) + 1)::int4 as key FROM demo_source_table GROUP BY id2) s +ON t.id1 = s.key +WHEN NOT MATCHED THEN INSERT VALUES(s.key, 1) +WHEN MATCHED THEN UPDATE SET val1 = 150; +SELECT COUNT(*) FROM demo_distributed where val1 = 150; + count +--------------------------------------------------------------------- + 1 +(1 row) + +SELECT COUNT(*) FROM demo_distributed where id1 = 2; + count +--------------------------------------------------------------------- + 7 +(1 row) + -- -- Error and Unsupported scenarios -- +-- Test explain analyze with repartition +EXPLAIN ANALYZE +MERGE INTO demo_distributed t +USING (SELECT 999 as s3, demo_source_table.* FROM (SELECT * FROM demo_source_table ORDER BY 1 LIMIT 3) as foo LEFT JOIN demo_source_table USING(id2)) AS s +ON t.id1 = s.id2 +WHEN MATCHED THEN + UPDATE SET val1 = s3 +WHEN NOT MATCHED THEN + INSERT VALUES(id2, s3); +ERROR: EXPLAIN ANALYZE is currently not supported for MERGE INTO ... commands with repartitioning +-- Source without a table +MERGE INTO target_cj t +USING (VALUES (1, 1), (2, 1), (3, 3)) as s (sid, val) +ON t.tid = s.sid AND t.tid = 2 +WHEN MATCHED THEN + UPDATE SET val = s.val +WHEN NOT MATCHED THEN + DO NOTHING; +ERROR: To MERGE into a distributed table, source must be Citus table(s) +-- Incomplete source +MERGE INTO target_cj t +USING (source_cj1 s1 INNER JOIN source_cj2 s2 ON sid1 = val2) s +ON t.tid = s.sid1 AND t.tid = 2 +WHEN MATCHED THEN + UPDATE SET src = src2 +WHEN NOT MATCHED THEN + DO NOTHING; +ERROR: Source is not an explicit query +HINT: Source query is a Join expression, try converting into a query as SELECT * FROM (..Join..) -- Reference as a target and local as source MERGE INTO refsource_ref USING (SELECT * FROM reftarget_local UNION SELECT * FROM reftarget_local) AS foo ON refsource_ref.s1 = foo.t1 @@ -2530,40 +3013,20 @@ WHEN MATCHED THEN WHEN NOT MATCHED THEN INSERT VALUES(foo.t1); ERROR: Reference table as target is not allowed in MERGE command --- Reference as a source and distributed as target -MERGE INTO target_set t -USING refsource_ref AS s ON t.t1 = s.s1 -WHEN MATCHED THEN - DO NOTHING; -ERROR: MERGE command is not supported with combination of distributed/reference yet -HINT: If target is distributed, source must be distributed and co-located MERGE INTO target_set USING source_set AS foo ON target_set.t1 = foo.s1 WHEN MATCHED THEN UPDATE SET ctid = '(0,100)'; ERROR: cannot assign to system column "ctid" -MERGE INTO target_set -USING (SELECT s1,s2 FROM source_set UNION SELECT s2,s1 FROM source_set) AS foo ON target_set.t1 = foo.s1 -WHEN MATCHED THEN - UPDATE SET t2 = t2 + 1; -ERROR: cannot pushdown the subquery since not all subqueries in the UNION have the partition column in the same position -DETAIL: Each leaf query of the UNION should return the partition column in the same position and all joins must be on the partition column -MERGE INTO target_set -USING (SELECT 2 as s3, source_set.* FROM (SELECT * FROM source_set LIMIT 1) as foo LEFT JOIN source_set USING( s1)) AS foo -ON target_set.t1 = foo.s1 -WHEN MATCHED THEN UPDATE SET t2 = t2 + 1 -WHEN NOT MATCHED THEN INSERT VALUES(s1, s3); -ERROR: cannot push down this subquery -DETAIL: Limit clause is currently unsupported when a subquery references a column from another query -- modifying CTE not supported EXPLAIN -WITH cte_1 AS (DELETE FROM target_json) +WITH cte_1 AS (DELETE FROM target_json RETURNING *) MERGE INTO target_json sda -USING source_json sdn +USING cte_1 sdn ON sda.id = sdn.id WHEN NOT matched THEN INSERT (id, z) VALUES (sdn.id, 5); -ERROR: MERGE command is only supported when all distributed tables are co-located and joined on their distribution columns +ERROR: CTEs with modifying actions are not yet supported in MERGE -- Grouping sets not supported MERGE INTO citus_target t USING (SELECT count(*), id FROM citus_source GROUP BY GROUPING SETS (id, val)) subq @@ -2574,8 +3037,8 @@ WHEN NOT MATCHED THEN INSERT VALUES (subq.id, 99) WHEN MATCHED AND t.id < 350 THEN DELETE; -ERROR: cannot push down this subquery -DETAIL: could not run distributed query with GROUPING SETS, CUBE, or ROLLUP +ERROR: could not run distributed query with GROUPING SETS, CUBE, or ROLLUP +HINT: Consider using an equality filter on the distributed table's partition column. WITH subq AS ( SELECT count(*), id FROM citus_source GROUP BY GROUPING SETS (id, val) @@ -2589,8 +3052,8 @@ WHEN NOT MATCHED THEN INSERT VALUES (subq.id, 99) WHEN MATCHED AND t.id < 350 THEN DELETE; -ERROR: cannot push down this subquery -DETAIL: could not run distributed query with GROUPING SETS, CUBE, or ROLLUP +ERROR: could not run distributed query with GROUPING SETS, CUBE, or ROLLUP +HINT: Consider using an equality filter on the distributed table's partition column. -- try inserting unmatched distribution column value MERGE INTO citus_target t USING citus_source s @@ -2598,18 +3061,22 @@ ON t.id = s.id WHEN NOT MATCHED THEN INSERT DEFAULT VALUES; ERROR: cannot perform MERGE INSERT with DEFAULTS +DETAIL: Inserting arbitrary values that don't correspond to the joined column values can lead to unpredictable outcomes where rows are incorrectly distributed among different shards MERGE INTO citus_target t USING citus_source s ON t.id = s.id WHEN NOT MATCHED THEN INSERT VALUES(10000); -ERROR: MERGE INSERT must refer a source column for distribution column +ERROR: MERGE INSERT is using unsupported expression type for distribution column +DETAIL: Inserting arbitrary values that don't correspond to the joined column values can lead to unpredictable outcomes where rows are incorrectly distributed among different shards MERGE INTO citus_target t USING citus_source s ON t.id = s.id WHEN NOT MATCHED THEN INSERT (id) VALUES(1000); -ERROR: MERGE INSERT must refer a source column for distribution column +ERROR: MERGE INSERT is using unsupported expression type for distribution column +DETAIL: Inserting arbitrary values that don't correspond to the joined column values can lead to unpredictable outcomes where rows are incorrectly distributed among different shards +-- Colocated merge MERGE INTO t1 t USING s1 s ON t.id = s.id @@ -2622,6 +3089,13 @@ ON t.id = s.id WHEN NOT MATCHED THEN INSERT (val) VALUES(s.val); ERROR: MERGE INSERT must have distribution column as value +-- Non-colocated merge +MERGE INTO t1 t +USING s1 s +ON t.id = s.val +WHEN NOT MATCHED THEN + INSERT (id) VALUES(s.id); +ERROR: MERGE INSERT must use the source's joining column for target's distribution column -- try updating the distribution key column BEGIN; MERGE INTO target_cj t @@ -2691,7 +3165,7 @@ MERGE INTO t1 UPDATE SET val = t1.val + 1 WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s1.id, s1.val); -ERROR: MERGE command is not supported with combination of distributed/local tables yet +ERROR: MERGE involving repartition of rows is supported only if the target is distributed -- Now both s1 and t1 are distributed tables SELECT undistribute_table('t1'); NOTICE: creating a new table for merge_schema.t1 @@ -2757,17 +3231,7 @@ WHEN MATCHED AND (merge_when_and_write()) THEN UPDATE SET val = t1.val + s1.val; ERROR: non-IMMUTABLE functions are not yet supported in MERGE sql with distributed tables ROLLBACK; --- Joining on partition columns with sub-query -MERGE INTO t1 - USING (SELECT * FROM s1) sub ON (sub.val = t1.id) -- sub.val is not a distribution column - WHEN MATCHED AND sub.val = 0 THEN - DELETE - WHEN MATCHED THEN - UPDATE SET val = t1.val + 1 - WHEN NOT MATCHED THEN - INSERT (id, val) VALUES (sub.id, sub.val); -ERROR: MERGE command is only supported when all distributed tables are co-located and joined on their distribution columns --- Joining on partition columns with CTE +-- Joining on non-partition columns with CTE source, but INSERT incorrect column WITH s1_res AS ( SELECT * FROM s1 ) @@ -2779,7 +3243,7 @@ MERGE INTO t1 UPDATE SET val = t1.val + 1 WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s1_res.id, s1_res.val); -ERROR: MERGE command is only supported when all distributed tables are co-located and joined on their distribution columns +ERROR: MERGE INSERT must use the source's joining column for target's distribution column -- Constant Join condition WITH s1_res AS ( SELECT * FROM s1 @@ -2792,8 +3256,9 @@ MERGE INTO t1 UPDATE SET val = t1.val + 1 WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s1_res.id, s1_res.val); -ERROR: MERGE command is only supported when all distributed tables are co-located and joined on their distribution columns --- With a single WHEN clause, which causes a non-left join +ERROR: The required join operation is missing between the target's distribution column and any expression originating from the source. The issue may arise from either a non-equi-join or a mismatch in the datatypes of the columns being joined. +DETAIL: Without a equi-join condition on the target's distribution column, the source rows cannot be efficiently redistributed, and the NOT-MATCHED condition cannot be evaluated unambiguously. This can result in incorrect or unexpected results when attempting to merge tables in a distributed setting +-- Join condition without target distribution column WITH s1_res AS ( SELECT * FROM s1 ) @@ -2801,7 +3266,8 @@ WITH s1_res AS ( WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s1_res.id, s1_res.val); -ERROR: MERGE command is only supported when all distributed tables are co-located and joined on their distribution columns +ERROR: The required join operation is missing between the target's distribution column and any expression originating from the source. The issue may arise from either a non-equi-join or a mismatch in the datatypes of the columns being joined. +DETAIL: Without a equi-join condition on the target's distribution column, the source rows cannot be efficiently redistributed, and the NOT-MATCHED condition cannot be evaluated unambiguously. This can result in incorrect or unexpected results when attempting to merge tables in a distributed setting -- -- Reference tables -- @@ -2895,7 +3361,7 @@ MERGE INTO t1 UPDATE SET val = t1.val + 1 WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s1.id, s1.val); -ERROR: MERGE command is not supported with combination of distributed/local tables yet +ERROR: MERGE INTO an distributed table from Postgres table is not yet supported MERGE INTO t1 USING (SELECT * FROM s1) sub ON (sub.id = t1.id) WHEN MATCHED AND sub.val = 0 THEN @@ -2904,7 +3370,7 @@ MERGE INTO t1 UPDATE SET val = t1.val + 1 WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val); -ERROR: MERGE command is not supported with combination of distributed/local tables yet +ERROR: MERGE INTO an distributed table from Postgres table is not yet supported CREATE TABLE pg(val int); SELECT create_distributed_table('s1', 'id'); NOTICE: Copying data from local table... @@ -2925,7 +3391,7 @@ MERGE INTO t1 UPDATE SET val = t1.val + 1 WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val); -ERROR: MERGE command is not supported with combination of distributed/local tables yet +ERROR: MERGE INTO an distributed table from Postgres table is not yet supported -- Mix Postgres table in CTE WITH pg_res AS ( SELECT * FROM pg @@ -2938,7 +3404,7 @@ MERGE INTO t1 UPDATE SET val = t1.val + 1 WHEN NOT MATCHED THEN INSERT (id, val) VALUES (sub.id, sub.val); -ERROR: MERGE command is not supported with combination of distributed/local tables yet +ERROR: MERGE INTO an distributed table from Postgres table is not yet supported -- Match more than one source row should fail same as Postgres behavior SELECT undistribute_table('t1'); NOTICE: creating a new table for merge_schema.t1 @@ -2993,7 +3459,7 @@ WHEN NOT MATCHED THEN INSERT VALUES(mv_source.id, mv_source.val); ERROR: cannot execute MERGE on relation "mv_source" DETAIL: This operation is not supported for materialized views. --- Distributed tables *must* be colocated +-- Do not allow constant values into the distribution column CREATE TABLE dist_target(id int, val varchar); SELECT create_distributed_table('dist_target', 'id'); create_distributed_table @@ -3008,31 +3474,6 @@ SELECT create_distributed_table('dist_source', 'id', colocate_with => 'none'); (1 row) -MERGE INTO dist_target -USING dist_source -ON dist_target.id = dist_source.id -WHEN MATCHED THEN -UPDATE SET val = dist_source.val -WHEN NOT MATCHED THEN -INSERT VALUES(dist_source.id, dist_source.val); -ERROR: For MERGE command, all the distributed tables must be colocated --- Distributed tables *must* be joined on distribution column -CREATE TABLE dist_colocated(id int, val int); -SELECT create_distributed_table('dist_colocated', 'id', colocate_with => 'dist_target'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -MERGE INTO dist_target -USING dist_colocated -ON dist_target.id = dist_colocated.val -- val is not the distribution column -WHEN MATCHED THEN -UPDATE SET val = dist_colocated.val -WHEN NOT MATCHED THEN -INSERT VALUES(dist_colocated.id, dist_colocated.val); -ERROR: MERGE command is only supported when all distributed tables are co-located and joined on their distribution columns --- Both the source and target must be distributed MERGE INTO dist_target USING (SELECT 100 id) AS source ON dist_target.id = source.id AND dist_target.val = 'const' @@ -3040,7 +3481,7 @@ WHEN MATCHED THEN UPDATE SET val = 'source' WHEN NOT MATCHED THEN INSERT VALUES(source.id, 'source'); -ERROR: For MERGE command, both the source and target must be distributed +ERROR: To MERGE into a distributed table, source must be Citus table(s) -- Non-hash distributed tables (append/range). CREATE VIEW show_tables AS SELECT logicalrelid, partmethod @@ -3079,8 +3520,7 @@ WHEN MATCHED THEN UPDATE SET val = dist_source.val WHEN NOT MATCHED THEN INSERT VALUES(dist_source.id, dist_source.val); -ERROR: For MERGE command, all the distributed tables must be colocated, for append/range distribution, colocation is not supported -HINT: Consider using hash distribution instead +ERROR: For MERGE command, append/range distribution table is not supported yet SELECT undistribute_table('dist_source'); NOTICE: creating a new table for merge_schema.dist_source NOTICE: moving the data of merge_schema.dist_source @@ -3113,8 +3553,7 @@ WHEN MATCHED THEN UPDATE SET val = dist_source.val WHEN NOT MATCHED THEN INSERT VALUES(dist_source.id, dist_source.val); -ERROR: For MERGE command, all the distributed tables must be colocated, for append/range distribution, colocation is not supported -HINT: Consider using hash distribution instead +ERROR: For MERGE command, append/range distribution table is not supported yet -- Both are append tables SELECT undistribute_table('dist_target'); NOTICE: creating a new table for merge_schema.dist_target @@ -3166,8 +3605,7 @@ WHEN MATCHED THEN UPDATE SET val = dist_source.val WHEN NOT MATCHED THEN INSERT VALUES(dist_source.id, dist_source.val); -ERROR: For MERGE command, all the distributed tables must be colocated, for append/range distribution, colocation is not supported -HINT: Consider using hash distribution instead +ERROR: For MERGE command, append/range distribution table is not supported yet -- Both are range tables SELECT undistribute_table('dist_target'); NOTICE: creating a new table for merge_schema.dist_target @@ -3219,12 +3657,10 @@ WHEN MATCHED THEN UPDATE SET val = dist_source.val WHEN NOT MATCHED THEN INSERT VALUES(dist_source.id, dist_source.val); -ERROR: For MERGE command, all the distributed tables must be colocated, for append/range distribution, colocation is not supported -HINT: Consider using hash distribution instead +ERROR: For MERGE command, append/range distribution table is not supported yet -- test merge with single-shard tables CREATE SCHEMA query_single_shard_table; SET search_path TO query_single_shard_table; -SET client_min_messages TO DEBUG2; CREATE TABLE nullkey_c1_t1(a int, b int); CREATE TABLE nullkey_c1_t2(a int, b int); SELECT create_distributed_table('nullkey_c1_t1', null, colocate_with=>'none'); @@ -3254,32 +3690,33 @@ SELECT create_distributed_table('nullkey_c2_t2', null, colocate_with=>'nullkey_c (1 row) CREATE TABLE reference_table(a int, b int); +CREATE TABLE distributed_table(a int, b int); +CREATE TABLE citus_local_table(a int, b int); SELECT create_reference_table('reference_table'); create_reference_table --------------------------------------------------------------------- (1 row) -INSERT INTO reference_table SELECT i, i FROM generate_series(0, 5) i; -DEBUG: distributed INSERT ... SELECT can only select from distributed tables -DEBUG: Collecting INSERT ... SELECT results on coordinator -CREATE TABLE distributed_table(a int, b int); SELECT create_distributed_table('distributed_table', 'a'); create_distributed_table --------------------------------------------------------------------- (1 row) -INSERT INTO distributed_table SELECT i, i FROM generate_series(3, 8) i; -DEBUG: distributed INSERT ... SELECT can only select from distributed tables -DEBUG: Collecting INSERT ... SELECT results on coordinator -CREATE TABLE citus_local_table(a int, b int); SELECT citus_add_local_table_to_metadata('citus_local_table'); citus_add_local_table_to_metadata --------------------------------------------------------------------- (1 row) +SET client_min_messages TO DEBUG2; +INSERT INTO reference_table SELECT i, i FROM generate_series(0, 5) i; +DEBUG: distributed INSERT ... SELECT can only select from distributed tables +DEBUG: Collecting INSERT ... SELECT results on coordinator +INSERT INTO distributed_table SELECT i, i FROM generate_series(3, 8) i; +DEBUG: distributed INSERT ... SELECT can only select from distributed tables +DEBUG: Collecting INSERT ... SELECT results on coordinator INSERT INTO citus_local_table SELECT i, i FROM generate_series(0, 10) i; DEBUG: distributed INSERT ... SELECT can only select from distributed tables DEBUG: Collecting INSERT ... SELECT results on coordinator @@ -3288,44 +3725,70 @@ INSERT INTO postgres_local_table SELECT i, i FROM generate_series(5, 10) i; -- with a colocated table MERGE INTO nullkey_c1_t1 USING nullkey_c1_t2 ON (nullkey_c1_t1.a = nullkey_c1_t2.a) WHEN MATCHED THEN UPDATE SET b = nullkey_c1_t2.b; -DEBUG: +DEBUG: DEBUG: Creating MERGE router plan MERGE INTO nullkey_c1_t1 USING nullkey_c1_t2 ON (nullkey_c1_t1.a = nullkey_c1_t2.a) WHEN MATCHED THEN DELETE; -DEBUG: +DEBUG: DEBUG: Creating MERGE router plan MERGE INTO nullkey_c1_t1 USING nullkey_c1_t2 ON (nullkey_c1_t1.a = nullkey_c1_t2.a) WHEN MATCHED THEN UPDATE SET b = nullkey_c1_t2.b WHEN NOT MATCHED THEN INSERT VALUES (nullkey_c1_t2.a, nullkey_c1_t2.b); -DEBUG: +DEBUG: DEBUG: Creating MERGE router plan MERGE INTO nullkey_c1_t1 USING nullkey_c1_t2 ON (nullkey_c1_t1.a = nullkey_c1_t2.a) WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT VALUES (nullkey_c1_t2.a, nullkey_c1_t2.b); -DEBUG: +DEBUG: DEBUG: Creating MERGE router plan -- with non-colocated single-shard table MERGE INTO nullkey_c1_t1 USING nullkey_c2_t1 ON (nullkey_c1_t1.a = nullkey_c2_t1.a) WHEN MATCHED THEN UPDATE SET b = nullkey_c2_t1.b; -ERROR: For MERGE command, all the distributed tables must be colocated +DEBUG: Distributed tables are not co-located, try repartitioning +DEBUG: For MERGE command, all the distributed tables must be colocated +DEBUG: Creating MERGE repartition plan +ERROR: MERGE operation on non-colocated distributed table(s) without a shard key is not yet supported MERGE INTO nullkey_c1_t1 USING nullkey_c2_t1 ON (nullkey_c1_t1.a = nullkey_c2_t1.a) WHEN MATCHED THEN UPDATE SET b = nullkey_c2_t1.b WHEN NOT MATCHED THEN INSERT VALUES (nullkey_c2_t1.a, nullkey_c2_t1.b); -ERROR: For MERGE command, all the distributed tables must be colocated +DEBUG: Distributed tables are not co-located, try repartitioning +DEBUG: For MERGE command, all the distributed tables must be colocated +DEBUG: Creating MERGE repartition plan +ERROR: MERGE operation on non-colocated distributed table(s) without a shard key is not yet supported -- with a distributed table MERGE INTO nullkey_c1_t1 USING distributed_table ON (nullkey_c1_t1.a = distributed_table.a) WHEN MATCHED THEN UPDATE SET b = distributed_table.b WHEN NOT MATCHED THEN INSERT VALUES (distributed_table.a, distributed_table.b); -ERROR: For MERGE command, all the distributed tables must be colocated +DEBUG: Distributed tables are not co-located, try repartitioning +DEBUG: For MERGE command, all the distributed tables must be colocated +DEBUG: Creating MERGE repartition plan +ERROR: MERGE operation on non-colocated distributed table(s) without a shard key is not yet supported MERGE INTO distributed_table USING nullkey_c1_t1 ON (nullkey_c1_t1.a = distributed_table.a) WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT VALUES (nullkey_c1_t1.a, nullkey_c1_t1.b); -ERROR: For MERGE command, all the distributed tables must be colocated +DEBUG: Distributed tables are not co-located, try repartitioning +DEBUG: For MERGE command, all the distributed tables must be colocated +DEBUG: Creating MERGE repartition plan +DEBUG: Using column - index:0 from the source list to redistribute +DEBUG: Distributed planning for a fast-path router query +DEBUG: Creating router plan +DEBUG: Collect source query results on coordinator +DEBUG: Create a MERGE task list that needs to be routed +DEBUG: +DEBUG: distributed statement: MERGE INTO query_single_shard_table.distributed_table_4000173 citus_table_alias USING (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('merge_into_XXX_4000173'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) nullkey_c1_t1 ON (nullkey_c1_t1.a OPERATOR(pg_catalog.=) citus_table_alias.a) WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (a, b) VALUES (nullkey_c1_t1.a, nullkey_c1_t1.b) +DEBUG: +DEBUG: distributed statement: MERGE INTO query_single_shard_table.distributed_table_4000174 citus_table_alias USING (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('merge_into_XXX_4000174'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) nullkey_c1_t1 ON (nullkey_c1_t1.a OPERATOR(pg_catalog.=) citus_table_alias.a) WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (a, b) VALUES (nullkey_c1_t1.a, nullkey_c1_t1.b) +DEBUG: +DEBUG: distributed statement: MERGE INTO query_single_shard_table.distributed_table_4000175 citus_table_alias USING (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('merge_into_XXX_4000175'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) nullkey_c1_t1 ON (nullkey_c1_t1.a OPERATOR(pg_catalog.=) citus_table_alias.a) WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (a, b) VALUES (nullkey_c1_t1.a, nullkey_c1_t1.b) +DEBUG: +DEBUG: distributed statement: MERGE INTO query_single_shard_table.distributed_table_4000176 citus_table_alias USING (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('merge_into_XXX_4000176'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) nullkey_c1_t1 ON (nullkey_c1_t1.a OPERATOR(pg_catalog.=) citus_table_alias.a) WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (a, b) VALUES (nullkey_c1_t1.a, nullkey_c1_t1.b) -- with a reference table MERGE INTO nullkey_c1_t1 USING reference_table ON (nullkey_c1_t1.a = reference_table.a) WHEN MATCHED THEN UPDATE SET b = reference_table.b; -ERROR: MERGE command is not supported with combination of distributed/reference yet -HINT: If target is distributed, source must be distributed and co-located +DEBUG: A mix of distributed and reference table, try repartitioning +DEBUG: A mix of distributed and reference table, routable query is not possible +DEBUG: Creating MERGE repartition plan +ERROR: MERGE operation on non-colocated distributed table(s) without a shard key is not yet supported MERGE INTO reference_table USING nullkey_c1_t1 ON (nullkey_c1_t1.a = reference_table.a) WHEN MATCHED THEN UPDATE SET b = nullkey_c1_t1.b WHEN NOT MATCHED THEN INSERT VALUES (nullkey_c1_t1.a, nullkey_c1_t1.b); @@ -3333,42 +3796,189 @@ ERROR: Reference table as target is not allowed in MERGE command -- with a citus local table MERGE INTO nullkey_c1_t1 USING citus_local_table ON (nullkey_c1_t1.a = citus_local_table.a) WHEN MATCHED THEN UPDATE SET b = citus_local_table.b; -ERROR: MERGE command is not supported with combination of distributed/local tables yet +DEBUG: A mix of distributed and local table, try repartitioning +DEBUG: A mix of distributed and citus-local table, routable query is not possible +DEBUG: Creating MERGE repartition plan +ERROR: MERGE operation on non-colocated distributed table(s) without a shard key is not yet supported MERGE INTO citus_local_table USING nullkey_c1_t1 ON (nullkey_c1_t1.a = citus_local_table.a) WHEN MATCHED THEN DELETE; -ERROR: MERGE command is not supported with combination of distributed/local tables yet +DEBUG: A mix of distributed and local table, try repartitioning +DEBUG: A mix of distributed and citus-local table, routable query is not possible +DEBUG: Creating MERGE repartition plan +ERROR: MERGE involving repartition of rows is supported only if the target is distributed -- with a postgres table MERGE INTO nullkey_c1_t1 USING postgres_local_table ON (nullkey_c1_t1.a = postgres_local_table.a) WHEN MATCHED THEN UPDATE SET b = postgres_local_table.b; -ERROR: MERGE command is not supported with combination of distributed/local tables yet +DEBUG: There is only one distributed table, merge is not pushable, try repartitioning +DEBUG: Creating MERGE repartition plan +ERROR: MERGE INTO an distributed table from Postgres table is not yet supported MERGE INTO postgres_local_table USING nullkey_c1_t1 ON (nullkey_c1_t1.a = postgres_local_table.a) WHEN MATCHED THEN UPDATE SET b = nullkey_c1_t1.b WHEN NOT MATCHED THEN INSERT VALUES (nullkey_c1_t1.a, nullkey_c1_t1.b); -ERROR: MERGE command is not supported with combination of distributed/local tables yet +DEBUG: There is only one distributed table, merge is not pushable, try repartitioning +DEBUG: Creating MERGE repartition plan +ERROR: MERGE involving repartition of rows is supported only if the target is distributed -- using ctes WITH cte AS ( SELECT * FROM nullkey_c1_t1 ) MERGE INTO nullkey_c1_t1 USING cte ON (nullkey_c1_t1.a = cte.a) WHEN MATCHED THEN UPDATE SET b = cte.b; -DEBUG: +DEBUG: DEBUG: Creating MERGE router plan WITH cte AS ( SELECT * FROM distributed_table ) MERGE INTO nullkey_c1_t1 USING cte ON (nullkey_c1_t1.a = cte.a) WHEN MATCHED THEN UPDATE SET b = cte.b; -ERROR: For MERGE command, all the distributed tables must be colocated +DEBUG: Distributed tables are not co-located, try repartitioning +DEBUG: For MERGE command, all the distributed tables must be colocated +DEBUG: Creating MERGE repartition plan +ERROR: MERGE operation on non-colocated distributed table(s) without a shard key is not yet supported WITH cte AS materialized ( SELECT * FROM distributed_table ) MERGE INTO nullkey_c1_t1 USING cte ON (nullkey_c1_t1.a = cte.a) WHEN MATCHED THEN UPDATE SET b = cte.b; -ERROR: For MERGE command, all the distributed tables must be colocated +DEBUG: Distributed tables are not co-located, try repartitioning +DEBUG: For MERGE command, all the distributed tables must be colocated +DEBUG: Creating MERGE repartition plan +ERROR: MERGE operation on non-colocated distributed table(s) without a shard key is not yet supported SET client_min_messages TO WARNING; DROP SCHEMA query_single_shard_table CASCADE; -RESET client_min_messages; SET search_path TO merge_schema; +-- Test Columnar table +CREATE TABLE target_columnar(cid int, name text) USING columnar; +SELECT create_distributed_table('target_columnar', 'cid'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +MERGE INTO target_columnar t +USING demo_source_table s +ON t.cid = s.id2 +WHEN MATCHED THEN + UPDATE SET name = 'Columnar table updated by MERGE' +WHEN NOT MATCHED THEN + DO NOTHING; +ERROR: Columnar table as target is not allowed in MERGE command +MERGE INTO demo_distributed t +USING generate_series(0,100) as source(key) +ON (source.key + 1 = t.id1) + WHEN MATCHED THEN UPDATE SET val1 = 15; +ERROR: Currently, Citus only supports table, subquery, and CTEs as valid sources for the MERGE operation +-- This should fail in planning stage itself +EXPLAIN MERGE INTO demo_distributed t +USING demo_source_table s +ON (s.id2 + 1 = t.id1) + WHEN MATCHED THEN UPDATE SET val1 = 15; +ERROR: The required join operation is missing between the target's distribution column and any expression originating from the source. The issue may arise from either a non-equi-join or a mismatch in the datatypes of the columns being joined. +DETAIL: Without a equi-join condition on the target's distribution column, the source rows cannot be efficiently redistributed, and the NOT-MATCHED condition cannot be evaluated unambiguously. This can result in incorrect or unexpected results when attempting to merge tables in a distributed setting +-- Sub-queries and CTEs are not allowed in actions and ON clause +CREATE TABLE target_1 (a int, b int, c int); +SELECT create_distributed_table('target_1', 'a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE source_2 (a int, b int, c int); +SELECT create_distributed_table('source_2', 'a'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO target_1 VALUES(1, 2, 3); +INSERT INTO target_1 VALUES(4, 5, 6); +INSERT INTO target_1 VALUES(11, 12, 13); +INSERT INTO source_2 VALUES(1, 2, 3); +WITH cte_1 as (SELECT max(a) as max_a, max(b) as b FROM source_2) +MERGE INTO target_1 +USING cte_1 +ON (target_1.a = cte_1.b) +WHEN NOT MATCHED AND (SELECT max_a > 10 FROM cte_1) THEN + INSERT VALUES (cte_1.b, 100); +ERROR: Sub-queries and CTEs are not allowed in actions for MERGE with repartitioning +HINT: Consider making the source and target colocated and joined on the distribution column to make it a routable query +WITH cte_1 as (SELECT a, b FROM source_2) +MERGE INTO target_1 +USING cte_1 +ON (target_1.a = cte_1.b) +WHEN NOT MATCHED AND (SELECT a > 10 FROM cte_1) THEN + INSERT VALUES (cte_1.b, 100); +ERROR: Sub-queries and CTEs are not allowed in actions for MERGE with repartitioning +HINT: Consider making the source and target colocated and joined on the distribution column to make it a routable query +MERGE INTO target_1 +USING source_2 +ON (target_1.a = source_2.b) +WHEN NOT MATCHED AND (SELECT max_a > 10 FROM (SELECT max(a) as max_a, max(b) as b FROM target_1) as foo) THEN + INSERT VALUES (source_2.b, 100); +ERROR: Sub-queries and CTEs are not allowed in actions for MERGE with repartitioning +HINT: Consider making the source and target colocated and joined on the distribution column to make it a routable query +-- or same with CTEs +WITH cte_1 as (SELECT max(a) as max_a, max(b) as b FROM target_1) +MERGE INTO target_1 +USING source_2 +ON (target_1.a = source_2.b) +WHEN NOT MATCHED AND (SELECT max_a > 10 FROM (SELECT max(a) as max_a, max(b) as b FROM target_1) as foo) THEN + INSERT VALUES (source_2.b, 100); +ERROR: Sub-queries and CTEs are not allowed in actions for MERGE with repartitioning +HINT: Consider making the source and target colocated and joined on the distribution column to make it a routable query +WITH cte_1 as (SELECT a, b FROM target_1), cte_2 as (select b,a from target_1) +MERGE INTO target_1 +USING (SELECT * FROM source_2) as subq +ON (target_1.a = subq.b) +WHEN NOT MATCHED AND (SELECT a > 10 FROM cte_2) THEN + INSERT VALUES (subq.b, 100); +ERROR: Sub-queries and CTEs are not allowed in actions for MERGE with repartitioning +HINT: Consider making the source and target colocated and joined on the distribution column to make it a routable query +MERGE INTO source_2 +USING target_1 +ON (target_1.a = source_2.a) +WHEN MATCHED THEN + UPDATE SET b = (SELECT max(a) FROM source_2); +ERROR: Sub-queries and CTEs are not allowed in actions for MERGE with repartitioning +HINT: Consider making the source and target colocated and joined on the distribution column to make it a routable query +MERGE INTO source_2 +USING target_1 +ON (target_1.a = source_2.a) +WHEN NOT MATCHED THEN + INSERT VALUES (target_1.a,(select max(a) from target_1)); +ERROR: Sub-queries and CTEs are not allowed in actions for MERGE with repartitioning +HINT: Consider making the source and target colocated and joined on the distribution column to make it a routable query +MERGE INTO target_1 +USING source_2 +ON (target_1.a = source_2.b) +WHEN NOT MATCHED AND (SELECT max(c) > 10 FROM source_2) THEN + INSERT VALUES (source_2.b, 100); +ERROR: Sub-queries and CTEs are not allowed in actions for MERGE with repartitioning +HINT: Consider making the source and target colocated and joined on the distribution column to make it a routable query +-- Test in ON clause +MERGE INTO target_1 t2 +USING (SELECT * FROM source_2) AS t1 +ON (t1.a = t2.a AND (SELECT 1=1 FROM target_1)) +WHEN MATCHED THEN + DELETE; +ERROR: Sub-queries and CTEs are not allowed in ON clause for MERGE with repartitioning +HINT: Consider making the source and target colocated and joined on the distribution column to make it a routable query +MERGE INTO target_1 t2 +USING (SELECT * FROM source_2) AS t1 +ON (t1.a = t2.a AND (SELECT max(a) > 55 FROM target_1)) +WHEN MATCHED THEN + DELETE; +ERROR: Sub-queries and CTEs are not allowed in ON clause for MERGE with repartitioning +HINT: Consider making the source and target colocated and joined on the distribution column to make it a routable query +WITH cte_1 as (SELECT a, b FROM target_1), cte_2 as (select b,a from target_1) +MERGE INTO target_1 t2 +USING (SELECT * FROM cte_1) AS t1 +ON (t1.a = t2.a AND (SELECT max(a) > 55 FROM cte_2)) +WHEN MATCHED THEN + DELETE; +ERROR: Sub-queries and CTEs are not allowed in ON clause for MERGE with repartitioning +HINT: Consider making the source and target colocated and joined on the distribution column to make it a routable query +RESET client_min_messages; DROP SERVER foreign_server CASCADE; NOTICE: drop cascades to 3 other objects DETAIL: drop cascades to user mapping for postgres on server foreign_server @@ -3379,7 +3989,7 @@ CONTEXT: SQL statement "SELECT citus_drop_all_shards(v_obj.objid, v_obj.schema_ PL/pgSQL function citus_drop_trigger() line XX at PERFORM DROP FUNCTION merge_when_and_write(); DROP SCHEMA merge_schema CASCADE; -NOTICE: drop cascades to 90 other objects +NOTICE: drop cascades to 98 other objects DETAIL: drop cascades to function insert_data() drop cascades to table local_local drop cascades to table target @@ -3453,20 +4063,28 @@ drop cascades to table source_serial drop cascades to table target_serial drop cascades to table target_set drop cascades to table source_set -drop cascades to table reftarget_local_4000113 drop cascades to table refsource_ref drop cascades to table pg_result drop cascades to table refsource_ref_4000112 drop cascades to table pg_ref -drop cascades to table reftarget_local drop cascades to table local_ref +drop cascades to table reftarget_local +drop cascades to table dist_reftarget +drop cascades to function setup_demo_data() +drop cascades to function merge_demo_data() +drop cascades to table demo_distributed +drop cascades to table demo_source_table +drop cascades to table pg_demo_result +drop cascades to table dist_demo_result drop cascades to function add_s(integer,integer) drop cascades to table pg -drop cascades to table t1_4000133 -drop cascades to table s1_4000134 +drop cascades to table t1_4000158 +drop cascades to table s1_4000159 drop cascades to table t1 drop cascades to table s1 -drop cascades to table dist_colocated drop cascades to table dist_target drop cascades to table dist_source drop cascades to view show_tables +drop cascades to table target_columnar +drop cascades to table target_1 +drop cascades to table source_2 diff --git a/src/test/regress/expected/merge_arbitrary.out b/src/test/regress/expected/merge_arbitrary.out index 345ac1410..b55306b44 100644 --- a/src/test/regress/expected/merge_arbitrary.out +++ b/src/test/regress/expected/merge_arbitrary.out @@ -148,3 +148,51 @@ SELECT * FROM t1 order by id; (5 rows) ROLLBACK; +-- Test prepared statements with repartition +PREPARE merge_repartition_pg(int,int,int,int) as + MERGE INTO pg_target target + USING (SELECT id+1+$1 as key, val FROM (SELECT * FROM pg_source UNION SELECT * FROM pg_source WHERE id = $2) as foo) as source + ON (source.key = target.id AND $3 < 10000) + WHEN MATCHED THEN UPDATE SET val = (source.key::int+$4) + WHEN NOT MATCHED THEN INSERT VALUES (source.key, source.val); +PREPARE merge_repartition_citus(int,int,int,int) as + MERGE INTO citus_target target + USING (SELECT id+1+$1 as key, val FROM (SELECT * FROM citus_source UNION SELECT * FROM citus_source WHERE id = $2) as foo) as source + ON (source.key = target.id AND $3 < 10000) + WHEN MATCHED THEN UPDATE SET val = (source.key::int+$4) + WHEN NOT MATCHED THEN INSERT VALUES (source.key, source.val); +EXECUTE merge_repartition_pg(1,1,1,1); +EXECUTE merge_repartition_citus(1,1,1,1); +SET client_min_messages = NOTICE; +SELECT compare_data(); +NOTICE: The average of pg_target.id is equal to citus_target.id +NOTICE: The average of pg_target.val is equal to citus_target.val + compare_data +--------------------------------------------------------------------- + +(1 row) + +RESET client_min_messages; +EXECUTE merge_repartition_pg(1,100,1,1); +EXECUTE merge_repartition_citus(1,100,1,1); +EXECUTE merge_repartition_pg(2,200,1,1); +EXECUTE merge_repartition_citus(2,200,1,1); +EXECUTE merge_repartition_pg(3,300,1,1); +EXECUTE merge_repartition_citus(3,300,1,1); +EXECUTE merge_repartition_pg(4,400,1,1); +EXECUTE merge_repartition_citus(4,400,1,1); +EXECUTE merge_repartition_pg(5,500,1,1); +EXECUTE merge_repartition_citus(5,500,1,1); +-- Sixth time +EXECUTE merge_repartition_pg(6,600,1,6); +EXECUTE merge_repartition_citus(6,600,1,6); +SET client_min_messages = NOTICE; +SELECT compare_data(); +NOTICE: The average of pg_target.id is equal to citus_target.id +NOTICE: The average of pg_target.val is equal to citus_target.val + compare_data +--------------------------------------------------------------------- + +(1 row) + +RESET client_min_messages; diff --git a/src/test/regress/expected/merge_arbitrary_create.out b/src/test/regress/expected/merge_arbitrary_create.out index 9b2444f17..aff9ecd97 100644 --- a/src/test/regress/expected/merge_arbitrary_create.out +++ b/src/test/regress/expected/merge_arbitrary_create.out @@ -70,3 +70,77 @@ SELECT citus_add_local_table_to_metadata('s1'); (1 row) +-- Test prepared statements with repartition +CREATE TABLE pg_target(id int, val int); +CREATE TABLE pg_source(id int, val int, const int); +CREATE TABLE citus_target(id int, val int); +CREATE TABLE citus_source(id int, val int, const int); +SELECT citus_add_local_table_to_metadata('pg_target'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_add_local_table_to_metadata('pg_source'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +-- +-- Load same set of data to both Postgres and Citus tables +-- +CREATE OR REPLACE FUNCTION setup_data() RETURNS VOID AS $$ + INSERT INTO pg_source SELECT i, i+1, 1 FROM generate_series(1, 10000) i; + INSERT INTO pg_target SELECT i, 1 FROM generate_series(5001, 10000) i; + INSERT INTO citus_source SELECT i, i+1, 1 FROM generate_series(1, 10000) i; + INSERT INTO citus_target SELECT i, 1 FROM generate_series(5001, 10000) i; +$$ +LANGUAGE SQL; +-- +-- Compares the final target tables, merge-modified data, of both Postgres and Citus tables +-- +CREATE OR REPLACE FUNCTION check_data(table1_name text, column1_name text, table2_name text, column2_name text) +RETURNS VOID AS $$ +DECLARE + table1_avg numeric; + table2_avg numeric; +BEGIN + EXECUTE format('SELECT COALESCE(AVG(%I), 0) FROM %I', column1_name, table1_name) INTO table1_avg; + EXECUTE format('SELECT COALESCE(AVG(%I), 0) FROM %I', column2_name, table2_name) INTO table2_avg; + + IF table1_avg > table2_avg THEN + RAISE EXCEPTION 'The average of %.% is greater than %.%', table1_name, column1_name, table2_name, column2_name; + ELSIF table1_avg < table2_avg THEN + RAISE EXCEPTION 'The average of %.% is less than %.%', table1_name, column1_name, table2_name, column2_name; + ELSE + RAISE NOTICE 'The average of %.% is equal to %.%', table1_name, column1_name, table2_name, column2_name; + END IF; +END; +$$ LANGUAGE plpgsql; +CREATE OR REPLACE FUNCTION compare_data() RETURNS VOID AS $$ + SELECT check_data('pg_target', 'id', 'citus_target', 'id'); + SELECT check_data('pg_target', 'val', 'citus_target', 'val'); +$$ +LANGUAGE SQL; +-- +-- Target and source are distributed, and non-colocated +-- +SELECT setup_data(); + setup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_target', 'id'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_source', 'id', colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + diff --git a/src/test/regress/expected/merge_partition_tables.out b/src/test/regress/expected/merge_partition_tables.out new file mode 100644 index 000000000..5ac375817 --- /dev/null +++ b/src/test/regress/expected/merge_partition_tables.out @@ -0,0 +1,230 @@ +SHOW server_version \gset +SELECT substring(:'server_version', '\d+')::int >= 15 AS server_version_ge_15 +\gset +\if :server_version_ge_15 +\else +\q +\endif +-- We create two sets of source and target tables, one set in Postgres and +-- the other in Citus distributed. We run the _exact_ MERGE SQL on both sets +-- and compare the final results of the target tables in Postgres and Citus. +-- The results should match. This process is repeated for various combinations +-- of MERGE SQL. +DROP SCHEMA IF EXISTS merge_partition_tables CASCADE; +NOTICE: schema "merge_partition_tables" does not exist, skipping +CREATE SCHEMA merge_partition_tables; +SET search_path TO merge_partition_tables; +SET citus.shard_count TO 4; +SET citus.next_shard_id TO 7000000; +SET citus.explain_all_tasks TO true; +SET citus.shard_replication_factor TO 1; +SET citus.max_adaptive_executor_pool_size TO 1; +SET client_min_messages = warning; +SELECT 1 FROM master_add_node('localhost', :master_port, groupid => 0); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +RESET client_min_messages; +CREATE TABLE pg_target(id int, val int) PARTITION BY RANGE(id); +CREATE TABLE pg_source(id int, val int, const int) PARTITION BY RANGE(val); +CREATE TABLE citus_target(id int, val int) PARTITION BY RANGE(id); +CREATE TABLE citus_source(id int, val int, const int) PARTITION BY RANGE(val); +SELECT citus_add_local_table_to_metadata('citus_target'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_add_local_table_to_metadata('citus_source'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +CREATE TABLE part1 PARTITION OF pg_target FOR VALUES FROM (1) TO (2500) WITH (autovacuum_enabled=off); +CREATE TABLE part2 PARTITION OF pg_target FOR VALUES FROM (2501) TO (5000) WITH (autovacuum_enabled=off); +CREATE TABLE part3 PARTITION OF pg_target FOR VALUES FROM (5001) TO (7500) WITH (autovacuum_enabled=off); +CREATE TABLE part4 PARTITION OF pg_target DEFAULT WITH (autovacuum_enabled=off); +CREATE TABLE part5 PARTITION OF citus_target FOR VALUES FROM (1) TO (2500) WITH (autovacuum_enabled=off); +CREATE TABLE part6 PARTITION OF citus_target FOR VALUES FROM (2501) TO (5000) WITH (autovacuum_enabled=off); +CREATE TABLE part7 PARTITION OF citus_target FOR VALUES FROM (5001) TO (7500) WITH (autovacuum_enabled=off); +CREATE TABLE part8 PARTITION OF citus_target DEFAULT WITH (autovacuum_enabled=off); +CREATE TABLE part9 PARTITION OF pg_source FOR VALUES FROM (1) TO (2500) WITH (autovacuum_enabled=off); +CREATE TABLE part10 PARTITION OF pg_source FOR VALUES FROM (2501) TO (5000) WITH (autovacuum_enabled=off); +CREATE TABLE part11 PARTITION OF pg_source FOR VALUES FROM (5001) TO (7500) WITH (autovacuum_enabled=off); +CREATE TABLE part12 PARTITION OF pg_source DEFAULT WITH (autovacuum_enabled=off); +CREATE TABLE part13 PARTITION OF citus_source FOR VALUES FROM (1) TO (2500) WITH (autovacuum_enabled=off); +CREATE TABLE part14 PARTITION OF citus_source FOR VALUES FROM (2501) TO (5000) WITH (autovacuum_enabled=off); +CREATE TABLE part15 PARTITION OF citus_source FOR VALUES FROM (5001) TO (7500) WITH (autovacuum_enabled=off); +CREATE TABLE part16 PARTITION OF citus_source DEFAULT WITH (autovacuum_enabled=off); +CREATE OR REPLACE FUNCTION cleanup_data() RETURNS VOID SET search_path TO merge_partition_tables AS $$ + TRUNCATE pg_target; + TRUNCATE pg_source; + TRUNCATE citus_target; + TRUNCATE citus_source; + SELECT undistribute_table('citus_target'); + SELECT undistribute_table('citus_source'); +$$ +LANGUAGE SQL; +-- +-- Load same set of data to both Postgres and Citus tables +-- +CREATE OR REPLACE FUNCTION setup_data() RETURNS VOID SET search_path TO merge_partition_tables AS $$ + INSERT INTO pg_source SELECT i, i+1, 1 FROM generate_series(1, 10000) i; + INSERT INTO pg_target SELECT i, 1 FROM generate_series(5001, 10000) i; + INSERT INTO citus_source SELECT i, i+1, 1 FROM generate_series(1, 10000) i; + INSERT INTO citus_target SELECT i, 1 FROM generate_series(5001, 10000) i; +$$ +LANGUAGE SQL; +-- +-- Compares the final target tables, merge-modified data, of both Postgres and Citus tables +-- +CREATE OR REPLACE FUNCTION check_data(table1_name text, column1_name text, table2_name text, column2_name text) +RETURNS VOID SET search_path TO merge_partition_tables AS $$ +DECLARE + table1_avg numeric; + table2_avg numeric; +BEGIN + EXECUTE format('SELECT COALESCE(AVG(%I), 0) FROM %I', column1_name, table1_name) INTO table1_avg; + EXECUTE format('SELECT COALESCE(AVG(%I), 0) FROM %I', column2_name, table2_name) INTO table2_avg; + + IF table1_avg > table2_avg THEN + RAISE EXCEPTION 'The average of %.% is greater than %.%', table1_name, column1_name, table2_name, column2_name; + ELSIF table1_avg < table2_avg THEN + RAISE EXCEPTION 'The average of %.% is less than %.%', table1_name, column1_name, table2_name, column2_name; + ELSE + RAISE NOTICE 'The average of %.% is equal to %.%', table1_name, column1_name, table2_name, column2_name; + END IF; +END; +$$ LANGUAGE plpgsql; +CREATE OR REPLACE FUNCTION compare_data() RETURNS VOID SET search_path TO merge_partition_tables AS $$ + SELECT check_data('pg_target', 'id', 'citus_target', 'id'); + SELECT check_data('pg_target', 'val', 'citus_target', 'val'); +$$ +LANGUAGE SQL; +-- Test colocated partition tables +SET client_min_messages = ERROR; +SELECT cleanup_data(); + cleanup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT setup_data(); + setup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_target', 'id'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_source', 'id', colocate_with=>'citus_target'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +RESET client_min_messages; +MERGE INTO pg_target t +USING pg_source s +ON t.id = s.id +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); +MERGE INTO citus_target t +USING citus_source s +ON t.id = s.id +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); +SELECT compare_data(); +NOTICE: The average of pg_target.id is equal to citus_target.id +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 1 +NOTICE: The average of pg_target.val is equal to citus_target.val +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 2 + compare_data +--------------------------------------------------------------------- + +(1 row) + +-- Test non-colocated partition tables +SET client_min_messages = ERROR; +SELECT cleanup_data(); + cleanup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT setup_data(); + setup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_target', 'id'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_source', 'id', colocate_with=>'none'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +RESET client_min_messages; +MERGE INTO pg_target t +USING pg_source s +ON t.id = s.id +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); +MERGE INTO citus_target t +USING citus_source s +ON t.id = s.id +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); +SELECT compare_data(); +NOTICE: The average of pg_target.id is equal to citus_target.id +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 1 +NOTICE: The average of pg_target.val is equal to citus_target.val +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 2 + compare_data +--------------------------------------------------------------------- + +(1 row) + +DROP SCHEMA merge_partition_tables CASCADE; +NOTICE: drop cascades to 8 other objects +DETAIL: drop cascades to table pg_target +drop cascades to table pg_source +drop cascades to function cleanup_data() +drop cascades to function setup_data() +drop cascades to function check_data(text,text,text,text) +drop cascades to function compare_data() +drop cascades to table citus_target +drop cascades to table citus_source diff --git a/src/test/regress/expected/merge_partition_tables_0.out b/src/test/regress/expected/merge_partition_tables_0.out new file mode 100644 index 000000000..a7e3fbf20 --- /dev/null +++ b/src/test/regress/expected/merge_partition_tables_0.out @@ -0,0 +1,6 @@ +SHOW server_version \gset +SELECT substring(:'server_version', '\d+')::int >= 15 AS server_version_ge_15 +\gset +\if :server_version_ge_15 +\else +\q diff --git a/src/test/regress/expected/merge_repartition1.out b/src/test/regress/expected/merge_repartition1.out new file mode 100644 index 000000000..0c3c47389 --- /dev/null +++ b/src/test/regress/expected/merge_repartition1.out @@ -0,0 +1,1245 @@ +SHOW server_version \gset +SELECT substring(:'server_version', '\d+')::int >= 15 AS server_version_ge_15 +\gset +\if :server_version_ge_15 +\else +\q +\endif +-- We create two sets of source and target tables, one set in Postgres and +-- the other in Citus distributed. We run the _exact_ MERGE SQL on both sets +-- and compare the final results of the target tables in Postgres and Citus. +-- The results should match. This process is repeated for various combinations +-- of MERGE SQL. +DROP SCHEMA IF EXISTS merge_repartition1_schema CASCADE; +NOTICE: schema "merge_repartition1_schema" does not exist, skipping +CREATE SCHEMA merge_repartition1_schema; +SET search_path TO merge_repartition1_schema; +SET citus.shard_count TO 4; +SET citus.next_shard_id TO 5000000; +SET citus.explain_all_tasks TO true; +SET citus.shard_replication_factor TO 1; +SET citus.max_adaptive_executor_pool_size TO 1; +SET client_min_messages = warning; +SELECT 1 FROM master_add_node('localhost', :master_port, groupid => 0); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +RESET client_min_messages; +CREATE TABLE pg_target(id int, val int); +CREATE TABLE pg_source(id int, val int, const int); +CREATE TABLE citus_target(id int, val int); +CREATE TABLE citus_source(id int, val int, const int); +SELECT citus_add_local_table_to_metadata('citus_target'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_add_local_table_to_metadata('citus_source'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +CREATE OR REPLACE FUNCTION cleanup_data() RETURNS VOID SET search_path TO merge_repartition1_schema AS $$ + TRUNCATE pg_target; + TRUNCATE pg_source; + TRUNCATE citus_target; + TRUNCATE citus_source; + SELECT undistribute_table('citus_target'); + SELECT undistribute_table('citus_source'); +$$ +LANGUAGE SQL; +-- +-- Load same set of data to both Postgres and Citus tables +-- +CREATE OR REPLACE FUNCTION setup_data() RETURNS VOID SET search_path TO merge_repartition1_schema AS $$ + INSERT INTO pg_source SELECT i, i+1, 1 FROM generate_series(1, 10000) i; + INSERT INTO pg_target SELECT i, 1 FROM generate_series(5001, 10000) i; + INSERT INTO citus_source SELECT i, i+1, 1 FROM generate_series(1, 10000) i; + INSERT INTO citus_target SELECT i, 1 FROM generate_series(5001, 10000) i; +$$ +LANGUAGE SQL; +-- +-- Compares the final target tables, merge-modified data, of both Postgres and Citus tables +-- +CREATE OR REPLACE FUNCTION check_data(table1_name text, column1_name text, table2_name text, column2_name text) +RETURNS VOID SET search_path TO merge_repartition1_schema AS $$ +DECLARE + table1_avg numeric; + table2_avg numeric; +BEGIN + EXECUTE format('SELECT COALESCE(AVG(%I), 0) FROM %I', column1_name, table1_name) INTO table1_avg; + EXECUTE format('SELECT COALESCE(AVG(%I), 0) FROM %I', column2_name, table2_name) INTO table2_avg; + + IF table1_avg > table2_avg THEN + RAISE EXCEPTION 'The average of %.% is greater than %.%', table1_name, column1_name, table2_name, column2_name; + ELSIF table1_avg < table2_avg THEN + RAISE EXCEPTION 'The average of %.% is less than %.%', table1_name, column1_name, table2_name, column2_name; + ELSE + RAISE NOTICE 'The average of %.% is equal to %.%', table1_name, column1_name, table2_name, column2_name; + END IF; +END; +$$ LANGUAGE plpgsql; +CREATE OR REPLACE FUNCTION compare_data() RETURNS VOID SET search_path TO merge_repartition1_schema AS $$ + SELECT check_data('pg_target', 'id', 'citus_target', 'id'); + SELECT check_data('pg_target', 'val', 'citus_target', 'val'); +$$ +LANGUAGE SQL; +-- +-- Target and source are distributed, and non-colocated +-- +SELECT cleanup_data(); +NOTICE: creating a new table for merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: moving the data of merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: dropping the old merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: renaming the new table to merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: creating a new table for merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: moving the data of merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: dropping the old merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: renaming the new table to merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 + cleanup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT setup_data(); + setup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_target', 'id'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_target$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_source', 'id', colocate_with=>'none'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_source$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +MERGE INTO pg_target t +USING pg_source s +ON t.id = s.id +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); +MERGE INTO citus_target t +USING citus_source s +ON t.id = s.id +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); +SELECT compare_data(); +NOTICE: The average of pg_target.id is equal to citus_target.id +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 1 +NOTICE: The average of pg_target.val is equal to citus_target.val +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 2 + compare_data +--------------------------------------------------------------------- + +(1 row) + +-- +-- Target and source are distributed, and colocated but not joined on distribution column +-- +SELECT cleanup_data(); +NOTICE: creating a new table for merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: moving the data of merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: dropping the old merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: renaming the new table to merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: creating a new table for merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: moving the data of merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: dropping the old merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: renaming the new table to merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 + cleanup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT setup_data(); + setup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_target', 'id'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_target$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_source', 'id', colocate_with=>'citus_target'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_source$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +MERGE INTO pg_target t +USING (SELECT * FROM pg_source) subq +ON (subq.val = t.id) +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = subq.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(subq.val, subq.id); +MERGE INTO citus_target t +USING (SELECT * FROM citus_source) subq +ON (subq.val = t.id) +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = subq.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(subq.val, subq.id); +SELECT compare_data(); +NOTICE: The average of pg_target.id is equal to citus_target.id +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 1 +NOTICE: The average of pg_target.val is equal to citus_target.val +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 2 + compare_data +--------------------------------------------------------------------- + +(1 row) + +-- +-- Target and source are distributed, colocated, joined on distribution column +-- but with nondistribution values +-- +SELECT cleanup_data(); +NOTICE: creating a new table for merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: moving the data of merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: dropping the old merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: renaming the new table to merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: creating a new table for merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: moving the data of merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: dropping the old merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: renaming the new table to merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 + cleanup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT setup_data(); + setup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_target', 'id'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_target$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_source', 'id', colocate_with=>'citus_target'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_source$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +MERGE INTO pg_target t +USING (SELECT id,const FROM pg_source UNION SELECT const,id FROM pg_source ) AS s +ON t.id = s.id +WHEN MATCHED THEN + UPDATE SET val = s.const + 1 +WHEN NOT MATCHED THEN + INSERT VALUES(id, const); +MERGE INTO citus_target t +USING (SELECT id,const FROM citus_source UNION SELECT const,id FROM citus_source) AS s +ON t.id = s.id +WHEN MATCHED THEN + UPDATE SET val = s.const + 1 +WHEN NOT MATCHED THEN + INSERT VALUES(id, const); +SELECT compare_data(); +NOTICE: The average of pg_target.id is equal to citus_target.id +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 1 +NOTICE: The average of pg_target.val is equal to citus_target.val +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 2 + compare_data +--------------------------------------------------------------------- + +(1 row) + +-- +-- Repartition with a predicate on target_table_name rows in ON clause +-- +SELECT cleanup_data(); +NOTICE: creating a new table for merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: moving the data of merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: dropping the old merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: renaming the new table to merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: creating a new table for merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: moving the data of merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: dropping the old merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: renaming the new table to merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 + cleanup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT setup_data(); + setup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_target', 'id'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_target$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_source', 'id', colocate_with=>'none'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_source$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +MERGE INTO pg_target t +USING (SELECT * FROM pg_source WHERE id < 9500) s +ON t.id = s.id AND t.id < 9000 +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); +MERGE INTO citus_target t +USING (SELECT * FROM citus_source WHERE id < 9500) s +ON t.id = s.id AND t.id < 9000 +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); +SELECT compare_data(); +NOTICE: The average of pg_target.id is equal to citus_target.id +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 1 +NOTICE: The average of pg_target.val is equal to citus_target.val +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 2 + compare_data +--------------------------------------------------------------------- + +(1 row) + +-- +-- Test CTE and non-colocated tables +-- +SELECT cleanup_data(); +NOTICE: creating a new table for merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: moving the data of merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: dropping the old merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: renaming the new table to merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: creating a new table for merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: moving the data of merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: dropping the old merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: renaming the new table to merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 + cleanup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT setup_data(); + setup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_target', 'id'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_target$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_source', 'id', colocate_with=>'none'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_source$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +WITH cte AS ( + SELECT * FROM pg_source +) +MERGE INTO pg_target t +USING cte s +ON s.id = t.id +WHEN MATCHED AND t.id > 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES (s.id, s.val); +WITH cte AS ( + SELECT * FROM citus_source +) +MERGE INTO citus_target t +USING cte s +ON s.id = t.id +WHEN MATCHED AND t.id > 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES (s.id, s.val); +SELECT compare_data(); +NOTICE: The average of pg_target.id is equal to citus_target.id +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 1 +NOTICE: The average of pg_target.val is equal to citus_target.val +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 2 + compare_data +--------------------------------------------------------------------- + +(1 row) + +-- +-- Test nested CTEs +-- +SELECT cleanup_data(); +NOTICE: creating a new table for merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: moving the data of merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: dropping the old merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: renaming the new table to merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: creating a new table for merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: moving the data of merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: dropping the old merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: renaming the new table to merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 + cleanup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT setup_data(); + setup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_target', 'id'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_target$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_source', 'id', colocate_with=>'none'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_source$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +WITH cte1 AS ( + SELECT * FROM pg_source ORDER BY 1 LIMIT 9000 +), +cte2 AS( + SELECT * FROM cte1 +), +cte3 AS( + SELECT * FROM cte2 +) +MERGE INTO pg_target t +USING cte3 s +ON (s.id=t.id) +WHEN MATCHED AND t.id > 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES (s.id, s.val); +WITH cte1 AS ( + SELECT * FROM citus_source ORDER BY 1 LIMIT 9000 +), +cte2 AS( + SELECT * FROM cte1 +), +cte3 AS( + SELECT * FROM cte2 +) +MERGE INTO citus_target t +USING cte3 s +ON (s.id=t.id) +WHEN MATCHED AND t.id > 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES (s.id, s.val); +SELECT compare_data(); +NOTICE: The average of pg_target.id is equal to citus_target.id +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 1 +NOTICE: The average of pg_target.val is equal to citus_target.val +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 2 + compare_data +--------------------------------------------------------------------- + +(1 row) + +-- +-- Target and source are distributed and colocated +-- +SELECT cleanup_data(); +NOTICE: creating a new table for merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: moving the data of merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: dropping the old merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: renaming the new table to merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: creating a new table for merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: moving the data of merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: dropping the old merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: renaming the new table to merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 + cleanup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT setup_data(); + setup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_target', 'id'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_target$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_source', 'id', colocate_with => 'citus_target'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_source$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +MERGE INTO pg_target t +USING (SELECT 999 as newval, pg_source.* FROM (SELECT * FROM pg_source ORDER BY 1 LIMIT 6000) as src LEFT JOIN pg_source USING(id)) AS s +ON t.id = s.id +WHEN MATCHED AND t.id <= 5500 THEN + UPDATE SET val = newval +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(id, newval); +MERGE INTO citus_target t +USING (SELECT 999 as newval, citus_source.* FROM (SELECT * FROM citus_source ORDER BY 1 LIMIT 6000) as src LEFT JOIN citus_source USING(id)) AS s +ON t.id = s.id +WHEN MATCHED AND t.id <= 5500 THEN + UPDATE SET val = newval +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(id, newval); +SELECT compare_data(); +NOTICE: The average of pg_target.id is equal to citus_target.id +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 1 +NOTICE: The average of pg_target.val is equal to citus_target.val +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 2 + compare_data +--------------------------------------------------------------------- + +(1 row) + +-- +-- Target is distributed and source is reference +-- +SELECT cleanup_data(); +NOTICE: creating a new table for merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: moving the data of merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: dropping the old merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: renaming the new table to merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: creating a new table for merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: moving the data of merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: dropping the old merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: renaming the new table to merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 + cleanup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT setup_data(); + setup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_target', 'id'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_target$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_reference_table('citus_source'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_source$$) + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +MERGE INTO pg_target t +USING pg_source s +ON t.id = s.id +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); +MERGE INTO citus_target t +USING citus_source s +ON t.id = s.id +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); +SELECT compare_data(); +NOTICE: The average of pg_target.id is equal to citus_target.id +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 1 +NOTICE: The average of pg_target.val is equal to citus_target.val +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 2 + compare_data +--------------------------------------------------------------------- + +(1 row) + +-- +-- Target is distributed and reference as source in a sub-query +-- +SELECT cleanup_data(); +NOTICE: creating a new table for merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: moving the data of merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: dropping the old merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: renaming the new table to merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: creating a new table for merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: moving the data of merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: dropping the old merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: renaming the new table to merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 + cleanup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT setup_data(); + setup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_target', 'id'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_target$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_reference_table('citus_source'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_source$$) + create_reference_table +--------------------------------------------------------------------- + +(1 row) + +MERGE INTO pg_target t +USING (SELECT * FROM pg_source UNION SELECT * FROM pg_source) AS s ON t.id = s.id +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + t.val +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); +MERGE INTO citus_target t +USING (SELECT * FROM citus_source UNION SELECT * FROM citus_source) AS s ON t.id = s.id +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + t.val +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); +SELECT compare_data(); +NOTICE: The average of pg_target.id is equal to citus_target.id +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 1 +NOTICE: The average of pg_target.val is equal to citus_target.val +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 2 + compare_data +--------------------------------------------------------------------- + +(1 row) + +-- +-- Target is distributed and citus-local as source +-- +SELECT cleanup_data(); +NOTICE: creating a new table for merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: moving the data of merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: dropping the old merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: renaming the new table to merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: creating a new table for merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: moving the data of merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: dropping the old merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: renaming the new table to merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 + cleanup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT setup_data(); + setup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_target', 'id'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_target$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_add_local_table_to_metadata('citus_source'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +MERGE INTO pg_target t +USING pg_source s +ON t.id = s.id +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); +MERGE INTO citus_target t +USING citus_source s +ON t.id = s.id +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); +SELECT compare_data(); +NOTICE: The average of pg_target.id is equal to citus_target.id +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 1 +NOTICE: The average of pg_target.val is equal to citus_target.val +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 2 + compare_data +--------------------------------------------------------------------- + +(1 row) + +-- +-- Target and source distributed and non-colocated. The source query requires evaluation +-- at the coordinator +-- +SELECT cleanup_data(); +NOTICE: creating a new table for merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: moving the data of merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: dropping the old merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: renaming the new table to merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: creating a new table for merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: moving the data of merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: dropping the old merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: renaming the new table to merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 + cleanup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT setup_data(); + setup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_target', 'id'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_target$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_source', 'id', colocate_with=>'none'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_source$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +MERGE INTO pg_target t +USING (SELECT 100 AS insval, MAX(const) AS updval, val, MAX(id) AS sid + FROM pg_source + GROUP BY val ORDER BY sid LIMIT 6000) AS s +ON t.id = s.sid +WHEN MATCHED AND t.id <= 5500 THEN + UPDATE SET val = updval + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(sid, insval); +MERGE INTO citus_target t +USING (SELECT 100 AS insval, MAX(const) AS updval, val, MAX(id) AS sid + FROM citus_source + GROUP BY val ORDER BY sid LIMIT 6000) AS s +ON t.id = s.sid +WHEN MATCHED AND t.id <= 5500 THEN + UPDATE SET val = updval + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(sid, insval); +SELECT compare_data(); +NOTICE: The average of pg_target.id is equal to citus_target.id +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 1 +NOTICE: The average of pg_target.val is equal to citus_target.val +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 2 + compare_data +--------------------------------------------------------------------- + +(1 row) + +-- Test source-query that requires repartitioning on top of MERGE repartitioning +SELECT cleanup_data(); +NOTICE: creating a new table for merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: moving the data of merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: dropping the old merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: renaming the new table to merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: creating a new table for merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: moving the data of merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: dropping the old merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: renaming the new table to merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 + cleanup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT setup_data(); + setup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_target', 'id'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_target$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_source', 'id', colocate_with=>'none'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_source$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +MERGE INTO pg_target t +USING (SELECT s1.val FROM pg_source s1 JOIN pg_source s2 USING (val)) AS s +ON t.id = s.val +WHEN MATCHED THEN + UPDATE SET val = t.val + 1; +SET citus.enable_repartition_joins TO true; +MERGE INTO citus_target t +USING (SELECT s1.val FROM citus_source s1 JOIN citus_source s2 USING (val)) AS s +ON t.id = s.val +WHEN MATCHED THEN + UPDATE SET val = t.val + 1; +SELECT compare_data(); +NOTICE: The average of pg_target.id is equal to citus_target.id +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 1 +NOTICE: The average of pg_target.val is equal to citus_target.val +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 2 + compare_data +--------------------------------------------------------------------- + +(1 row) + +-- +-- Test columnar as source table +-- +SET client_min_messages TO WARNING; +SELECT cleanup_data(); + cleanup_data +--------------------------------------------------------------------- + +(1 row) + +RESET client_min_messages; +SELECT setup_data(); + setup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_target', 'id'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_target$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_source', 'id', colocate_with=>'none'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_source$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT alter_table_set_access_method('citus_source', 'columnar'); +NOTICE: creating a new table for merge_repartition1_schema.citus_source +NOTICE: moving the data of merge_repartition1_schema.citus_source +NOTICE: dropping the old merge_repartition1_schema.citus_source +NOTICE: renaming the new table to merge_repartition1_schema.citus_source + alter_table_set_access_method +--------------------------------------------------------------------- + +(1 row) + +MERGE INTO pg_target t +USING pg_source s +ON t.id = s.id +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); +MERGE INTO citus_target t +USING citus_source s +ON t.id = s.id +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); +SELECT compare_data(); +NOTICE: The average of pg_target.id is equal to citus_target.id +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 1 +NOTICE: The average of pg_target.val is equal to citus_target.val +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 2 + compare_data +--------------------------------------------------------------------- + +(1 row) + +SELECT alter_table_set_access_method('citus_source', 'heap'); +NOTICE: creating a new table for merge_repartition1_schema.citus_source +NOTICE: moving the data of merge_repartition1_schema.citus_source +NOTICE: dropping the old merge_repartition1_schema.citus_source +NOTICE: renaming the new table to merge_repartition1_schema.citus_source + alter_table_set_access_method +--------------------------------------------------------------------- + +(1 row) + +-- Test CTE/Subquery in merge-actions (works only for router query) +SELECT cleanup_data(); +NOTICE: creating a new table for merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: moving the data of merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: dropping the old merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: renaming the new table to merge_repartition1_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: creating a new table for merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: moving the data of merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: dropping the old merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: renaming the new table to merge_repartition1_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 + cleanup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT setup_data(); + setup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_target', 'id'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_target$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_source', 'id', colocate_with=>'citus_target'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_source$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +MERGE INTO pg_target +USING pg_source +ON (pg_target.id = pg_source.id) +WHEN MATCHED AND (SELECT max_a > 5001 FROM (SELECT max(id) as max_a, max(val) as b FROM pg_target WHERE id = pg_source.id) AS foo) THEN + DELETE +WHEN NOT MATCHED AND (SELECT max_a < 5001 FROM (SELECT max(id) as max_a, max(val) as b FROM pg_target WHERE id = pg_source.id) AS foo) THEN + INSERT VALUES (pg_source.id, 100); +MERGE INTO citus_target +USING citus_source +ON (citus_target.id = citus_source.id) +WHEN MATCHED AND (SELECT max_a > 5001 FROM (SELECT max(id) as max_a, max(val) as b FROM citus_target WHERE id = citus_source.id) AS foo) THEN + DELETE +WHEN NOT MATCHED AND (SELECT max_a < 5001 FROM (SELECT max(id) as max_a, max(val) as b FROM citus_target WHERE id = citus_source.id) AS foo) THEN + INSERT VALUES (citus_source.id, 100); +SELECT compare_data(); +NOTICE: The average of pg_target.id is equal to citus_target.id +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 1 +NOTICE: The average of pg_target.val is equal to citus_target.val +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 2 + compare_data +--------------------------------------------------------------------- + +(1 row) + +DROP SCHEMA merge_repartition1_schema CASCADE; +NOTICE: drop cascades to 8 other objects +DETAIL: drop cascades to table pg_target +drop cascades to table pg_source +drop cascades to function cleanup_data() +drop cascades to function setup_data() +drop cascades to function check_data(text,text,text,text) +drop cascades to function compare_data() +drop cascades to table citus_target +drop cascades to table citus_source diff --git a/src/test/regress/expected/merge_repartition1_0.out b/src/test/regress/expected/merge_repartition1_0.out new file mode 100644 index 000000000..a7e3fbf20 --- /dev/null +++ b/src/test/regress/expected/merge_repartition1_0.out @@ -0,0 +1,6 @@ +SHOW server_version \gset +SELECT substring(:'server_version', '\d+')::int >= 15 AS server_version_ge_15 +\gset +\if :server_version_ge_15 +\else +\q diff --git a/src/test/regress/expected/merge_repartition2.out b/src/test/regress/expected/merge_repartition2.out new file mode 100644 index 000000000..898b7c77a --- /dev/null +++ b/src/test/regress/expected/merge_repartition2.out @@ -0,0 +1,212 @@ +SHOW server_version \gset +SELECT substring(:'server_version', '\d+')::int >= 15 AS server_version_ge_15 +\gset +\if :server_version_ge_15 +\else +\q +\endif +-- We create two sets of source and target tables, one set in Postgres and +-- the other in Citus distributed. We run the _exact_ MERGE SQL on both sets +-- and compare the final results of the target tables in Postgres and Citus. +-- The results should match. This process is repeated for various combinations +-- of MERGE SQL. +DROP SCHEMA IF EXISTS merge_repartition2_schema CASCADE; +NOTICE: schema "merge_repartition2_schema" does not exist, skipping +CREATE SCHEMA merge_repartition2_schema; +SET search_path TO merge_repartition2_schema; +SET citus.shard_count TO 4; +SET citus.next_shard_id TO 6000000; +SET citus.explain_all_tasks TO true; +SET citus.shard_replication_factor TO 1; +SET citus.max_adaptive_executor_pool_size TO 1; +SET client_min_messages = warning; +SELECT 1 FROM master_add_node('localhost', :master_port, groupid => 0); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +RESET client_min_messages; +CREATE TABLE pg_target(id int, val int); +CREATE TABLE pg_source(id int, val int, const int); +CREATE TABLE citus_target(id int, val int); +CREATE TABLE citus_source(id int, val int, const int); +SELECT citus_add_local_table_to_metadata('citus_target'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +SELECT citus_add_local_table_to_metadata('citus_source'); + citus_add_local_table_to_metadata +--------------------------------------------------------------------- + +(1 row) + +CREATE OR REPLACE FUNCTION cleanup_data() RETURNS VOID SET search_path TO merge_repartition2_schema AS $$ + TRUNCATE pg_target; + TRUNCATE pg_source; + TRUNCATE citus_target; + TRUNCATE citus_source; + SELECT undistribute_table('citus_target'); + SELECT undistribute_table('citus_source'); +$$ +LANGUAGE SQL; +-- +-- Load same set of data to both Postgres and Citus tables +-- +CREATE OR REPLACE FUNCTION setup_data() RETURNS VOID SET search_path TO merge_repartition2_schema AS $$ + INSERT INTO pg_source SELECT i, i+1, 1 FROM generate_series(1, 100000) i; + INSERT INTO pg_target SELECT i, 1 FROM generate_series(50001, 100000) i; + INSERT INTO citus_source SELECT i, i+1, 1 FROM generate_series(1, 100000) i; + INSERT INTO citus_target SELECT i, 1 FROM generate_series(50001, 100000) i; +$$ +LANGUAGE SQL; +-- +-- Compares the final target tables, merge-modified data, of both Postgres and Citus tables +-- +CREATE OR REPLACE FUNCTION check_data(table1_name text, column1_name text, table2_name text, column2_name text) +RETURNS VOID SET search_path TO merge_repartition2_schema AS $$ +DECLARE + table1_avg numeric; + table2_avg numeric; +BEGIN + EXECUTE format('SELECT COALESCE(AVG(%I), 0) FROM %I', column1_name, table1_name) INTO table1_avg; + EXECUTE format('SELECT COALESCE(AVG(%I), 0) FROM %I', column2_name, table2_name) INTO table2_avg; + + IF table1_avg > table2_avg THEN + RAISE EXCEPTION 'The average of %.% is greater than %.%', table1_name, column1_name, table2_name, column2_name; + ELSIF table1_avg < table2_avg THEN + RAISE EXCEPTION 'The average of %.% is less than %.%', table1_name, column1_name, table2_name, column2_name; + ELSE + RAISE NOTICE 'The average of %.% is equal to %.%', table1_name, column1_name, table2_name, column2_name; + END IF; +END; +$$ LANGUAGE plpgsql; +CREATE OR REPLACE FUNCTION compare_data() RETURNS VOID SET search_path TO merge_repartition2_schema AS $$ + SELECT check_data('pg_target', 'id', 'citus_target', 'id'); + SELECT check_data('pg_target', 'val', 'citus_target', 'val'); +$$ +LANGUAGE SQL; +-- Test nested cte +SELECT cleanup_data(); +NOTICE: creating a new table for merge_repartition2_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: moving the data of merge_repartition2_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: dropping the old merge_repartition2_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: renaming the new table to merge_repartition2_schema.citus_target +CONTEXT: SQL function "cleanup_data" statement 5 +NOTICE: creating a new table for merge_repartition2_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: moving the data of merge_repartition2_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: dropping the old merge_repartition2_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 +NOTICE: renaming the new table to merge_repartition2_schema.citus_source +CONTEXT: SQL function "cleanup_data" statement 6 + cleanup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT setup_data(); + setup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_target', 'id'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition2_schema.citus_target$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_source', 'id', colocate_with=>'none'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition2_schema.citus_source$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +WITH cte_top AS(WITH cte_1 AS (WITH cte_2 AS (SELECT id, val FROM pg_source) SELECT * FROM cte_2) SELECT * FROM cte_1) +MERGE INTO pg_target t +USING (SELECT const, val, id FROM pg_source WHERE id IN (SELECT id FROM cte_top)) as s +ON (s.id = t.id) +WHEN MATCHED AND t.id <= 75000 THEN + UPDATE SET val = (s.val::int8+1) +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES (s.id, s.val); +WITH cte_top AS(WITH cte_1 AS (WITH cte_2 AS (SELECT id, val FROM citus_source) SELECT * FROM cte_2) SELECT * FROM cte_1) +MERGE INTO citus_target t +USING (SELECT const, val, id FROM citus_source WHERE id IN (SELECT id FROM cte_top)) as s +ON (s.id = t.id) +WHEN MATCHED AND t.id <= 75000 THEN + UPDATE SET val = (s.val::int8+1) +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES (s.id, s.val); +SELECT compare_data(); +NOTICE: The average of pg_target.id is equal to citus_target.id +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 1 +NOTICE: The average of pg_target.val is equal to citus_target.val +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 2 + compare_data +--------------------------------------------------------------------- + +(1 row) + +-- Test aggregate function in source query +MERGE INTO pg_target t +USING (SELECT count(id+1)::text as value, val as key FROM pg_source group by key) s +ON t.id = s.key +WHEN MATCHED AND t.id <= 75000 THEN + UPDATE SET val = (s.value::int8+1) +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.key, value::int4+10); +MERGE INTO citus_target t +USING (SELECT count(id+1)::text as value, val as key FROM citus_source group by key) s +ON t.id = s.key +WHEN MATCHED AND t.id <= 75000 THEN + UPDATE SET val = (s.value::int8+1) +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.key, value::int4+10); +SELECT compare_data(); +NOTICE: The average of pg_target.id is equal to citus_target.id +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 1 +NOTICE: The average of pg_target.val is equal to citus_target.val +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 2 + compare_data +--------------------------------------------------------------------- + +(1 row) + +DROP SCHEMA merge_repartition2_schema CASCADE; +NOTICE: drop cascades to 8 other objects +DETAIL: drop cascades to table pg_target +drop cascades to table pg_source +drop cascades to function cleanup_data() +drop cascades to function setup_data() +drop cascades to function check_data(text,text,text,text) +drop cascades to function compare_data() +drop cascades to table citus_target +drop cascades to table citus_source diff --git a/src/test/regress/expected/merge_repartition2_0.out b/src/test/regress/expected/merge_repartition2_0.out new file mode 100644 index 000000000..a7e3fbf20 --- /dev/null +++ b/src/test/regress/expected/merge_repartition2_0.out @@ -0,0 +1,6 @@ +SHOW server_version \gset +SELECT substring(:'server_version', '\d+')::int >= 15 AS server_version_ge_15 +\gset +\if :server_version_ge_15 +\else +\q diff --git a/src/test/regress/expected/pg15.out b/src/test/regress/expected/pg15.out index 68c4c4466..667305225 100644 --- a/src/test/regress/expected/pg15.out +++ b/src/test/regress/expected/pg15.out @@ -406,14 +406,16 @@ SELECT create_distributed_table('tbl2', 'x'); MERGE INTO tbl1 USING tbl2 ON (true) WHEN MATCHED THEN DELETE; -ERROR: MERGE command is only supported when all distributed tables are co-located and joined on their distribution columns --- also, not inside subqueries & ctes +ERROR: The required join operation is missing between the target's distribution column and any expression originating from the source. The issue may arise from either a non-equi-join or a mismatch in the datatypes of the columns being joined. +DETAIL: Without a equi-join condition on the target's distribution column, the source rows cannot be efficiently redistributed, and the NOT-MATCHED condition cannot be evaluated unambiguously. This can result in incorrect or unexpected results when attempting to merge tables in a distributed setting +-- also, inside subqueries & ctes WITH targq AS ( SELECT * FROM tbl2 ) MERGE INTO tbl1 USING targq ON (true) WHEN MATCHED THEN DELETE; -ERROR: MERGE command is only supported when all distributed tables are co-located and joined on their distribution columns +ERROR: The required join operation is missing between the target's distribution column and any expression originating from the source. The issue may arise from either a non-equi-join or a mismatch in the datatypes of the columns being joined. +DETAIL: Without a equi-join condition on the target's distribution column, the source rows cannot be efficiently redistributed, and the NOT-MATCHED condition cannot be evaluated unambiguously. This can result in incorrect or unexpected results when attempting to merge tables in a distributed setting WITH foo AS ( MERGE INTO tbl1 USING tbl2 ON (true) WHEN MATCHED THEN DELETE @@ -429,7 +431,8 @@ USING tbl2 ON (true) WHEN MATCHED THEN DO NOTHING; -ERROR: MERGE command is only supported when all distributed tables are co-located and joined on their distribution columns +ERROR: The required join operation is missing between the target's distribution column and any expression originating from the source. The issue may arise from either a non-equi-join or a mismatch in the datatypes of the columns being joined. +DETAIL: Without a equi-join condition on the target's distribution column, the source rows cannot be efficiently redistributed, and the NOT-MATCHED condition cannot be evaluated unambiguously. This can result in incorrect or unexpected results when attempting to merge tables in a distributed setting MERGE INTO tbl1 t USING tbl2 ON (true) diff --git a/src/test/regress/expected/pgmerge.out b/src/test/regress/expected/pgmerge.out index 7742610f4..895bf0680 100644 --- a/src/test/regress/expected/pgmerge.out +++ b/src/test/regress/expected/pgmerge.out @@ -15,6 +15,14 @@ SET search_path TO pgmerge_schema; SET citus.use_citus_managed_tables to true; \set SHOW_CONTEXT errors SET citus.next_shard_id TO 4001000; +SET client_min_messages = warning; +SELECT 1 FROM master_add_node('localhost', :master_port, groupid => 0); + ?column? +--------------------------------------------------------------------- + 1 +(1 row) + +RESET client_min_messages; CREATE USER regress_merge_privs; CREATE USER regress_merge_no_privs; DROP TABLE IF EXISTS target; diff --git a/src/test/regress/multi_schedule b/src/test/regress/multi_schedule index 527dec8f7..4d42dbc78 100644 --- a/src/test/regress/multi_schedule +++ b/src/test/regress/multi_schedule @@ -111,8 +111,9 @@ test: background_task_queue_monitor test: clock # MERGE tests -test: merge -test: pgmerge +test: merge pgmerge merge_repartition2 +test: merge_repartition1 +test: merge_partition_tables # --------- # test that no tests leaked intermediate results. This should always be last diff --git a/src/test/regress/sql/merge.sql b/src/test/regress/sql/merge.sql index 911642812..db3a76fb6 100644 --- a/src/test/regress/sql/merge.sql +++ b/src/test/regress/sql/merge.sql @@ -21,6 +21,9 @@ SET citus.next_shard_id TO 4000000; SET citus.explain_all_tasks TO true; SET citus.shard_replication_factor TO 1; SET citus.max_adaptive_executor_pool_size TO 1; +SET client_min_messages = warning; +SELECT 1 FROM master_add_node('localhost', :master_port, groupid => 0); +RESET client_min_messages; CREATE TABLE source ( @@ -142,7 +145,7 @@ SELECT insert_data(); SELECT undistribute_table('target'); SELECT undistribute_table('source'); SELECT create_distributed_table('target', 'customer_id'); -SELECT create_distributed_table('source', 'customer_id'); +SELECT create_distributed_table('source', 'customer_id', colocate_with=>'target'); -- Updates one of the row with customer_id = 30002 SELECT * from target t WHERE t.customer_id = 30002; @@ -280,7 +283,7 @@ TRUNCATE t1; TRUNCATE s1; SELECT load(); SELECT create_distributed_table('t1', 'id'); -SELECT create_distributed_table('s1', 'id'); +SELECT create_distributed_table('s1', 'id', colocate_with=>'t1'); SELECT * FROM t1 order by id; @@ -368,7 +371,7 @@ SELECT insert_data(); SELECT undistribute_table('t2'); SELECT undistribute_table('s2'); SELECT create_distributed_table('t2', 'id'); -SELECT create_distributed_table('s2', 'id'); +SELECT create_distributed_table('s2', 'id', colocate_with => 't2'); SELECT * FROM t2 ORDER BY 1; SET citus.log_remote_commands to true; @@ -924,27 +927,25 @@ ROLLBACK; -- Test the same scenarios with distributed tables SELECT create_distributed_table('target_cj', 'tid'); -SELECT create_distributed_table('source_cj1', 'sid1'); -SELECT create_distributed_table('source_cj2', 'sid2'); +SELECT create_distributed_table('source_cj1', 'sid1', colocate_with => 'target_cj'); +SELECT create_distributed_table('source_cj2', 'sid2', colocate_with => 'target_cj'); BEGIN; -SET citus.log_remote_commands to true; MERGE INTO target_cj t -USING source_cj1 s1 INNER JOIN source_cj2 s2 ON sid1 = sid2 +USING (SELECT * FROM source_cj1 s1 INNER JOIN source_cj2 s2 ON sid1 = sid2) s ON t.tid = sid1 AND t.tid = 2 WHEN MATCHED THEN UPDATE SET src = src2 WHEN NOT MATCHED THEN DO NOTHING; -SET citus.log_remote_commands to false; SELECT * FROM target_cj ORDER BY 1; ROLLBACK; BEGIN; -- try accessing columns from either side of the source join MERGE INTO target_cj t -USING source_cj1 s2 - INNER JOIN source_cj2 s1 ON sid1 = sid2 AND val1 = 10 +USING (SELECT * FROM source_cj1 s2 + INNER JOIN source_cj2 s1 ON sid1 = sid2 AND val1 = 10) s ON t.tid = sid1 AND t.tid = 2 WHEN MATCHED THEN UPDATE SET src = src1, val = val2 @@ -982,7 +983,7 @@ ROLLBACK; -- Test PREPARE -PREPARE foo(int) AS +PREPARE merge_prepare(int) AS MERGE INTO target_cj target USING (SELECT * FROM source_cj1) sub ON target.tid = sub.sid1 AND target.tid = $1 @@ -994,11 +995,11 @@ WHEN NOT MATCHED THEN SELECT * FROM target_cj ORDER BY 1; BEGIN; -EXECUTE foo(2); -EXECUTE foo(2); -EXECUTE foo(2); -EXECUTE foo(2); -EXECUTE foo(2); +EXECUTE merge_prepare(2); +EXECUTE merge_prepare(2); +EXECUTE merge_prepare(2); +EXECUTE merge_prepare(2); +EXECUTE merge_prepare(2); SELECT * FROM target_cj ORDER BY 1; ROLLBACK; @@ -1006,10 +1007,10 @@ BEGIN; SET citus.log_remote_commands to true; SET client_min_messages TO DEBUG1; -EXECUTE foo(2); +EXECUTE merge_prepare(2); RESET client_min_messages; -EXECUTE foo(2); +EXECUTE merge_prepare(2); SET citus.log_remote_commands to false; SELECT * FROM target_cj ORDER BY 1; @@ -1036,7 +1037,7 @@ INSERT INTO citus_target SELECT i, 'target' FROM generate_series(250, 500) i; INSERT INTO citus_source SELECT i, 'source' FROM generate_series(1, 500) i; SELECT create_distributed_table('citus_target', 'id'); -SELECT create_distributed_table('citus_source', 'id'); +SELECT create_distributed_table('citus_source', 'id', colocate_with => 'citus_target'); -- -- This routine compares the target tables of Postgres and Citus and @@ -1622,10 +1623,271 @@ SELECT count(*) FROM pg_result FULL OUTER JOIN local_ref ON pg_result.t1 = local_ref.t1 WHERE pg_result.t1 IS NULL OR local_ref.t1 IS NULL; +-- Now make target as distributed, keep reference as source +TRUNCATE reftarget_local; +TRUNCATE refsource_ref; +INSERT INTO reftarget_local VALUES(1, 0); +INSERT INTO reftarget_local VALUES(3, 100); +INSERT INTO refsource_ref VALUES(1, 1); +INSERT INTO refsource_ref VALUES(2, 2); +INSERT INTO refsource_ref VALUES(3, 3); + +SELECT create_distributed_table('reftarget_local', 't1'); + +MERGE INTO reftarget_local +USING (SELECT * FROM refsource_ref UNION SELECT * FROM refsource_ref) AS foo ON reftarget_local.t1 = foo.s1 +WHEN MATCHED AND reftarget_local.t2 = 100 THEN + DELETE +WHEN MATCHED THEN + UPDATE SET t2 = t2 + 100 +WHEN NOT MATCHED THEN + INSERT VALUES(foo.s1); +SELECT * INTO dist_reftarget FROM reftarget_local ORDER BY 1, 2; + +-- Should be equal +SELECT c.*, p.* +FROM dist_reftarget c, pg_result p +WHERE c.t1 = p.t1 +ORDER BY 1,2; + +-- Must return zero rows +SELECT count(*) +FROM pg_result FULL OUTER JOIN dist_reftarget ON pg_result.t1 = dist_reftarget.t1 +WHERE pg_result.t1 IS NULL OR dist_reftarget.t1 IS NULL; + +-- +-- Distributed (target), Reference(source) +-- +CREATE TABLE demo_distributed(id1 int, val1 int); +CREATE TABLE demo_source_table(id2 int, val2 int); + +CREATE FUNCTION setup_demo_data() RETURNS VOID AS $$ +INSERT INTO demo_distributed VALUES(1, 100); +INSERT INTO demo_distributed VALUES(7, 100); +INSERT INTO demo_distributed VALUES(15, 100); +INSERT INTO demo_distributed VALUES(100, 0); +INSERT INTO demo_distributed VALUES(300, 100); +INSERT INTO demo_distributed VALUES(400, 0); + +INSERT INTO demo_source_table VALUES(1, 77); +INSERT INTO demo_source_table VALUES(15, 77); +INSERT INTO demo_source_table VALUES(75, 77); +INSERT INTO demo_source_table VALUES(100, 77); +INSERT INTO demo_source_table VALUES(300, 77); +INSERT INTO demo_source_table VALUES(400, 77); +INSERT INTO demo_source_table VALUES(500, 77); +$$ +LANGUAGE SQL; + +CREATE FUNCTION merge_demo_data() RETURNS VOID AS $$ +MERGE INTO demo_distributed t +USING demo_source_table s ON s.id2 = t.id1 +WHEN MATCHED AND t.val1= 0 THEN + DELETE +WHEN MATCHED THEN + UPDATE SET val1 = val1 + s.val2 +WHEN NOT MATCHED THEN + INSERT VALUES(s.id2, s.val2); +$$ +LANGUAGE SQL; + +SELECT setup_demo_data(); +SELECT merge_demo_data(); +SELECT * INTO pg_demo_result FROM demo_distributed ORDER BY 1, 2; + +TRUNCATE demo_distributed; +TRUNCATE demo_source_table; + +SELECT create_distributed_table('demo_distributed', 'id1'); +SELECT create_reference_table('demo_source_table'); + +SELECT setup_demo_data(); +SELECT merge_demo_data(); + +SELECT * INTO dist_demo_result FROM demo_distributed ORDER BY 1, 2; + +-- Should be equal +SELECT c.*, p.* +FROM dist_demo_result c, pg_demo_result p +WHERE c.id1 = p.id1 +ORDER BY 1,2; + +-- Must return zero rows +SELECT count(*) +FROM pg_demo_result p FULL OUTER JOIN dist_demo_result d ON p.id1 = d.id1 +WHERE p.id1 IS NULL OR d.id1 IS NULL; + +-- Now convert source as distributed, but non-colocated with target +DROP TABLE pg_demo_result, dist_demo_result; +SELECT undistribute_table('demo_distributed'); +SELECT undistribute_table('demo_source_table'); + +CREATE OR REPLACE FUNCTION merge_demo_data() RETURNS VOID AS $$ +MERGE INTO demo_distributed t +USING (SELECT id2,val2 FROM demo_source_table UNION SELECT val2,id2 FROM demo_source_table) AS s +ON t.id1 = s.id2 +WHEN MATCHED THEN + UPDATE SET val1 = val1 + 1; +$$ +LANGUAGE SQL; + +TRUNCATE demo_distributed; +TRUNCATE demo_source_table; + +SELECT setup_demo_data(); +SELECT merge_demo_data(); +SELECT * INTO pg_demo_result FROM demo_distributed ORDER BY 1, 2; + +SELECT create_distributed_table('demo_distributed', 'id1'); +SELECT create_distributed_table('demo_source_table', 'id2', colocate_with=>'none'); + +TRUNCATE demo_distributed; +TRUNCATE demo_source_table; + +SELECT setup_demo_data(); +SELECT merge_demo_data(); +SELECT * INTO dist_demo_result FROM demo_distributed ORDER BY 1, 2; + +-- Should be equal +SELECT c.*, p.* +FROM dist_demo_result c, pg_demo_result p +WHERE c.id1 = p.id1 +ORDER BY 1,2; + +-- Must return zero rows +SELECT count(*) +FROM pg_demo_result p FULL OUTER JOIN dist_demo_result d ON p.id1 = d.id1 +WHERE p.id1 IS NULL OR d.id1 IS NULL; + +-- Test with LIMIT + +CREATE OR REPLACE FUNCTION merge_demo_data() RETURNS VOID AS $$ +MERGE INTO demo_distributed t +USING (SELECT 999 as s3, demo_source_table.* FROM (SELECT * FROM demo_source_table ORDER BY 1 LIMIT 3) as foo LEFT JOIN demo_source_table USING(id2)) AS s +ON t.id1 = s.id2 +WHEN MATCHED THEN + UPDATE SET val1 = s3 +WHEN NOT MATCHED THEN + INSERT VALUES(id2, s3); +$$ +LANGUAGE SQL; + +DROP TABLE pg_demo_result, dist_demo_result; +SELECT undistribute_table('demo_distributed'); +SELECT undistribute_table('demo_source_table'); + +TRUNCATE demo_distributed; +TRUNCATE demo_source_table; + +SELECT setup_demo_data(); +SELECT merge_demo_data(); +SELECT * INTO pg_demo_result FROM demo_distributed ORDER BY 1, 2; + +SELECT create_distributed_table('demo_distributed', 'id1'); +SELECT create_distributed_table('demo_source_table', 'id2', colocate_with=>'none'); + +TRUNCATE demo_distributed; +TRUNCATE demo_source_table; + +SELECT setup_demo_data(); +SELECT merge_demo_data(); +SELECT * INTO dist_demo_result FROM demo_distributed ORDER BY 1, 2; + +-- Should be equal +SELECT c.*, p.* +FROM dist_demo_result c, pg_demo_result p +WHERE c.id1 = p.id1 +ORDER BY 1,2; + +-- Must return zero rows +SELECT count(*) +FROM pg_demo_result p FULL OUTER JOIN dist_demo_result d ON p.id1 = d.id1 +WHERE p.id1 IS NULL OR d.id1 IS NULL; + +-- Test explain with repartition +SET citus.explain_all_tasks TO false; +EXPLAIN (COSTS OFF) +MERGE INTO demo_distributed t +USING (SELECT 999 as s3, demo_source_table.* FROM (SELECT * FROM demo_source_table ORDER BY 1 LIMIT 3) as foo LEFT JOIN demo_source_table USING(id2)) AS s +ON t.id1 = s.id2 +WHEN MATCHED THEN + UPDATE SET val1 = s3 +WHEN NOT MATCHED THEN + INSERT VALUES(id2, s3); + +-- Test multiple join conditions on distribution column +MERGE INTO demo_distributed t +USING (SELECT id2+1 as key, id2+3 as key2 FROM demo_source_table) s +ON t.id1 = s.key2 ANd t.id1 = s.key +WHEN NOT MATCHED THEN + INSERT VALUES(s.key2, 333); + +MERGE INTO demo_distributed t +USING (SELECT id2+1 as key, id2+2 as key2 FROM demo_source_table) s +ON t.id1 = s.key2 AND t.id1 = s.key +WHEN NOT MATCHED THEN + DO NOTHING; + +MERGE INTO demo_distributed t +USING (SELECT id2+1 as key, id2+3 as key2 FROM demo_source_table) s +ON t.val1 = s.key2 AND t.id1 = s.key AND t.id1 = s.key2 +WHEN NOT MATCHED THEN + INSERT VALUES(s.key2, 444); + +-- Test aggregate functions in source-query +SELECT COUNT(*) FROM demo_distributed where val1 = 150; +SELECT COUNT(*) FROM demo_distributed where id1 = 2; + +-- One row with Key=7 updated in demo_distributed to 150 +MERGE INTO demo_distributed t +USING (SELECT count(DISTINCT id2)::int4 as key FROM demo_source_table GROUP BY val2) s +ON t.id1 = s.key +WHEN NOT MATCHED THEN INSERT VALUES(s.key, 1) +WHEN MATCHED THEN UPDATE SET val1 = 150; + +-- Seven rows with Key=2 inserted in demo_distributed +MERGE INTO demo_distributed t +USING (SELECT (count(DISTINCT val2) + 1)::int4 as key FROM demo_source_table GROUP BY id2) s +ON t.id1 = s.key +WHEN NOT MATCHED THEN INSERT VALUES(s.key, 1) +WHEN MATCHED THEN UPDATE SET val1 = 150; + +SELECT COUNT(*) FROM demo_distributed where val1 = 150; +SELECT COUNT(*) FROM demo_distributed where id1 = 2; + -- -- Error and Unsupported scenarios -- + +-- Test explain analyze with repartition +EXPLAIN ANALYZE +MERGE INTO demo_distributed t +USING (SELECT 999 as s3, demo_source_table.* FROM (SELECT * FROM demo_source_table ORDER BY 1 LIMIT 3) as foo LEFT JOIN demo_source_table USING(id2)) AS s +ON t.id1 = s.id2 +WHEN MATCHED THEN + UPDATE SET val1 = s3 +WHEN NOT MATCHED THEN + INSERT VALUES(id2, s3); + +-- Source without a table +MERGE INTO target_cj t +USING (VALUES (1, 1), (2, 1), (3, 3)) as s (sid, val) +ON t.tid = s.sid AND t.tid = 2 +WHEN MATCHED THEN + UPDATE SET val = s.val +WHEN NOT MATCHED THEN + DO NOTHING; + +-- Incomplete source +MERGE INTO target_cj t +USING (source_cj1 s1 INNER JOIN source_cj2 s2 ON sid1 = val2) s +ON t.tid = s.sid1 AND t.tid = 2 +WHEN MATCHED THEN + UPDATE SET src = src2 +WHEN NOT MATCHED THEN + DO NOTHING; + -- Reference as a target and local as source MERGE INTO refsource_ref USING (SELECT * FROM reftarget_local UNION SELECT * FROM reftarget_local) AS foo ON refsource_ref.s1 = foo.t1 @@ -1634,34 +1896,16 @@ WHEN MATCHED THEN WHEN NOT MATCHED THEN INSERT VALUES(foo.t1); --- Reference as a source and distributed as target -MERGE INTO target_set t -USING refsource_ref AS s ON t.t1 = s.s1 -WHEN MATCHED THEN - DO NOTHING; - MERGE INTO target_set USING source_set AS foo ON target_set.t1 = foo.s1 WHEN MATCHED THEN UPDATE SET ctid = '(0,100)'; -MERGE INTO target_set -USING (SELECT s1,s2 FROM source_set UNION SELECT s2,s1 FROM source_set) AS foo ON target_set.t1 = foo.s1 -WHEN MATCHED THEN - UPDATE SET t2 = t2 + 1; - -MERGE INTO target_set -USING (SELECT 2 as s3, source_set.* FROM (SELECT * FROM source_set LIMIT 1) as foo LEFT JOIN source_set USING( s1)) AS foo -ON target_set.t1 = foo.s1 -WHEN MATCHED THEN UPDATE SET t2 = t2 + 1 -WHEN NOT MATCHED THEN INSERT VALUES(s1, s3); - - -- modifying CTE not supported EXPLAIN -WITH cte_1 AS (DELETE FROM target_json) +WITH cte_1 AS (DELETE FROM target_json RETURNING *) MERGE INTO target_json sda -USING source_json sdn +USING cte_1 sdn ON sda.id = sdn.id WHEN NOT matched THEN INSERT (id, z) VALUES (sdn.id, 5); @@ -1710,6 +1954,7 @@ ON t.id = s.id WHEN NOT MATCHED THEN INSERT (id) VALUES(1000); +-- Colocated merge MERGE INTO t1 t USING s1 s ON t.id = s.id @@ -1722,6 +1967,13 @@ ON t.id = s.id WHEN NOT MATCHED THEN INSERT (val) VALUES(s.val); +-- Non-colocated merge +MERGE INTO t1 t +USING s1 s +ON t.id = s.val +WHEN NOT MATCHED THEN + INSERT (id) VALUES(s.id); + -- try updating the distribution key column BEGIN; MERGE INTO target_cj t @@ -1810,17 +2062,7 @@ WHEN MATCHED AND (merge_when_and_write()) THEN ROLLBACK; --- Joining on partition columns with sub-query -MERGE INTO t1 - USING (SELECT * FROM s1) sub ON (sub.val = t1.id) -- sub.val is not a distribution column - WHEN MATCHED AND sub.val = 0 THEN - DELETE - WHEN MATCHED THEN - UPDATE SET val = t1.val + 1 - WHEN NOT MATCHED THEN - INSERT (id, val) VALUES (sub.id, sub.val); - --- Joining on partition columns with CTE +-- Joining on non-partition columns with CTE source, but INSERT incorrect column WITH s1_res AS ( SELECT * FROM s1 ) @@ -1846,7 +2088,7 @@ MERGE INTO t1 WHEN NOT MATCHED THEN INSERT (id, val) VALUES (s1_res.id, s1_res.val); --- With a single WHEN clause, which causes a non-left join +-- Join condition without target distribution column WITH s1_res AS ( SELECT * FROM s1 ) @@ -1953,34 +2195,12 @@ WHEN MATCHED THEN WHEN NOT MATCHED THEN INSERT VALUES(mv_source.id, mv_source.val); --- Distributed tables *must* be colocated +-- Do not allow constant values into the distribution column CREATE TABLE dist_target(id int, val varchar); SELECT create_distributed_table('dist_target', 'id'); CREATE TABLE dist_source(id int, val varchar); SELECT create_distributed_table('dist_source', 'id', colocate_with => 'none'); -MERGE INTO dist_target -USING dist_source -ON dist_target.id = dist_source.id -WHEN MATCHED THEN -UPDATE SET val = dist_source.val -WHEN NOT MATCHED THEN -INSERT VALUES(dist_source.id, dist_source.val); - --- Distributed tables *must* be joined on distribution column -CREATE TABLE dist_colocated(id int, val int); -SELECT create_distributed_table('dist_colocated', 'id', colocate_with => 'dist_target'); - -MERGE INTO dist_target -USING dist_colocated -ON dist_target.id = dist_colocated.val -- val is not the distribution column -WHEN MATCHED THEN -UPDATE SET val = dist_colocated.val -WHEN NOT MATCHED THEN -INSERT VALUES(dist_colocated.id, dist_colocated.val); - - --- Both the source and target must be distributed MERGE INTO dist_target USING (SELECT 100 id) AS source ON dist_target.id = source.id AND dist_target.val = 'const' @@ -2055,7 +2275,6 @@ INSERT VALUES(dist_source.id, dist_source.val); CREATE SCHEMA query_single_shard_table; SET search_path TO query_single_shard_table; -SET client_min_messages TO DEBUG2; CREATE TABLE nullkey_c1_t1(a int, b int); CREATE TABLE nullkey_c1_t2(a int, b int); @@ -2068,15 +2287,17 @@ SELECT create_distributed_table('nullkey_c2_t1', null, colocate_with=>'none'); SELECT create_distributed_table('nullkey_c2_t2', null, colocate_with=>'nullkey_c2_t1', distribution_type=>null); CREATE TABLE reference_table(a int, b int); +CREATE TABLE distributed_table(a int, b int); +CREATE TABLE citus_local_table(a int, b int); SELECT create_reference_table('reference_table'); +SELECT create_distributed_table('distributed_table', 'a'); +SELECT citus_add_local_table_to_metadata('citus_local_table'); + +SET client_min_messages TO DEBUG2; INSERT INTO reference_table SELECT i, i FROM generate_series(0, 5) i; -CREATE TABLE distributed_table(a int, b int); -SELECT create_distributed_table('distributed_table', 'a'); INSERT INTO distributed_table SELECT i, i FROM generate_series(3, 8) i; -CREATE TABLE citus_local_table(a int, b int); -SELECT citus_add_local_table_to_metadata('citus_local_table'); INSERT INTO citus_local_table SELECT i, i FROM generate_series(0, 10) i; CREATE TABLE postgres_local_table(a int, b int); @@ -2159,9 +2380,117 @@ WHEN MATCHED THEN UPDATE SET b = cte.b; SET client_min_messages TO WARNING; DROP SCHEMA query_single_shard_table CASCADE; -RESET client_min_messages; SET search_path TO merge_schema; +-- Test Columnar table +CREATE TABLE target_columnar(cid int, name text) USING columnar; +SELECT create_distributed_table('target_columnar', 'cid'); +MERGE INTO target_columnar t +USING demo_source_table s +ON t.cid = s.id2 +WHEN MATCHED THEN + UPDATE SET name = 'Columnar table updated by MERGE' +WHEN NOT MATCHED THEN + DO NOTHING; + +MERGE INTO demo_distributed t +USING generate_series(0,100) as source(key) +ON (source.key + 1 = t.id1) + WHEN MATCHED THEN UPDATE SET val1 = 15; + +-- This should fail in planning stage itself +EXPLAIN MERGE INTO demo_distributed t +USING demo_source_table s +ON (s.id2 + 1 = t.id1) + WHEN MATCHED THEN UPDATE SET val1 = 15; + +-- Sub-queries and CTEs are not allowed in actions and ON clause +CREATE TABLE target_1 (a int, b int, c int); +SELECT create_distributed_table('target_1', 'a'); + +CREATE TABLE source_2 (a int, b int, c int); +SELECT create_distributed_table('source_2', 'a'); + +INSERT INTO target_1 VALUES(1, 2, 3); +INSERT INTO target_1 VALUES(4, 5, 6); +INSERT INTO target_1 VALUES(11, 12, 13); + +INSERT INTO source_2 VALUES(1, 2, 3); + +WITH cte_1 as (SELECT max(a) as max_a, max(b) as b FROM source_2) +MERGE INTO target_1 +USING cte_1 +ON (target_1.a = cte_1.b) +WHEN NOT MATCHED AND (SELECT max_a > 10 FROM cte_1) THEN + INSERT VALUES (cte_1.b, 100); + +WITH cte_1 as (SELECT a, b FROM source_2) +MERGE INTO target_1 +USING cte_1 +ON (target_1.a = cte_1.b) +WHEN NOT MATCHED AND (SELECT a > 10 FROM cte_1) THEN + INSERT VALUES (cte_1.b, 100); + +MERGE INTO target_1 +USING source_2 +ON (target_1.a = source_2.b) +WHEN NOT MATCHED AND (SELECT max_a > 10 FROM (SELECT max(a) as max_a, max(b) as b FROM target_1) as foo) THEN + INSERT VALUES (source_2.b, 100); + +-- or same with CTEs +WITH cte_1 as (SELECT max(a) as max_a, max(b) as b FROM target_1) +MERGE INTO target_1 +USING source_2 +ON (target_1.a = source_2.b) +WHEN NOT MATCHED AND (SELECT max_a > 10 FROM (SELECT max(a) as max_a, max(b) as b FROM target_1) as foo) THEN + INSERT VALUES (source_2.b, 100); + +WITH cte_1 as (SELECT a, b FROM target_1), cte_2 as (select b,a from target_1) +MERGE INTO target_1 +USING (SELECT * FROM source_2) as subq +ON (target_1.a = subq.b) +WHEN NOT MATCHED AND (SELECT a > 10 FROM cte_2) THEN + INSERT VALUES (subq.b, 100); + +MERGE INTO source_2 +USING target_1 +ON (target_1.a = source_2.a) +WHEN MATCHED THEN + UPDATE SET b = (SELECT max(a) FROM source_2); + +MERGE INTO source_2 +USING target_1 +ON (target_1.a = source_2.a) +WHEN NOT MATCHED THEN + INSERT VALUES (target_1.a,(select max(a) from target_1)); + +MERGE INTO target_1 +USING source_2 +ON (target_1.a = source_2.b) +WHEN NOT MATCHED AND (SELECT max(c) > 10 FROM source_2) THEN + INSERT VALUES (source_2.b, 100); + +-- Test in ON clause +MERGE INTO target_1 t2 +USING (SELECT * FROM source_2) AS t1 +ON (t1.a = t2.a AND (SELECT 1=1 FROM target_1)) +WHEN MATCHED THEN + DELETE; + +MERGE INTO target_1 t2 +USING (SELECT * FROM source_2) AS t1 +ON (t1.a = t2.a AND (SELECT max(a) > 55 FROM target_1)) +WHEN MATCHED THEN + DELETE; + +WITH cte_1 as (SELECT a, b FROM target_1), cte_2 as (select b,a from target_1) +MERGE INTO target_1 t2 +USING (SELECT * FROM cte_1) AS t1 +ON (t1.a = t2.a AND (SELECT max(a) > 55 FROM cte_2)) +WHEN MATCHED THEN + DELETE; + +RESET client_min_messages; DROP SERVER foreign_server CASCADE; DROP FUNCTION merge_when_and_write(); DROP SCHEMA merge_schema CASCADE; diff --git a/src/test/regress/sql/merge_arbitrary.sql b/src/test/regress/sql/merge_arbitrary.sql index 17b7d4f90..6c0a931dc 100644 --- a/src/test/regress/sql/merge_arbitrary.sql +++ b/src/test/regress/sql/merge_arbitrary.sql @@ -131,3 +131,48 @@ BEGIN; EXECUTE local(0, 1); SELECT * FROM t1 order by id; ROLLBACK; + +-- Test prepared statements with repartition +PREPARE merge_repartition_pg(int,int,int,int) as + MERGE INTO pg_target target + USING (SELECT id+1+$1 as key, val FROM (SELECT * FROM pg_source UNION SELECT * FROM pg_source WHERE id = $2) as foo) as source + ON (source.key = target.id AND $3 < 10000) + WHEN MATCHED THEN UPDATE SET val = (source.key::int+$4) + WHEN NOT MATCHED THEN INSERT VALUES (source.key, source.val); + +PREPARE merge_repartition_citus(int,int,int,int) as + MERGE INTO citus_target target + USING (SELECT id+1+$1 as key, val FROM (SELECT * FROM citus_source UNION SELECT * FROM citus_source WHERE id = $2) as foo) as source + ON (source.key = target.id AND $3 < 10000) + WHEN MATCHED THEN UPDATE SET val = (source.key::int+$4) + WHEN NOT MATCHED THEN INSERT VALUES (source.key, source.val); + +EXECUTE merge_repartition_pg(1,1,1,1); +EXECUTE merge_repartition_citus(1,1,1,1); + +SET client_min_messages = NOTICE; +SELECT compare_data(); +RESET client_min_messages; + +EXECUTE merge_repartition_pg(1,100,1,1); +EXECUTE merge_repartition_citus(1,100,1,1); + +EXECUTE merge_repartition_pg(2,200,1,1); +EXECUTE merge_repartition_citus(2,200,1,1); + +EXECUTE merge_repartition_pg(3,300,1,1); +EXECUTE merge_repartition_citus(3,300,1,1); + +EXECUTE merge_repartition_pg(4,400,1,1); +EXECUTE merge_repartition_citus(4,400,1,1); + +EXECUTE merge_repartition_pg(5,500,1,1); +EXECUTE merge_repartition_citus(5,500,1,1); + +-- Sixth time +EXECUTE merge_repartition_pg(6,600,1,6); +EXECUTE merge_repartition_citus(6,600,1,6); + +SET client_min_messages = NOTICE; +SELECT compare_data(); +RESET client_min_messages; diff --git a/src/test/regress/sql/merge_arbitrary_create.sql b/src/test/regress/sql/merge_arbitrary_create.sql index edf9b0d9d..efa3185da 100644 --- a/src/test/regress/sql/merge_arbitrary_create.sql +++ b/src/test/regress/sql/merge_arbitrary_create.sql @@ -48,3 +48,57 @@ CREATE TABLE s1(id int, val int); SELECT citus_add_local_table_to_metadata('t1'); SELECT citus_add_local_table_to_metadata('s1'); + +-- Test prepared statements with repartition +CREATE TABLE pg_target(id int, val int); +CREATE TABLE pg_source(id int, val int, const int); +CREATE TABLE citus_target(id int, val int); +CREATE TABLE citus_source(id int, val int, const int); +SELECT citus_add_local_table_to_metadata('pg_target'); +SELECT citus_add_local_table_to_metadata('pg_source'); + +-- +-- Load same set of data to both Postgres and Citus tables +-- +CREATE OR REPLACE FUNCTION setup_data() RETURNS VOID AS $$ + INSERT INTO pg_source SELECT i, i+1, 1 FROM generate_series(1, 10000) i; + INSERT INTO pg_target SELECT i, 1 FROM generate_series(5001, 10000) i; + INSERT INTO citus_source SELECT i, i+1, 1 FROM generate_series(1, 10000) i; + INSERT INTO citus_target SELECT i, 1 FROM generate_series(5001, 10000) i; +$$ +LANGUAGE SQL; + +-- +-- Compares the final target tables, merge-modified data, of both Postgres and Citus tables +-- +CREATE OR REPLACE FUNCTION check_data(table1_name text, column1_name text, table2_name text, column2_name text) +RETURNS VOID AS $$ +DECLARE + table1_avg numeric; + table2_avg numeric; +BEGIN + EXECUTE format('SELECT COALESCE(AVG(%I), 0) FROM %I', column1_name, table1_name) INTO table1_avg; + EXECUTE format('SELECT COALESCE(AVG(%I), 0) FROM %I', column2_name, table2_name) INTO table2_avg; + + IF table1_avg > table2_avg THEN + RAISE EXCEPTION 'The average of %.% is greater than %.%', table1_name, column1_name, table2_name, column2_name; + ELSIF table1_avg < table2_avg THEN + RAISE EXCEPTION 'The average of %.% is less than %.%', table1_name, column1_name, table2_name, column2_name; + ELSE + RAISE NOTICE 'The average of %.% is equal to %.%', table1_name, column1_name, table2_name, column2_name; + END IF; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION compare_data() RETURNS VOID AS $$ + SELECT check_data('pg_target', 'id', 'citus_target', 'id'); + SELECT check_data('pg_target', 'val', 'citus_target', 'val'); +$$ +LANGUAGE SQL; + +-- +-- Target and source are distributed, and non-colocated +-- +SELECT setup_data(); +SELECT create_distributed_table('citus_target', 'id'); +SELECT create_distributed_table('citus_source', 'id', colocate_with=>'none'); diff --git a/src/test/regress/sql/merge_partition_tables.sql b/src/test/regress/sql/merge_partition_tables.sql new file mode 100644 index 000000000..ab40fd23e --- /dev/null +++ b/src/test/regress/sql/merge_partition_tables.sql @@ -0,0 +1,164 @@ + +SHOW server_version \gset +SELECT substring(:'server_version', '\d+')::int >= 15 AS server_version_ge_15 +\gset +\if :server_version_ge_15 +\else +\q +\endif + +-- We create two sets of source and target tables, one set in Postgres and +-- the other in Citus distributed. We run the _exact_ MERGE SQL on both sets +-- and compare the final results of the target tables in Postgres and Citus. +-- The results should match. This process is repeated for various combinations +-- of MERGE SQL. + +DROP SCHEMA IF EXISTS merge_partition_tables CASCADE; +CREATE SCHEMA merge_partition_tables; +SET search_path TO merge_partition_tables; +SET citus.shard_count TO 4; +SET citus.next_shard_id TO 7000000; +SET citus.explain_all_tasks TO true; +SET citus.shard_replication_factor TO 1; +SET citus.max_adaptive_executor_pool_size TO 1; +SET client_min_messages = warning; +SELECT 1 FROM master_add_node('localhost', :master_port, groupid => 0); +RESET client_min_messages; + + +CREATE TABLE pg_target(id int, val int) PARTITION BY RANGE(id); +CREATE TABLE pg_source(id int, val int, const int) PARTITION BY RANGE(val); +CREATE TABLE citus_target(id int, val int) PARTITION BY RANGE(id); +CREATE TABLE citus_source(id int, val int, const int) PARTITION BY RANGE(val); +SELECT citus_add_local_table_to_metadata('citus_target'); +SELECT citus_add_local_table_to_metadata('citus_source'); + +CREATE TABLE part1 PARTITION OF pg_target FOR VALUES FROM (1) TO (2500) WITH (autovacuum_enabled=off); +CREATE TABLE part2 PARTITION OF pg_target FOR VALUES FROM (2501) TO (5000) WITH (autovacuum_enabled=off); +CREATE TABLE part3 PARTITION OF pg_target FOR VALUES FROM (5001) TO (7500) WITH (autovacuum_enabled=off); +CREATE TABLE part4 PARTITION OF pg_target DEFAULT WITH (autovacuum_enabled=off); +CREATE TABLE part5 PARTITION OF citus_target FOR VALUES FROM (1) TO (2500) WITH (autovacuum_enabled=off); +CREATE TABLE part6 PARTITION OF citus_target FOR VALUES FROM (2501) TO (5000) WITH (autovacuum_enabled=off); +CREATE TABLE part7 PARTITION OF citus_target FOR VALUES FROM (5001) TO (7500) WITH (autovacuum_enabled=off); +CREATE TABLE part8 PARTITION OF citus_target DEFAULT WITH (autovacuum_enabled=off); + +CREATE TABLE part9 PARTITION OF pg_source FOR VALUES FROM (1) TO (2500) WITH (autovacuum_enabled=off); +CREATE TABLE part10 PARTITION OF pg_source FOR VALUES FROM (2501) TO (5000) WITH (autovacuum_enabled=off); +CREATE TABLE part11 PARTITION OF pg_source FOR VALUES FROM (5001) TO (7500) WITH (autovacuum_enabled=off); +CREATE TABLE part12 PARTITION OF pg_source DEFAULT WITH (autovacuum_enabled=off); +CREATE TABLE part13 PARTITION OF citus_source FOR VALUES FROM (1) TO (2500) WITH (autovacuum_enabled=off); +CREATE TABLE part14 PARTITION OF citus_source FOR VALUES FROM (2501) TO (5000) WITH (autovacuum_enabled=off); +CREATE TABLE part15 PARTITION OF citus_source FOR VALUES FROM (5001) TO (7500) WITH (autovacuum_enabled=off); +CREATE TABLE part16 PARTITION OF citus_source DEFAULT WITH (autovacuum_enabled=off); + +CREATE OR REPLACE FUNCTION cleanup_data() RETURNS VOID SET search_path TO merge_partition_tables AS $$ + TRUNCATE pg_target; + TRUNCATE pg_source; + TRUNCATE citus_target; + TRUNCATE citus_source; + SELECT undistribute_table('citus_target'); + SELECT undistribute_table('citus_source'); +$$ +LANGUAGE SQL; + +-- +-- Load same set of data to both Postgres and Citus tables +-- +CREATE OR REPLACE FUNCTION setup_data() RETURNS VOID SET search_path TO merge_partition_tables AS $$ + INSERT INTO pg_source SELECT i, i+1, 1 FROM generate_series(1, 10000) i; + INSERT INTO pg_target SELECT i, 1 FROM generate_series(5001, 10000) i; + INSERT INTO citus_source SELECT i, i+1, 1 FROM generate_series(1, 10000) i; + INSERT INTO citus_target SELECT i, 1 FROM generate_series(5001, 10000) i; +$$ +LANGUAGE SQL; + +-- +-- Compares the final target tables, merge-modified data, of both Postgres and Citus tables +-- +CREATE OR REPLACE FUNCTION check_data(table1_name text, column1_name text, table2_name text, column2_name text) +RETURNS VOID SET search_path TO merge_partition_tables AS $$ +DECLARE + table1_avg numeric; + table2_avg numeric; +BEGIN + EXECUTE format('SELECT COALESCE(AVG(%I), 0) FROM %I', column1_name, table1_name) INTO table1_avg; + EXECUTE format('SELECT COALESCE(AVG(%I), 0) FROM %I', column2_name, table2_name) INTO table2_avg; + + IF table1_avg > table2_avg THEN + RAISE EXCEPTION 'The average of %.% is greater than %.%', table1_name, column1_name, table2_name, column2_name; + ELSIF table1_avg < table2_avg THEN + RAISE EXCEPTION 'The average of %.% is less than %.%', table1_name, column1_name, table2_name, column2_name; + ELSE + RAISE NOTICE 'The average of %.% is equal to %.%', table1_name, column1_name, table2_name, column2_name; + END IF; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION compare_data() RETURNS VOID SET search_path TO merge_partition_tables AS $$ + SELECT check_data('pg_target', 'id', 'citus_target', 'id'); + SELECT check_data('pg_target', 'val', 'citus_target', 'val'); +$$ +LANGUAGE SQL; + +-- Test colocated partition tables + +SET client_min_messages = ERROR; +SELECT cleanup_data(); +SELECT setup_data(); +SELECT create_distributed_table('citus_target', 'id'); +SELECT create_distributed_table('citus_source', 'id', colocate_with=>'citus_target'); +RESET client_min_messages; + +MERGE INTO pg_target t +USING pg_source s +ON t.id = s.id +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); + +MERGE INTO citus_target t +USING citus_source s +ON t.id = s.id +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); + +SELECT compare_data(); + +-- Test non-colocated partition tables + +SET client_min_messages = ERROR; +SELECT cleanup_data(); +SELECT setup_data(); +SELECT create_distributed_table('citus_target', 'id'); +SELECT create_distributed_table('citus_source', 'id', colocate_with=>'none'); +RESET client_min_messages; + +MERGE INTO pg_target t +USING pg_source s +ON t.id = s.id +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); + +MERGE INTO citus_target t +USING citus_source s +ON t.id = s.id +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); + +SELECT compare_data(); +DROP SCHEMA merge_partition_tables CASCADE; diff --git a/src/test/regress/sql/merge_repartition1.sql b/src/test/regress/sql/merge_repartition1.sql new file mode 100644 index 000000000..4d73e999d --- /dev/null +++ b/src/test/regress/sql/merge_repartition1.sql @@ -0,0 +1,515 @@ +SHOW server_version \gset +SELECT substring(:'server_version', '\d+')::int >= 15 AS server_version_ge_15 +\gset +\if :server_version_ge_15 +\else +\q +\endif + +-- We create two sets of source and target tables, one set in Postgres and +-- the other in Citus distributed. We run the _exact_ MERGE SQL on both sets +-- and compare the final results of the target tables in Postgres and Citus. +-- The results should match. This process is repeated for various combinations +-- of MERGE SQL. + +DROP SCHEMA IF EXISTS merge_repartition1_schema CASCADE; +CREATE SCHEMA merge_repartition1_schema; +SET search_path TO merge_repartition1_schema; +SET citus.shard_count TO 4; +SET citus.next_shard_id TO 5000000; +SET citus.explain_all_tasks TO true; +SET citus.shard_replication_factor TO 1; +SET citus.max_adaptive_executor_pool_size TO 1; +SET client_min_messages = warning; +SELECT 1 FROM master_add_node('localhost', :master_port, groupid => 0); +RESET client_min_messages; + + +CREATE TABLE pg_target(id int, val int); +CREATE TABLE pg_source(id int, val int, const int); +CREATE TABLE citus_target(id int, val int); +CREATE TABLE citus_source(id int, val int, const int); +SELECT citus_add_local_table_to_metadata('citus_target'); +SELECT citus_add_local_table_to_metadata('citus_source'); + +CREATE OR REPLACE FUNCTION cleanup_data() RETURNS VOID SET search_path TO merge_repartition1_schema AS $$ + TRUNCATE pg_target; + TRUNCATE pg_source; + TRUNCATE citus_target; + TRUNCATE citus_source; + SELECT undistribute_table('citus_target'); + SELECT undistribute_table('citus_source'); +$$ +LANGUAGE SQL; +-- +-- Load same set of data to both Postgres and Citus tables +-- +CREATE OR REPLACE FUNCTION setup_data() RETURNS VOID SET search_path TO merge_repartition1_schema AS $$ + INSERT INTO pg_source SELECT i, i+1, 1 FROM generate_series(1, 10000) i; + INSERT INTO pg_target SELECT i, 1 FROM generate_series(5001, 10000) i; + INSERT INTO citus_source SELECT i, i+1, 1 FROM generate_series(1, 10000) i; + INSERT INTO citus_target SELECT i, 1 FROM generate_series(5001, 10000) i; +$$ +LANGUAGE SQL; + +-- +-- Compares the final target tables, merge-modified data, of both Postgres and Citus tables +-- +CREATE OR REPLACE FUNCTION check_data(table1_name text, column1_name text, table2_name text, column2_name text) +RETURNS VOID SET search_path TO merge_repartition1_schema AS $$ +DECLARE + table1_avg numeric; + table2_avg numeric; +BEGIN + EXECUTE format('SELECT COALESCE(AVG(%I), 0) FROM %I', column1_name, table1_name) INTO table1_avg; + EXECUTE format('SELECT COALESCE(AVG(%I), 0) FROM %I', column2_name, table2_name) INTO table2_avg; + + IF table1_avg > table2_avg THEN + RAISE EXCEPTION 'The average of %.% is greater than %.%', table1_name, column1_name, table2_name, column2_name; + ELSIF table1_avg < table2_avg THEN + RAISE EXCEPTION 'The average of %.% is less than %.%', table1_name, column1_name, table2_name, column2_name; + ELSE + RAISE NOTICE 'The average of %.% is equal to %.%', table1_name, column1_name, table2_name, column2_name; + END IF; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION compare_data() RETURNS VOID SET search_path TO merge_repartition1_schema AS $$ + SELECT check_data('pg_target', 'id', 'citus_target', 'id'); + SELECT check_data('pg_target', 'val', 'citus_target', 'val'); +$$ +LANGUAGE SQL; + +-- +-- Target and source are distributed, and non-colocated +-- +SELECT cleanup_data(); +SELECT setup_data(); +SELECT create_distributed_table('citus_target', 'id'); +SELECT create_distributed_table('citus_source', 'id', colocate_with=>'none'); + +MERGE INTO pg_target t +USING pg_source s +ON t.id = s.id +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); + +MERGE INTO citus_target t +USING citus_source s +ON t.id = s.id +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); + +SELECT compare_data(); + +-- +-- Target and source are distributed, and colocated but not joined on distribution column +-- +SELECT cleanup_data(); +SELECT setup_data(); +SELECT create_distributed_table('citus_target', 'id'); +SELECT create_distributed_table('citus_source', 'id', colocate_with=>'citus_target'); + +MERGE INTO pg_target t +USING (SELECT * FROM pg_source) subq +ON (subq.val = t.id) +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = subq.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(subq.val, subq.id); + +MERGE INTO citus_target t +USING (SELECT * FROM citus_source) subq +ON (subq.val = t.id) +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = subq.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(subq.val, subq.id); + +SELECT compare_data(); + +-- +-- Target and source are distributed, colocated, joined on distribution column +-- but with nondistribution values +-- +SELECT cleanup_data(); +SELECT setup_data(); +SELECT create_distributed_table('citus_target', 'id'); +SELECT create_distributed_table('citus_source', 'id', colocate_with=>'citus_target'); + +MERGE INTO pg_target t +USING (SELECT id,const FROM pg_source UNION SELECT const,id FROM pg_source ) AS s +ON t.id = s.id +WHEN MATCHED THEN + UPDATE SET val = s.const + 1 +WHEN NOT MATCHED THEN + INSERT VALUES(id, const); + +MERGE INTO citus_target t +USING (SELECT id,const FROM citus_source UNION SELECT const,id FROM citus_source) AS s +ON t.id = s.id +WHEN MATCHED THEN + UPDATE SET val = s.const + 1 +WHEN NOT MATCHED THEN + INSERT VALUES(id, const); + +SELECT compare_data(); + +-- +-- Repartition with a predicate on target_table_name rows in ON clause +-- +SELECT cleanup_data(); +SELECT setup_data(); +SELECT create_distributed_table('citus_target', 'id'); +SELECT create_distributed_table('citus_source', 'id', colocate_with=>'none'); + +MERGE INTO pg_target t +USING (SELECT * FROM pg_source WHERE id < 9500) s +ON t.id = s.id AND t.id < 9000 +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); + +MERGE INTO citus_target t +USING (SELECT * FROM citus_source WHERE id < 9500) s +ON t.id = s.id AND t.id < 9000 +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); + +SELECT compare_data(); + +-- +-- Test CTE and non-colocated tables +-- +SELECT cleanup_data(); +SELECT setup_data(); +SELECT create_distributed_table('citus_target', 'id'); +SELECT create_distributed_table('citus_source', 'id', colocate_with=>'none'); + +WITH cte AS ( + SELECT * FROM pg_source +) +MERGE INTO pg_target t +USING cte s +ON s.id = t.id +WHEN MATCHED AND t.id > 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES (s.id, s.val); + +WITH cte AS ( + SELECT * FROM citus_source +) +MERGE INTO citus_target t +USING cte s +ON s.id = t.id +WHEN MATCHED AND t.id > 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES (s.id, s.val); + +SELECT compare_data(); + +-- +-- Test nested CTEs +-- +SELECT cleanup_data(); +SELECT setup_data(); +SELECT create_distributed_table('citus_target', 'id'); +SELECT create_distributed_table('citus_source', 'id', colocate_with=>'none'); + +WITH cte1 AS ( + SELECT * FROM pg_source ORDER BY 1 LIMIT 9000 +), +cte2 AS( + SELECT * FROM cte1 +), +cte3 AS( + SELECT * FROM cte2 +) +MERGE INTO pg_target t +USING cte3 s +ON (s.id=t.id) +WHEN MATCHED AND t.id > 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES (s.id, s.val); + +WITH cte1 AS ( + SELECT * FROM citus_source ORDER BY 1 LIMIT 9000 +), +cte2 AS( + SELECT * FROM cte1 +), +cte3 AS( + SELECT * FROM cte2 +) +MERGE INTO citus_target t +USING cte3 s +ON (s.id=t.id) +WHEN MATCHED AND t.id > 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES (s.id, s.val); + +SELECT compare_data(); + +-- +-- Target and source are distributed and colocated +-- +SELECT cleanup_data(); +SELECT setup_data(); +SELECT create_distributed_table('citus_target', 'id'); +SELECT create_distributed_table('citus_source', 'id', colocate_with => 'citus_target'); + +MERGE INTO pg_target t +USING (SELECT 999 as newval, pg_source.* FROM (SELECT * FROM pg_source ORDER BY 1 LIMIT 6000) as src LEFT JOIN pg_source USING(id)) AS s +ON t.id = s.id +WHEN MATCHED AND t.id <= 5500 THEN + UPDATE SET val = newval +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(id, newval); + +MERGE INTO citus_target t +USING (SELECT 999 as newval, citus_source.* FROM (SELECT * FROM citus_source ORDER BY 1 LIMIT 6000) as src LEFT JOIN citus_source USING(id)) AS s +ON t.id = s.id +WHEN MATCHED AND t.id <= 5500 THEN + UPDATE SET val = newval +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(id, newval); + +SELECT compare_data(); + +-- +-- Target is distributed and source is reference +-- +SELECT cleanup_data(); +SELECT setup_data(); +SELECT create_distributed_table('citus_target', 'id'); +SELECT create_reference_table('citus_source'); + +MERGE INTO pg_target t +USING pg_source s +ON t.id = s.id +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); + +MERGE INTO citus_target t +USING citus_source s +ON t.id = s.id +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); + +SELECT compare_data(); + +-- +-- Target is distributed and reference as source in a sub-query +-- +SELECT cleanup_data(); +SELECT setup_data(); +SELECT create_distributed_table('citus_target', 'id'); +SELECT create_reference_table('citus_source'); + +MERGE INTO pg_target t +USING (SELECT * FROM pg_source UNION SELECT * FROM pg_source) AS s ON t.id = s.id +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + t.val +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); + +MERGE INTO citus_target t +USING (SELECT * FROM citus_source UNION SELECT * FROM citus_source) AS s ON t.id = s.id +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + t.val +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); +SELECT compare_data(); + +-- +-- Target is distributed and citus-local as source +-- +SELECT cleanup_data(); +SELECT setup_data(); +SELECT create_distributed_table('citus_target', 'id'); +SELECT citus_add_local_table_to_metadata('citus_source'); + +MERGE INTO pg_target t +USING pg_source s +ON t.id = s.id +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); + +MERGE INTO citus_target t +USING citus_source s +ON t.id = s.id +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); + +SELECT compare_data(); + +-- +-- Target and source distributed and non-colocated. The source query requires evaluation +-- at the coordinator +-- +SELECT cleanup_data(); +SELECT setup_data(); +SELECT create_distributed_table('citus_target', 'id'); +SELECT create_distributed_table('citus_source', 'id', colocate_with=>'none'); + +MERGE INTO pg_target t +USING (SELECT 100 AS insval, MAX(const) AS updval, val, MAX(id) AS sid + FROM pg_source + GROUP BY val ORDER BY sid LIMIT 6000) AS s +ON t.id = s.sid +WHEN MATCHED AND t.id <= 5500 THEN + UPDATE SET val = updval + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(sid, insval); + +MERGE INTO citus_target t +USING (SELECT 100 AS insval, MAX(const) AS updval, val, MAX(id) AS sid + FROM citus_source + GROUP BY val ORDER BY sid LIMIT 6000) AS s +ON t.id = s.sid +WHEN MATCHED AND t.id <= 5500 THEN + UPDATE SET val = updval + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(sid, insval); + +SELECT compare_data(); + +-- Test source-query that requires repartitioning on top of MERGE repartitioning +SELECT cleanup_data(); +SELECT setup_data(); +SELECT create_distributed_table('citus_target', 'id'); +SELECT create_distributed_table('citus_source', 'id', colocate_with=>'none'); + +MERGE INTO pg_target t +USING (SELECT s1.val FROM pg_source s1 JOIN pg_source s2 USING (val)) AS s +ON t.id = s.val +WHEN MATCHED THEN + UPDATE SET val = t.val + 1; + +SET citus.enable_repartition_joins TO true; +MERGE INTO citus_target t +USING (SELECT s1.val FROM citus_source s1 JOIN citus_source s2 USING (val)) AS s +ON t.id = s.val +WHEN MATCHED THEN + UPDATE SET val = t.val + 1; + +SELECT compare_data(); + +-- +-- Test columnar as source table +-- +SET client_min_messages TO WARNING; +SELECT cleanup_data(); +RESET client_min_messages; +SELECT setup_data(); +SELECT create_distributed_table('citus_target', 'id'); +SELECT create_distributed_table('citus_source', 'id', colocate_with=>'none'); +SELECT alter_table_set_access_method('citus_source', 'columnar'); + +MERGE INTO pg_target t +USING pg_source s +ON t.id = s.id +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); + +MERGE INTO citus_target t +USING citus_source s +ON t.id = s.id +WHEN MATCHED AND t.id <= 7500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); + +SELECT compare_data(); +SELECT alter_table_set_access_method('citus_source', 'heap'); + +-- Test CTE/Subquery in merge-actions (works only for router query) +SELECT cleanup_data(); +SELECT setup_data(); +SELECT create_distributed_table('citus_target', 'id'); +SELECT create_distributed_table('citus_source', 'id', colocate_with=>'citus_target'); + +MERGE INTO pg_target +USING pg_source +ON (pg_target.id = pg_source.id) +WHEN MATCHED AND (SELECT max_a > 5001 FROM (SELECT max(id) as max_a, max(val) as b FROM pg_target WHERE id = pg_source.id) AS foo) THEN + DELETE +WHEN NOT MATCHED AND (SELECT max_a < 5001 FROM (SELECT max(id) as max_a, max(val) as b FROM pg_target WHERE id = pg_source.id) AS foo) THEN + INSERT VALUES (pg_source.id, 100); + +MERGE INTO citus_target +USING citus_source +ON (citus_target.id = citus_source.id) +WHEN MATCHED AND (SELECT max_a > 5001 FROM (SELECT max(id) as max_a, max(val) as b FROM citus_target WHERE id = citus_source.id) AS foo) THEN + DELETE +WHEN NOT MATCHED AND (SELECT max_a < 5001 FROM (SELECT max(id) as max_a, max(val) as b FROM citus_target WHERE id = citus_source.id) AS foo) THEN + INSERT VALUES (citus_source.id, 100); + +SELECT compare_data(); + +DROP SCHEMA merge_repartition1_schema CASCADE; diff --git a/src/test/regress/sql/merge_repartition2.sql b/src/test/regress/sql/merge_repartition2.sql new file mode 100644 index 000000000..7a4812274 --- /dev/null +++ b/src/test/regress/sql/merge_repartition2.sql @@ -0,0 +1,139 @@ + +SHOW server_version \gset +SELECT substring(:'server_version', '\d+')::int >= 15 AS server_version_ge_15 +\gset +\if :server_version_ge_15 +\else +\q +\endif + +-- We create two sets of source and target tables, one set in Postgres and +-- the other in Citus distributed. We run the _exact_ MERGE SQL on both sets +-- and compare the final results of the target tables in Postgres and Citus. +-- The results should match. This process is repeated for various combinations +-- of MERGE SQL. + +DROP SCHEMA IF EXISTS merge_repartition2_schema CASCADE; +CREATE SCHEMA merge_repartition2_schema; +SET search_path TO merge_repartition2_schema; +SET citus.shard_count TO 4; +SET citus.next_shard_id TO 6000000; +SET citus.explain_all_tasks TO true; +SET citus.shard_replication_factor TO 1; +SET citus.max_adaptive_executor_pool_size TO 1; +SET client_min_messages = warning; +SELECT 1 FROM master_add_node('localhost', :master_port, groupid => 0); +RESET client_min_messages; + + +CREATE TABLE pg_target(id int, val int); +CREATE TABLE pg_source(id int, val int, const int); +CREATE TABLE citus_target(id int, val int); +CREATE TABLE citus_source(id int, val int, const int); +SELECT citus_add_local_table_to_metadata('citus_target'); +SELECT citus_add_local_table_to_metadata('citus_source'); + +CREATE OR REPLACE FUNCTION cleanup_data() RETURNS VOID SET search_path TO merge_repartition2_schema AS $$ + TRUNCATE pg_target; + TRUNCATE pg_source; + TRUNCATE citus_target; + TRUNCATE citus_source; + SELECT undistribute_table('citus_target'); + SELECT undistribute_table('citus_source'); +$$ +LANGUAGE SQL; +-- +-- Load same set of data to both Postgres and Citus tables +-- +CREATE OR REPLACE FUNCTION setup_data() RETURNS VOID SET search_path TO merge_repartition2_schema AS $$ + INSERT INTO pg_source SELECT i, i+1, 1 FROM generate_series(1, 100000) i; + INSERT INTO pg_target SELECT i, 1 FROM generate_series(50001, 100000) i; + INSERT INTO citus_source SELECT i, i+1, 1 FROM generate_series(1, 100000) i; + INSERT INTO citus_target SELECT i, 1 FROM generate_series(50001, 100000) i; +$$ +LANGUAGE SQL; + +-- +-- Compares the final target tables, merge-modified data, of both Postgres and Citus tables +-- +CREATE OR REPLACE FUNCTION check_data(table1_name text, column1_name text, table2_name text, column2_name text) +RETURNS VOID SET search_path TO merge_repartition2_schema AS $$ +DECLARE + table1_avg numeric; + table2_avg numeric; +BEGIN + EXECUTE format('SELECT COALESCE(AVG(%I), 0) FROM %I', column1_name, table1_name) INTO table1_avg; + EXECUTE format('SELECT COALESCE(AVG(%I), 0) FROM %I', column2_name, table2_name) INTO table2_avg; + + IF table1_avg > table2_avg THEN + RAISE EXCEPTION 'The average of %.% is greater than %.%', table1_name, column1_name, table2_name, column2_name; + ELSIF table1_avg < table2_avg THEN + RAISE EXCEPTION 'The average of %.% is less than %.%', table1_name, column1_name, table2_name, column2_name; + ELSE + RAISE NOTICE 'The average of %.% is equal to %.%', table1_name, column1_name, table2_name, column2_name; + END IF; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION compare_data() RETURNS VOID SET search_path TO merge_repartition2_schema AS $$ + SELECT check_data('pg_target', 'id', 'citus_target', 'id'); + SELECT check_data('pg_target', 'val', 'citus_target', 'val'); +$$ +LANGUAGE SQL; + +-- Test nested cte +SELECT cleanup_data(); +SELECT setup_data(); +SELECT create_distributed_table('citus_target', 'id'); +SELECT create_distributed_table('citus_source', 'id', colocate_with=>'none'); + +WITH cte_top AS(WITH cte_1 AS (WITH cte_2 AS (SELECT id, val FROM pg_source) SELECT * FROM cte_2) SELECT * FROM cte_1) +MERGE INTO pg_target t +USING (SELECT const, val, id FROM pg_source WHERE id IN (SELECT id FROM cte_top)) as s +ON (s.id = t.id) +WHEN MATCHED AND t.id <= 75000 THEN + UPDATE SET val = (s.val::int8+1) +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES (s.id, s.val); + +WITH cte_top AS(WITH cte_1 AS (WITH cte_2 AS (SELECT id, val FROM citus_source) SELECT * FROM cte_2) SELECT * FROM cte_1) +MERGE INTO citus_target t +USING (SELECT const, val, id FROM citus_source WHERE id IN (SELECT id FROM cte_top)) as s +ON (s.id = t.id) +WHEN MATCHED AND t.id <= 75000 THEN + UPDATE SET val = (s.val::int8+1) +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES (s.id, s.val); + +SELECT compare_data(); + +-- Test aggregate function in source query + +MERGE INTO pg_target t +USING (SELECT count(id+1)::text as value, val as key FROM pg_source group by key) s +ON t.id = s.key +WHEN MATCHED AND t.id <= 75000 THEN + UPDATE SET val = (s.value::int8+1) +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.key, value::int4+10); + +MERGE INTO citus_target t +USING (SELECT count(id+1)::text as value, val as key FROM citus_source group by key) s +ON t.id = s.key +WHEN MATCHED AND t.id <= 75000 THEN + UPDATE SET val = (s.value::int8+1) +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.key, value::int4+10); + +SELECT compare_data(); + +DROP SCHEMA merge_repartition2_schema CASCADE; + diff --git a/src/test/regress/sql/pg15.sql b/src/test/regress/sql/pg15.sql index e29ceff28..a8ac91901 100644 --- a/src/test/regress/sql/pg15.sql +++ b/src/test/regress/sql/pg15.sql @@ -255,7 +255,7 @@ SELECT create_distributed_table('tbl2', 'x'); MERGE INTO tbl1 USING tbl2 ON (true) WHEN MATCHED THEN DELETE; --- also, not inside subqueries & ctes +-- also, inside subqueries & ctes WITH targq AS ( SELECT * FROM tbl2 ) diff --git a/src/test/regress/sql/pgmerge.sql b/src/test/regress/sql/pgmerge.sql index 86dc15040..ab1f4a40d 100644 --- a/src/test/regress/sql/pgmerge.sql +++ b/src/test/regress/sql/pgmerge.sql @@ -19,6 +19,10 @@ SET citus.use_citus_managed_tables to true; SET citus.next_shard_id TO 4001000; +SET client_min_messages = warning; +SELECT 1 FROM master_add_node('localhost', :master_port, groupid => 0); +RESET client_min_messages; + CREATE USER regress_merge_privs; CREATE USER regress_merge_no_privs; DROP TABLE IF EXISTS target; From f667f14029d64ab5fb4148f7a692bd69e85a41c4 Mon Sep 17 00:00:00 2001 From: aykut-bozkurt <51649454+aykut-bozkurt@users.noreply.github.com> Date: Mon, 19 Jun 2023 23:00:18 +0300 Subject: [PATCH 108/118] Rewind tuple store to fix scrollable with hold cursor fetches (#7014) We need to rewind the tuplestorestate's tuple index to get correct results on fetching scrollable with hold cursors. `PersistHoldablePortal` is responsible for persisting out tuplestorestate inside a with hold cursor before commiting a transaction. It rewinds the cursor like below (`ExecutorRewindcalls` calls `rescan`): ```c if (portal->cursorOptions & CURSOR_OPT_SCROLL) { ExecutorRewind(queryDesc); } ``` At the end, it adjusts tuple index for holdStore in the portal properly. ```c if (portal->cursorOptions & CURSOR_OPT_SCROLL) { if (!tuplestore_skiptuples(portal->holdStore, portal->portalPos, true)) elog(ERROR, "unexpected end of tuple stream"); } ``` DESCRIPTION: Fixes incorrect results on fetching scrollable with hold cursors. Fixes https://github.com/citusdata/citus/issues/7010 --- .../distributed/commands/utility_hook.c | 4 +- .../distributed/executor/citus_custom_scan.c | 14 +++- .../expected/multi_utility_statements.out | 70 +++++++++++++++++++ .../regress/sql/multi_utility_statements.sql | 24 +++++++ 4 files changed, 110 insertions(+), 2 deletions(-) diff --git a/src/backend/distributed/commands/utility_hook.c b/src/backend/distributed/commands/utility_hook.c index ad2f5de1b..7cc997fa1 100644 --- a/src/backend/distributed/commands/utility_hook.c +++ b/src/backend/distributed/commands/utility_hook.c @@ -186,7 +186,9 @@ multi_ProcessUtility(PlannedStmt *pstmt, IsA(parsetree, ExecuteStmt) || IsA(parsetree, PrepareStmt) || IsA(parsetree, DiscardStmt) || - IsA(parsetree, DeallocateStmt)) + IsA(parsetree, DeallocateStmt) || + IsA(parsetree, DeclareCursorStmt) || + IsA(parsetree, FetchStmt)) { /* * Skip additional checks for common commands that do not have any diff --git a/src/backend/distributed/executor/citus_custom_scan.c b/src/backend/distributed/executor/citus_custom_scan.c index a2fbb1f59..a2a2ff6cb 100644 --- a/src/backend/distributed/executor/citus_custom_scan.c +++ b/src/backend/distributed/executor/citus_custom_scan.c @@ -820,7 +820,19 @@ CitusEndScan(CustomScanState *node) */ static void CitusReScan(CustomScanState *node) -{ } +{ + if (node->ss.ps.ps_ResultTupleSlot) + { + ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); + } + ExecScanReScan(&node->ss); + + CitusScanState *scanState = (CitusScanState *) node; + if (scanState->tuplestorestate) + { + tuplestore_rescan(scanState->tuplestorestate); + } +} /* diff --git a/src/test/regress/expected/multi_utility_statements.out b/src/test/regress/expected/multi_utility_statements.out index ad97dd267..ccfe3a333 100644 --- a/src/test/regress/expected/multi_utility_statements.out +++ b/src/test/regress/expected/multi_utility_statements.out @@ -254,6 +254,76 @@ FETCH FORWARD 3 FROM holdCursor; 1 | 19 (3 rows) +CLOSE holdCursor; +-- Test DECLARE CURSOR .. WITH HOLD inside transaction block +BEGIN; +DECLARE holdCursor CURSOR WITH HOLD FOR + SELECT * FROM cursor_me WHERE x = 1 ORDER BY y; +FETCH 3 FROM holdCursor; + x | y +--------------------------------------------------------------------- + 1 | 10 + 1 | 11 + 1 | 12 +(3 rows) + +FETCH BACKWARD 3 FROM holdCursor; + x | y +--------------------------------------------------------------------- + 1 | 11 + 1 | 10 +(2 rows) + +FETCH FORWARD 3 FROM holdCursor; + x | y +--------------------------------------------------------------------- + 1 | 10 + 1 | 11 + 1 | 12 +(3 rows) + +COMMIT; +FETCH 3 FROM holdCursor; + x | y +--------------------------------------------------------------------- + 1 | 13 + 1 | 14 + 1 | 15 +(3 rows) + +CLOSE holdCursor; +-- Test DECLARE NO SCROLL CURSOR .. WITH HOLD inside transaction block +BEGIN; +DECLARE holdCursor NO SCROLL CURSOR WITH HOLD FOR + SELECT * FROM cursor_me WHERE x = 1 ORDER BY y; +FETCH 3 FROM holdCursor; + x | y +--------------------------------------------------------------------- + 1 | 10 + 1 | 11 + 1 | 12 +(3 rows) + +FETCH FORWARD 3 FROM holdCursor; + x | y +--------------------------------------------------------------------- + 1 | 13 + 1 | 14 + 1 | 15 +(3 rows) + +COMMIT; +FETCH 3 FROM holdCursor; + x | y +--------------------------------------------------------------------- + 1 | 16 + 1 | 17 + 1 | 18 +(3 rows) + +FETCH BACKWARD 3 FROM holdCursor; +ERROR: cursor can only scan forward +HINT: Declare it with SCROLL option to enable backward scan. CLOSE holdCursor; -- Test DECLARE CURSOR .. WITH HOLD with parameter CREATE OR REPLACE FUNCTION declares_cursor(p int) diff --git a/src/test/regress/sql/multi_utility_statements.sql b/src/test/regress/sql/multi_utility_statements.sql index 36f1bf876..bec722aef 100644 --- a/src/test/regress/sql/multi_utility_statements.sql +++ b/src/test/regress/sql/multi_utility_statements.sql @@ -137,6 +137,30 @@ FETCH FORWARD 3 FROM holdCursor; CLOSE holdCursor; +-- Test DECLARE CURSOR .. WITH HOLD inside transaction block +BEGIN; +DECLARE holdCursor CURSOR WITH HOLD FOR + SELECT * FROM cursor_me WHERE x = 1 ORDER BY y; +FETCH 3 FROM holdCursor; +FETCH BACKWARD 3 FROM holdCursor; +FETCH FORWARD 3 FROM holdCursor; +COMMIT; + +FETCH 3 FROM holdCursor; +CLOSE holdCursor; + +-- Test DECLARE NO SCROLL CURSOR .. WITH HOLD inside transaction block +BEGIN; +DECLARE holdCursor NO SCROLL CURSOR WITH HOLD FOR + SELECT * FROM cursor_me WHERE x = 1 ORDER BY y; +FETCH 3 FROM holdCursor; +FETCH FORWARD 3 FROM holdCursor; +COMMIT; + +FETCH 3 FROM holdCursor; +FETCH BACKWARD 3 FROM holdCursor; +CLOSE holdCursor; + -- Test DECLARE CURSOR .. WITH HOLD with parameter CREATE OR REPLACE FUNCTION declares_cursor(p int) RETURNS void AS $$ From 1bb667ce6e80c50d812a4f68e6a6137facf73d0b Mon Sep 17 00:00:00 2001 From: aykut-bozkurt <51649454+aykut-bozkurt@users.noreply.github.com> Date: Tue, 20 Jun 2023 22:05:17 +0300 Subject: [PATCH 109/118] Fix create schema authorization bug (#7015) Fixes a bug related to `CREATE SCHEMA AUTHORIZATION ` for single shard tables. We should properly fetch schema name from role specification if schema name is not given. --- src/backend/distributed/commands/schema.c | 16 ++++++++++---- .../expected/schema_based_sharding.out | 21 +++++++++++++++++-- .../regress/sql/schema_based_sharding.sql | 16 ++++++++++++-- 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/backend/distributed/commands/schema.c b/src/backend/distributed/commands/schema.c index c0affce58..c16b87fa2 100644 --- a/src/backend/distributed/commands/schema.c +++ b/src/backend/distributed/commands/schema.c @@ -68,6 +68,16 @@ PostprocessCreateSchemaStmt(Node *node, const char *queryString) EnsureSequentialMode(OBJECT_SCHEMA); + bool missingOk = createSchemaStmt->if_not_exists; + List *schemaAdressList = CreateSchemaStmtObjectAddress(node, missingOk, true); + Assert(list_length(schemaAdressList) == 1); + ObjectAddress *schemaAdress = linitial(schemaAdressList); + Oid schemaId = schemaAdress->objectId; + if (!OidIsValid(schemaId)) + { + return NIL; + } + /* to prevent recursion with mx we disable ddl propagation */ List *commands = list_make1(DISABLE_DDL_PROPAGATION); @@ -78,7 +88,8 @@ PostprocessCreateSchemaStmt(Node *node, const char *queryString) commands = list_concat(commands, GetGrantCommandsFromCreateSchemaStmt(node)); - if (ShouldUseSchemaBasedSharding(createSchemaStmt->schemaname)) + char *schemaName = get_namespace_name(schemaId); + if (ShouldUseSchemaBasedSharding(schemaName)) { /* for now, we don't allow creating tenant tables when creating the schema itself */ if (CreateSchemaStmtCreatesTable(createSchemaStmt)) @@ -90,9 +101,6 @@ PostprocessCreateSchemaStmt(Node *node, const char *queryString) "tenant tables."))); } - bool missingOk = false; - Oid schemaId = get_namespace_oid(createSchemaStmt->schemaname, missingOk); - /* * Register the tenant schema on the coordinator and save the command * to register it on the workers. diff --git a/src/test/regress/expected/schema_based_sharding.out b/src/test/regress/expected/schema_based_sharding.out index b041708cf..ad456ad8e 100644 --- a/src/test/regress/expected/schema_based_sharding.out +++ b/src/test/regress/expected/schema_based_sharding.out @@ -1675,9 +1675,26 @@ FROM public.citus_schemas WHERE schema_name::text LIKE 'citus\_sch_' ORDER BY sc \c - - - :master_port SET search_path TO regular_schema; +-- test we handle create schema with authorization properly for distributed schema +SET citus.enable_schema_based_sharding TO ON; +CREATE ROLE authschema; +CREATE SCHEMA AUTHORIZATION authschema; +SET citus.enable_schema_based_sharding TO OFF; +SELECT result FROM run_command_on_all_nodes($$ + SELECT COUNT(*)=1 + FROM pg_dist_schema + WHERE schemaid::regnamespace::text = 'authschema'; +$$); + result +--------------------------------------------------------------------- + t + t + t +(3 rows) + SET client_min_messages TO WARNING; -DROP SCHEMA regular_schema, tenant_3, tenant_5, tenant_7, tenant_6, type_sch, citus_sch1, citus_sch2, citus_empty_sch1, citus_empty_sch2 CASCADE; -DROP ROLE citus_schema_role, citus_schema_nonpri; +DROP SCHEMA regular_schema, tenant_3, tenant_5, tenant_7, tenant_6, type_sch, citus_sch1, citus_sch2, citus_empty_sch1, citus_empty_sch2, authschema CASCADE; +DROP ROLE citus_schema_role, citus_schema_nonpri, authschema; SELECT citus_remove_node('localhost', :master_port); citus_remove_node --------------------------------------------------------------------- diff --git a/src/test/regress/sql/schema_based_sharding.sql b/src/test/regress/sql/schema_based_sharding.sql index 7b16ba904..1e5208332 100644 --- a/src/test/regress/sql/schema_based_sharding.sql +++ b/src/test/regress/sql/schema_based_sharding.sql @@ -1141,8 +1141,20 @@ FROM public.citus_schemas WHERE schema_name::text LIKE 'citus\_sch_' ORDER BY sc \c - - - :master_port SET search_path TO regular_schema; +-- test we handle create schema with authorization properly for distributed schema +SET citus.enable_schema_based_sharding TO ON; +CREATE ROLE authschema; +CREATE SCHEMA AUTHORIZATION authschema; +SET citus.enable_schema_based_sharding TO OFF; + +SELECT result FROM run_command_on_all_nodes($$ + SELECT COUNT(*)=1 + FROM pg_dist_schema + WHERE schemaid::regnamespace::text = 'authschema'; +$$); + SET client_min_messages TO WARNING; -DROP SCHEMA regular_schema, tenant_3, tenant_5, tenant_7, tenant_6, type_sch, citus_sch1, citus_sch2, citus_empty_sch1, citus_empty_sch2 CASCADE; -DROP ROLE citus_schema_role, citus_schema_nonpri; +DROP SCHEMA regular_schema, tenant_3, tenant_5, tenant_7, tenant_6, type_sch, citus_sch1, citus_sch2, citus_empty_sch1, citus_empty_sch2, authschema CASCADE; +DROP ROLE citus_schema_role, citus_schema_nonpri, authschema; SELECT citus_remove_node('localhost', :master_port); From 69af3e8509d346024bfe346196c45277aa0a409c Mon Sep 17 00:00:00 2001 From: Naisila Puka <37271756+naisila@users.noreply.github.com> Date: Wed, 21 Jun 2023 14:18:23 +0300 Subject: [PATCH 110/118] Drop PG13 Support Phase 2 - Remove PG13 specific paths/tests (#7007) This commit is the second and last phase of dropping PG13 support. It consists of the following: - Removes all PG_VERSION_13 & PG_VERSION_14 from codepaths - Removes pg_version_compat entries and columnar_version_compat entries specific for PG13 - Removes alternative pg13 test outputs - Removes PG13 normalize lines and fix the test outputs based on that It is a continuation of https://github.com/citusdata/citus/commit/5bf163a27d42b0813110fe05452903dfa6c3db43 --- src/backend/columnar/columnar_debug.c | 2 +- src/backend/columnar/columnar_metadata.c | 19 +- src/backend/columnar/columnar_tableam.c | 35 +- .../commands/citus_global_signal.c | 12 - src/backend/distributed/commands/cluster.c | 8 - .../distributed/commands/dependencies.c | 12 +- .../commands/distribute_object_ops.c | 2 +- src/backend/distributed/commands/domain.c | 4 - src/backend/distributed/commands/function.c | 6 +- .../distributed/commands/local_multi_copy.c | 8 +- src/backend/distributed/commands/multi_copy.c | 166 +- src/backend/distributed/commands/sequence.c | 12 +- src/backend/distributed/commands/table.c | 37 +- src/backend/distributed/commands/type.c | 4 +- .../distributed/commands/utility_hook.c | 84 +- src/backend/distributed/commands/vacuum.c | 18 +- src/backend/distributed/commands/view.c | 2 +- .../connection/connection_management.c | 27 - .../distributed/deparser/citus_ruleutils.c | 21 +- .../deparser/deparse_sequence_stmts.c | 8 +- .../deparser/deparse_statistics_stmts.c | 31 - .../deparser/deparse_table_stmts.c | 4 +- .../distributed/deparser/deparse_type_stmts.c | 4 +- .../distributed/deparser/qualify_domain.c | 4 - .../deparser/qualify_sequence_stmt.c | 4 +- .../distributed/deparser/qualify_type_stmt.c | 2 +- .../distributed/deparser/ruleutils_13.c | 8131 ----------------- .../distributed/executor/adaptive_executor.c | 4 - .../distributed/executor/multi_executor.c | 6 +- .../distributed/executor/query_stats.c | 4 - src/backend/distributed/metadata/dependency.c | 11 +- src/backend/distributed/metadata/distobject.c | 8 +- .../distributed/metadata/metadata_sync.c | 8 +- .../distributed/metadata/metadata_utility.c | 4 - .../operations/worker_node_manager.c | 4 - .../operations/worker_shard_copy.c | 14 +- .../planner/insert_select_planner.c | 4 +- .../distributed/planner/multi_explain.c | 10 +- .../planner/multi_logical_optimizer.c | 13 +- .../planner/multi_router_planner.c | 11 - .../relation_restriction_equivalence.c | 4 +- .../replication/multi_logical_replication.c | 2 +- src/backend/distributed/shared_library_init.c | 4 - src/backend/distributed/test/fake_am.c | 19 - src/backend/distributed/test/xact_stats.c | 4 +- .../distributed/transaction/backend_data.c | 4 - .../distributed/transaction/lock_graph.c | 2 +- .../distributed/utils/background_jobs.c | 2 - src/backend/distributed/utils/citus_clauses.c | 6 +- src/backend/distributed/utils/enable_ssl.c | 5 - .../distributed/utils/function_utils.c | 2 +- src/backend/distributed/utils/listutils.c | 2 - src/backend/distributed/utils/log_utils.c | 2 - .../utils/multi_partitioning_utils.c | 8 +- .../columnar/columnar_version_compat.h | 23 - src/include/distributed/commands/multi_copy.h | 5 - .../distributed/commands/utility_hook.h | 2 - .../distributed/connection_management.h | 3 - .../distributed/pg_version_constants.h | 1 - src/include/pg_version_compat.h | 64 +- src/test/regress/bin/normalize.sed | 38 +- src/test/regress/citus_tests/run_test.py | 1 + .../background_task_queue_monitor.out | 8 +- src/test/regress/expected/cpu_priority.out | 43 +- .../regress/expected/generated_identity.out | 8 - .../regress/expected/generated_identity_0.out | 431 - .../expected/grant_on_schema_propagation.out | 4 +- .../grant_on_schema_propagation_0.out | 4 +- .../isolation_master_update_node_1.out | 66 - .../expected/local_shard_execution.out | 16 +- .../expected/local_shard_execution_0.out | 16 +- .../local_shard_execution_replicated.out | 16 +- .../local_shard_execution_replicated_0.out | 16 +- .../multi_alter_table_row_level_security.out | 3 +- src/test/regress/expected/multi_explain.out | 48 + .../regress/expected/multi_metadata_sync.out | 2 +- .../expected/multi_metadata_sync_0.out | 2 +- .../regress/expected/multi_mx_explain.out | 96 + src/test/regress/expected/pg14.out | 7 - src/test/regress/expected/pg14_0.out | 6 - src/test/regress/expected/sql_procedure.out | 4 +- src/test/regress/expected/stat_statements.out | 15 - src/test/regress/expected/tableam.out | 2 +- .../regress/expected/window_functions.out | 2 - .../regress/expected/window_functions_0.out | 1657 ---- .../spec/isolation_master_update_node.spec | 3 +- src/test/regress/sql/cpu_priority.sql | 3 - src/test/regress/sql/generated_identity.sql | 4 - .../sql/grant_on_schema_propagation.sql | 5 +- src/test/regress/sql/multi_metadata_sync.sql | 2 +- src/test/regress/sql/pg14.sql | 8 - src/test/regress/sql/stat_statements.sql | 15 - src/test/regress/sql/window_functions.sql | 2 - 93 files changed, 348 insertions(+), 11137 deletions(-) delete mode 100644 src/backend/distributed/deparser/ruleutils_13.c delete mode 100644 src/test/regress/expected/generated_identity_0.out delete mode 100644 src/test/regress/expected/isolation_master_update_node_1.out delete mode 100644 src/test/regress/expected/pg14_0.out delete mode 100644 src/test/regress/expected/window_functions_0.out diff --git a/src/backend/columnar/columnar_debug.c b/src/backend/columnar/columnar_debug.c index e6b19f768..cbb0d554f 100644 --- a/src/backend/columnar/columnar_debug.c +++ b/src/backend/columnar/columnar_debug.c @@ -159,5 +159,5 @@ MemoryContextTotals(MemoryContext context, MemoryContextCounters *counters) MemoryContextTotals(child, counters); } - context->methods->stats_compat(context, NULL, NULL, counters, true); + context->methods->stats(context, NULL, NULL, counters, true); } diff --git a/src/backend/columnar/columnar_metadata.c b/src/backend/columnar/columnar_metadata.c index 015df65eb..7fbc96419 100644 --- a/src/backend/columnar/columnar_metadata.c +++ b/src/backend/columnar/columnar_metadata.c @@ -1623,12 +1623,8 @@ StartModifyRelation(Relation rel) { EState *estate = create_estate_for_relation(rel); -#if PG_VERSION_NUM >= PG_VERSION_14 ResultRelInfo *resultRelInfo = makeNode(ResultRelInfo); InitResultRelInfo(resultRelInfo, rel, 1, NULL, 0); -#else - ResultRelInfo *resultRelInfo = estate->es_result_relation_info; -#endif /* ExecSimpleRelationInsert, ... require caller to open indexes */ ExecOpenIndices(resultRelInfo, false); @@ -1658,7 +1654,7 @@ InsertTupleAndEnforceConstraints(ModifyState *state, Datum *values, bool *nulls) ExecStoreHeapTuple(tuple, slot, false); /* use ExecSimpleRelationInsert to enforce constraints */ - ExecSimpleRelationInsert_compat(state->resultRelInfo, state->estate, slot); + ExecSimpleRelationInsert(state->resultRelInfo, state->estate, slot); } @@ -1689,12 +1685,8 @@ FinishModifyRelation(ModifyState *state) ExecCloseIndices(state->resultRelInfo); AfterTriggerEndQuery(state->estate); -#if PG_VERSION_NUM >= PG_VERSION_14 ExecCloseResultRelations(state->estate); ExecCloseRangeTableRelations(state->estate); -#else - ExecCleanUpTriggerState(state->estate); -#endif ExecResetTupleTable(state->estate->es_tupleTable, false); FreeExecutorState(state->estate); @@ -1723,15 +1715,6 @@ create_estate_for_relation(Relation rel) rte->rellockmode = AccessShareLock; ExecInitRangeTable(estate, list_make1(rte)); -#if PG_VERSION_NUM < PG_VERSION_14 - ResultRelInfo *resultRelInfo = makeNode(ResultRelInfo); - InitResultRelInfo(resultRelInfo, rel, 1, NULL, 0); - - estate->es_result_relations = resultRelInfo; - estate->es_num_result_relations = 1; - estate->es_result_relation_info = resultRelInfo; -#endif - estate->es_output_cid = GetCurrentCommandId(true); /* Prepare to catch AFTER triggers. */ diff --git a/src/backend/columnar/columnar_tableam.c b/src/backend/columnar/columnar_tableam.c index 9fd6ba62d..4a08feb54 100644 --- a/src/backend/columnar/columnar_tableam.c +++ b/src/backend/columnar/columnar_tableam.c @@ -115,9 +115,7 @@ static RangeVar * ColumnarProcessAlterTable(AlterTableStmt *alterTableStmt, List **columnarOptions); static void ColumnarProcessUtility(PlannedStmt *pstmt, const char *queryString, -#if PG_VERSION_NUM >= PG_VERSION_14 bool readOnlyTree, -#endif ProcessUtilityContext context, ParamListInfo params, struct QueryEnvironment *queryEnv, @@ -665,7 +663,6 @@ columnar_tuple_satisfies_snapshot(Relation rel, TupleTableSlot *slot, } -#if PG_VERSION_NUM >= PG_VERSION_14 static TransactionId columnar_index_delete_tuples(Relation rel, TM_IndexDeleteOp *delstate) @@ -714,19 +711,6 @@ columnar_index_delete_tuples(Relation rel, } -#else -static TransactionId -columnar_compute_xid_horizon_for_tuples(Relation rel, - ItemPointerData *tids, - int nitems) -{ - elog(ERROR, "columnar_compute_xid_horizon_for_tuples not implemented"); -} - - -#endif - - static void columnar_tuple_insert(Relation relation, TupleTableSlot *slot, CommandId cid, int options, BulkInsertState bistate) @@ -1484,8 +1468,7 @@ columnar_index_build_range_scan(Relation columnarRelation, if (!IsBootstrapProcessingMode() && !indexInfo->ii_Concurrent) { /* ignore lazy VACUUM's */ - OldestXmin = GetOldestNonRemovableTransactionId_compat(columnarRelation, - PROCARRAY_FLAGS_VACUUM); + OldestXmin = GetOldestNonRemovableTransactionId(columnarRelation); } Snapshot snapshot = { 0 }; @@ -1813,8 +1796,8 @@ ColumnarReadMissingRowsIntoIndex(TableScanDesc scan, Relation indexRelation, Relation columnarRelation = scan->rs_rd; IndexUniqueCheck indexUniqueCheck = indexInfo->ii_Unique ? UNIQUE_CHECK_YES : UNIQUE_CHECK_NO; - index_insert_compat(indexRelation, indexValues, indexNulls, columnarItemPointer, - columnarRelation, indexUniqueCheck, false, indexInfo); + index_insert(indexRelation, indexValues, indexNulls, columnarItemPointer, + columnarRelation, indexUniqueCheck, false, indexInfo); validateIndexState->tups_inserted += 1; } @@ -2240,21 +2223,17 @@ ColumnarProcessAlterTable(AlterTableStmt *alterTableStmt, List **columnarOptions static void ColumnarProcessUtility(PlannedStmt *pstmt, const char *queryString, -#if PG_VERSION_NUM >= PG_VERSION_14 bool readOnlyTree, -#endif ProcessUtilityContext context, ParamListInfo params, struct QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *completionTag) { -#if PG_VERSION_NUM >= PG_VERSION_14 if (readOnlyTree) { pstmt = copyObject(pstmt); } -#endif Node *parsetree = pstmt->utilityStmt; @@ -2371,8 +2350,8 @@ ColumnarProcessUtility(PlannedStmt *pstmt, CheckCitusColumnarAlterExtensionStmt(parsetree); } - PrevProcessUtilityHook_compat(pstmt, queryString, false, context, - params, queryEnv, dest, completionTag); + PrevProcessUtilityHook(pstmt, queryString, false, context, + params, queryEnv, dest, completionTag); if (columnarOptions != NIL) { @@ -2500,11 +2479,7 @@ static const TableAmRoutine columnar_am_methods = { .tuple_get_latest_tid = columnar_get_latest_tid, .tuple_tid_valid = columnar_tuple_tid_valid, .tuple_satisfies_snapshot = columnar_tuple_satisfies_snapshot, -#if PG_VERSION_NUM >= PG_VERSION_14 .index_delete_tuples = columnar_index_delete_tuples, -#else - .compute_xid_horizon_for_tuples = columnar_compute_xid_horizon_for_tuples, -#endif .tuple_insert = columnar_tuple_insert, .tuple_insert_speculative = columnar_tuple_insert_speculative, diff --git a/src/backend/distributed/commands/citus_global_signal.c b/src/backend/distributed/commands/citus_global_signal.c index 05b210ee2..8183d6673 100644 --- a/src/backend/distributed/commands/citus_global_signal.c +++ b/src/backend/distributed/commands/citus_global_signal.c @@ -81,13 +81,6 @@ CitusSignalBackend(uint64 globalPID, uint64 timeout, int sig) { Assert((sig == SIGINT) || (sig == SIGTERM)); -#if PG_VERSION_NUM < PG_VERSION_14 - if (timeout != 0) - { - elog(ERROR, "timeout parameter is only supported on Postgres 14 or later"); - } -#endif - bool missingOk = false; int nodeId = ExtractNodeIdFromGlobalPID(globalPID, missingOk); int processId = ExtractProcessIdFromGlobalPID(globalPID); @@ -102,14 +95,9 @@ CitusSignalBackend(uint64 globalPID, uint64 timeout, int sig) } else { -#if PG_VERSION_NUM >= PG_VERSION_14 appendStringInfo(cancelQuery, "SELECT pg_terminate_backend(%d::integer, %lu::bigint)", processId, timeout); -#else - appendStringInfo(cancelQuery, "SELECT pg_terminate_backend(%d::integer)", - processId); -#endif } int connectionFlags = 0; diff --git a/src/backend/distributed/commands/cluster.c b/src/backend/distributed/commands/cluster.c index 4cffbaf51..92fcb3ec6 100644 --- a/src/backend/distributed/commands/cluster.c +++ b/src/backend/distributed/commands/cluster.c @@ -114,13 +114,6 @@ PreprocessClusterStmt(Node *node, const char *clusterCommand, static bool IsClusterStmtVerbose_compat(ClusterStmt *clusterStmt) { -#if PG_VERSION_NUM < PG_VERSION_14 - if (clusterStmt->options & CLUOPT_VERBOSE) - { - return true; - } - return false; -#else DefElem *opt = NULL; foreach_ptr(opt, clusterStmt->params) { @@ -130,5 +123,4 @@ IsClusterStmtVerbose_compat(ClusterStmt *clusterStmt) } } return false; -#endif } diff --git a/src/backend/distributed/commands/dependencies.c b/src/backend/distributed/commands/dependencies.c index 0f736df7a..ceec83324 100644 --- a/src/backend/distributed/commands/dependencies.c +++ b/src/backend/distributed/commands/dependencies.c @@ -214,13 +214,7 @@ DeferErrorIfCircularDependencyExists(const ObjectAddress *objectAddress) dependency->objectId == objectAddress->objectId && dependency->objectSubId == objectAddress->objectSubId) { - char *objectDescription = NULL; - - #if PG_VERSION_NUM >= PG_VERSION_14 - objectDescription = getObjectDescription(objectAddress, false); - #else - objectDescription = getObjectDescription(objectAddress); - #endif + char *objectDescription = getObjectDescription(objectAddress, false); StringInfo detailInfo = makeStringInfo(); appendStringInfo(detailInfo, "\"%s\" circularly depends itself, resolve " @@ -529,9 +523,9 @@ GetDependencyCreateDDLCommands(const ObjectAddress *dependency) */ Assert(false); ereport(ERROR, (errmsg("unsupported object %s for distribution by citus", - getObjectTypeDescription_compat(dependency, + getObjectTypeDescription(dependency, - /* missingOk: */ false)), + /* missingOk: */ false)), errdetail( "citus tries to recreate an unsupported object on its workers"), errhint("please report a bug as this should not be happening"))); diff --git a/src/backend/distributed/commands/distribute_object_ops.c b/src/backend/distributed/commands/distribute_object_ops.c index b417e416e..3442b07f2 100644 --- a/src/backend/distributed/commands/distribute_object_ops.c +++ b/src/backend/distributed/commands/distribute_object_ops.c @@ -1531,7 +1531,7 @@ GetDistributeObjectOps(Node *node) case T_AlterTableStmt: { AlterTableStmt *stmt = castNode(AlterTableStmt, node); - switch (AlterTableStmtObjType_compat(stmt)) + switch (stmt->objtype) { case OBJECT_TYPE: { diff --git a/src/backend/distributed/commands/domain.c b/src/backend/distributed/commands/domain.c index f14157278..392cbd6e2 100644 --- a/src/backend/distributed/commands/domain.c +++ b/src/backend/distributed/commands/domain.c @@ -206,11 +206,7 @@ MakeCollateClauseFromOid(Oid collationOid) List *objName = NIL; List *objArgs = NIL; - #if PG_VERSION_NUM >= PG_VERSION_14 getObjectIdentityParts(&collateAddress, &objName, &objArgs, false); - #else - getObjectIdentityParts(&collateAddress, &objName, &objArgs); - #endif char *name = NULL; foreach_ptr(name, objName) diff --git a/src/backend/distributed/commands/function.c b/src/backend/distributed/commands/function.c index c992bc4fb..9f579f5dc 100644 --- a/src/backend/distributed/commands/function.c +++ b/src/backend/distributed/commands/function.c @@ -1641,7 +1641,7 @@ PreprocessAlterFunctionDependsStmt(Node *node, const char *queryString, * workers */ const char *functionName = - getObjectIdentity_compat(address, /* missingOk: */ false); + getObjectIdentity(address, /* missingOk: */ false); ereport(ERROR, (errmsg("distrtibuted functions are not allowed to depend on an " "extension"), errdetail("Function \"%s\" is already distributed. Functions from " @@ -1811,8 +1811,8 @@ GenerateBackupNameForProcCollision(const ObjectAddress *address) List *newProcName = list_make2(namespace, makeString(newName)); /* don't need to rename if the input arguments don't match */ - FuncCandidateList clist = FuncnameGetCandidates_compat(newProcName, numargs, NIL, - false, false, false, true); + FuncCandidateList clist = FuncnameGetCandidates(newProcName, numargs, NIL, + false, false, false, true); for (; clist; clist = clist->next) { if (memcmp(clist->args, argtypes, sizeof(Oid) * numargs) == 0) diff --git a/src/backend/distributed/commands/local_multi_copy.c b/src/backend/distributed/commands/local_multi_copy.c index 5cf01baf4..7dbf0ae36 100644 --- a/src/backend/distributed/commands/local_multi_copy.c +++ b/src/backend/distributed/commands/local_multi_copy.c @@ -216,10 +216,10 @@ DoLocalCopy(StringInfo buffer, Oid relationId, int64 shardId, CopyStmt *copyStat ParseState *pState = make_parsestate(NULL); (void) addRangeTableEntryForRelation(pState, shard, AccessShareLock, NULL, false, false); - CopyFromState cstate = BeginCopyFrom_compat(pState, shard, NULL, NULL, false, - ReadFromLocalBufferCallback, - copyStatement->attlist, - copyStatement->options); + CopyFromState cstate = BeginCopyFrom(pState, shard, NULL, NULL, false, + ReadFromLocalBufferCallback, + copyStatement->attlist, + copyStatement->options); CopyFrom(cstate); EndCopyFrom(cstate); diff --git a/src/backend/distributed/commands/multi_copy.c b/src/backend/distributed/commands/multi_copy.c index f8e6378d4..5d7c279a6 100644 --- a/src/backend/distributed/commands/multi_copy.c +++ b/src/backend/distributed/commands/multi_copy.c @@ -258,9 +258,6 @@ static CopyCoercionData * ColumnCoercionPaths(TupleDesc destTupleDescriptor, Oid *finalColumnTypeArray); static FmgrInfo * TypeOutputFunctions(uint32 columnCount, Oid *typeIdArray, bool binaryFormat); -#if PG_VERSION_NUM < PG_VERSION_14 -static List * CopyGetAttnums(TupleDesc tupDesc, Relation rel, List *attnamelist); -#endif static bool CopyStatementHasFormat(CopyStmt *copyStatement, char *formatName); static void CitusCopyFrom(CopyStmt *copyStatement, QueryCompletion *completionTag); static void EnsureCopyCanRunOnRelation(Oid relationId); @@ -609,14 +606,14 @@ CopyToExistingShards(CopyStmt *copyStatement, QueryCompletion *completionTag) } /* initialize copy state to read from COPY data source */ - CopyFromState copyState = BeginCopyFrom_compat(NULL, - copiedDistributedRelation, - NULL, - copyStatement->filename, - copyStatement->is_program, - NULL, - copyStatement->attlist, - copyStatement->options); + CopyFromState copyState = BeginCopyFrom(NULL, + copiedDistributedRelation, + NULL, + copyStatement->filename, + copyStatement->is_program, + NULL, + copyStatement->attlist, + copyStatement->options); /* set up callback to identify error line number */ errorCallback.callback = CopyFromErrorCallback; @@ -648,9 +645,7 @@ CopyToExistingShards(CopyStmt *copyStatement, QueryCompletion *completionTag) ++processedRowCount; -#if PG_VERSION_NUM >= PG_VERSION_14 pgstat_progress_update_param(PROGRESS_COPY_TUPLES_PROCESSED, processedRowCount); -#endif } EndCopyFrom(copyState); @@ -890,28 +885,8 @@ CanUseBinaryCopyFormatForType(Oid typeId) HeapTuple typeTup = typeidType(typeId); Form_pg_type type = (Form_pg_type) GETSTRUCT(typeTup); Oid elementType = type->typelem; -#if PG_VERSION_NUM < PG_VERSION_14 - char typeCategory = type->typcategory; -#endif ReleaseSysCache(typeTup); -#if PG_VERSION_NUM < PG_VERSION_14 - - /* - * In PG versions before PG14 the array_recv function would error out more - * than necessary. - * - * It errors out when the element type its oids don't match with the oid in - * the received data. This happens pretty much always for non built in - * types, because their oids differ between postgres intallations. So we - * skip binary encoding when the element type is a non built in type. - */ - if (typeCategory == TYPCATEGORY_ARRAY && elementType >= FirstNormalObjectId) - { - return false; - } -#endif - /* * Any type that is a wrapper around an element type (e.g. arrays and * ranges) require the element type to also has support for binary @@ -1682,20 +1657,6 @@ AppendCopyBinaryFooters(CopyOutState footerOutputState) static void SendCopyBegin(CopyOutState cstate) { -#if PG_VERSION_NUM < PG_VERSION_14 - if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3) { - /* old way */ - if (cstate->binary) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("COPY BINARY is not supported to stdout or from stdin"))); - pq_putemptymessage('H'); - /* grottiness needed for old COPY OUT protocol */ - pq_startcopyout(); - cstate->copy_dest = COPY_OLD_FE; - return; - } -#endif StringInfoData buf; int natts = list_length(cstate->attnumlist); int16 format = (cstate->binary ? 1 : 0); @@ -1715,16 +1676,6 @@ SendCopyBegin(CopyOutState cstate) static void SendCopyEnd(CopyOutState cstate) { -#if PG_VERSION_NUM < PG_VERSION_14 - if (cstate->copy_dest != COPY_NEW_FE) - { - CopySendData(cstate, "\\.", 2); - /* Need to flush out the trailer (this also appends a newline) */ - CopySendEndOfRow(cstate, true); - pq_endcopyout(false); - return; - } -#endif /* Shouldn't have any unsent data */ Assert(cstate->fe_msgbuf->len == 0); /* Send Copy Done message */ @@ -1782,21 +1733,6 @@ CopySendEndOfRow(CopyOutState cstate, bool includeEndOfLine) switch (cstate->copy_dest) { -#if PG_VERSION_NUM < PG_VERSION_14 - case COPY_OLD_FE: - /* The FE/BE protocol uses \n as newline for all platforms */ - if (!cstate->binary && includeEndOfLine) - CopySendChar(cstate, '\n'); - - if (pq_putbytes(fe_msgbuf->data, fe_msgbuf->len)) - { - /* no hope of recovering connection sync, so FATAL */ - ereport(FATAL, - (errcode(ERRCODE_CONNECTION_FAILURE), - errmsg("connection lost during COPY to stdout"))); - } - break; -#endif case COPY_FRONTEND: /* The FE/BE protocol uses \n as newline for all platforms */ if (!cstate->binary && includeEndOfLine) @@ -3256,92 +3192,6 @@ CreateRangeTable(Relation rel, AclMode requiredAccess) } -#if PG_VERSION_NUM < PG_VERSION_14 - -/* Helper for CheckCopyPermissions(), copied from postgres */ -static List * -CopyGetAttnums(TupleDesc tupDesc, Relation rel, List *attnamelist) -{ - /* *INDENT-OFF* */ - List *attnums = NIL; - - if (attnamelist == NIL) - { - /* Generate default column list */ - int attr_count = tupDesc->natts; - int i; - - for (i = 0; i < attr_count; i++) - { - if (TupleDescAttr(tupDesc, i)->attisdropped) - continue; - if (TupleDescAttr(tupDesc, i)->attgenerated) - continue; - attnums = lappend_int(attnums, i + 1); - } - } - else - { - /* Validate the user-supplied list and extract attnums */ - ListCell *l; - - foreach(l, attnamelist) - { - char *name = strVal(lfirst(l)); - int attnum; - int i; - - /* Lookup column name */ - attnum = InvalidAttrNumber; - for (i = 0; i < tupDesc->natts; i++) - { - Form_pg_attribute att = TupleDescAttr(tupDesc, i); - - if (att->attisdropped) - continue; - if (namestrcmp(&(att->attname), name) == 0) - { - if (att->attgenerated) - ereport(ERROR, - (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("column \"%s\" is a generated column", - name), - errdetail("Generated columns cannot be used in COPY."))); - attnum = att->attnum; - break; - } - } - if (attnum == InvalidAttrNumber) - { - if (rel != NULL) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), - errmsg("column \"%s\" of relation \"%s\" does not exist", - name, RelationGetRelationName(rel)))); - else - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), - errmsg("column \"%s\" does not exist", - name))); - } - /* Check for duplicates */ - if (list_member_int(attnums, attnum)) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_COLUMN), - errmsg("column \"%s\" specified more than once", - name))); - attnums = lappend_int(attnums, attnum); - } - } - - return attnums; - /* *INDENT-ON* */ -} - - -#endif - - /* * CreateConnectionStateHash constructs a hash table which maps from socket * number to CopyConnectionState, passing the provided MemoryContext to diff --git a/src/backend/distributed/commands/sequence.c b/src/backend/distributed/commands/sequence.c index 9289dcd58..9ff586c8c 100644 --- a/src/backend/distributed/commands/sequence.c +++ b/src/backend/distributed/commands/sequence.c @@ -668,7 +668,7 @@ PreprocessAlterSequenceOwnerStmt(Node *node, const char *queryString, ProcessUtilityContext processUtilityContext) { AlterTableStmt *stmt = castNode(AlterTableStmt, node); - Assert(AlterTableStmtObjType_compat(stmt) == OBJECT_SEQUENCE); + Assert(stmt->objtype == OBJECT_SEQUENCE); List *sequenceAddresses = GetObjectAddressListFromParseTree((Node *) stmt, false, false); @@ -701,7 +701,7 @@ List * AlterSequenceOwnerStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess) { AlterTableStmt *stmt = castNode(AlterTableStmt, node); - Assert(AlterTableStmtObjType_compat(stmt) == OBJECT_SEQUENCE); + Assert(stmt->objtype == OBJECT_SEQUENCE); RangeVar *sequence = stmt->relation; Oid seqOid = RangeVarGetRelid(sequence, NoLock, missing_ok); @@ -721,7 +721,7 @@ List * PostprocessAlterSequenceOwnerStmt(Node *node, const char *queryString) { AlterTableStmt *stmt = castNode(AlterTableStmt, node); - Assert(AlterTableStmtObjType_compat(stmt) == OBJECT_SEQUENCE); + Assert(stmt->objtype == OBJECT_SEQUENCE); List *sequenceAddresses = GetObjectAddressListFromParseTree((Node *) stmt, false, true); @@ -755,7 +755,7 @@ PreprocessAlterSequencePersistenceStmt(Node *node, const char *queryString, ProcessUtilityContext processUtilityContext) { AlterTableStmt *stmt = castNode(AlterTableStmt, node); - Assert(AlterTableStmtObjType_compat(stmt) == OBJECT_SEQUENCE); + Assert(stmt->objtype == OBJECT_SEQUENCE); List *sequenceAddresses = GetObjectAddressListFromParseTree((Node *) stmt, false, false); @@ -788,7 +788,7 @@ List * AlterSequencePersistenceStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess) { AlterTableStmt *stmt = castNode(AlterTableStmt, node); - Assert(AlterTableStmtObjType_compat(stmt) == OBJECT_SEQUENCE); + Assert(stmt->objtype == OBJECT_SEQUENCE); RangeVar *sequence = stmt->relation; Oid seqOid = RangeVarGetRelid(sequence, NoLock, missing_ok); @@ -811,7 +811,7 @@ PreprocessSequenceAlterTableStmt(Node *node, const char *queryString, ProcessUtilityContext processUtilityContext) { AlterTableStmt *stmt = castNode(AlterTableStmt, node); - Assert(AlterTableStmtObjType_compat(stmt) == OBJECT_SEQUENCE); + Assert(stmt->objtype == OBJECT_SEQUENCE); ListCell *cmdCell = NULL; foreach(cmdCell, stmt->cmds) diff --git a/src/backend/distributed/commands/table.c b/src/backend/distributed/commands/table.c index 4ea28c71d..174c34946 100644 --- a/src/backend/distributed/commands/table.c +++ b/src/backend/distributed/commands/table.c @@ -1135,7 +1135,7 @@ PreprocessAlterTableStmt(Node *node, const char *alterTableCommand, if (relKind == RELKIND_SEQUENCE) { AlterTableStmt *stmtCopy = copyObject(alterTableStatement); - AlterTableStmtObjType_compat(stmtCopy) = OBJECT_SEQUENCE; + stmtCopy->objtype = OBJECT_SEQUENCE; #if (PG_VERSION_NUM >= PG_VERSION_15) /* @@ -1165,7 +1165,7 @@ PreprocessAlterTableStmt(Node *node, const char *alterTableCommand, * passes through an AlterTableStmt */ AlterTableStmt *stmtCopy = copyObject(alterTableStatement); - AlterTableStmtObjType_compat(stmtCopy) = OBJECT_VIEW; + stmtCopy->objtype = OBJECT_VIEW; return PreprocessAlterViewStmt((Node *) stmtCopy, alterTableCommand, processUtilityContext); } @@ -2521,13 +2521,13 @@ PostprocessAlterTableStmt(AlterTableStmt *alterTableStatement) char relKind = get_rel_relkind(relationId); if (relKind == RELKIND_SEQUENCE) { - AlterTableStmtObjType_compat(alterTableStatement) = OBJECT_SEQUENCE; + alterTableStatement->objtype = OBJECT_SEQUENCE; PostprocessAlterSequenceOwnerStmt((Node *) alterTableStatement, NULL); return; } else if (relKind == RELKIND_VIEW) { - AlterTableStmtObjType_compat(alterTableStatement) = OBJECT_VIEW; + alterTableStatement->objtype = OBJECT_VIEW; PostprocessAlterViewStmt((Node *) alterTableStatement, NULL); return; } @@ -3517,7 +3517,6 @@ ErrorIfUnsupportedAlterTableStmt(AlterTableStmt *alterTableStatement) break; } -#if PG_VERSION_NUM >= PG_VERSION_14 case AT_DetachPartitionFinalize: { ereport(ERROR, (errmsg("ALTER TABLE .. DETACH PARTITION .. FINALIZE " @@ -3525,7 +3524,6 @@ ErrorIfUnsupportedAlterTableStmt(AlterTableStmt *alterTableStatement) break; } -#endif case AT_DetachPartition: { /* we only allow partitioning commands if they are only subcommand */ @@ -3537,7 +3535,7 @@ ErrorIfUnsupportedAlterTableStmt(AlterTableStmt *alterTableStatement) errhint("You can issue each subcommand " "separately."))); } - #if PG_VERSION_NUM >= PG_VERSION_14 + PartitionCmd *partitionCommand = (PartitionCmd *) command->def; if (partitionCommand->concurrent) @@ -3546,7 +3544,6 @@ ErrorIfUnsupportedAlterTableStmt(AlterTableStmt *alterTableStatement) "CONCURRENTLY commands are currently " "unsupported."))); } - #endif break; } @@ -3589,20 +3586,18 @@ ErrorIfUnsupportedAlterTableStmt(AlterTableStmt *alterTableStatement) case AT_NoForceRowSecurity: case AT_ValidateConstraint: case AT_DropConstraint: /* we do the check for invalidation in AlterTableDropsForeignKey */ -#if PG_VERSION_NUM >= PG_VERSION_14 case AT_SetCompression: -#endif - { - /* - * We will not perform any special check for: - * ALTER TABLE .. SET ACCESS METHOD .. - * ALTER TABLE .. ALTER COLUMN .. SET NOT NULL - * ALTER TABLE .. REPLICA IDENTITY .. - * ALTER TABLE .. VALIDATE CONSTRAINT .. - * ALTER TABLE .. ALTER COLUMN .. SET COMPRESSION .. - */ - break; - } + { + /* + * We will not perform any special check for: + * ALTER TABLE .. SET ACCESS METHOD .. + * ALTER TABLE .. ALTER COLUMN .. SET NOT NULL + * ALTER TABLE .. REPLICA IDENTITY .. + * ALTER TABLE .. VALIDATE CONSTRAINT .. + * ALTER TABLE .. ALTER COLUMN .. SET COMPRESSION .. + */ + break; + } case AT_SetRelOptions: /* SET (...) */ case AT_ResetRelOptions: /* RESET (...) */ diff --git a/src/backend/distributed/commands/type.c b/src/backend/distributed/commands/type.c index 3e641fad0..24ca91aeb 100644 --- a/src/backend/distributed/commands/type.c +++ b/src/backend/distributed/commands/type.c @@ -350,7 +350,7 @@ List * AlterTypeStmtObjectAddress(Node *node, bool missing_ok, bool isPostprocess) { AlterTableStmt *stmt = castNode(AlterTableStmt, node); - Assert(AlterTableStmtObjType_compat(stmt) == OBJECT_TYPE); + Assert(stmt->objtype == OBJECT_TYPE); TypeName *typeName = MakeTypeNameFromRangeVar(stmt->relation); Oid typeOid = LookupTypeNameOid(NULL, typeName, missing_ok); @@ -549,7 +549,7 @@ CreateTypeDDLCommandsIdempotent(const ObjectAddress *typeAddress) const char *username = GetUserNameFromId(GetTypeOwner(typeAddress->objectId), false); initStringInfo(&buf); appendStringInfo(&buf, ALTER_TYPE_OWNER_COMMAND, - getObjectIdentity_compat(typeAddress, false), + getObjectIdentity(typeAddress, false), quote_identifier(username)); ddlCommands = lappend(ddlCommands, buf.data); diff --git a/src/backend/distributed/commands/utility_hook.c b/src/backend/distributed/commands/utility_hook.c index 7cc997fa1..888b3dfed 100644 --- a/src/backend/distributed/commands/utility_hook.c +++ b/src/backend/distributed/commands/utility_hook.c @@ -33,9 +33,6 @@ #include "access/attnum.h" #include "access/heapam.h" #include "access/htup_details.h" -#if PG_VERSION_NUM < 140000 -#include "access/xact.h" -#endif #include "catalog/catalog.h" #include "catalog/dependency.h" #include "citus_version.h" @@ -60,9 +57,6 @@ #include "distributed/maintenanced.h" #include "distributed/multi_logical_replication.h" #include "distributed/multi_partitioning_utils.h" -#if PG_VERSION_NUM < 140000 -#include "distributed/metadata_cache.h" -#endif #include "distributed/metadata_sync.h" #include "distributed/metadata/distobject.h" #include "distributed/multi_executor.h" @@ -107,9 +101,7 @@ static void ProcessUtilityInternal(PlannedStmt *pstmt, struct QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *completionTag); -#if PG_VERSION_NUM >= 140000 static void set_indexsafe_procflags(void); -#endif static char * CurrentSearchPath(void); static void IncrementUtilityHookCountersIfNecessary(Node *parsetree); static void PostStandardProcessUtility(Node *parsetree); @@ -131,8 +123,8 @@ ProcessUtilityParseTree(Node *node, const char *queryString, ProcessUtilityConte plannedStmt->commandType = CMD_UTILITY; plannedStmt->utilityStmt = node; - ProcessUtility_compat(plannedStmt, queryString, false, context, params, NULL, dest, - completionTag); + ProcessUtility(plannedStmt, queryString, false, context, params, NULL, dest, + completionTag); } @@ -148,25 +140,19 @@ ProcessUtilityParseTree(Node *node, const char *queryString, ProcessUtilityConte void multi_ProcessUtility(PlannedStmt *pstmt, const char *queryString, -#if PG_VERSION_NUM >= PG_VERSION_14 bool readOnlyTree, -#endif ProcessUtilityContext context, ParamListInfo params, struct QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *completionTag) { - Node *parsetree; - -#if PG_VERSION_NUM >= PG_VERSION_14 if (readOnlyTree) { pstmt = copyObject(pstmt); } -#endif - parsetree = pstmt->utilityStmt; + Node *parsetree = pstmt->utilityStmt; if (IsA(parsetree, TransactionStmt)) { @@ -199,8 +185,8 @@ multi_ProcessUtility(PlannedStmt *pstmt, * that state. Since we never need to intercept transaction statements, * skip our checks and immediately fall into standard_ProcessUtility. */ - PrevProcessUtility_compat(pstmt, queryString, false, context, - params, queryEnv, dest, completionTag); + PrevProcessUtility(pstmt, queryString, false, context, + params, queryEnv, dest, completionTag); return; } @@ -244,8 +230,8 @@ multi_ProcessUtility(PlannedStmt *pstmt, * Ensure that utility commands do not behave any differently until CREATE * EXTENSION is invoked. */ - PrevProcessUtility_compat(pstmt, queryString, false, context, - params, queryEnv, dest, completionTag); + PrevProcessUtility(pstmt, queryString, false, context, + params, queryEnv, dest, completionTag); return; } @@ -276,8 +262,8 @@ multi_ProcessUtility(PlannedStmt *pstmt, PG_TRY(); { - PrevProcessUtility_compat(pstmt, queryString, false, context, - params, queryEnv, dest, completionTag); + PrevProcessUtility(pstmt, queryString, false, context, + params, queryEnv, dest, completionTag); StoredProcedureLevel -= 1; @@ -310,8 +296,8 @@ multi_ProcessUtility(PlannedStmt *pstmt, PG_TRY(); { - PrevProcessUtility_compat(pstmt, queryString, false, context, - params, queryEnv, dest, completionTag); + PrevProcessUtility(pstmt, queryString, false, context, + params, queryEnv, dest, completionTag); DoBlockLevel -= 1; } @@ -649,8 +635,8 @@ ProcessUtilityInternal(PlannedStmt *pstmt, if (IsA(parsetree, AlterTableStmt)) { AlterTableStmt *alterTableStmt = (AlterTableStmt *) parsetree; - if (AlterTableStmtObjType_compat(alterTableStmt) == OBJECT_TABLE || - AlterTableStmtObjType_compat(alterTableStmt) == OBJECT_FOREIGN_TABLE) + if (alterTableStmt->objtype == OBJECT_TABLE || + alterTableStmt->objtype == OBJECT_FOREIGN_TABLE) { ErrorIfAlterDropsPartitionColumn(alterTableStmt); @@ -769,8 +755,8 @@ ProcessUtilityInternal(PlannedStmt *pstmt, PreprocessAlterExtensionCitusStmtForCitusColumnar(parsetree); } - PrevProcessUtility_compat(pstmt, queryString, false, context, - params, queryEnv, dest, completionTag); + PrevProcessUtility(pstmt, queryString, false, context, + params, queryEnv, dest, completionTag); if (isAlterExtensionUpdateCitusStmt) { @@ -1208,38 +1194,6 @@ ExecuteDistributedDDLJob(DDLJob *ddlJob) */ if (ddlJob->startNewTransaction) { -#if PG_VERSION_NUM < 140000 - - /* - * Older versions of postgres doesn't have PROC_IN_SAFE_IC flag - * so we cannot use set_indexsafe_procflags in those versions. - * - * For this reason, we do our best to ensure not grabbing any - * snapshots later in the executor. - */ - - /* - * If cache is not populated, system catalog lookups will cause - * the xmin of current backend to change. Then the last phase - * of CREATE INDEX CONCURRENTLY, which is in a separate backend, - * will hang waiting for our backend and result in a deadlock. - * - * We populate the cache before starting the next transaction to - * avoid this. Most of the metadata has already been resolved in - * planning phase, we only need to lookup metadata needed for - * connection establishment. - */ - (void) CurrentDatabaseName(); - - /* - * ConnParams (AuthInfo and PoolInfo) gets a snapshot, which - * will blocks the remote connections to localhost. Hence we warm up - * the cache here so that after we start a new transaction, the entries - * will already be in the hash table, hence we won't be holding any snapshots. - */ - WarmUpConnParamsHash(); -#endif - /* * Since it is not certain whether the code-path that we followed * until reaching here caused grabbing any snapshots or not, we @@ -1258,8 +1212,6 @@ ExecuteDistributedDDLJob(DDLJob *ddlJob) CommitTransactionCommand(); StartTransactionCommand(); -#if PG_VERSION_NUM >= 140000 - /* * Tell other backends to ignore us, even if we grab any * snapshots via adaptive executor. @@ -1274,7 +1226,6 @@ ExecuteDistributedDDLJob(DDLJob *ddlJob) * given above. */ Assert(localExecutionSupported == false); -#endif } MemoryContext savedContext = CurrentMemoryContext; @@ -1340,8 +1291,6 @@ ExecuteDistributedDDLJob(DDLJob *ddlJob) } -#if PG_VERSION_NUM >= 140000 - /* * set_indexsafe_procflags sets PROC_IN_SAFE_IC flag in MyProc->statusFlags. * @@ -1364,9 +1313,6 @@ set_indexsafe_procflags(void) } -#endif - - /* * CurrentSearchPath is a C interface for calling current_schemas(bool) that * PostgreSQL exports. diff --git a/src/backend/distributed/commands/vacuum.c b/src/backend/distributed/commands/vacuum.c index 274aebb8f..6bc76b7b8 100644 --- a/src/backend/distributed/commands/vacuum.c +++ b/src/backend/distributed/commands/vacuum.c @@ -359,12 +359,12 @@ DeparseVacuumStmtPrefix(CitusVacuumParams vacuumParams) { appendStringInfoString(vacuumPrefix, "SKIP_LOCKED,"); } - #if PG_VERSION_NUM >= PG_VERSION_14 + if (vacuumFlags & VACOPT_PROCESS_TOAST) { appendStringInfoString(vacuumPrefix, "PROCESS_TOAST,"); } - #endif + if (vacuumParams.truncate != VACOPTVALUE_UNSPECIFIED) { appendStringInfoString(vacuumPrefix, @@ -389,13 +389,11 @@ DeparseVacuumStmtPrefix(CitusVacuumParams vacuumParams) break; } - #if PG_VERSION_NUM >= PG_VERSION_14 case VACOPTVALUE_AUTO: { appendStringInfoString(vacuumPrefix, "INDEX_CLEANUP auto,"); break; } - #endif default: { @@ -501,9 +499,7 @@ VacuumStmtParams(VacuumStmt *vacstmt) bool freeze = false; bool full = false; bool disable_page_skipping = false; - #if PG_VERSION_NUM >= PG_VERSION_14 bool process_toast = false; - #endif /* Set default value */ params.index_cleanup = VACOPTVALUE_UNSPECIFIED; @@ -547,16 +543,12 @@ VacuumStmtParams(VacuumStmt *vacstmt) { disable_page_skipping = defGetBoolean(opt); } - #if PG_VERSION_NUM >= PG_VERSION_14 else if (strcmp(opt->defname, "process_toast") == 0) { process_toast = defGetBoolean(opt); } - #endif else if (strcmp(opt->defname, "index_cleanup") == 0) { - #if PG_VERSION_NUM >= PG_VERSION_14 - /* Interpret no string as the default, which is 'auto' */ if (!opt->arg) { @@ -577,10 +569,6 @@ VacuumStmtParams(VacuumStmt *vacstmt) VACOPTVALUE_DISABLED; } } - #else - params.index_cleanup = defGetBoolean(opt) ? VACOPTVALUE_ENABLED : - VACOPTVALUE_DISABLED; - #endif } else if (strcmp(opt->defname, "truncate") == 0) { @@ -625,9 +613,7 @@ VacuumStmtParams(VacuumStmt *vacstmt) (analyze ? VACOPT_ANALYZE : 0) | (freeze ? VACOPT_FREEZE : 0) | (full ? VACOPT_FULL : 0) | - #if PG_VERSION_NUM >= PG_VERSION_14 (process_toast ? VACOPT_PROCESS_TOAST : 0) | - #endif (disable_page_skipping ? VACOPT_DISABLE_PAGE_SKIPPING : 0); return params; } diff --git a/src/backend/distributed/commands/view.c b/src/backend/distributed/commands/view.c index 8219a2907..02d6815d9 100644 --- a/src/backend/distributed/commands/view.c +++ b/src/backend/distributed/commands/view.c @@ -598,7 +598,7 @@ List * PostprocessAlterViewStmt(Node *node, const char *queryString) { AlterTableStmt *stmt = castNode(AlterTableStmt, node); - Assert(AlterTableStmtObjType_compat(stmt) == OBJECT_VIEW); + Assert(stmt->objtype == OBJECT_VIEW); List *viewAddresses = GetObjectAddressListFromParseTree((Node *) stmt, true, true); diff --git a/src/backend/distributed/connection/connection_management.c b/src/backend/distributed/connection/connection_management.c index e4aca3ee7..46e757bfe 100644 --- a/src/backend/distributed/connection/connection_management.c +++ b/src/backend/distributed/connection/connection_management.c @@ -1314,33 +1314,6 @@ StartConnectionEstablishment(MultiConnection *connection, ConnectionHashKey *key } -#if PG_VERSION_NUM < 140000 - -/* - * WarmUpConnParamsHash warms up the ConnParamsHash by loading all the - * conn params for active primary nodes. - */ -void -WarmUpConnParamsHash(void) -{ - List *workerNodeList = ActivePrimaryNodeList(AccessShareLock); - WorkerNode *workerNode = NULL; - foreach_ptr(workerNode, workerNodeList) - { - ConnectionHashKey key; - strlcpy(key.hostname, workerNode->workerName, MAX_NODE_LENGTH); - key.port = workerNode->workerPort; - strlcpy(key.database, CurrentDatabaseName(), NAMEDATALEN); - strlcpy(key.user, CurrentUserName(), NAMEDATALEN); - key.replicationConnParam = false; - FindOrCreateConnParamsEntry(&key); - } -} - - -#endif - - /* * FindOrCreateConnParamsEntry searches ConnParamsHash for the given key, * if it is not found, it is created. diff --git a/src/backend/distributed/deparser/citus_ruleutils.c b/src/backend/distributed/deparser/citus_ruleutils.c index 05e483766..6b865e061 100644 --- a/src/backend/distributed/deparser/citus_ruleutils.c +++ b/src/backend/distributed/deparser/citus_ruleutils.c @@ -22,9 +22,7 @@ #include "access/skey.h" #include "access/stratnum.h" #include "access/sysattr.h" -#if PG_VERSION_NUM >= PG_VERSION_14 #include "access/toast_compression.h" -#endif #include "access/tupdesc.h" #include "catalog/dependency.h" #include "catalog/indexing.h" @@ -386,13 +384,11 @@ pg_get_tableschemadef_string(Oid tableRelationId, IncludeSequenceDefaults atttypmod); appendStringInfoString(&buffer, attributeTypeName); -#if PG_VERSION_NUM >= PG_VERSION_14 if (CompressionMethodIsValid(attributeForm->attcompression)) { appendStringInfo(&buffer, " COMPRESSION %s", GetCompressionMethodName(attributeForm->attcompression)); } -#endif if (attributeForm->attidentity && includeIdentityDefaults) { @@ -939,17 +935,6 @@ deparse_shard_reindex_statement(ReindexStmt *origStmt, Oid distrelid, int64 shar bool IsReindexWithParam_compat(ReindexStmt *reindexStmt, char *param) { -#if PG_VERSION_NUM < PG_VERSION_14 - if (strcmp(param, "concurrently") == 0) - { - return reindexStmt->concurrent; - } - else if (strcmp(param, "verbose") == 0) - { - return reindexStmt->options & REINDEXOPT_VERBOSE; - } - return false; -#else DefElem *opt = NULL; foreach_ptr(opt, reindexStmt->params) { @@ -959,7 +944,6 @@ IsReindexWithParam_compat(ReindexStmt *reindexStmt, char *param) } } return false; -#endif } @@ -974,7 +958,7 @@ AddVacuumParams(ReindexStmt *reindexStmt, StringInfo buffer) { appendStringInfoString(temp, "VERBOSE"); } -#if PG_VERSION_NUM >= PG_VERSION_14 + char *tableSpaceName = NULL; DefElem *opt = NULL; foreach_ptr(opt, reindexStmt->params) @@ -997,7 +981,6 @@ AddVacuumParams(ReindexStmt *reindexStmt, StringInfo buffer) appendStringInfo(temp, "TABLESPACE %s", tableSpaceName); } } -#endif if (temp->len > 0) { @@ -1627,9 +1610,7 @@ RoleSpecString(RoleSpec *spec, bool withQuoteIdentifier) spec->rolename; } - #if PG_VERSION_NUM >= PG_VERSION_14 case ROLESPEC_CURRENT_ROLE: - #endif case ROLESPEC_CURRENT_USER: { return withQuoteIdentifier ? diff --git a/src/backend/distributed/deparser/deparse_sequence_stmts.c b/src/backend/distributed/deparser/deparse_sequence_stmts.c index 80c4e2dd4..de2afdeec 100644 --- a/src/backend/distributed/deparser/deparse_sequence_stmts.c +++ b/src/backend/distributed/deparser/deparse_sequence_stmts.c @@ -193,7 +193,7 @@ DeparseAlterSequenceOwnerStmt(Node *node) StringInfoData str = { 0 }; initStringInfo(&str); - Assert(AlterTableStmtObjType_compat(stmt) == OBJECT_SEQUENCE); + Assert(stmt->objtype == OBJECT_SEQUENCE); AppendAlterSequenceOwnerStmt(&str, stmt); @@ -208,7 +208,7 @@ DeparseAlterSequenceOwnerStmt(Node *node) static void AppendAlterSequenceOwnerStmt(StringInfo buf, AlterTableStmt *stmt) { - Assert(AlterTableStmtObjType_compat(stmt) == OBJECT_SEQUENCE); + Assert(stmt->objtype == OBJECT_SEQUENCE); RangeVar *seq = stmt->relation; char *qualifiedSequenceName = quote_qualified_identifier(seq->schemaname, seq->relname); @@ -274,7 +274,7 @@ DeparseAlterSequencePersistenceStmt(Node *node) StringInfoData str = { 0 }; initStringInfo(&str); - Assert(AlterTableStmtObjType_compat(stmt) == OBJECT_SEQUENCE); + Assert(stmt->objtype == OBJECT_SEQUENCE); AppendAlterSequencePersistenceStmt(&str, stmt); @@ -289,7 +289,7 @@ DeparseAlterSequencePersistenceStmt(Node *node) static void AppendAlterSequencePersistenceStmt(StringInfo buf, AlterTableStmt *stmt) { - Assert(AlterTableStmtObjType_compat(stmt) == OBJECT_SEQUENCE); + Assert(stmt->objtype == OBJECT_SEQUENCE); RangeVar *seq = stmt->relation; char *qualifiedSequenceName = quote_qualified_identifier(seq->schemaname, diff --git a/src/backend/distributed/deparser/deparse_statistics_stmts.c b/src/backend/distributed/deparser/deparse_statistics_stmts.c index 923af645e..4a165ec72 100644 --- a/src/backend/distributed/deparser/deparse_statistics_stmts.c +++ b/src/backend/distributed/deparser/deparse_statistics_stmts.c @@ -229,7 +229,6 @@ AppendStatTypes(StringInfo buf, CreateStatsStmt *stmt) } -#if PG_VERSION_NUM >= PG_VERSION_14 static void AppendColumnNames(StringInfo buf, CreateStatsStmt *stmt) { @@ -257,36 +256,6 @@ AppendColumnNames(StringInfo buf, CreateStatsStmt *stmt) } -#else -static void -AppendColumnNames(StringInfo buf, CreateStatsStmt *stmt) -{ - ColumnRef *column = NULL; - - foreach_ptr(column, stmt->exprs) - { - if (!IsA(column, ColumnRef) || list_length(column->fields) != 1) - { - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg( - "only simple column references are allowed in CREATE STATISTICS"))); - } - - char *columnName = NameListToQuotedString(column->fields); - - appendStringInfoString(buf, columnName); - - if (column != llast(stmt->exprs)) - { - appendStringInfoString(buf, ", "); - } - } -} - - -#endif - static void AppendTableName(StringInfo buf, CreateStatsStmt *stmt) { diff --git a/src/backend/distributed/deparser/deparse_table_stmts.c b/src/backend/distributed/deparser/deparse_table_stmts.c index 6e0dd3f06..1d9ee1739 100644 --- a/src/backend/distributed/deparser/deparse_table_stmts.c +++ b/src/backend/distributed/deparser/deparse_table_stmts.c @@ -77,7 +77,7 @@ DeparseAlterTableStmt(Node *node) StringInfoData str = { 0 }; initStringInfo(&str); - Assert(AlterTableStmtObjType_compat(stmt) == OBJECT_TABLE); + Assert(stmt->objtype == OBJECT_TABLE); AppendAlterTableStmt(&str, stmt); return str.data; @@ -96,7 +96,7 @@ AppendAlterTableStmt(StringInfo buf, AlterTableStmt *stmt) stmt->relation->relname); ListCell *cmdCell = NULL; - Assert(AlterTableStmtObjType_compat(stmt) == OBJECT_TABLE); + Assert(stmt->objtype == OBJECT_TABLE); appendStringInfo(buf, "ALTER TABLE %s", identifier); foreach(cmdCell, stmt->cmds) diff --git a/src/backend/distributed/deparser/deparse_type_stmts.c b/src/backend/distributed/deparser/deparse_type_stmts.c index e12d96ad9..1d70c6791 100644 --- a/src/backend/distributed/deparser/deparse_type_stmts.c +++ b/src/backend/distributed/deparser/deparse_type_stmts.c @@ -122,7 +122,7 @@ DeparseAlterTypeStmt(Node *node) StringInfoData str = { 0 }; initStringInfo(&str); - Assert(AlterTableStmtObjType_compat(stmt) == OBJECT_TYPE); + Assert(stmt->objtype == OBJECT_TYPE); AppendAlterTypeStmt(&str, stmt); @@ -137,7 +137,7 @@ AppendAlterTypeStmt(StringInfo buf, AlterTableStmt *stmt) stmt->relation->relname); ListCell *cmdCell = NULL; - Assert(AlterTableStmtObjType_compat(stmt) == OBJECT_TYPE); + Assert(stmt->objtype == OBJECT_TYPE); appendStringInfo(buf, "ALTER TYPE %s", identifier); foreach(cmdCell, stmt->cmds) diff --git a/src/backend/distributed/deparser/qualify_domain.c b/src/backend/distributed/deparser/qualify_domain.c index b36a0a713..2e163dad0 100644 --- a/src/backend/distributed/deparser/qualify_domain.c +++ b/src/backend/distributed/deparser/qualify_domain.c @@ -245,11 +245,7 @@ QualifyCollate(CollateClause *collClause, bool missing_ok) List *objName = NIL; List *objArgs = NIL; - #if PG_VERSION_NUM >= PG_VERSION_14 getObjectIdentityParts(&collationAddress, &objName, &objArgs, false); - #else - getObjectIdentityParts(&collationAddress, &objName, &objArgs); - #endif collClause->collname = NIL; char *name = NULL; diff --git a/src/backend/distributed/deparser/qualify_sequence_stmt.c b/src/backend/distributed/deparser/qualify_sequence_stmt.c index cece902a6..384e0c953 100644 --- a/src/backend/distributed/deparser/qualify_sequence_stmt.c +++ b/src/backend/distributed/deparser/qualify_sequence_stmt.c @@ -34,7 +34,7 @@ void QualifyAlterSequenceOwnerStmt(Node *node) { AlterTableStmt *stmt = castNode(AlterTableStmt, node); - Assert(AlterTableStmtObjType_compat(stmt) == OBJECT_SEQUENCE); + Assert(stmt->objtype == OBJECT_SEQUENCE); RangeVar *seq = stmt->relation; @@ -62,7 +62,7 @@ void QualifyAlterSequencePersistenceStmt(Node *node) { AlterTableStmt *stmt = castNode(AlterTableStmt, node); - Assert(AlterTableStmtObjType_compat(stmt) == OBJECT_SEQUENCE); + Assert(stmt->objtype == OBJECT_SEQUENCE); RangeVar *seq = stmt->relation; diff --git a/src/backend/distributed/deparser/qualify_type_stmt.c b/src/backend/distributed/deparser/qualify_type_stmt.c index 33c80f527..487e6fc97 100644 --- a/src/backend/distributed/deparser/qualify_type_stmt.c +++ b/src/backend/distributed/deparser/qualify_type_stmt.c @@ -123,7 +123,7 @@ void QualifyAlterTypeStmt(Node *node) { AlterTableStmt *stmt = castNode(AlterTableStmt, node); - Assert(AlterTableStmtObjType_compat(stmt) == OBJECT_TYPE); + Assert(stmt->objtype == OBJECT_TYPE); if (stmt->relation->schemaname == NULL) { diff --git a/src/backend/distributed/deparser/ruleutils_13.c b/src/backend/distributed/deparser/ruleutils_13.c deleted file mode 100644 index 31ef67f97..000000000 --- a/src/backend/distributed/deparser/ruleutils_13.c +++ /dev/null @@ -1,8131 +0,0 @@ -/*------------------------------------------------------------------------- - * - * ruleutils_13.c - * Functions to convert stored expressions/querytrees back to - * source text - * - * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * src/backend/distributed/deparser/ruleutils_13.c - * - * This needs to be closely in sync with the core code. - *------------------------------------------------------------------------- - */ -#include "distributed/pg_version_constants.h" - -#include "pg_config.h" - -#if (PG_VERSION_NUM >= PG_VERSION_13) && (PG_VERSION_NUM < PG_VERSION_14) - -#include "postgres.h" - -#include -#include -#include - -#include "access/amapi.h" -#include "access/htup_details.h" -#include "access/relation.h" -#include "access/sysattr.h" -#include "access/table.h" -#include "catalog/dependency.h" -#include "catalog/indexing.h" -#include "catalog/pg_aggregate.h" -#include "catalog/pg_am.h" -#include "catalog/pg_authid.h" -#include "catalog/pg_collation.h" -#include "catalog/pg_constraint.h" -#include "catalog/pg_depend.h" -#include "catalog/pg_extension.h" -#include "catalog/pg_foreign_data_wrapper.h" -#include "catalog/pg_language.h" -#include "catalog/pg_opclass.h" -#include "catalog/pg_operator.h" -#include "catalog/pg_partitioned_table.h" -#include "catalog/pg_proc.h" -#include "catalog/pg_statistic_ext.h" -#include "catalog/pg_trigger.h" -#include "catalog/pg_type.h" -#include "commands/defrem.h" -#include "commands/extension.h" -#include "commands/tablespace.h" -#include "common/keywords.h" -#include "distributed/citus_nodefuncs.h" -#include "distributed/citus_ruleutils.h" -#include "executor/spi.h" -#include "foreign/foreign.h" -#include "funcapi.h" -#include "mb/pg_wchar.h" -#include "miscadmin.h" -#include "nodes/makefuncs.h" -#include "nodes/nodeFuncs.h" -#include "nodes/pathnodes.h" -#include "optimizer/optimizer.h" -#include "parser/parse_node.h" -#include "parser/parse_agg.h" -#include "parser/parse_func.h" -#include "parser/parse_node.h" -#include "parser/parse_oper.h" -#include "parser/parser.h" -#include "parser/parsetree.h" -#include "rewrite/rewriteHandler.h" -#include "rewrite/rewriteManip.h" -#include "rewrite/rewriteSupport.h" -#include "utils/array.h" -#include "utils/builtins.h" -#include "utils/fmgroids.h" -#include "utils/hsearch.h" -#include "utils/lsyscache.h" -#include "utils/rel.h" -#include "utils/ruleutils.h" -#include "utils/snapmgr.h" -#include "utils/syscache.h" -#include "utils/typcache.h" -#include "utils/varlena.h" -#include "utils/xml.h" - - -/* ---------- - * Pretty formatting constants - * ---------- - */ - -/* Indent counts */ -#define PRETTYINDENT_STD 8 -#define PRETTYINDENT_JOIN 4 -#define PRETTYINDENT_VAR 4 - -#define PRETTYINDENT_LIMIT 40 /* wrap limit */ - -/* Pretty flags */ -#define PRETTYFLAG_PAREN 0x0001 -#define PRETTYFLAG_INDENT 0x0002 - -/* Default line length for pretty-print wrapping: 0 means wrap always */ -#define WRAP_COLUMN_DEFAULT 0 - -/* macros to test if pretty action needed */ -#define PRETTY_PAREN(context) ((context)->prettyFlags & PRETTYFLAG_PAREN) -#define PRETTY_INDENT(context) ((context)->prettyFlags & PRETTYFLAG_INDENT) - - -/* ---------- - * Local data types - * ---------- - */ - -/* Context info needed for invoking a recursive querytree display routine */ -typedef struct -{ - StringInfo buf; /* output buffer to append to */ - List *namespaces; /* List of deparse_namespace nodes */ - List *windowClause; /* Current query level's WINDOW clause */ - List *windowTList; /* targetlist for resolving WINDOW clause */ - int prettyFlags; /* enabling of pretty-print functions */ - int wrapColumn; /* max line length, or -1 for no limit */ - int indentLevel; /* current indent level for prettyprint */ - bool varprefix; /* true to print prefixes on Vars */ - Oid distrelid; /* the distributed table being modified, if valid */ - int64 shardid; /* a distributed table's shardid, if positive */ - ParseExprKind special_exprkind; /* set only for exprkinds needing special - * handling */ - Bitmapset *appendparents; /* if not null, map child Vars of these relids - * back to the parent rel */ -} deparse_context; - -/* - * Each level of query context around a subtree needs a level of Var namespace. - * A Var having varlevelsup=N refers to the N'th item (counting from 0) in - * the current context's namespaces list. - * - * The rangetable is the list of actual RTEs from the query tree, and the - * cte list is the list of actual CTEs. - * - * rtable_names holds the alias name to be used for each RTE (either a C - * string, or NULL for nameless RTEs such as unnamed joins). - * rtable_columns holds the column alias names to be used for each RTE. - * - * In some cases we need to make names of merged JOIN USING columns unique - * across the whole query, not only per-RTE. If so, unique_using is true - * and using_names is a list of C strings representing names already assigned - * to USING columns. - * - * When deparsing plan trees, there is always just a single item in the - * deparse_namespace list (since a plan tree never contains Vars with - * varlevelsup > 0). We store the PlanState node that is the immediate - * parent of the expression to be deparsed, as well as a list of that - * PlanState's ancestors. In addition, we store its outer and inner subplan - * state nodes, as well as their plan nodes' targetlists, and the index tlist - * if the current plan node might contain INDEX_VAR Vars. (These fields could - * be derived on-the-fly from the current PlanState, but it seems notationally - * clearer to set them up as separate fields.) - */ -typedef struct -{ - List *rtable; /* List of RangeTblEntry nodes */ - List *rtable_names; /* Parallel list of names for RTEs */ - List *rtable_columns; /* Parallel list of deparse_columns structs */ - List *subplans; /* List of Plan trees for SubPlans */ - List *ctes; /* List of CommonTableExpr nodes */ - AppendRelInfo **appendrels; /* Array of AppendRelInfo nodes, or NULL */ - /* Workspace for column alias assignment: */ - bool unique_using; /* Are we making USING names globally unique */ - List *using_names; /* List of assigned names for USING columns */ - /* Remaining fields are used only when deparsing a Plan tree: */ - Plan *plan; /* immediate parent of current expression */ - List *ancestors; /* ancestors of planstate */ - Plan *outer_plan; /* outer subnode, or NULL if none */ - Plan *inner_plan; /* inner subnode, or NULL if none */ - List *outer_tlist; /* referent for OUTER_VAR Vars */ - List *inner_tlist; /* referent for INNER_VAR Vars */ - List *index_tlist; /* referent for INDEX_VAR Vars */ -} deparse_namespace; - -/* Callback signature for resolve_special_varno() */ -typedef void (*rsv_callback) (Node *node, deparse_context *context, - void *callback_arg); - -/* - * Per-relation data about column alias names. - * - * Selecting aliases is unreasonably complicated because of the need to dump - * rules/views whose underlying tables may have had columns added, deleted, or - * renamed since the query was parsed. We must nonetheless print the rule/view - * in a form that can be reloaded and will produce the same results as before. - * - * For each RTE used in the query, we must assign column aliases that are - * unique within that RTE. SQL does not require this of the original query, - * but due to factors such as *-expansion we need to be able to uniquely - * reference every column in a decompiled query. As long as we qualify all - * column references, per-RTE uniqueness is sufficient for that. - * - * However, we can't ensure per-column name uniqueness for unnamed join RTEs, - * since they just inherit column names from their input RTEs, and we can't - * rename the columns at the join level. Most of the time this isn't an issue - * because we don't need to reference the join's output columns as such; we - * can reference the input columns instead. That approach can fail for merged - * JOIN USING columns, however, so when we have one of those in an unnamed - * join, we have to make that column's alias globally unique across the whole - * query to ensure it can be referenced unambiguously. - * - * Another problem is that a JOIN USING clause requires the columns to be - * merged to have the same aliases in both input RTEs, and that no other - * columns in those RTEs or their children conflict with the USING names. - * To handle that, we do USING-column alias assignment in a recursive - * traversal of the query's jointree. When descending through a JOIN with - * USING, we preassign the USING column names to the child columns, overriding - * other rules for column alias assignment. We also mark each RTE with a list - * of all USING column names selected for joins containing that RTE, so that - * when we assign other columns' aliases later, we can avoid conflicts. - * - * Another problem is that if a JOIN's input tables have had columns added or - * deleted since the query was parsed, we must generate a column alias list - * for the join that matches the current set of input columns --- otherwise, a - * change in the number of columns in the left input would throw off matching - * of aliases to columns of the right input. Thus, positions in the printable - * column alias list are not necessarily one-for-one with varattnos of the - * JOIN, so we need a separate new_colnames[] array for printing purposes. - */ -typedef struct -{ - /* - * colnames is an array containing column aliases to use for columns that - * existed when the query was parsed. Dropped columns have NULL entries. - * This array can be directly indexed by varattno to get a Var's name. - * - * Non-NULL entries are guaranteed unique within the RTE, *except* when - * this is for an unnamed JOIN RTE. In that case we merely copy up names - * from the two input RTEs. - * - * During the recursive descent in set_using_names(), forcible assignment - * of a child RTE's column name is represented by pre-setting that element - * of the child's colnames array. So at that stage, NULL entries in this - * array just mean that no name has been preassigned, not necessarily that - * the column is dropped. - */ - int num_cols; /* length of colnames[] array */ - char **colnames; /* array of C strings and NULLs */ - - /* - * new_colnames is an array containing column aliases to use for columns - * that would exist if the query was re-parsed against the current - * definitions of its base tables. This is what to print as the column - * alias list for the RTE. This array does not include dropped columns, - * but it will include columns added since original parsing. Indexes in - * it therefore have little to do with current varattno values. As above, - * entries are unique unless this is for an unnamed JOIN RTE. (In such an - * RTE, we never actually print this array, but we must compute it anyway - * for possible use in computing column names of upper joins.) The - * parallel array is_new_col marks which of these columns are new since - * original parsing. Entries with is_new_col false must match the - * non-NULL colnames entries one-for-one. - */ - int num_new_cols; /* length of new_colnames[] array */ - char **new_colnames; /* array of C strings */ - bool *is_new_col; /* array of bool flags */ - - /* This flag tells whether we should actually print a column alias list */ - bool printaliases; - - /* This list has all names used as USING names in joins above this RTE */ - List *parentUsing; /* names assigned to parent merged columns */ - - /* - * If this struct is for a JOIN RTE, we fill these fields during the - * set_using_names() pass to describe its relationship to its child RTEs. - * - * leftattnos and rightattnos are arrays with one entry per existing - * output column of the join (hence, indexable by join varattno). For a - * simple reference to a column of the left child, leftattnos[i] is the - * child RTE's attno and rightattnos[i] is zero; and conversely for a - * column of the right child. But for merged columns produced by JOIN - * USING/NATURAL JOIN, both leftattnos[i] and rightattnos[i] are nonzero. - * Also, if the column has been dropped, both are zero. - * - * If it's a JOIN USING, usingNames holds the alias names selected for the - * merged columns (these might be different from the original USING list, - * if we had to modify names to achieve uniqueness). - */ - int leftrti; /* rangetable index of left child */ - int rightrti; /* rangetable index of right child */ - int *leftattnos; /* left-child varattnos of join cols, or 0 */ - int *rightattnos; /* right-child varattnos of join cols, or 0 */ - List *usingNames; /* names assigned to merged columns */ -} deparse_columns; - -/* This macro is analogous to rt_fetch(), but for deparse_columns structs */ -#define deparse_columns_fetch(rangetable_index, dpns) \ - ((deparse_columns *) list_nth((dpns)->rtable_columns, (rangetable_index)-1)) - -/* - * Entry in set_rtable_names' hash table - */ -typedef struct -{ - char name[NAMEDATALEN]; /* Hash key --- must be first */ - int counter; /* Largest addition used so far for name */ -} NameHashEntry; - - -/* ---------- - * Local functions - * - * Most of these functions used to use fixed-size buffers to build their - * results. Now, they take an (already initialized) StringInfo object - * as a parameter, and append their text output to its contents. - * ---------- - */ -static void set_rtable_names(deparse_namespace *dpns, List *parent_namespaces, - Bitmapset *rels_used); -static void set_deparse_for_query(deparse_namespace *dpns, Query *query, - List *parent_namespaces); -static bool has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode); -static void set_using_names(deparse_namespace *dpns, Node *jtnode, - List *parentUsing); -static void set_relation_column_names(deparse_namespace *dpns, - RangeTblEntry *rte, - deparse_columns *colinfo); -static void set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte, - deparse_columns *colinfo); -static bool colname_is_unique(const char *colname, deparse_namespace *dpns, - deparse_columns *colinfo); -static char *make_colname_unique(char *colname, deparse_namespace *dpns, - deparse_columns *colinfo); -static void expand_colnames_array_to(deparse_columns *colinfo, int n); -static void identify_join_columns(JoinExpr *j, RangeTblEntry *jrte, - deparse_columns *colinfo); -static char *get_rtable_name(int rtindex, deparse_context *context); -static void set_deparse_plan(deparse_namespace *dpns, Plan *plan); -static void push_child_plan(deparse_namespace *dpns, Plan *plan, - deparse_namespace *save_dpns); -static void pop_child_plan(deparse_namespace *dpns, - deparse_namespace *save_dpns); -static void push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell, - deparse_namespace *save_dpns); -static void pop_ancestor_plan(deparse_namespace *dpns, - deparse_namespace *save_dpns); -static void get_query_def(Query *query, StringInfo buf, List *parentnamespace, - TupleDesc resultDesc, - int prettyFlags, int wrapColumn, int startIndent); -static void get_query_def_extended(Query *query, StringInfo buf, - List *parentnamespace, Oid distrelid, int64 shardid, - TupleDesc resultDesc, int prettyFlags, int wrapColumn, - int startIndent); -static void get_values_def(List *values_lists, deparse_context *context); -static void get_with_clause(Query *query, deparse_context *context); -static void get_select_query_def(Query *query, deparse_context *context, - TupleDesc resultDesc); -static void get_insert_query_def(Query *query, deparse_context *context); -static void get_update_query_def(Query *query, deparse_context *context); -static void get_update_query_targetlist_def(Query *query, List *targetList, - deparse_context *context, - RangeTblEntry *rte); -static void get_delete_query_def(Query *query, deparse_context *context); -static void get_utility_query_def(Query *query, deparse_context *context); -static void get_basic_select_query(Query *query, deparse_context *context, - TupleDesc resultDesc); -static void get_target_list(List *targetList, deparse_context *context, - TupleDesc resultDesc); -static void get_setop_query(Node *setOp, Query *query, - deparse_context *context, - TupleDesc resultDesc); -static Node *get_rule_sortgroupclause(Index ref, List *tlist, - bool force_colno, - deparse_context *context); -static void get_rule_groupingset(GroupingSet *gset, List *targetlist, - bool omit_parens, deparse_context *context); -static void get_rule_orderby(List *orderList, List *targetList, - bool force_colno, deparse_context *context); -static void get_rule_windowclause(Query *query, deparse_context *context); -static void get_rule_windowspec(WindowClause *wc, List *targetList, - deparse_context *context); -static char *get_variable(Var *var, int levelsup, bool istoplevel, - deparse_context *context); -static void get_special_variable(Node *node, deparse_context *context, - void *callback_arg); -static void resolve_special_varno(Node *node, deparse_context *context, - rsv_callback callback, void *callback_arg); -static Node *find_param_referent(Param *param, deparse_context *context, - deparse_namespace **dpns_p, ListCell **ancestor_cell_p); -static void get_parameter(Param *param, deparse_context *context); -static const char *get_simple_binary_op_name(OpExpr *expr); -static bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags); -static void appendContextKeyword(deparse_context *context, const char *str, - int indentBefore, int indentAfter, int indentPlus); -static void removeStringInfoSpaces(StringInfo str); -static void get_rule_expr(Node *node, deparse_context *context, - bool showimplicit); -static void get_rule_expr_toplevel(Node *node, deparse_context *context, - bool showimplicit); -static void get_rule_expr_funccall(Node *node, deparse_context *context, - bool showimplicit); -static bool looks_like_function(Node *node); -static void get_oper_expr(OpExpr *expr, deparse_context *context); -static void get_func_expr(FuncExpr *expr, deparse_context *context, - bool showimplicit); -static void get_agg_expr(Aggref *aggref, deparse_context *context, - Aggref *original_aggref); -static void get_agg_combine_expr(Node *node, deparse_context *context, - void *callback_arg); -static void get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context); -static void get_coercion_expr(Node *arg, deparse_context *context, - Oid resulttype, int32 resulttypmod, - Node *parentNode); -static void get_const_expr(Const *constval, deparse_context *context, - int showtype); -static void get_const_collation(Const *constval, deparse_context *context); -static void simple_quote_literal(StringInfo buf, const char *val); -static void get_sublink_expr(SubLink *sublink, deparse_context *context); -static void get_tablefunc(TableFunc *tf, deparse_context *context, - bool showimplicit); -static void get_from_clause(Query *query, const char *prefix, - deparse_context *context); -static void get_from_clause_item(Node *jtnode, Query *query, - deparse_context *context); -static void get_column_alias_list(deparse_columns *colinfo, - deparse_context *context); -static void get_from_clause_coldeflist(RangeTblFunction *rtfunc, - deparse_columns *colinfo, - deparse_context *context); -static void get_tablesample_def(TableSampleClause *tablesample, - deparse_context *context); -static void get_opclass_name(Oid opclass, Oid actual_datatype, - StringInfo buf); -static Node *processIndirection(Node *node, deparse_context *context); -static void printSubscripts(SubscriptingRef *aref, deparse_context *context); -static char *get_relation_name(Oid relid); -static char *generate_relation_or_shard_name(Oid relid, Oid distrelid, - int64 shardid, List *namespaces); -static char *generate_rte_shard_name(RangeTblEntry *rangeTableEntry); -static char *generate_fragment_name(char *schemaName, char *tableName); -static char *generate_function_name(Oid funcid, int nargs, - List *argnames, Oid *argtypes, - bool has_variadic, bool *use_variadic_p, - ParseExprKind special_exprkind); - -#define only_marker(rte) ((rte)->inh ? "" : "ONLY ") - - - -/* - * pg_get_query_def parses back one query tree, and outputs the resulting query - * string into given buffer. - */ -void -pg_get_query_def(Query *query, StringInfo buffer) -{ - get_query_def(query, buffer, NIL, NULL, 0, WRAP_COLUMN_DEFAULT, 0); -} - -/* - * get_merged_argument_list merges both IN and OUT arguments lists into one and also - * eliminates the INOUT duplicates(present in both the lists). - */ -bool -get_merged_argument_list(CallStmt *stmt, List **mergedNamedArgList, - Oid **mergedNamedArgTypes, - List **mergedArgumentList, - int *totalArguments) -{ - /* No OUT argument support in Postgres 13 */ - return false; -} - -/* - * pg_get_rule_expr deparses an expression and returns the result as a string. - */ -char * -pg_get_rule_expr(Node *expression) -{ - bool showImplicitCasts = true; - deparse_context context; - OverrideSearchPath *overridePath = NULL; - StringInfo buffer = makeStringInfo(); - - /* - * Set search_path to NIL so that all objects outside of pg_catalog will be - * schema-prefixed. pg_catalog will be added automatically when we call - * PushOverrideSearchPath(), since we set addCatalog to true; - */ - overridePath = GetOverrideSearchPath(CurrentMemoryContext); - overridePath->schemas = NIL; - overridePath->addCatalog = true; - PushOverrideSearchPath(overridePath); - - context.buf = buffer; - context.namespaces = NIL; - context.windowClause = NIL; - context.windowTList = NIL; - context.varprefix = false; - context.prettyFlags = 0; - context.wrapColumn = WRAP_COLUMN_DEFAULT; - context.indentLevel = 0; - context.special_exprkind = EXPR_KIND_NONE; - context.distrelid = InvalidOid; - context.shardid = INVALID_SHARD_ID; - - get_rule_expr(expression, &context, showImplicitCasts); - - /* revert back to original search_path */ - PopOverrideSearchPath(); - - return buffer->data; -} - - -/* - * set_rtable_names: select RTE aliases to be used in printing a query - * - * We fill in dpns->rtable_names with a list of names that is one-for-one with - * the already-filled dpns->rtable list. Each RTE name is unique among those - * in the new namespace plus any ancestor namespaces listed in - * parent_namespaces. - * - * If rels_used isn't NULL, only RTE indexes listed in it are given aliases. - * - * Note that this function is only concerned with relation names, not column - * names. - */ -static void -set_rtable_names(deparse_namespace *dpns, List *parent_namespaces, - Bitmapset *rels_used) -{ - HASHCTL hash_ctl; - HTAB *names_hash; - NameHashEntry *hentry; - bool found; - int rtindex; - ListCell *lc; - - dpns->rtable_names = NIL; - /* nothing more to do if empty rtable */ - if (dpns->rtable == NIL) - return; - - /* - * We use a hash table to hold known names, so that this process is O(N) - * not O(N^2) for N names. - */ - MemSet(&hash_ctl, 0, sizeof(hash_ctl)); - hash_ctl.keysize = NAMEDATALEN; - hash_ctl.entrysize = sizeof(NameHashEntry); - hash_ctl.hcxt = CurrentMemoryContext; - names_hash = hash_create("set_rtable_names names", - list_length(dpns->rtable), - &hash_ctl, - HASH_ELEM | HASH_CONTEXT); - /* Preload the hash table with names appearing in parent_namespaces */ - foreach(lc, parent_namespaces) - { - deparse_namespace *olddpns = (deparse_namespace *) lfirst(lc); - ListCell *lc2; - - foreach(lc2, olddpns->rtable_names) - { - char *oldname = (char *) lfirst(lc2); - - if (oldname == NULL) - continue; - hentry = (NameHashEntry *) hash_search(names_hash, - oldname, - HASH_ENTER, - &found); - /* we do not complain about duplicate names in parent namespaces */ - hentry->counter = 0; - } - } - - /* Now we can scan the rtable */ - rtindex = 1; - foreach(lc, dpns->rtable) - { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); - char *refname; - - /* Just in case this takes an unreasonable amount of time ... */ - CHECK_FOR_INTERRUPTS(); - - if (rels_used && !bms_is_member(rtindex, rels_used)) - { - /* Ignore unreferenced RTE */ - refname = NULL; - } - else if (rte->alias) - { - /* If RTE has a user-defined alias, prefer that */ - refname = rte->alias->aliasname; - } - else if (rte->rtekind == RTE_RELATION) - { - /* Use the current actual name of the relation */ - refname = get_rel_name(rte->relid); - } - else if (rte->rtekind == RTE_JOIN) - { - /* Unnamed join has no refname */ - refname = NULL; - } - else - { - /* Otherwise use whatever the parser assigned */ - refname = rte->eref->aliasname; - } - - /* - * If the selected name isn't unique, append digits to make it so, and - * make a new hash entry for it once we've got a unique name. For a - * very long input name, we might have to truncate to stay within - * NAMEDATALEN. - */ - if (refname) - { - hentry = (NameHashEntry *) hash_search(names_hash, - refname, - HASH_ENTER, - &found); - if (found) - { - /* Name already in use, must choose a new one */ - int refnamelen = strlen(refname); - char *modname = (char *) palloc(refnamelen + 16); - NameHashEntry *hentry2; - - do - { - hentry->counter++; - for (;;) - { - /* - * We avoid using %.*s here because it can misbehave - * if the data is not valid in what libc thinks is the - * prevailing encoding. - */ - memcpy(modname, refname, refnamelen); - sprintf(modname + refnamelen, "_%d", hentry->counter); - if (strlen(modname) < NAMEDATALEN) - break; - /* drop chars from refname to keep all the digits */ - refnamelen = pg_mbcliplen(refname, refnamelen, - refnamelen - 1); - } - hentry2 = (NameHashEntry *) hash_search(names_hash, - modname, - HASH_ENTER, - &found); - } while (found); - hentry2->counter = 0; /* init new hash entry */ - refname = modname; - } - else - { - /* Name not previously used, need only initialize hentry */ - hentry->counter = 0; - } - } - - dpns->rtable_names = lappend(dpns->rtable_names, refname); - rtindex++; - } - - hash_destroy(names_hash); -} - -/* - * set_deparse_for_query: set up deparse_namespace for deparsing a Query tree - * - * For convenience, this is defined to initialize the deparse_namespace struct - * from scratch. - */ -static void -set_deparse_for_query(deparse_namespace *dpns, Query *query, - List *parent_namespaces) -{ - ListCell *lc; - ListCell *lc2; - - /* Initialize *dpns and fill rtable/ctes links */ - memset(dpns, 0, sizeof(deparse_namespace)); - dpns->rtable = query->rtable; - dpns->subplans = NIL; - dpns->ctes = query->cteList; - dpns->appendrels = NULL; - - /* Assign a unique relation alias to each RTE */ - set_rtable_names(dpns, parent_namespaces, NULL); - - /* Initialize dpns->rtable_columns to contain zeroed structs */ - dpns->rtable_columns = NIL; - while (list_length(dpns->rtable_columns) < list_length(dpns->rtable)) - dpns->rtable_columns = lappend(dpns->rtable_columns, - palloc0(sizeof(deparse_columns))); - - /* If it's a utility query, it won't have a jointree */ - if (query->jointree) - { - /* Detect whether global uniqueness of USING names is needed */ - dpns->unique_using = - has_dangerous_join_using(dpns, (Node *) query->jointree); - - /* - * Select names for columns merged by USING, via a recursive pass over - * the query jointree. - */ - set_using_names(dpns, (Node *) query->jointree, NIL); - } - - /* - * Now assign remaining column aliases for each RTE. We do this in a - * linear scan of the rtable, so as to process RTEs whether or not they - * are in the jointree (we mustn't miss NEW.*, INSERT target relations, - * etc). JOIN RTEs must be processed after their children, but this is - * okay because they appear later in the rtable list than their children - * (cf Asserts in identify_join_columns()). - */ - forboth(lc, dpns->rtable, lc2, dpns->rtable_columns) - { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); - deparse_columns *colinfo = (deparse_columns *) lfirst(lc2); - - if (rte->rtekind == RTE_JOIN) - set_join_column_names(dpns, rte, colinfo); - else - set_relation_column_names(dpns, rte, colinfo); - } -} - -/* - * has_dangerous_join_using: search jointree for unnamed JOIN USING - * - * Merged columns of a JOIN USING may act differently from either of the input - * columns, either because they are merged with COALESCE (in a FULL JOIN) or - * because an implicit coercion of the underlying input column is required. - * In such a case the column must be referenced as a column of the JOIN not as - * a column of either input. And this is problematic if the join is unnamed - * (alias-less): we cannot qualify the column's name with an RTE name, since - * there is none. (Forcibly assigning an alias to the join is not a solution, - * since that will prevent legal references to tables below the join.) - * To ensure that every column in the query is unambiguously referenceable, - * we must assign such merged columns names that are globally unique across - * the whole query, aliasing other columns out of the way as necessary. - * - * Because the ensuing re-aliasing is fairly damaging to the readability of - * the query, we don't do this unless we have to. So, we must pre-scan - * the join tree to see if we have to, before starting set_using_names(). - */ -static bool -has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode) -{ - if (IsA(jtnode, RangeTblRef)) - { - /* nothing to do here */ - } - else if (IsA(jtnode, FromExpr)) - { - FromExpr *f = (FromExpr *) jtnode; - ListCell *lc; - - foreach(lc, f->fromlist) - { - if (has_dangerous_join_using(dpns, (Node *) lfirst(lc))) - return true; - } - } - else if (IsA(jtnode, JoinExpr)) - { - JoinExpr *j = (JoinExpr *) jtnode; - - /* Is it an unnamed JOIN with USING? */ - if (j->alias == NULL && j->usingClause) - { - /* - * Yes, so check each join alias var to see if any of them are not - * simple references to underlying columns. If so, we have a - * dangerous situation and must pick unique aliases. - */ - RangeTblEntry *jrte = rt_fetch(j->rtindex, dpns->rtable); - - /* We need only examine the merged columns */ - for (int i = 0; i < jrte->joinmergedcols; i++) - { - Node *aliasvar = list_nth(jrte->joinaliasvars, i); - - if (!IsA(aliasvar, Var)) - return true; - } - } - - /* Nope, but inspect children */ - if (has_dangerous_join_using(dpns, j->larg)) - return true; - if (has_dangerous_join_using(dpns, j->rarg)) - return true; - } - else - elog(ERROR, "unrecognized node type: %d", - (int) nodeTag(jtnode)); - return false; -} - -/* - * set_using_names: select column aliases to be used for merged USING columns - * - * We do this during a recursive descent of the query jointree. - * dpns->unique_using must already be set to determine the global strategy. - * - * Column alias info is saved in the dpns->rtable_columns list, which is - * assumed to be filled with pre-zeroed deparse_columns structs. - * - * parentUsing is a list of all USING aliases assigned in parent joins of - * the current jointree node. (The passed-in list must not be modified.) - */ -static void -set_using_names(deparse_namespace *dpns, Node *jtnode, List *parentUsing) -{ - if (IsA(jtnode, RangeTblRef)) - { - /* nothing to do now */ - } - else if (IsA(jtnode, FromExpr)) - { - FromExpr *f = (FromExpr *) jtnode; - ListCell *lc; - - foreach(lc, f->fromlist) - set_using_names(dpns, (Node *) lfirst(lc), parentUsing); - } - else if (IsA(jtnode, JoinExpr)) - { - JoinExpr *j = (JoinExpr *) jtnode; - RangeTblEntry *rte = rt_fetch(j->rtindex, dpns->rtable); - deparse_columns *colinfo = deparse_columns_fetch(j->rtindex, dpns); - int *leftattnos; - int *rightattnos; - deparse_columns *leftcolinfo; - deparse_columns *rightcolinfo; - int i; - ListCell *lc; - - /* Get info about the shape of the join */ - identify_join_columns(j, rte, colinfo); - leftattnos = colinfo->leftattnos; - rightattnos = colinfo->rightattnos; - - /* Look up the not-yet-filled-in child deparse_columns structs */ - leftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns); - rightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns); - - /* - * If this join is unnamed, then we cannot substitute new aliases at - * this level, so any name requirements pushed down to here must be - * pushed down again to the children. - */ - if (rte->alias == NULL) - { - for (i = 0; i < colinfo->num_cols; i++) - { - char *colname = colinfo->colnames[i]; - - if (colname == NULL) - continue; - - /* Push down to left column, unless it's a system column */ - if (leftattnos[i] > 0) - { - expand_colnames_array_to(leftcolinfo, leftattnos[i]); - leftcolinfo->colnames[leftattnos[i] - 1] = colname; - } - - /* Same on the righthand side */ - if (rightattnos[i] > 0) - { - expand_colnames_array_to(rightcolinfo, rightattnos[i]); - rightcolinfo->colnames[rightattnos[i] - 1] = colname; - } - } - } - - /* - * If there's a USING clause, select the USING column names and push - * those names down to the children. We have two strategies: - * - * If dpns->unique_using is true, we force all USING names to be - * unique across the whole query level. In principle we'd only need - * the names of dangerous USING columns to be globally unique, but to - * safely assign all USING names in a single pass, we have to enforce - * the same uniqueness rule for all of them. However, if a USING - * column's name has been pushed down from the parent, we should use - * it as-is rather than making a uniqueness adjustment. This is - * necessary when we're at an unnamed join, and it creates no risk of - * ambiguity. Also, if there's a user-written output alias for a - * merged column, we prefer to use that rather than the input name; - * this simplifies the logic and seems likely to lead to less aliasing - * overall. - * - * If dpns->unique_using is false, we only need USING names to be - * unique within their own join RTE. We still need to honor - * pushed-down names, though. - * - * Though significantly different in results, these two strategies are - * implemented by the same code, with only the difference of whether - * to put assigned names into dpns->using_names. - */ - if (j->usingClause) - { - /* Copy the input parentUsing list so we don't modify it */ - parentUsing = list_copy(parentUsing); - - /* USING names must correspond to the first join output columns */ - expand_colnames_array_to(colinfo, list_length(j->usingClause)); - i = 0; - foreach(lc, j->usingClause) - { - char *colname = strVal(lfirst(lc)); - - /* Assert it's a merged column */ - Assert(leftattnos[i] != 0 && rightattnos[i] != 0); - - /* Adopt passed-down name if any, else select unique name */ - if (colinfo->colnames[i] != NULL) - colname = colinfo->colnames[i]; - else - { - /* Prefer user-written output alias if any */ - if (rte->alias && i < list_length(rte->alias->colnames)) - colname = strVal(list_nth(rte->alias->colnames, i)); - /* Make it appropriately unique */ - colname = make_colname_unique(colname, dpns, colinfo); - if (dpns->unique_using) - dpns->using_names = lappend(dpns->using_names, - colname); - /* Save it as output column name, too */ - colinfo->colnames[i] = colname; - } - - /* Remember selected names for use later */ - colinfo->usingNames = lappend(colinfo->usingNames, colname); - parentUsing = lappend(parentUsing, colname); - - /* Push down to left column, unless it's a system column */ - if (leftattnos[i] > 0) - { - expand_colnames_array_to(leftcolinfo, leftattnos[i]); - leftcolinfo->colnames[leftattnos[i] - 1] = colname; - } - - /* Same on the righthand side */ - if (rightattnos[i] > 0) - { - expand_colnames_array_to(rightcolinfo, rightattnos[i]); - rightcolinfo->colnames[rightattnos[i] - 1] = colname; - } - - i++; - } - } - - /* Mark child deparse_columns structs with correct parentUsing info */ - leftcolinfo->parentUsing = parentUsing; - rightcolinfo->parentUsing = parentUsing; - - /* Now recursively assign USING column names in children */ - set_using_names(dpns, j->larg, parentUsing); - set_using_names(dpns, j->rarg, parentUsing); - } - else - elog(ERROR, "unrecognized node type: %d", - (int) nodeTag(jtnode)); -} - -/* - * set_relation_column_names: select column aliases for a non-join RTE - * - * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed. - * If any colnames entries are already filled in, those override local - * choices. - */ -static void -set_relation_column_names(deparse_namespace *dpns, RangeTblEntry *rte, - deparse_columns *colinfo) -{ - int ncolumns; - char **real_colnames; - bool changed_any; - bool has_anonymous; - int noldcolumns; - int i; - int j; - - /* - * Extract the RTE's "real" column names. This is comparable to - * get_rte_attribute_name, except that it's important to disregard dropped - * columns. We put NULL into the array for a dropped column. - */ - if (rte->rtekind == RTE_RELATION || - GetRangeTblKind(rte) == CITUS_RTE_SHARD) - { - /* Relation --- look to the system catalogs for up-to-date info */ - Relation rel; - TupleDesc tupdesc; - - rel = relation_open(rte->relid, AccessShareLock); - tupdesc = RelationGetDescr(rel); - - ncolumns = tupdesc->natts; - real_colnames = (char **) palloc(ncolumns * sizeof(char *)); - - for (i = 0; i < ncolumns; i++) - { - Form_pg_attribute attr = TupleDescAttr(tupdesc, i); - - if (attr->attisdropped) - real_colnames[i] = NULL; - else - real_colnames[i] = pstrdup(NameStr(attr->attname)); - } - relation_close(rel, AccessShareLock); - } - else - { - /* Otherwise use the column names from eref */ - ListCell *lc; - - ncolumns = list_length(rte->eref->colnames); - real_colnames = (char **) palloc(ncolumns * sizeof(char *)); - - i = 0; - foreach(lc, rte->eref->colnames) - { - /* - * If the column name shown in eref is an empty string, then it's - * a column that was dropped at the time of parsing the query, so - * treat it as dropped. - */ - char *cname = strVal(lfirst(lc)); - - if (cname[0] == '\0') - cname = NULL; - real_colnames[i] = cname; - i++; - } - } - - /* - * Ensure colinfo->colnames has a slot for each column. (It could be long - * enough already, if we pushed down a name for the last column.) Note: - * it's possible that there are now more columns than there were when the - * query was parsed, ie colnames could be longer than rte->eref->colnames. - * We must assign unique aliases to the new columns too, else there could - * be unresolved conflicts when the view/rule is reloaded. - */ - expand_colnames_array_to(colinfo, ncolumns); - Assert(colinfo->num_cols == ncolumns); - - /* - * Make sufficiently large new_colnames and is_new_col arrays, too. - * - * Note: because we leave colinfo->num_new_cols zero until after the loop, - * colname_is_unique will not consult that array, which is fine because it - * would only be duplicate effort. - */ - colinfo->new_colnames = (char **) palloc(ncolumns * sizeof(char *)); - colinfo->is_new_col = (bool *) palloc(ncolumns * sizeof(bool)); - - /* - * Scan the columns, select a unique alias for each one, and store it in - * colinfo->colnames and colinfo->new_colnames. The former array has NULL - * entries for dropped columns, the latter omits them. Also mark - * new_colnames entries as to whether they are new since parse time; this - * is the case for entries beyond the length of rte->eref->colnames. - */ - noldcolumns = list_length(rte->eref->colnames); - changed_any = false; - has_anonymous = false; - j = 0; - for (i = 0; i < ncolumns; i++) - { - char *real_colname = real_colnames[i]; - char *colname = colinfo->colnames[i]; - - /* Skip dropped columns */ - if (real_colname == NULL) - { - Assert(colname == NULL); /* colnames[i] is already NULL */ - continue; - } - - /* If alias already assigned, that's what to use */ - if (colname == NULL) - { - /* If user wrote an alias, prefer that over real column name */ - if (rte->alias && i < list_length(rte->alias->colnames)) - colname = strVal(list_nth(rte->alias->colnames, i)); - else - colname = real_colname; - - /* Unique-ify and insert into colinfo */ - colname = make_colname_unique(colname, dpns, colinfo); - - colinfo->colnames[i] = colname; - } - - /* Put names of non-dropped columns in new_colnames[] too */ - colinfo->new_colnames[j] = colname; - /* And mark them as new or not */ - colinfo->is_new_col[j] = (i >= noldcolumns); - j++; - - /* Remember if any assigned aliases differ from "real" name */ - if (!changed_any && strcmp(colname, real_colname) != 0) - changed_any = true; - - /* - * Remember if there is a reference to an anonymous column as named by - * char * FigureColname(Node *node) - */ - if (!has_anonymous && strcmp(real_colname, "?column?") == 0) - has_anonymous = true; - } - - /* - * Set correct length for new_colnames[] array. (Note: if columns have - * been added, colinfo->num_cols includes them, which is not really quite - * right but is harmless, since any new columns must be at the end where - * they won't affect varattnos of pre-existing columns.) - */ - colinfo->num_new_cols = j; - - /* - * For a relation RTE, we need only print the alias column names if any - * are different from the underlying "real" names. For a function RTE, - * always emit a complete column alias list; this is to protect against - * possible instability of the default column names (eg, from altering - * parameter names). For tablefunc RTEs, we never print aliases, because - * the column names are part of the clause itself. For other RTE types, - * print if we changed anything OR if there were user-written column - * aliases (since the latter would be part of the underlying "reality"). - */ - if (rte->rtekind == RTE_RELATION) - colinfo->printaliases = changed_any; - else if (rte->rtekind == RTE_FUNCTION) - colinfo->printaliases = true; - else if (rte->rtekind == RTE_TABLEFUNC) - colinfo->printaliases = false; - else if (rte->alias && rte->alias->colnames != NIL) - colinfo->printaliases = true; - else - colinfo->printaliases = changed_any || has_anonymous; -} - -/* - * set_join_column_names: select column aliases for a join RTE - * - * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed. - * If any colnames entries are already filled in, those override local - * choices. Also, names for USING columns were already chosen by - * set_using_names(). We further expect that column alias selection has been - * completed for both input RTEs. - */ -static void -set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte, - deparse_columns *colinfo) -{ - deparse_columns *leftcolinfo; - deparse_columns *rightcolinfo; - bool changed_any; - int noldcolumns; - int nnewcolumns; - Bitmapset *leftmerged = NULL; - Bitmapset *rightmerged = NULL; - int i; - int j; - int ic; - int jc; - - /* Look up the previously-filled-in child deparse_columns structs */ - leftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns); - rightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns); - - /* - * Ensure colinfo->colnames has a slot for each column. (It could be long - * enough already, if we pushed down a name for the last column.) Note: - * it's possible that one or both inputs now have more columns than there - * were when the query was parsed, but we'll deal with that below. We - * only need entries in colnames for pre-existing columns. - */ - noldcolumns = list_length(rte->eref->colnames); - expand_colnames_array_to(colinfo, noldcolumns); - Assert(colinfo->num_cols == noldcolumns); - - /* - * Scan the join output columns, select an alias for each one, and store - * it in colinfo->colnames. If there are USING columns, set_using_names() - * already selected their names, so we can start the loop at the first - * non-merged column. - */ - changed_any = false; - for (i = list_length(colinfo->usingNames); i < noldcolumns; i++) - { - char *colname = colinfo->colnames[i]; - char *real_colname; - - /* Join column must refer to at least one input column */ - Assert(colinfo->leftattnos[i] != 0 || colinfo->rightattnos[i] != 0); - - /* Get the child column name */ - if (colinfo->leftattnos[i] > 0) - real_colname = leftcolinfo->colnames[colinfo->leftattnos[i] - 1]; - else if (colinfo->rightattnos[i] > 0) - real_colname = rightcolinfo->colnames[colinfo->rightattnos[i] - 1]; - else - { - /* We're joining system columns --- use eref name */ - real_colname = strVal(list_nth(rte->eref->colnames, i)); - } - /* If child col has been dropped, no need to assign a join colname */ - if (real_colname == NULL) - { - colinfo->colnames[i] = NULL; - continue; - } - - /* In an unnamed join, just report child column names as-is */ - if (rte->alias == NULL) - { - colinfo->colnames[i] = real_colname; - continue; - } - - /* If alias already assigned, that's what to use */ - if (colname == NULL) - { - /* If user wrote an alias, prefer that over real column name */ - if (rte->alias && i < list_length(rte->alias->colnames)) - colname = strVal(list_nth(rte->alias->colnames, i)); - else - colname = real_colname; - - /* Unique-ify and insert into colinfo */ - colname = make_colname_unique(colname, dpns, colinfo); - - colinfo->colnames[i] = colname; - } - - /* Remember if any assigned aliases differ from "real" name */ - if (!changed_any && strcmp(colname, real_colname) != 0) - changed_any = true; - } - - /* - * Calculate number of columns the join would have if it were re-parsed - * now, and create storage for the new_colnames and is_new_col arrays. - * - * Note: colname_is_unique will be consulting new_colnames[] during the - * loops below, so its not-yet-filled entries must be zeroes. - */ - nnewcolumns = leftcolinfo->num_new_cols + rightcolinfo->num_new_cols - - list_length(colinfo->usingNames); - colinfo->num_new_cols = nnewcolumns; - colinfo->new_colnames = (char **) palloc0(nnewcolumns * sizeof(char *)); - colinfo->is_new_col = (bool *) palloc0(nnewcolumns * sizeof(bool)); - - /* - * Generating the new_colnames array is a bit tricky since any new columns - * added since parse time must be inserted in the right places. This code - * must match the parser, which will order a join's columns as merged - * columns first (in USING-clause order), then non-merged columns from the - * left input (in attnum order), then non-merged columns from the right - * input (ditto). If one of the inputs is itself a join, its columns will - * be ordered according to the same rule, which means newly-added columns - * might not be at the end. We can figure out what's what by consulting - * the leftattnos and rightattnos arrays plus the input is_new_col arrays. - * - * In these loops, i indexes leftattnos/rightattnos (so it's join varattno - * less one), j indexes new_colnames/is_new_col, and ic/jc have similar - * meanings for the current child RTE. - */ - - /* Handle merged columns; they are first and can't be new */ - i = j = 0; - while (i < noldcolumns && - colinfo->leftattnos[i] != 0 && - colinfo->rightattnos[i] != 0) - { - /* column name is already determined and known unique */ - colinfo->new_colnames[j] = colinfo->colnames[i]; - colinfo->is_new_col[j] = false; - - /* build bitmapsets of child attnums of merged columns */ - if (colinfo->leftattnos[i] > 0) - leftmerged = bms_add_member(leftmerged, colinfo->leftattnos[i]); - if (colinfo->rightattnos[i] > 0) - rightmerged = bms_add_member(rightmerged, colinfo->rightattnos[i]); - - i++, j++; - } - - /* Handle non-merged left-child columns */ - ic = 0; - for (jc = 0; jc < leftcolinfo->num_new_cols; jc++) - { - char *child_colname = leftcolinfo->new_colnames[jc]; - - if (!leftcolinfo->is_new_col[jc]) - { - /* Advance ic to next non-dropped old column of left child */ - while (ic < leftcolinfo->num_cols && - leftcolinfo->colnames[ic] == NULL) - ic++; - Assert(ic < leftcolinfo->num_cols); - ic++; - /* If it is a merged column, we already processed it */ - if (bms_is_member(ic, leftmerged)) - continue; - /* Else, advance i to the corresponding existing join column */ - while (i < colinfo->num_cols && - colinfo->colnames[i] == NULL) - i++; - Assert(i < colinfo->num_cols); - Assert(ic == colinfo->leftattnos[i]); - /* Use the already-assigned name of this column */ - colinfo->new_colnames[j] = colinfo->colnames[i]; - i++; - } - else - { - /* - * Unique-ify the new child column name and assign, unless we're - * in an unnamed join, in which case just copy - */ - if (rte->alias != NULL) - { - colinfo->new_colnames[j] = - make_colname_unique(child_colname, dpns, colinfo); - if (!changed_any && - strcmp(colinfo->new_colnames[j], child_colname) != 0) - changed_any = true; - } - else - colinfo->new_colnames[j] = child_colname; - } - - colinfo->is_new_col[j] = leftcolinfo->is_new_col[jc]; - j++; - } - - /* Handle non-merged right-child columns in exactly the same way */ - ic = 0; - for (jc = 0; jc < rightcolinfo->num_new_cols; jc++) - { - char *child_colname = rightcolinfo->new_colnames[jc]; - - if (!rightcolinfo->is_new_col[jc]) - { - /* Advance ic to next non-dropped old column of right child */ - while (ic < rightcolinfo->num_cols && - rightcolinfo->colnames[ic] == NULL) - ic++; - Assert(ic < rightcolinfo->num_cols); - ic++; - /* If it is a merged column, we already processed it */ - if (bms_is_member(ic, rightmerged)) - continue; - /* Else, advance i to the corresponding existing join column */ - while (i < colinfo->num_cols && - colinfo->colnames[i] == NULL) - i++; - Assert(i < colinfo->num_cols); - Assert(ic == colinfo->rightattnos[i]); - /* Use the already-assigned name of this column */ - colinfo->new_colnames[j] = colinfo->colnames[i]; - i++; - } - else - { - /* - * Unique-ify the new child column name and assign, unless we're - * in an unnamed join, in which case just copy - */ - if (rte->alias != NULL) - { - colinfo->new_colnames[j] = - make_colname_unique(child_colname, dpns, colinfo); - if (!changed_any && - strcmp(colinfo->new_colnames[j], child_colname) != 0) - changed_any = true; - } - else - colinfo->new_colnames[j] = child_colname; - } - - colinfo->is_new_col[j] = rightcolinfo->is_new_col[jc]; - j++; - } - - /* Assert we processed the right number of columns */ -#ifdef USE_ASSERT_CHECKING - while (i < colinfo->num_cols && colinfo->colnames[i] == NULL) - i++; - Assert(i == colinfo->num_cols); - Assert(j == nnewcolumns); -#endif - - /* - * For a named join, print column aliases if we changed any from the child - * names. Unnamed joins cannot print aliases. - */ - if (rte->alias != NULL) - colinfo->printaliases = changed_any; - else - colinfo->printaliases = false; -} - -/* - * colname_is_unique: is colname distinct from already-chosen column names? - * - * dpns is query-wide info, colinfo is for the column's RTE - */ -static bool -colname_is_unique(const char *colname, deparse_namespace *dpns, - deparse_columns *colinfo) -{ - int i; - ListCell *lc; - - /* Check against already-assigned column aliases within RTE */ - for (i = 0; i < colinfo->num_cols; i++) - { - char *oldname = colinfo->colnames[i]; - - if (oldname && strcmp(oldname, colname) == 0) - return false; - } - - /* - * If we're building a new_colnames array, check that too (this will be - * partially but not completely redundant with the previous checks) - */ - for (i = 0; i < colinfo->num_new_cols; i++) - { - char *oldname = colinfo->new_colnames[i]; - - if (oldname && strcmp(oldname, colname) == 0) - return false; - } - - /* Also check against USING-column names that must be globally unique */ - foreach(lc, dpns->using_names) - { - char *oldname = (char *) lfirst(lc); - - if (strcmp(oldname, colname) == 0) - return false; - } - - /* Also check against names already assigned for parent-join USING cols */ - foreach(lc, colinfo->parentUsing) - { - char *oldname = (char *) lfirst(lc); - - if (strcmp(oldname, colname) == 0) - return false; - } - - return true; -} - -/* - * make_colname_unique: modify colname if necessary to make it unique - * - * dpns is query-wide info, colinfo is for the column's RTE - */ -static char * -make_colname_unique(char *colname, deparse_namespace *dpns, - deparse_columns *colinfo) -{ - /* - * If the selected name isn't unique, append digits to make it so. For a - * very long input name, we might have to truncate to stay within - * NAMEDATALEN. - */ - if (!colname_is_unique(colname, dpns, colinfo)) - { - int colnamelen = strlen(colname); - char *modname = (char *) palloc(colnamelen + 16); - int i = 0; - - do - { - i++; - for (;;) - { - /* - * We avoid using %.*s here because it can misbehave if the - * data is not valid in what libc thinks is the prevailing - * encoding. - */ - memcpy(modname, colname, colnamelen); - sprintf(modname + colnamelen, "_%d", i); - if (strlen(modname) < NAMEDATALEN) - break; - /* drop chars from colname to keep all the digits */ - colnamelen = pg_mbcliplen(colname, colnamelen, - colnamelen - 1); - } - } while (!colname_is_unique(modname, dpns, colinfo)); - colname = modname; - } - return colname; -} - -/* - * expand_colnames_array_to: make colinfo->colnames at least n items long - * - * Any added array entries are initialized to zero. - */ -static void -expand_colnames_array_to(deparse_columns *colinfo, int n) -{ - if (n > colinfo->num_cols) - { - if (colinfo->colnames == NULL) - colinfo->colnames = (char **) palloc0(n * sizeof(char *)); - else - { - colinfo->colnames = (char **) repalloc(colinfo->colnames, - n * sizeof(char *)); - memset(colinfo->colnames + colinfo->num_cols, 0, - (n - colinfo->num_cols) * sizeof(char *)); - } - colinfo->num_cols = n; - } -} - -/* - * identify_join_columns: figure out where columns of a join come from - * - * Fills the join-specific fields of the colinfo struct, except for - * usingNames which is filled later. - */ -static void -identify_join_columns(JoinExpr *j, RangeTblEntry *jrte, - deparse_columns *colinfo) -{ - int numjoincols; - int jcolno; - int rcolno; - ListCell *lc; - - /* Extract left/right child RT indexes */ - if (IsA(j->larg, RangeTblRef)) - colinfo->leftrti = ((RangeTblRef *) j->larg)->rtindex; - else if (IsA(j->larg, JoinExpr)) - colinfo->leftrti = ((JoinExpr *) j->larg)->rtindex; - else - elog(ERROR, "unrecognized node type in jointree: %d", - (int) nodeTag(j->larg)); - if (IsA(j->rarg, RangeTblRef)) - colinfo->rightrti = ((RangeTblRef *) j->rarg)->rtindex; - else if (IsA(j->rarg, JoinExpr)) - colinfo->rightrti = ((JoinExpr *) j->rarg)->rtindex; - else - elog(ERROR, "unrecognized node type in jointree: %d", - (int) nodeTag(j->rarg)); - - /* Assert children will be processed earlier than join in second pass */ - Assert(colinfo->leftrti < j->rtindex); - Assert(colinfo->rightrti < j->rtindex); - - /* Initialize result arrays with zeroes */ - numjoincols = list_length(jrte->joinaliasvars); - Assert(numjoincols == list_length(jrte->eref->colnames)); - colinfo->leftattnos = (int *) palloc0(numjoincols * sizeof(int)); - colinfo->rightattnos = (int *) palloc0(numjoincols * sizeof(int)); - - /* - * Deconstruct RTE's joinleftcols/joinrightcols into desired format. - * Recall that the column(s) merged due to USING are the first column(s) - * of the join output. We need not do anything special while scanning - * joinleftcols, but while scanning joinrightcols we must distinguish - * merged from unmerged columns. - */ - jcolno = 0; - foreach(lc, jrte->joinleftcols) - { - int leftattno = lfirst_int(lc); - - colinfo->leftattnos[jcolno++] = leftattno; - } - rcolno = 0; - foreach(lc, jrte->joinrightcols) - { - int rightattno = lfirst_int(lc); - - if (rcolno < jrte->joinmergedcols) /* merged column? */ - colinfo->rightattnos[rcolno] = rightattno; - else - colinfo->rightattnos[jcolno++] = rightattno; - rcolno++; - } - Assert(jcolno == numjoincols); -} - -/* - * get_rtable_name: convenience function to get a previously assigned RTE alias - * - * The RTE must belong to the topmost namespace level in "context". - */ -static char * -get_rtable_name(int rtindex, deparse_context *context) -{ - deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces); - - Assert(rtindex > 0 && rtindex <= list_length(dpns->rtable_names)); - return (char *) list_nth(dpns->rtable_names, rtindex - 1); -} - -/* - * set_deparse_plan: set up deparse_namespace to parse subexpressions - * of a given Plan node - * - * This sets the plan, outer_planstate, inner_planstate, outer_tlist, - * inner_tlist, and index_tlist fields. Caller is responsible for adjusting - * the ancestors list if necessary. Note that the rtable and ctes fields do - * not need to change when shifting attention to different plan nodes in a - * single plan tree. - */ -static void -set_deparse_plan(deparse_namespace *dpns, Plan *plan) -{ - dpns->plan = plan; - - /* - * We special-case Append and MergeAppend to pretend that the first child - * plan is the OUTER referent; we have to interpret OUTER Vars in their - * tlists according to one of the children, and the first one is the most - * natural choice. Likewise special-case ModifyTable to pretend that the - * first child plan is the OUTER referent; this is to support RETURNING - * lists containing references to non-target relations. - */ - if (IsA(plan, Append)) - dpns->outer_plan = linitial(((Append *) plan)->appendplans); - else if (IsA(plan, MergeAppend)) - dpns->outer_plan = linitial(((MergeAppend *) plan)->mergeplans); - else if (IsA(plan, ModifyTable)) - dpns->outer_plan = linitial(((ModifyTable *) plan)->plans); - else - dpns->outer_plan = outerPlan(plan); - - if (dpns->outer_plan) - dpns->outer_tlist = dpns->outer_plan->targetlist; - else - dpns->outer_tlist = NIL; - - /* - * For a SubqueryScan, pretend the subplan is INNER referent. (We don't - * use OUTER because that could someday conflict with the normal meaning.) - * Likewise, for a CteScan, pretend the subquery's plan is INNER referent. - * For ON CONFLICT .. UPDATE we just need the inner tlist to point to the - * excluded expression's tlist. (Similar to the SubqueryScan we don't want - * to reuse OUTER, it's used for RETURNING in some modify table cases, - * although not INSERT .. CONFLICT). - */ - if (IsA(plan, SubqueryScan)) - dpns->inner_plan = ((SubqueryScan *) plan)->subplan; - else if (IsA(plan, CteScan)) - dpns->inner_plan = list_nth(dpns->subplans, - ((CteScan *) plan)->ctePlanId - 1); - else if (IsA(plan, ModifyTable)) - dpns->inner_plan = plan; - else - dpns->inner_plan = innerPlan(plan); - - if (IsA(plan, ModifyTable)) - dpns->inner_tlist = ((ModifyTable *) plan)->exclRelTlist; - else if (dpns->inner_plan) - dpns->inner_tlist = dpns->inner_plan->targetlist; - else - dpns->inner_tlist = NIL; - - /* Set up referent for INDEX_VAR Vars, if needed */ - if (IsA(plan, IndexOnlyScan)) - dpns->index_tlist = ((IndexOnlyScan *) plan)->indextlist; - else if (IsA(plan, ForeignScan)) - dpns->index_tlist = ((ForeignScan *) plan)->fdw_scan_tlist; - else if (IsA(plan, CustomScan)) - dpns->index_tlist = ((CustomScan *) plan)->custom_scan_tlist; - else - dpns->index_tlist = NIL; -} - -/* - * push_child_plan: temporarily transfer deparsing attention to a child plan - * - * When expanding an OUTER_VAR or INNER_VAR reference, we must adjust the - * deparse context in case the referenced expression itself uses - * OUTER_VAR/INNER_VAR. We modify the top stack entry in-place to avoid - * affecting levelsup issues (although in a Plan tree there really shouldn't - * be any). - * - * Caller must provide a local deparse_namespace variable to save the - * previous state for pop_child_plan. - */ -static void -push_child_plan(deparse_namespace *dpns, Plan *plan, - deparse_namespace *save_dpns) -{ - /* Save state for restoration later */ - *save_dpns = *dpns; - - /* Link current plan node into ancestors list */ - dpns->ancestors = lcons(dpns->plan, dpns->ancestors); - - /* Set attention on selected child */ - set_deparse_plan(dpns, plan); -} - -/* - * pop_child_plan: undo the effects of push_child_plan - */ -static void -pop_child_plan(deparse_namespace *dpns, deparse_namespace *save_dpns) -{ - List *ancestors; - - /* Get rid of ancestors list cell added by push_child_plan */ - ancestors = list_delete_first(dpns->ancestors); - - /* Restore fields changed by push_child_plan */ - *dpns = *save_dpns; - - /* Make sure dpns->ancestors is right (may be unnecessary) */ - dpns->ancestors = ancestors; -} - -/* - * push_ancestor_plan: temporarily transfer deparsing attention to an - * ancestor plan - * - * When expanding a Param reference, we must adjust the deparse context - * to match the plan node that contains the expression being printed; - * otherwise we'd fail if that expression itself contains a Param or - * OUTER_VAR/INNER_VAR/INDEX_VAR variable. - * - * The target ancestor is conveniently identified by the ListCell holding it - * in dpns->ancestors. - * - * Caller must provide a local deparse_namespace variable to save the - * previous state for pop_ancestor_plan. - */ -static void -push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell, - deparse_namespace *save_dpns) -{ - Plan *plan = (Plan *) lfirst(ancestor_cell); - - /* Save state for restoration later */ - *save_dpns = *dpns; - - /* Build a new ancestor list with just this node's ancestors */ - dpns->ancestors = - list_copy_tail(dpns->ancestors, - list_cell_number(dpns->ancestors, ancestor_cell) + 1); - - /* Set attention on selected ancestor */ - set_deparse_plan(dpns, plan); -} - -/* - * pop_ancestor_plan: undo the effects of push_ancestor_plan - */ -static void -pop_ancestor_plan(deparse_namespace *dpns, deparse_namespace *save_dpns) -{ - /* Free the ancestor list made in push_ancestor_plan */ - list_free(dpns->ancestors); - - /* Restore fields changed by push_ancestor_plan */ - *dpns = *save_dpns; -} - - -/* ---------- - * deparse_shard_query - Parse back a query for execution on a shard - * - * Builds an SQL string to perform the provided query on a specific shard and - * places this string into the provided buffer. - * ---------- - */ -void -deparse_shard_query(Query *query, Oid distrelid, int64 shardid, - StringInfo buffer) -{ - get_query_def_extended(query, buffer, NIL, distrelid, shardid, NULL, 0, - WRAP_COLUMN_DEFAULT, 0); -} - - -/* ---------- - * get_query_def - Parse back one query parsetree - * - * If resultDesc is not NULL, then it is the output tuple descriptor for - * the view represented by a SELECT query. - * ---------- - */ -static void -get_query_def(Query *query, StringInfo buf, List *parentnamespace, - TupleDesc resultDesc, - int prettyFlags, int wrapColumn, int startIndent) -{ - get_query_def_extended(query, buf, parentnamespace, InvalidOid, 0, resultDesc, - prettyFlags, wrapColumn, startIndent); -} - - -/* ---------- - * get_query_def_extended - Parse back one query parsetree, optionally - * with extension using a shard identifier. - * - * If distrelid is valid and shardid is positive, the provided shardid is added - * any time the provided relid is deparsed, so that the query may be executed - * on a placement for the given shard. - * ---------- - */ -static void -get_query_def_extended(Query *query, StringInfo buf, List *parentnamespace, - Oid distrelid, int64 shardid, TupleDesc resultDesc, - int prettyFlags, int wrapColumn, int startIndent) -{ - deparse_context context; - deparse_namespace dpns; - - OverrideSearchPath *overridePath = NULL; - - /* Guard against excessively long or deeply-nested queries */ - CHECK_FOR_INTERRUPTS(); - check_stack_depth(); - - /* - * Before we begin to examine the query, acquire locks on referenced - * relations, and fix up deleted columns in JOIN RTEs. This ensures - * consistent results. Note we assume it's OK to scribble on the passed - * querytree! - * - * We are only deparsing the query (we are not about to execute it), so we - * only need AccessShareLock on the relations it mentions. - */ - AcquireRewriteLocks(query, false, false); - - /* - * Set search_path to NIL so that all objects outside of pg_catalog will be - * schema-prefixed. pg_catalog will be added automatically when we call - * PushOverrideSearchPath(), since we set addCatalog to true; - */ - overridePath = GetOverrideSearchPath(CurrentMemoryContext); - overridePath->schemas = NIL; - overridePath->addCatalog = true; - PushOverrideSearchPath(overridePath); - - context.buf = buf; - context.namespaces = lcons(&dpns, list_copy(parentnamespace)); - context.windowClause = NIL; - context.windowTList = NIL; - context.varprefix = (parentnamespace != NIL || - list_length(query->rtable) != 1); - context.prettyFlags = prettyFlags; - context.wrapColumn = wrapColumn; - context.indentLevel = startIndent; - context.special_exprkind = EXPR_KIND_NONE; - context.appendparents = NULL; - context.distrelid = distrelid; - context.shardid = shardid; - - set_deparse_for_query(&dpns, query, parentnamespace); - - switch (query->commandType) - { - case CMD_SELECT: - get_select_query_def(query, &context, resultDesc); - break; - - case CMD_UPDATE: - get_update_query_def(query, &context); - break; - - case CMD_INSERT: - get_insert_query_def(query, &context); - break; - - case CMD_DELETE: - get_delete_query_def(query, &context); - break; - - case CMD_NOTHING: - appendStringInfoString(buf, "NOTHING"); - break; - - case CMD_UTILITY: - get_utility_query_def(query, &context); - break; - - default: - elog(ERROR, "unrecognized query command type: %d", - query->commandType); - break; - } - - /* revert back to original search_path */ - PopOverrideSearchPath(); -} - -/* ---------- - * get_values_def - Parse back a VALUES list - * ---------- - */ -static void -get_values_def(List *values_lists, deparse_context *context) -{ - StringInfo buf = context->buf; - bool first_list = true; - ListCell *vtl; - - appendStringInfoString(buf, "VALUES "); - - foreach(vtl, values_lists) - { - List *sublist = (List *) lfirst(vtl); - bool first_col = true; - ListCell *lc; - - if (first_list) - first_list = false; - else - appendStringInfoString(buf, ", "); - - appendStringInfoChar(buf, '('); - foreach(lc, sublist) - { - Node *col = (Node *) lfirst(lc); - - if (first_col) - first_col = false; - else - appendStringInfoChar(buf, ','); - - /* - * Print the value. Whole-row Vars need special treatment. - */ - get_rule_expr_toplevel(col, context, false); - } - appendStringInfoChar(buf, ')'); - } -} - -/* ---------- - * get_with_clause - Parse back a WITH clause - * ---------- - */ -static void -get_with_clause(Query *query, deparse_context *context) -{ - StringInfo buf = context->buf; - const char *sep; - ListCell *l; - - if (query->cteList == NIL) - return; - - if (PRETTY_INDENT(context)) - { - context->indentLevel += PRETTYINDENT_STD; - appendStringInfoChar(buf, ' '); - } - - if (query->hasRecursive) - sep = "WITH RECURSIVE "; - else - sep = "WITH "; - foreach(l, query->cteList) - { - CommonTableExpr *cte = (CommonTableExpr *) lfirst(l); - - appendStringInfoString(buf, sep); - appendStringInfoString(buf, quote_identifier(cte->ctename)); - if (cte->aliascolnames) - { - bool first = true; - ListCell *col; - - appendStringInfoChar(buf, '('); - foreach(col, cte->aliascolnames) - { - if (first) - first = false; - else - appendStringInfoString(buf, ", "); - appendStringInfoString(buf, - quote_identifier(strVal(lfirst(col)))); - } - appendStringInfoChar(buf, ')'); - } - appendStringInfoString(buf, " AS "); - switch (cte->ctematerialized) - { - case CTEMaterializeDefault: - break; - case CTEMaterializeAlways: - appendStringInfoString(buf, "MATERIALIZED "); - break; - case CTEMaterializeNever: - appendStringInfoString(buf, "NOT MATERIALIZED "); - break; - } - appendStringInfoChar(buf, '('); - if (PRETTY_INDENT(context)) - appendContextKeyword(context, "", 0, 0, 0); - get_query_def((Query *) cte->ctequery, buf, context->namespaces, NULL, - context->prettyFlags, context->wrapColumn, - context->indentLevel); - if (PRETTY_INDENT(context)) - appendContextKeyword(context, "", 0, 0, 0); - appendStringInfoChar(buf, ')'); - sep = ", "; - } - - if (PRETTY_INDENT(context)) - { - context->indentLevel -= PRETTYINDENT_STD; - appendContextKeyword(context, "", 0, 0, 0); - } - else - appendStringInfoChar(buf, ' '); -} - -/* ---------- - * get_select_query_def - Parse back a SELECT parsetree - * ---------- - */ -static void -get_select_query_def(Query *query, deparse_context *context, - TupleDesc resultDesc) -{ - StringInfo buf = context->buf; - List *save_windowclause; - List *save_windowtlist; - bool force_colno; - ListCell *l; - - /* Insert the WITH clause if given */ - get_with_clause(query, context); - - /* Set up context for possible window functions */ - save_windowclause = context->windowClause; - context->windowClause = query->windowClause; - save_windowtlist = context->windowTList; - context->windowTList = query->targetList; - - /* - * If the Query node has a setOperations tree, then it's the top level of - * a UNION/INTERSECT/EXCEPT query; only the WITH, ORDER BY and LIMIT - * fields are interesting in the top query itself. - */ - if (query->setOperations) - { - get_setop_query(query->setOperations, query, context, resultDesc); - /* ORDER BY clauses must be simple in this case */ - force_colno = true; - } - else - { - get_basic_select_query(query, context, resultDesc); - force_colno = false; - } - - /* Add the ORDER BY clause if given */ - if (query->sortClause != NIL) - { - appendContextKeyword(context, " ORDER BY ", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); - get_rule_orderby(query->sortClause, query->targetList, - force_colno, context); - } - - /* - * Add the LIMIT/OFFSET clauses if given. If non-default options, use the - * standard spelling of LIMIT. - */ - if (query->limitOffset != NULL) - { - appendContextKeyword(context, " OFFSET ", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); - get_rule_expr(query->limitOffset, context, false); - } - if (query->limitCount != NULL) - { - if (query->limitOption == LIMIT_OPTION_WITH_TIES) - { - // had to add '(' and ')' here because it fails with casting - appendContextKeyword(context, " FETCH FIRST (", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); - get_rule_expr(query->limitCount, context, false); - appendStringInfo(buf, ") ROWS WITH TIES"); - } - else - { - appendContextKeyword(context, " LIMIT ", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); - if (IsA(query->limitCount, Const) && - ((Const *) query->limitCount)->constisnull) - appendStringInfoString(buf, "ALL"); - else - get_rule_expr(query->limitCount, context, false); - } - } - - /* Add FOR [KEY] UPDATE/SHARE clauses if present */ - if (query->hasForUpdate) - { - foreach(l, query->rowMarks) - { - RowMarkClause *rc = (RowMarkClause *) lfirst(l); - - /* don't print implicit clauses */ - if (rc->pushedDown) - continue; - - switch (rc->strength) - { - case LCS_NONE: - /* we intentionally throw an error for LCS_NONE */ - elog(ERROR, "unrecognized LockClauseStrength %d", - (int) rc->strength); - break; - case LCS_FORKEYSHARE: - appendContextKeyword(context, " FOR KEY SHARE", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); - break; - case LCS_FORSHARE: - appendContextKeyword(context, " FOR SHARE", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); - break; - case LCS_FORNOKEYUPDATE: - appendContextKeyword(context, " FOR NO KEY UPDATE", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); - break; - case LCS_FORUPDATE: - appendContextKeyword(context, " FOR UPDATE", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); - break; - } - - appendStringInfo(buf, " OF %s", - quote_identifier(get_rtable_name(rc->rti, - context))); - if (rc->waitPolicy == LockWaitError) - appendStringInfoString(buf, " NOWAIT"); - else if (rc->waitPolicy == LockWaitSkip) - appendStringInfoString(buf, " SKIP LOCKED"); - } - } - - context->windowClause = save_windowclause; - context->windowTList = save_windowtlist; -} - -/* - * Detect whether query looks like SELECT ... FROM VALUES(); - * if so, return the VALUES RTE. Otherwise return NULL. - */ -static RangeTblEntry * -get_simple_values_rte(Query *query, TupleDesc resultDesc) -{ - RangeTblEntry *result = NULL; - ListCell *lc; - int colno; - - /* - * We want to return true even if the Query also contains OLD or NEW rule - * RTEs. So the idea is to scan the rtable and see if there is only one - * inFromCl RTE that is a VALUES RTE. - */ - foreach(lc, query->rtable) - { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc); - - if (rte->rtekind == RTE_VALUES && rte->inFromCl) - { - if (result) - return NULL; /* multiple VALUES (probably not possible) */ - result = rte; - } - else if (rte->rtekind == RTE_RELATION && !rte->inFromCl) - continue; /* ignore rule entries */ - else - return NULL; /* something else -> not simple VALUES */ - } - - /* - * We don't need to check the targetlist in any great detail, because - * parser/analyze.c will never generate a "bare" VALUES RTE --- they only - * appear inside auto-generated sub-queries with very restricted - * structure. However, DefineView might have modified the tlist by - * injecting new column aliases; so compare tlist resnames against the - * RTE's names to detect that. - */ - if (result) - { - ListCell *lcn; - - if (list_length(query->targetList) != list_length(result->eref->colnames)) - return NULL; /* this probably cannot happen */ - colno = 0; - forboth(lc, query->targetList, lcn, result->eref->colnames) - { - TargetEntry *tle = (TargetEntry *) lfirst(lc); - char *cname = strVal(lfirst(lcn)); - char *colname; - - if (tle->resjunk) - return NULL; /* this probably cannot happen */ - /* compute name that get_target_list would use for column */ - colno++; - if (resultDesc && colno <= resultDesc->natts) - colname = NameStr(TupleDescAttr(resultDesc, colno - 1)->attname); - else - colname = tle->resname; - - /* does it match the VALUES RTE? */ - if (colname == NULL || strcmp(colname, cname) != 0) - return NULL; /* column name has been changed */ - } - } - - return result; -} - -static void -get_basic_select_query(Query *query, deparse_context *context, - TupleDesc resultDesc) -{ - StringInfo buf = context->buf; - RangeTblEntry *values_rte; - char *sep; - ListCell *l; - - if (PRETTY_INDENT(context)) - { - context->indentLevel += PRETTYINDENT_STD; - appendStringInfoChar(buf, ' '); - } - - /* - * If the query looks like SELECT * FROM (VALUES ...), then print just the - * VALUES part. This reverses what transformValuesClause() did at parse - * time. - */ - values_rte = get_simple_values_rte(query, resultDesc); - if (values_rte) - { - get_values_def(values_rte->values_lists, context); - return; - } - - /* - * Build up the query string - first we say SELECT - */ - appendStringInfoString(buf, "SELECT"); - - /* Add the DISTINCT clause if given */ - if (query->distinctClause != NIL) - { - if (query->hasDistinctOn) - { - appendStringInfoString(buf, " DISTINCT ON ("); - sep = ""; - foreach(l, query->distinctClause) - { - SortGroupClause *srt = (SortGroupClause *) lfirst(l); - - appendStringInfoString(buf, sep); - get_rule_sortgroupclause(srt->tleSortGroupRef, query->targetList, - false, context); - sep = ", "; - } - appendStringInfoChar(buf, ')'); - } - else - appendStringInfoString(buf, " DISTINCT"); - } - - /* Then we tell what to select (the targetlist) */ - get_target_list(query->targetList, context, resultDesc); - - /* Add the FROM clause if needed */ - get_from_clause(query, " FROM ", context); - - /* Add the WHERE clause if given */ - if (query->jointree->quals != NULL) - { - appendContextKeyword(context, " WHERE ", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); - get_rule_expr(query->jointree->quals, context, false); - } - - /* Add the GROUP BY clause if given */ - if (query->groupClause != NULL || query->groupingSets != NULL) - { - ParseExprKind save_exprkind; - - appendContextKeyword(context, " GROUP BY ", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); - - save_exprkind = context->special_exprkind; - context->special_exprkind = EXPR_KIND_GROUP_BY; - - if (query->groupingSets == NIL) - { - sep = ""; - foreach(l, query->groupClause) - { - SortGroupClause *grp = (SortGroupClause *) lfirst(l); - - appendStringInfoString(buf, sep); - get_rule_sortgroupclause(grp->tleSortGroupRef, query->targetList, - false, context); - sep = ", "; - } - } - else - { - sep = ""; - foreach(l, query->groupingSets) - { - GroupingSet *grp = lfirst(l); - - appendStringInfoString(buf, sep); - get_rule_groupingset(grp, query->targetList, true, context); - sep = ", "; - } - } - - context->special_exprkind = save_exprkind; - } - - /* Add the HAVING clause if given */ - if (query->havingQual != NULL) - { - appendContextKeyword(context, " HAVING ", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); - get_rule_expr(query->havingQual, context, false); - } - - /* Add the WINDOW clause if needed */ - if (query->windowClause != NIL) - get_rule_windowclause(query, context); -} - -/* ---------- - * get_target_list - Parse back a SELECT target list - * - * This is also used for RETURNING lists in INSERT/UPDATE/DELETE. - * ---------- - */ -static void -get_target_list(List *targetList, deparse_context *context, - TupleDesc resultDesc) -{ - StringInfo buf = context->buf; - StringInfoData targetbuf; - bool last_was_multiline = false; - char *sep; - int colno; - ListCell *l; - - /* we use targetbuf to hold each TLE's text temporarily */ - initStringInfo(&targetbuf); - - sep = " "; - colno = 0; - foreach(l, targetList) - { - TargetEntry *tle = (TargetEntry *) lfirst(l); - char *colname; - char *attname; - - if (tle->resjunk) - continue; /* ignore junk entries */ - - appendStringInfoString(buf, sep); - sep = ", "; - colno++; - - /* - * Put the new field text into targetbuf so we can decide after we've - * got it whether or not it needs to go on a new line. - */ - resetStringInfo(&targetbuf); - context->buf = &targetbuf; - - /* - * We special-case Var nodes rather than using get_rule_expr. This is - * needed because get_rule_expr will display a whole-row Var as - * "foo.*", which is the preferred notation in most contexts, but at - * the top level of a SELECT list it's not right (the parser will - * expand that notation into multiple columns, yielding behavior - * different from a whole-row Var). We need to call get_variable - * directly so that we can tell it to do the right thing, and so that - * we can get the attribute name which is the default AS label. - */ - if (tle->expr && (IsA(tle->expr, Var))) - { - attname = get_variable((Var *) tle->expr, 0, true, context); - } - else - { - get_rule_expr((Node *) tle->expr, context, true); - /* We'll show the AS name unless it's this: */ - attname = "?column?"; - } - - /* - * Figure out what the result column should be called. In the context - * of a view, use the view's tuple descriptor (so as to pick up the - * effects of any column RENAME that's been done on the view). - * Otherwise, just use what we can find in the TLE. - */ - if (resultDesc && colno <= resultDesc->natts) - colname = NameStr(TupleDescAttr(resultDesc, colno - 1)->attname); - else - colname = tle->resname; - - /* Show AS unless the column's name is correct as-is */ - if (colname) /* resname could be NULL */ - { - if (attname == NULL || strcmp(attname, colname) != 0) - appendStringInfo(&targetbuf, " AS %s", quote_identifier(colname)); - } - - /* Restore context's output buffer */ - context->buf = buf; - - /* Consider line-wrapping if enabled */ - if (PRETTY_INDENT(context) && context->wrapColumn >= 0) - { - int leading_nl_pos; - - /* Does the new field start with a new line? */ - if (targetbuf.len > 0 && targetbuf.data[0] == '\n') - leading_nl_pos = 0; - else - leading_nl_pos = -1; - - /* If so, we shouldn't add anything */ - if (leading_nl_pos >= 0) - { - /* instead, remove any trailing spaces currently in buf */ - removeStringInfoSpaces(buf); - } - else - { - char *trailing_nl; - - /* Locate the start of the current line in the output buffer */ - trailing_nl = strrchr(buf->data, '\n'); - if (trailing_nl == NULL) - trailing_nl = buf->data; - else - trailing_nl++; - - /* - * Add a newline, plus some indentation, if the new field is - * not the first and either the new field would cause an - * overflow or the last field used more than one line. - */ - if (colno > 1 && - ((strlen(trailing_nl) + targetbuf.len > context->wrapColumn) || - last_was_multiline)) - appendContextKeyword(context, "", -PRETTYINDENT_STD, - PRETTYINDENT_STD, PRETTYINDENT_VAR); - } - - /* Remember this field's multiline status for next iteration */ - last_was_multiline = - (strchr(targetbuf.data + leading_nl_pos + 1, '\n') != NULL); - } - - /* Add the new field */ - appendStringInfoString(buf, targetbuf.data); - } - - /* clean up */ - pfree(targetbuf.data); -} - -static void -get_setop_query(Node *setOp, Query *query, deparse_context *context, - TupleDesc resultDesc) -{ - StringInfo buf = context->buf; - bool need_paren; - - /* Guard against excessively long or deeply-nested queries */ - CHECK_FOR_INTERRUPTS(); - check_stack_depth(); - - if (IsA(setOp, RangeTblRef)) - { - RangeTblRef *rtr = (RangeTblRef *) setOp; - RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable); - Query *subquery = rte->subquery; - - Assert(subquery != NULL); - Assert(subquery->setOperations == NULL); - /* Need parens if WITH, ORDER BY, FOR UPDATE, or LIMIT; see gram.y */ - need_paren = (subquery->cteList || - subquery->sortClause || - subquery->rowMarks || - subquery->limitOffset || - subquery->limitCount); - if (need_paren) - appendStringInfoChar(buf, '('); - get_query_def(subquery, buf, context->namespaces, resultDesc, - context->prettyFlags, context->wrapColumn, - context->indentLevel); - if (need_paren) - appendStringInfoChar(buf, ')'); - } - else if (IsA(setOp, SetOperationStmt)) - { - SetOperationStmt *op = (SetOperationStmt *) setOp; - int subindent; - - /* - * We force parens when nesting two SetOperationStmts, except when the - * lefthand input is another setop of the same kind. Syntactically, - * we could omit parens in rather more cases, but it seems best to use - * parens to flag cases where the setop operator changes. If we use - * parens, we also increase the indentation level for the child query. - * - * There are some cases in which parens are needed around a leaf query - * too, but those are more easily handled at the next level down (see - * code above). - */ - if (IsA(op->larg, SetOperationStmt)) - { - SetOperationStmt *lop = (SetOperationStmt *) op->larg; - - if (op->op == lop->op && op->all == lop->all) - need_paren = false; - else - need_paren = true; - } - else - need_paren = false; - - if (need_paren) - { - appendStringInfoChar(buf, '('); - subindent = PRETTYINDENT_STD; - appendContextKeyword(context, "", subindent, 0, 0); - } - else - subindent = 0; - - get_setop_query(op->larg, query, context, resultDesc); - - if (need_paren) - appendContextKeyword(context, ") ", -subindent, 0, 0); - else if (PRETTY_INDENT(context)) - appendContextKeyword(context, "", -subindent, 0, 0); - else - appendStringInfoChar(buf, ' '); - - switch (op->op) - { - case SETOP_UNION: - appendStringInfoString(buf, "UNION "); - break; - case SETOP_INTERSECT: - appendStringInfoString(buf, "INTERSECT "); - break; - case SETOP_EXCEPT: - appendStringInfoString(buf, "EXCEPT "); - break; - default: - elog(ERROR, "unrecognized set op: %d", - (int) op->op); - } - if (op->all) - appendStringInfoString(buf, "ALL "); - - /* Always parenthesize if RHS is another setop */ - need_paren = IsA(op->rarg, SetOperationStmt); - - /* - * The indentation code here is deliberately a bit different from that - * for the lefthand input, because we want the line breaks in - * different places. - */ - if (need_paren) - { - appendStringInfoChar(buf, '('); - subindent = PRETTYINDENT_STD; - } - else - subindent = 0; - appendContextKeyword(context, "", subindent, 0, 0); - - get_setop_query(op->rarg, query, context, resultDesc); - - if (PRETTY_INDENT(context)) - context->indentLevel -= subindent; - if (need_paren) - appendContextKeyword(context, ")", 0, 0, 0); - } - else - { - elog(ERROR, "unrecognized node type: %d", - (int) nodeTag(setOp)); - } -} - -/* - * Display a sort/group clause. - * - * Also returns the expression tree, so caller need not find it again. - */ -static Node * -get_rule_sortgroupclause(Index ref, List *tlist, bool force_colno, - deparse_context *context) -{ - StringInfo buf = context->buf; - TargetEntry *tle; - Node *expr; - - tle = get_sortgroupref_tle(ref, tlist); - expr = (Node *) tle->expr; - - /* - * Use column-number form if requested by caller. Otherwise, if - * expression is a constant, force it to be dumped with an explicit cast - * as decoration --- this is because a simple integer constant is - * ambiguous (and will be misinterpreted by findTargetlistEntry()) if we - * dump it without any decoration. If it's anything more complex than a - * simple Var, then force extra parens around it, to ensure it can't be - * misinterpreted as a cube() or rollup() construct. - */ - if (force_colno) - { - Assert(!tle->resjunk); - appendStringInfo(buf, "%d", tle->resno); - } - else if (expr && IsA(expr, Const)) - get_const_expr((Const *) expr, context, 1); - else if (!expr || IsA(expr, Var)) - get_rule_expr(expr, context, true); - else - { - /* - * We must force parens for function-like expressions even if - * PRETTY_PAREN is off, since those are the ones in danger of - * misparsing. For other expressions we need to force them only if - * PRETTY_PAREN is on, since otherwise the expression will output them - * itself. (We can't skip the parens.) - */ - bool need_paren = (PRETTY_PAREN(context) - || IsA(expr, FuncExpr) - ||IsA(expr, Aggref) - ||IsA(expr, WindowFunc)); - - if (need_paren) - appendStringInfoChar(context->buf, '('); - get_rule_expr(expr, context, true); - if (need_paren) - appendStringInfoChar(context->buf, ')'); - } - - return expr; -} - -/* - * Display a GroupingSet - */ -static void -get_rule_groupingset(GroupingSet *gset, List *targetlist, - bool omit_parens, deparse_context *context) -{ - ListCell *l; - StringInfo buf = context->buf; - bool omit_child_parens = true; - char *sep = ""; - - switch (gset->kind) - { - case GROUPING_SET_EMPTY: - appendStringInfoString(buf, "()"); - return; - - case GROUPING_SET_SIMPLE: - { - if (!omit_parens || list_length(gset->content) != 1) - appendStringInfoChar(buf, '('); - - foreach(l, gset->content) - { - Index ref = lfirst_int(l); - - appendStringInfoString(buf, sep); - get_rule_sortgroupclause(ref, targetlist, - false, context); - sep = ", "; - } - - if (!omit_parens || list_length(gset->content) != 1) - appendStringInfoChar(buf, ')'); - } - return; - - case GROUPING_SET_ROLLUP: - appendStringInfoString(buf, "ROLLUP("); - break; - case GROUPING_SET_CUBE: - appendStringInfoString(buf, "CUBE("); - break; - case GROUPING_SET_SETS: - appendStringInfoString(buf, "GROUPING SETS ("); - omit_child_parens = false; - break; - } - - foreach(l, gset->content) - { - appendStringInfoString(buf, sep); - get_rule_groupingset(lfirst(l), targetlist, omit_child_parens, context); - sep = ", "; - } - - appendStringInfoChar(buf, ')'); -} - -/* - * Display an ORDER BY list. - */ -static void -get_rule_orderby(List *orderList, List *targetList, - bool force_colno, deparse_context *context) -{ - StringInfo buf = context->buf; - const char *sep; - ListCell *l; - - sep = ""; - foreach(l, orderList) - { - SortGroupClause *srt = (SortGroupClause *) lfirst(l); - Node *sortexpr; - Oid sortcoltype; - TypeCacheEntry *typentry; - - appendStringInfoString(buf, sep); - sortexpr = get_rule_sortgroupclause(srt->tleSortGroupRef, targetList, - force_colno, context); - sortcoltype = exprType(sortexpr); - /* See whether operator is default < or > for datatype */ - typentry = lookup_type_cache(sortcoltype, - TYPECACHE_LT_OPR | TYPECACHE_GT_OPR); - if (srt->sortop == typentry->lt_opr) - { - /* ASC is default, so emit nothing for it */ - if (srt->nulls_first) - appendStringInfoString(buf, " NULLS FIRST"); - } - else if (srt->sortop == typentry->gt_opr) - { - appendStringInfoString(buf, " DESC"); - /* DESC defaults to NULLS FIRST */ - if (!srt->nulls_first) - appendStringInfoString(buf, " NULLS LAST"); - } - else - { - appendStringInfo(buf, " USING %s", - generate_operator_name(srt->sortop, - sortcoltype, - sortcoltype)); - /* be specific to eliminate ambiguity */ - if (srt->nulls_first) - appendStringInfoString(buf, " NULLS FIRST"); - else - appendStringInfoString(buf, " NULLS LAST"); - } - sep = ", "; - } -} - -/* - * Display a WINDOW clause. - * - * Note that the windowClause list might contain only anonymous window - * specifications, in which case we should print nothing here. - */ -static void -get_rule_windowclause(Query *query, deparse_context *context) -{ - StringInfo buf = context->buf; - const char *sep; - ListCell *l; - - sep = NULL; - foreach(l, query->windowClause) - { - WindowClause *wc = (WindowClause *) lfirst(l); - - if (wc->name == NULL) - continue; /* ignore anonymous windows */ - - if (sep == NULL) - appendContextKeyword(context, " WINDOW ", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); - else - appendStringInfoString(buf, sep); - - appendStringInfo(buf, "%s AS ", quote_identifier(wc->name)); - - get_rule_windowspec(wc, query->targetList, context); - - sep = ", "; - } -} - -/* - * Display a window definition - */ -static void -get_rule_windowspec(WindowClause *wc, List *targetList, - deparse_context *context) -{ - StringInfo buf = context->buf; - bool needspace = false; - const char *sep; - ListCell *l; - - appendStringInfoChar(buf, '('); - if (wc->refname) - { - appendStringInfoString(buf, quote_identifier(wc->refname)); - needspace = true; - } - /* partition clauses are always inherited, so only print if no refname */ - if (wc->partitionClause && !wc->refname) - { - if (needspace) - appendStringInfoChar(buf, ' '); - appendStringInfoString(buf, "PARTITION BY "); - sep = ""; - foreach(l, wc->partitionClause) - { - SortGroupClause *grp = (SortGroupClause *) lfirst(l); - - appendStringInfoString(buf, sep); - get_rule_sortgroupclause(grp->tleSortGroupRef, targetList, - false, context); - sep = ", "; - } - needspace = true; - } - /* print ordering clause only if not inherited */ - if (wc->orderClause && !wc->copiedOrder) - { - if (needspace) - appendStringInfoChar(buf, ' '); - appendStringInfoString(buf, "ORDER BY "); - get_rule_orderby(wc->orderClause, targetList, false, context); - needspace = true; - } - /* framing clause is never inherited, so print unless it's default */ - if (wc->frameOptions & FRAMEOPTION_NONDEFAULT) - { - if (needspace) - appendStringInfoChar(buf, ' '); - if (wc->frameOptions & FRAMEOPTION_RANGE) - appendStringInfoString(buf, "RANGE "); - else if (wc->frameOptions & FRAMEOPTION_ROWS) - appendStringInfoString(buf, "ROWS "); - else if (wc->frameOptions & FRAMEOPTION_GROUPS) - appendStringInfoString(buf, "GROUPS "); - else - Assert(false); - if (wc->frameOptions & FRAMEOPTION_BETWEEN) - appendStringInfoString(buf, "BETWEEN "); - if (wc->frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING) - appendStringInfoString(buf, "UNBOUNDED PRECEDING "); - else if (wc->frameOptions & FRAMEOPTION_START_CURRENT_ROW) - appendStringInfoString(buf, "CURRENT ROW "); - else if (wc->frameOptions & FRAMEOPTION_START_OFFSET) - { - get_rule_expr(wc->startOffset, context, false); - if (wc->frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING) - appendStringInfoString(buf, " PRECEDING "); - else if (wc->frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING) - appendStringInfoString(buf, " FOLLOWING "); - else - Assert(false); - } - else - Assert(false); - if (wc->frameOptions & FRAMEOPTION_BETWEEN) - { - appendStringInfoString(buf, "AND "); - if (wc->frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING) - appendStringInfoString(buf, "UNBOUNDED FOLLOWING "); - else if (wc->frameOptions & FRAMEOPTION_END_CURRENT_ROW) - appendStringInfoString(buf, "CURRENT ROW "); - else if (wc->frameOptions & FRAMEOPTION_END_OFFSET) - { - get_rule_expr(wc->endOffset, context, false); - if (wc->frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING) - appendStringInfoString(buf, " PRECEDING "); - else if (wc->frameOptions & FRAMEOPTION_END_OFFSET_FOLLOWING) - appendStringInfoString(buf, " FOLLOWING "); - else - Assert(false); - } - else - Assert(false); - } - if (wc->frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW) - appendStringInfoString(buf, "EXCLUDE CURRENT ROW "); - else if (wc->frameOptions & FRAMEOPTION_EXCLUDE_GROUP) - appendStringInfoString(buf, "EXCLUDE GROUP "); - else if (wc->frameOptions & FRAMEOPTION_EXCLUDE_TIES) - appendStringInfoString(buf, "EXCLUDE TIES "); - /* we will now have a trailing space; remove it */ - buf->len--; - } - appendStringInfoChar(buf, ')'); -} - -/* ---------- - * get_insert_query_def - Parse back an INSERT parsetree - * ---------- - */ -static void -get_insert_query_def(Query *query, deparse_context *context) -{ - StringInfo buf = context->buf; - RangeTblEntry *select_rte = NULL; - RangeTblEntry *values_rte = NULL; - RangeTblEntry *rte; - char *sep; - ListCell *l; - List *strippedexprs; - - /* Insert the WITH clause if given */ - get_with_clause(query, context); - - /* - * If it's an INSERT ... SELECT or multi-row VALUES, there will be a - * single RTE for the SELECT or VALUES. Plain VALUES has neither. - */ - foreach(l, query->rtable) - { - rte = (RangeTblEntry *) lfirst(l); - - if (rte->rtekind == RTE_SUBQUERY) - { - if (select_rte) - elog(ERROR, "too many subquery RTEs in INSERT"); - select_rte = rte; - } - - if (rte->rtekind == RTE_VALUES) - { - if (values_rte) - elog(ERROR, "too many values RTEs in INSERT"); - values_rte = rte; - } - } - if (select_rte && values_rte) - elog(ERROR, "both subquery and values RTEs in INSERT"); - - /* - * Start the query with INSERT INTO relname - */ - rte = rt_fetch(query->resultRelation, query->rtable); - Assert(rte->rtekind == RTE_RELATION); - - if (PRETTY_INDENT(context)) - { - context->indentLevel += PRETTYINDENT_STD; - appendStringInfoChar(buf, ' '); - } - appendStringInfo(buf, "INSERT INTO %s ", - generate_relation_or_shard_name(rte->relid, - context->distrelid, - context->shardid, NIL)); - /* INSERT requires AS keyword for target alias */ - if (rte->alias != NULL) - appendStringInfo(buf, "AS %s ", - quote_identifier(get_rtable_name(query->resultRelation, context))); - - /* - * Add the insert-column-names list. Any indirection decoration needed on - * the column names can be inferred from the top targetlist. - */ - strippedexprs = NIL; - sep = ""; - if (query->targetList) - appendStringInfoChar(buf, '('); - foreach(l, query->targetList) - { - TargetEntry *tle = (TargetEntry *) lfirst(l); - - if (tle->resjunk) - continue; /* ignore junk entries */ - - appendStringInfoString(buf, sep); - sep = ", "; - - /* - * Put out name of target column; look in the catalogs, not at - * tle->resname, since resname will fail to track RENAME. - */ - appendStringInfoString(buf, - quote_identifier(get_attname(rte->relid, - tle->resno, - false))); - - /* - * Print any indirection needed (subfields or subscripts), and strip - * off the top-level nodes representing the indirection assignments. - * Add the stripped expressions to strippedexprs. (If it's a - * single-VALUES statement, the stripped expressions are the VALUES to - * print below. Otherwise they're just Vars and not really - * interesting.) - */ - strippedexprs = lappend(strippedexprs, - processIndirection((Node *) tle->expr, - context)); - } - if (query->targetList) - appendStringInfoString(buf, ") "); - - if (query->override) - { - if (query->override == OVERRIDING_SYSTEM_VALUE) - appendStringInfoString(buf, "OVERRIDING SYSTEM VALUE "); - else if (query->override == OVERRIDING_USER_VALUE) - appendStringInfoString(buf, "OVERRIDING USER VALUE "); - } - - if (select_rte) - { - /* Add the SELECT */ - get_query_def(select_rte->subquery, buf, NIL, NULL, - context->prettyFlags, context->wrapColumn, - context->indentLevel); - } - else if (values_rte) - { - /* Add the multi-VALUES expression lists */ - get_values_def(values_rte->values_lists, context); - } - else if (strippedexprs) - { - /* Add the single-VALUES expression list */ - appendContextKeyword(context, "VALUES (", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 2); - get_rule_expr((Node *) strippedexprs, context, false); - appendStringInfoChar(buf, ')'); - } - else - { - /* No expressions, so it must be DEFAULT VALUES */ - appendStringInfoString(buf, "DEFAULT VALUES"); - } - - /* Add ON CONFLICT if present */ - if (query->onConflict) - { - OnConflictExpr *confl = query->onConflict; - - appendStringInfoString(buf, " ON CONFLICT"); - - if (confl->arbiterElems) - { - /* Add the single-VALUES expression list */ - appendStringInfoChar(buf, '('); - get_rule_expr((Node *) confl->arbiterElems, context, false); - appendStringInfoChar(buf, ')'); - - /* Add a WHERE clause (for partial indexes) if given */ - if (confl->arbiterWhere != NULL) - { - bool save_varprefix; - - /* - * Force non-prefixing of Vars, since parser assumes that they - * belong to target relation. WHERE clause does not use - * InferenceElem, so this is separately required. - */ - save_varprefix = context->varprefix; - context->varprefix = false; - - appendContextKeyword(context, " WHERE ", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); - get_rule_expr(confl->arbiterWhere, context, false); - - context->varprefix = save_varprefix; - } - } - else if (OidIsValid(confl->constraint)) - { - char *constraint = get_constraint_name(confl->constraint); - int64 shardId = context->shardid; - - if (shardId > 0) - { - AppendShardIdToName(&constraint, shardId); - } - - if (!constraint) - elog(ERROR, "cache lookup failed for constraint %u", - confl->constraint); - appendStringInfo(buf, " ON CONSTRAINT %s", - quote_identifier(constraint)); - } - - if (confl->action == ONCONFLICT_NOTHING) - { - appendStringInfoString(buf, " DO NOTHING"); - } - else - { - appendStringInfoString(buf, " DO UPDATE SET "); - /* Deparse targetlist */ - get_update_query_targetlist_def(query, confl->onConflictSet, - context, rte); - - /* Add a WHERE clause if given */ - if (confl->onConflictWhere != NULL) - { - appendContextKeyword(context, " WHERE ", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); - get_rule_expr(confl->onConflictWhere, context, false); - } - } - } - - /* Add RETURNING if present */ - if (query->returningList) - { - appendContextKeyword(context, " RETURNING", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); - get_target_list(query->returningList, context, NULL); - } -} - - -/* ---------- - * get_update_query_def - Parse back an UPDATE parsetree - * ---------- - */ -static void -get_update_query_def(Query *query, deparse_context *context) -{ - StringInfo buf = context->buf; - RangeTblEntry *rte; - - /* Insert the WITH clause if given */ - get_with_clause(query, context); - - /* - * Start the query with UPDATE relname SET - */ - rte = rt_fetch(query->resultRelation, query->rtable); - - if (PRETTY_INDENT(context)) - { - appendStringInfoChar(buf, ' '); - context->indentLevel += PRETTYINDENT_STD; - } - - /* if it's a shard, do differently */ - if (GetRangeTblKind(rte) == CITUS_RTE_SHARD) - { - char *fragmentSchemaName = NULL; - char *fragmentTableName = NULL; - - ExtractRangeTblExtraData(rte, NULL, &fragmentSchemaName, &fragmentTableName, NULL); - - /* use schema and table name from the remote alias */ - appendStringInfo(buf, "UPDATE %s%s", - only_marker(rte), - generate_fragment_name(fragmentSchemaName, fragmentTableName)); - - if(rte->eref != NULL) - appendStringInfo(buf, " %s", - quote_identifier(get_rtable_name(query->resultRelation, context))); - } - else - { - appendStringInfo(buf, "UPDATE %s%s", - only_marker(rte), - generate_relation_or_shard_name(rte->relid, - context->distrelid, - context->shardid, NIL)); - - if (rte->alias != NULL) - appendStringInfo(buf, " %s", - quote_identifier(get_rtable_name(query->resultRelation, context))); - } - - appendStringInfoString(buf, " SET "); - - /* Deparse targetlist */ - get_update_query_targetlist_def(query, query->targetList, context, rte); - - /* Add the FROM clause if needed */ - get_from_clause(query, " FROM ", context); - - /* Add a WHERE clause if given */ - if (query->jointree->quals != NULL) - { - appendContextKeyword(context, " WHERE ", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); - get_rule_expr(query->jointree->quals, context, false); - } - - /* Add RETURNING if present */ - if (query->returningList) - { - appendContextKeyword(context, " RETURNING", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); - get_target_list(query->returningList, context, NULL); - } -} - - -/* ---------- - * get_update_query_targetlist_def - Parse back an UPDATE targetlist - * ---------- - */ -static void -get_update_query_targetlist_def(Query *query, List *targetList, - deparse_context *context, RangeTblEntry *rte) -{ - StringInfo buf = context->buf; - ListCell *l; - ListCell *next_ma_cell; - int remaining_ma_columns; - const char *sep; - SubLink *cur_ma_sublink; - List *ma_sublinks; - - /* - * Prepare to deal with MULTIEXPR assignments: collect the source SubLinks - * into a list. We expect them to appear, in ID order, in resjunk tlist - * entries. - */ - ma_sublinks = NIL; - if (query->hasSubLinks) /* else there can't be any */ - { - foreach(l, targetList) - { - TargetEntry *tle = (TargetEntry *) lfirst(l); - - if (tle->resjunk && IsA(tle->expr, SubLink)) - { - SubLink *sl = (SubLink *) tle->expr; - - if (sl->subLinkType == MULTIEXPR_SUBLINK) - { - ma_sublinks = lappend(ma_sublinks, sl); - Assert(sl->subLinkId == list_length(ma_sublinks)); - } - } - } - } - next_ma_cell = list_head(ma_sublinks); - cur_ma_sublink = NULL; - remaining_ma_columns = 0; - - /* Add the comma separated list of 'attname = value' */ - sep = ""; - foreach(l, targetList) - { - TargetEntry *tle = (TargetEntry *) lfirst(l); - Node *expr; - - if (tle->resjunk) - continue; /* ignore junk entries */ - - /* Emit separator (OK whether we're in multiassignment or not) */ - appendStringInfoString(buf, sep); - sep = ", "; - - /* - * Check to see if we're starting a multiassignment group: if so, - * output a left paren. - */ - if (next_ma_cell != NULL && cur_ma_sublink == NULL) - { - /* - * We must dig down into the expr to see if it's a PARAM_MULTIEXPR - * Param. That could be buried under FieldStores and - * SubscriptingRefs and CoerceToDomains (cf processIndirection()), - * and underneath those there could be an implicit type coercion. - * Because we would ignore implicit type coercions anyway, we - * don't need to be as careful as processIndirection() is about - * descending past implicit CoerceToDomains. - */ - expr = (Node *) tle->expr; - while (expr) - { - if (IsA(expr, FieldStore)) - { - FieldStore *fstore = (FieldStore *) expr; - - expr = (Node *) linitial(fstore->newvals); - } - else if (IsA(expr, SubscriptingRef)) - { - SubscriptingRef *sbsref = (SubscriptingRef *) expr; - - if (sbsref->refassgnexpr == NULL) - break; - expr = (Node *) sbsref->refassgnexpr; - } - else if (IsA(expr, CoerceToDomain)) - { - CoerceToDomain *cdomain = (CoerceToDomain *) expr; - - if (cdomain->coercionformat != COERCE_IMPLICIT_CAST) - break; - expr = (Node *) cdomain->arg; - } - else - break; - } - expr = strip_implicit_coercions(expr); - - if (expr && IsA(expr, Param) && - ((Param *) expr)->paramkind == PARAM_MULTIEXPR) - { - cur_ma_sublink = (SubLink *) lfirst(next_ma_cell); - next_ma_cell = lnext(ma_sublinks, next_ma_cell); - remaining_ma_columns = count_nonjunk_tlist_entries( - ((Query *) cur_ma_sublink->subselect)->targetList); - Assert(((Param *) expr)->paramid == - ((cur_ma_sublink->subLinkId << 16) | 1)); - appendStringInfoChar(buf, '('); - } - } - - /* - * Put out name of target column; look in the catalogs, not at - * tle->resname, since resname will fail to track RENAME. - */ - appendStringInfoString(buf, - quote_identifier(get_attname(rte->relid, - tle->resno, - false))); - - /* - * Print any indirection needed (subfields or subscripts), and strip - * off the top-level nodes representing the indirection assignments. - */ - expr = processIndirection((Node *) tle->expr, context); - - /* - * If we're in a multiassignment, skip printing anything more, unless - * this is the last column; in which case, what we print should be the - * sublink, not the Param. - */ - if (cur_ma_sublink != NULL) - { - if (--remaining_ma_columns > 0) - continue; /* not the last column of multiassignment */ - appendStringInfoChar(buf, ')'); - expr = (Node *) cur_ma_sublink; - cur_ma_sublink = NULL; - } - - appendStringInfoString(buf, " = "); - - get_rule_expr(expr, context, false); - } -} - - -/* ---------- - * get_delete_query_def - Parse back a DELETE parsetree - * ---------- - */ -static void -get_delete_query_def(Query *query, deparse_context *context) -{ - StringInfo buf = context->buf; - RangeTblEntry *rte; - - /* Insert the WITH clause if given */ - get_with_clause(query, context); - - /* - * Start the query with DELETE FROM relname - */ - rte = rt_fetch(query->resultRelation, query->rtable); - - if (PRETTY_INDENT(context)) - { - appendStringInfoChar(buf, ' '); - context->indentLevel += PRETTYINDENT_STD; - } - - /* if it's a shard, do differently */ - if (GetRangeTblKind(rte) == CITUS_RTE_SHARD) - { - char *fragmentSchemaName = NULL; - char *fragmentTableName = NULL; - - ExtractRangeTblExtraData(rte, NULL, &fragmentSchemaName, &fragmentTableName, NULL); - - /* use schema and table name from the remote alias */ - appendStringInfo(buf, "DELETE FROM %s%s", - only_marker(rte), - generate_fragment_name(fragmentSchemaName, fragmentTableName)); - - if(rte->eref != NULL) - appendStringInfo(buf, " %s", - quote_identifier(get_rtable_name(query->resultRelation, context))); - } - else - { - appendStringInfo(buf, "DELETE FROM %s%s", - only_marker(rte), - generate_relation_or_shard_name(rte->relid, - context->distrelid, - context->shardid, NIL)); - - if (rte->alias != NULL) - appendStringInfo(buf, " %s", - quote_identifier(get_rtable_name(query->resultRelation, context))); - } - - /* Add the USING clause if given */ - get_from_clause(query, " USING ", context); - - /* Add a WHERE clause if given */ - if (query->jointree->quals != NULL) - { - appendContextKeyword(context, " WHERE ", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); - get_rule_expr(query->jointree->quals, context, false); - } - - /* Add RETURNING if present */ - if (query->returningList) - { - appendContextKeyword(context, " RETURNING", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 1); - get_target_list(query->returningList, context, NULL); - } -} - - -/* ---------- - * get_utility_query_def - Parse back a UTILITY parsetree - * ---------- - */ -static void -get_utility_query_def(Query *query, deparse_context *context) -{ - StringInfo buf = context->buf; - - if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt)) - { - NotifyStmt *stmt = (NotifyStmt *) query->utilityStmt; - - appendContextKeyword(context, "", - 0, PRETTYINDENT_STD, 1); - appendStringInfo(buf, "NOTIFY %s", - quote_identifier(stmt->conditionname)); - if (stmt->payload) - { - appendStringInfoString(buf, ", "); - simple_quote_literal(buf, stmt->payload); - } - } - else if (query->utilityStmt && IsA(query->utilityStmt, TruncateStmt)) - { - TruncateStmt *stmt = (TruncateStmt *) query->utilityStmt; - List *relationList = stmt->relations; - ListCell *relationCell = NULL; - - appendContextKeyword(context, "", - 0, PRETTYINDENT_STD, 1); - - appendStringInfo(buf, "TRUNCATE TABLE"); - - foreach(relationCell, relationList) - { - RangeVar *relationVar = (RangeVar *) lfirst(relationCell); - Oid relationId = RangeVarGetRelid(relationVar, NoLock, false); - char *relationName = generate_relation_or_shard_name(relationId, - context->distrelid, - context->shardid, NIL); - appendStringInfo(buf, " %s", relationName); - - if (lnext(relationList, relationCell) != NULL) - { - appendStringInfo(buf, ","); - } - } - - if (stmt->restart_seqs) - { - appendStringInfo(buf, " RESTART IDENTITY"); - } - - if (stmt->behavior == DROP_CASCADE) - { - appendStringInfo(buf, " CASCADE"); - } - } - else - { - /* Currently only NOTIFY utility commands can appear in rules */ - elog(ERROR, "unexpected utility statement type"); - } -} - -/* - * Display a Var appropriately. - * - * In some cases (currently only when recursing into an unnamed join) - * the Var's varlevelsup has to be interpreted with respect to a context - * above the current one; levelsup indicates the offset. - * - * If istoplevel is true, the Var is at the top level of a SELECT's - * targetlist, which means we need special treatment of whole-row Vars. - * Instead of the normal "tab.*", we'll print "tab.*::typename", which is a - * dirty hack to prevent "tab.*" from being expanded into multiple columns. - * (The parser will strip the useless coercion, so no inefficiency is added in - * dump and reload.) We used to print just "tab" in such cases, but that is - * ambiguous and will yield the wrong result if "tab" is also a plain column - * name in the query. - * - * Returns the attname of the Var, or NULL if the Var has no attname (because - * it is a whole-row Var or a subplan output reference). - */ -static char * -get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context) -{ - StringInfo buf = context->buf; - RangeTblEntry *rte; - AttrNumber attnum; - Index varno; - AttrNumber varattno; - int netlevelsup; - deparse_namespace *dpns; - deparse_columns *colinfo; - char *refname; - char *attname; - - /* Find appropriate nesting depth */ - netlevelsup = var->varlevelsup + levelsup; - if (netlevelsup >= list_length(context->namespaces)) - elog(ERROR, "bogus varlevelsup: %d offset %d", - var->varlevelsup, levelsup); - dpns = (deparse_namespace *) list_nth(context->namespaces, - netlevelsup); - - varno = var->varno; - varattno = var->varattno; - - - if (var->varnosyn > 0 && var->varnosyn <= list_length(dpns->rtable) && dpns->plan == NULL) { - rte = rt_fetch(var->varnosyn, dpns->rtable); - - /* - * if the rte var->varnosyn points to is not a regular table and it is a join - * then the correct relname will be found with var->varnosyn and var->varattnosyn - */ - if (rte->rtekind == RTE_JOIN && rte->relid == 0 && var->varnosyn != var->varno) { - varno = var->varnosyn; - varattno = var->varattnosyn; - } - } - - /* - * Try to find the relevant RTE in this rtable. In a plan tree, it's - * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig - * down into the subplans, or INDEX_VAR, which is resolved similarly. Also - * find the aliases previously assigned for this RTE. - */ - if (varno >= 1 && varno <= list_length(dpns->rtable)) - { - - /* - * We might have been asked to map child Vars to some parent relation. - */ - if (context->appendparents && dpns->appendrels) - { - - Index pvarno = varno; - AttrNumber pvarattno = varattno; - AppendRelInfo *appinfo = dpns->appendrels[pvarno]; - bool found = false; - - /* Only map up to inheritance parents, not UNION ALL appendrels */ - while (appinfo && - rt_fetch(appinfo->parent_relid, - dpns->rtable)->rtekind == RTE_RELATION) - { - found = false; - if (pvarattno > 0) /* system columns stay as-is */ - { - if (pvarattno > appinfo->num_child_cols) - break; /* safety check */ - pvarattno = appinfo->parent_colnos[pvarattno - 1]; - if (pvarattno == 0) - break; /* Var is local to child */ - } - - pvarno = appinfo->parent_relid; - found = true; - - /* If the parent is itself a child, continue up. */ - Assert(pvarno > 0 && pvarno <= list_length(dpns->rtable)); - appinfo = dpns->appendrels[pvarno]; - } - - /* - * If we found an ancestral rel, and that rel is included in - * appendparents, print that column not the original one. - */ - if (found && bms_is_member(pvarno, context->appendparents)) - { - varno = pvarno; - varattno = pvarattno; - } - } - - rte = rt_fetch(varno, dpns->rtable); - refname = (char *) list_nth(dpns->rtable_names, varno - 1); - colinfo = deparse_columns_fetch(varno, dpns); - attnum = varattno; - } - else - { - resolve_special_varno((Node *) var, context, get_special_variable, - NULL); - return NULL; - } - - /* - * The planner will sometimes emit Vars referencing resjunk elements of a - * subquery's target list (this is currently only possible if it chooses - * to generate a "physical tlist" for a SubqueryScan or CteScan node). - * Although we prefer to print subquery-referencing Vars using the - * subquery's alias, that's not possible for resjunk items since they have - * no alias. So in that case, drill down to the subplan and print the - * contents of the referenced tlist item. This works because in a plan - * tree, such Vars can only occur in a SubqueryScan or CteScan node, and - * we'll have set dpns->inner_plan to reference the child plan node. - */ - if ((rte->rtekind == RTE_SUBQUERY || rte->rtekind == RTE_CTE) && - attnum > list_length(rte->eref->colnames) && - dpns->inner_plan) - { - TargetEntry *tle; - deparse_namespace save_dpns; - - tle = get_tle_by_resno(dpns->inner_tlist, attnum); - if (!tle) - elog(ERROR, "invalid attnum %d for relation \"%s\"", - attnum, rte->eref->aliasname); - - Assert(netlevelsup == 0); - push_child_plan(dpns, dpns->inner_plan, &save_dpns); - - /* - * Force parentheses because our caller probably assumed a Var is a - * simple expression. - */ - if (!IsA(tle->expr, Var)) - appendStringInfoChar(buf, '('); - get_rule_expr((Node *) tle->expr, context, true); - if (!IsA(tle->expr, Var)) - appendStringInfoChar(buf, ')'); - - pop_child_plan(dpns, &save_dpns); - return NULL; - } - - /* - * If it's an unnamed join, look at the expansion of the alias variable. - * If it's a simple reference to one of the input vars, then recursively - * print the name of that var instead. When it's not a simple reference, - * we have to just print the unqualified join column name. (This can only - * happen with "dangerous" merged columns in a JOIN USING; we took pains - * previously to make the unqualified column name unique in such cases.) - * - * This wouldn't work in decompiling plan trees, because we don't store - * joinaliasvars lists after planning; but a plan tree should never - * contain a join alias variable. - */ - if (rte->rtekind == RTE_JOIN && rte->alias == NULL) - { - if (rte->joinaliasvars == NIL) - elog(ERROR, "cannot decompile join alias var in plan tree"); - if (attnum > 0) - { - Var *aliasvar; - - aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1); - /* we intentionally don't strip implicit coercions here */ - if (aliasvar && IsA(aliasvar, Var)) - { - return get_variable(aliasvar, var->varlevelsup + levelsup, - istoplevel, context); - } - } - - /* - * Unnamed join has no refname. (Note: since it's unnamed, there is - * no way the user could have referenced it to create a whole-row Var - * for it. So we don't have to cover that case below.) - */ - Assert(refname == NULL); - } - - if (attnum == InvalidAttrNumber) - attname = NULL; - else if (attnum > 0) - { - /* Get column name to use from the colinfo struct */ - if (attnum > colinfo->num_cols) - elog(ERROR, "invalid attnum %d for relation \"%s\"", - attnum, rte->eref->aliasname); - attname = colinfo->colnames[attnum - 1]; - if (attname == NULL) /* dropped column? */ - elog(ERROR, "invalid attnum %d for relation \"%s\"", - attnum, rte->eref->aliasname); - } - else if (GetRangeTblKind(rte) == CITUS_RTE_SHARD) - { - /* System column on a Citus shard */ - attname = get_attname(rte->relid, attnum, false); - } - else - { - /* System column - name is fixed, get it from the catalog */ - attname = get_rte_attribute_name(rte, attnum); - } - - if (refname && (context->varprefix || attname == NULL)) - { - appendStringInfoString(buf, quote_identifier(refname)); - appendStringInfoChar(buf, '.'); - } - if (attname) - appendStringInfoString(buf, quote_identifier(attname)); - else - { - appendStringInfoChar(buf, '*'); - - if (istoplevel) - { - if (GetRangeTblKind(rte) == CITUS_RTE_SHARD) - { - /* use rel.*::shard_name instead of rel.*::table_name */ - appendStringInfo(buf, "::%s", - generate_rte_shard_name(rte)); - } - else - { - appendStringInfo(buf, "::%s", - format_type_with_typemod(var->vartype, - var->vartypmod)); - } - } - } - - return attname; -} - -/* - * Deparse a Var which references OUTER_VAR, INNER_VAR, or INDEX_VAR. This - * routine is actually a callback for get_special_varno, which handles finding - * the correct TargetEntry. We get the expression contained in that - * TargetEntry and just need to deparse it, a job we can throw back on - * get_rule_expr. - */ -static void -get_special_variable(Node *node, deparse_context *context, void *callback_arg) -{ - StringInfo buf = context->buf; - - /* - * For a non-Var referent, force parentheses because our caller probably - * assumed a Var is a simple expression. - */ - if (!IsA(node, Var)) - appendStringInfoChar(buf, '('); - get_rule_expr(node, context, true); - if (!IsA(node, Var)) - appendStringInfoChar(buf, ')'); -} - -/* - * Chase through plan references to special varnos (OUTER_VAR, INNER_VAR, - * INDEX_VAR) until we find a real Var or some kind of non-Var node; then, - * invoke the callback provided. - */ -static void -resolve_special_varno(Node *node, deparse_context *context, rsv_callback callback, void *callback_arg) -{ - Var *var; - deparse_namespace *dpns; - - /* This function is recursive, so let's be paranoid. */ - check_stack_depth(); - - /* If it's not a Var, invoke the callback. */ - if (!IsA(node, Var)) - { - (*callback) (node, context, callback_arg); - return; - } - - /* Find appropriate nesting depth */ - var = (Var *) node; - dpns = (deparse_namespace *) list_nth(context->namespaces, - var->varlevelsup); - - /* - * It's a special RTE, so recurse. - */ - if (var->varno == OUTER_VAR && dpns->outer_tlist) - { - TargetEntry *tle; - deparse_namespace save_dpns; - Bitmapset *save_appendparents; - - tle = get_tle_by_resno(dpns->outer_tlist, var->varattno); - if (!tle) - elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno); - - /* If we're descending to the first child of an Append or MergeAppend, - * update appendparents. This will affect deparsing of all Vars - * appearing within the eventually-resolved subexpression. - */ - save_appendparents = context->appendparents; - - if (IsA(dpns->plan, Append)) - context->appendparents = bms_union(context->appendparents, - ((Append *) dpns->plan)->apprelids); - else if (IsA(dpns->plan, MergeAppend)) - context->appendparents = bms_union(context->appendparents, - ((MergeAppend *) dpns->plan)->apprelids); - - push_child_plan(dpns, dpns->outer_plan, &save_dpns); - resolve_special_varno((Node *) tle->expr, context, - callback, callback_arg); - pop_child_plan(dpns, &save_dpns); - context->appendparents = save_appendparents; - return; - } - else if (var->varno == INNER_VAR && dpns->inner_tlist) - { - TargetEntry *tle; - deparse_namespace save_dpns; - - tle = get_tle_by_resno(dpns->inner_tlist, var->varattno); - if (!tle) - elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno); - - push_child_plan(dpns, dpns->inner_plan, &save_dpns); - resolve_special_varno((Node *) tle->expr, context, callback, callback_arg); - pop_child_plan(dpns, &save_dpns); - return; - } - else if (var->varno == INDEX_VAR && dpns->index_tlist) - { - TargetEntry *tle; - - tle = get_tle_by_resno(dpns->index_tlist, var->varattno); - if (!tle) - elog(ERROR, "bogus varattno for INDEX_VAR var: %d", var->varattno); - - resolve_special_varno((Node *) tle->expr, context, callback, callback_arg); - return; - } - else if (var->varno < 1 || var->varno > list_length(dpns->rtable)) - elog(ERROR, "bogus varno: %d", var->varno); - - /* Not special. Just invoke the callback. */ - (*callback) (node, context, callback_arg); -} - -/* - * Get the name of a field of an expression of composite type. The - * expression is usually a Var, but we handle other cases too. - * - * levelsup is an extra offset to interpret the Var's varlevelsup correctly. - * - * This is fairly straightforward when the expression has a named composite - * type; we need only look up the type in the catalogs. However, the type - * could also be RECORD. Since no actual table or view column is allowed to - * have type RECORD, a Var of type RECORD must refer to a JOIN or FUNCTION RTE - * or to a subquery output. We drill down to find the ultimate defining - * expression and attempt to infer the field name from it. We ereport if we - * can't determine the name. - * - * Similarly, a PARAM of type RECORD has to refer to some expression of - * a determinable composite type. - */ -static const char * -get_name_for_var_field(Var *var, int fieldno, - int levelsup, deparse_context *context) -{ - RangeTblEntry *rte; - AttrNumber attnum; - int netlevelsup; - deparse_namespace *dpns; - Index varno; - AttrNumber varattno; - TupleDesc tupleDesc; - Node *expr; - - /* - * If it's a RowExpr that was expanded from a whole-row Var, use the - * column names attached to it. - */ - if (IsA(var, RowExpr)) - { - RowExpr *r = (RowExpr *) var; - - if (fieldno > 0 && fieldno <= list_length(r->colnames)) - return strVal(list_nth(r->colnames, fieldno - 1)); - } - - /* - * If it's a Param of type RECORD, try to find what the Param refers to. - */ - if (IsA(var, Param)) - { - Param *param = (Param *) var; - ListCell *ancestor_cell; - - expr = find_param_referent(param, context, &dpns, &ancestor_cell); - if (expr) - { - /* Found a match, so recurse to decipher the field name */ - deparse_namespace save_dpns; - const char *result; - - push_ancestor_plan(dpns, ancestor_cell, &save_dpns); - result = get_name_for_var_field((Var *) expr, fieldno, - 0, context); - pop_ancestor_plan(dpns, &save_dpns); - return result; - } - } - - /* - * If it's a Var of type RECORD, we have to find what the Var refers to; - * if not, we can use get_expr_result_tupdesc(). - */ - if (!IsA(var, Var) || - var->vartype != RECORDOID) - { - tupleDesc = get_expr_result_tupdesc((Node *) var, false); - /* Got the tupdesc, so we can extract the field name */ - Assert(fieldno >= 1 && fieldno <= tupleDesc->natts); - return NameStr(TupleDescAttr(tupleDesc, fieldno - 1)->attname); - } - - /* Find appropriate nesting depth */ - netlevelsup = var->varlevelsup + levelsup; - if (netlevelsup >= list_length(context->namespaces)) - elog(ERROR, "bogus varlevelsup: %d offset %d", - var->varlevelsup, levelsup); - dpns = (deparse_namespace *) list_nth(context->namespaces, - netlevelsup); - - varno = var->varno; - varattno = var->varattno; - - if (var->varnosyn > 0 && var->varnosyn <= list_length(dpns->rtable) && dpns->plan == NULL) { - rte = rt_fetch(var->varnosyn, dpns->rtable); - - /* - * if the rte var->varnosyn points to is not a regular table and it is a join - * then the correct relname will be found with var->varnosyn and var->varattnosyn - */ - if (rte->rtekind == RTE_JOIN && rte->relid == 0 && var->varnosyn != var->varno) { - varno = var->varnosyn; - varattno = var->varattnosyn; - } - } - - /* - * Try to find the relevant RTE in this rtable. In a plan tree, it's - * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig - * down into the subplans, or INDEX_VAR, which is resolved similarly. - */ - if (varno >= 1 && varno <= list_length(dpns->rtable)) - { - rte = rt_fetch(varno, dpns->rtable); - attnum = varattno; - } - else if (varno == OUTER_VAR && dpns->outer_tlist) - { - TargetEntry *tle; - deparse_namespace save_dpns; - const char *result; - - tle = get_tle_by_resno(dpns->outer_tlist, varattno); - if (!tle) - elog(ERROR, "bogus varattno for OUTER_VAR var: %d", varattno); - - Assert(netlevelsup == 0); - push_child_plan(dpns, dpns->outer_plan, &save_dpns); - - result = get_name_for_var_field((Var *) tle->expr, fieldno, - levelsup, context); - - pop_child_plan(dpns, &save_dpns); - return result; - } - else if (varno == INNER_VAR && dpns->inner_tlist) - { - TargetEntry *tle; - deparse_namespace save_dpns; - const char *result; - - tle = get_tle_by_resno(dpns->inner_tlist, varattno); - if (!tle) - elog(ERROR, "bogus varattno for INNER_VAR var: %d", varattno); - - Assert(netlevelsup == 0); - push_child_plan(dpns, dpns->inner_plan, &save_dpns); - - result = get_name_for_var_field((Var *) tle->expr, fieldno, - levelsup, context); - - pop_child_plan(dpns, &save_dpns); - return result; - } - else if (varno == INDEX_VAR && dpns->index_tlist) - { - TargetEntry *tle; - const char *result; - - tle = get_tle_by_resno(dpns->index_tlist, varattno); - if (!tle) - elog(ERROR, "bogus varattno for INDEX_VAR var: %d", varattno); - - Assert(netlevelsup == 0); - - result = get_name_for_var_field((Var *) tle->expr, fieldno, - levelsup, context); - - return result; - } - else - { - elog(ERROR, "bogus varno: %d", varno); - return NULL; /* keep compiler quiet */ - } - - if (attnum == InvalidAttrNumber) - { - /* Var is whole-row reference to RTE, so select the right field */ - return get_rte_attribute_name(rte, fieldno); - } - - /* - * This part has essentially the same logic as the parser's - * expandRecordVariable() function, but we are dealing with a different - * representation of the input context, and we only need one field name - * not a TupleDesc. Also, we need special cases for finding subquery and - * CTE subplans when deparsing Plan trees. - */ - expr = (Node *) var; /* default if we can't drill down */ - - switch (rte->rtekind) - { - case RTE_RELATION: - case RTE_VALUES: - case RTE_NAMEDTUPLESTORE: - case RTE_RESULT: - - /* - * This case should not occur: a column of a table or values list - * shouldn't have type RECORD. Fall through and fail (most - * likely) at the bottom. - */ - break; - case RTE_SUBQUERY: - /* Subselect-in-FROM: examine sub-select's output expr */ - { - if (rte->subquery) - { - TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList, - attnum); - - if (ste == NULL || ste->resjunk) - elog(ERROR, "subquery %s does not have attribute %d", - rte->eref->aliasname, attnum); - expr = (Node *) ste->expr; - if (IsA(expr, Var)) - { - /* - * Recurse into the sub-select to see what its Var - * refers to. We have to build an additional level of - * namespace to keep in step with varlevelsup in the - * subselect. - */ - deparse_namespace mydpns; - const char *result; - - set_deparse_for_query(&mydpns, rte->subquery, - context->namespaces); - - context->namespaces = lcons(&mydpns, - context->namespaces); - - result = get_name_for_var_field((Var *) expr, fieldno, - 0, context); - - context->namespaces = - list_delete_first(context->namespaces); - - return result; - } - /* else fall through to inspect the expression */ - } - else - { - /* - * We're deparsing a Plan tree so we don't have complete - * RTE entries (in particular, rte->subquery is NULL). But - * the only place we'd see a Var directly referencing a - * SUBQUERY RTE is in a SubqueryScan plan node, and we can - * look into the child plan's tlist instead. - */ - TargetEntry *tle; - deparse_namespace save_dpns; - const char *result; - - if (!dpns->inner_plan) - elog(ERROR, "failed to find plan for subquery %s", - rte->eref->aliasname); - tle = get_tle_by_resno(dpns->inner_tlist, attnum); - if (!tle) - elog(ERROR, "bogus varattno for subquery var: %d", - attnum); - Assert(netlevelsup == 0); - push_child_plan(dpns, dpns->inner_plan, &save_dpns); - - result = get_name_for_var_field((Var *) tle->expr, fieldno, - levelsup, context); - - pop_child_plan(dpns, &save_dpns); - return result; - } - } - break; - case RTE_JOIN: - /* Join RTE --- recursively inspect the alias variable */ - if (rte->joinaliasvars == NIL) - elog(ERROR, "cannot decompile join alias var in plan tree"); - Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars)); - expr = (Node *) list_nth(rte->joinaliasvars, attnum - 1); - Assert(expr != NULL); - /* we intentionally don't strip implicit coercions here */ - if (IsA(expr, Var)) - return get_name_for_var_field((Var *) expr, fieldno, - var->varlevelsup + levelsup, - context); - /* else fall through to inspect the expression */ - break; - case RTE_FUNCTION: - case RTE_TABLEFUNC: - - /* - * We couldn't get here unless a function is declared with one of - * its result columns as RECORD, which is not allowed. - */ - break; - case RTE_CTE: - /* CTE reference: examine subquery's output expr */ - { - CommonTableExpr *cte = NULL; - Index ctelevelsup; - ListCell *lc; - - /* - * Try to find the referenced CTE using the namespace stack. - */ - ctelevelsup = rte->ctelevelsup + netlevelsup; - if (ctelevelsup >= list_length(context->namespaces)) - lc = NULL; - else - { - deparse_namespace *ctedpns; - - ctedpns = (deparse_namespace *) - list_nth(context->namespaces, ctelevelsup); - foreach(lc, ctedpns->ctes) - { - cte = (CommonTableExpr *) lfirst(lc); - if (strcmp(cte->ctename, rte->ctename) == 0) - break; - } - } - if (lc != NULL) - { - Query *ctequery = (Query *) cte->ctequery; - TargetEntry *ste = get_tle_by_resno(GetCTETargetList(cte), - attnum); - - if (ste == NULL || ste->resjunk) - elog(ERROR, "subquery %s does not have attribute %d", - rte->eref->aliasname, attnum); - expr = (Node *) ste->expr; - if (IsA(expr, Var)) - { - /* - * Recurse into the CTE to see what its Var refers to. - * We have to build an additional level of namespace - * to keep in step with varlevelsup in the CTE. - * Furthermore it could be an outer CTE, so we may - * have to delete some levels of namespace. - */ - List *save_nslist = context->namespaces; - List *new_nslist; - deparse_namespace mydpns; - const char *result; - - set_deparse_for_query(&mydpns, ctequery, - context->namespaces); - - new_nslist = list_copy_tail(context->namespaces, - ctelevelsup); - context->namespaces = lcons(&mydpns, new_nslist); - - result = get_name_for_var_field((Var *) expr, fieldno, - 0, context); - - context->namespaces = save_nslist; - - return result; - } - /* else fall through to inspect the expression */ - } - else - { - /* - * We're deparsing a Plan tree so we don't have a CTE - * list. But the only place we'd see a Var directly - * referencing a CTE RTE is in a CteScan plan node, and we - * can look into the subplan's tlist instead. - */ - TargetEntry *tle; - deparse_namespace save_dpns; - const char *result; - - if (!dpns->inner_plan) - elog(ERROR, "failed to find plan for CTE %s", - rte->eref->aliasname); - tle = get_tle_by_resno(dpns->inner_tlist, attnum); - if (!tle) - elog(ERROR, "bogus varattno for subquery var: %d", - attnum); - Assert(netlevelsup == 0); - push_child_plan(dpns, dpns->inner_plan, &save_dpns); - - result = get_name_for_var_field((Var *) tle->expr, fieldno, - levelsup, context); - - pop_child_plan(dpns, &save_dpns); - return result; - } - } - break; - } - - /* - * We now have an expression we can't expand any more, so see if - * get_expr_result_tupdesc() can do anything with it. - */ - tupleDesc = get_expr_result_tupdesc(expr, false); - /* Got the tupdesc, so we can extract the field name */ - Assert(fieldno >= 1 && fieldno <= tupleDesc->natts); - return NameStr(TupleDescAttr(tupleDesc, fieldno - 1)->attname); -} - -/* - * Try to find the referenced expression for a PARAM_EXEC Param that might - * reference a parameter supplied by an upper NestLoop or SubPlan plan node. - * - * If successful, return the expression and set *dpns_p and *ancestor_cell_p - * appropriately for calling push_ancestor_plan(). If no referent can be - * found, return NULL. - */ -static Node * -find_param_referent(Param *param, deparse_context *context, - deparse_namespace **dpns_p, ListCell **ancestor_cell_p) -{ - /* Initialize output parameters to prevent compiler warnings */ - *dpns_p = NULL; - *ancestor_cell_p = NULL; - - /* - * If it's a PARAM_EXEC parameter, look for a matching NestLoopParam or - * SubPlan argument. This will necessarily be in some ancestor of the - * current expression's Plan. - */ - if (param->paramkind == PARAM_EXEC) - { - deparse_namespace *dpns; - Plan *child_plan; - bool in_same_plan_level; - ListCell *lc; - - dpns = (deparse_namespace *) linitial(context->namespaces); - child_plan = dpns->plan; - in_same_plan_level = true; - - foreach(lc, dpns->ancestors) - { - Node *ancestor = (Node *) lfirst(lc); - ListCell *lc2; - - /* - * NestLoops transmit params to their inner child only; also, once - * we've crawled up out of a subplan, this couldn't possibly be - * the right match. - */ - if (IsA(ancestor, NestLoop) && - child_plan == innerPlan(ancestor) && - in_same_plan_level) - { - NestLoop *nl = (NestLoop *) ancestor; - - foreach(lc2, nl->nestParams) - { - NestLoopParam *nlp = (NestLoopParam *) lfirst(lc2); - - if (nlp->paramno == param->paramid) - { - /* Found a match, so return it */ - *dpns_p = dpns; - *ancestor_cell_p = lc; - return (Node *) nlp->paramval; - } - } - } - - /* - * Check to see if we're crawling up from a subplan. - */ - if(IsA(ancestor, SubPlan)) - { - SubPlan *subplan = (SubPlan *) ancestor; - ListCell *lc3; - ListCell *lc4; - - /* Matched subplan, so check its arguments */ - forboth(lc3, subplan->parParam, lc4, subplan->args) - { - int paramid = lfirst_int(lc3); - Node *arg = (Node *) lfirst(lc4); - - if (paramid == param->paramid) - { - /* - * Found a match, so return it. But, since Vars in - * the arg are to be evaluated in the surrounding - * context, we have to point to the next ancestor item - * that is *not* a SubPlan. - */ - ListCell *rest; - - for_each_cell(rest, dpns->ancestors, - lnext(dpns->ancestors, lc)) - { - Node *ancestor2 = (Node *) lfirst(rest); - - if (!IsA(ancestor2, SubPlan)) - { - *dpns_p = dpns; - *ancestor_cell_p = rest; - return arg; - } - } - elog(ERROR, "SubPlan cannot be outermost ancestor"); - } - } - - /* We have emerged from a subplan. */ - in_same_plan_level = false; - - /* SubPlan isn't a kind of Plan, so skip the rest */ - continue; - } - - /* - * Check to see if we're emerging from an initplan of the current - * ancestor plan. Initplans never have any parParams, so no need - * to search that list, but we need to know if we should reset - * in_same_plan_level. - */ - foreach(lc2, ((Plan *) ancestor)->initPlan) - { - SubPlan *subplan = castNode(SubPlan, lfirst(lc2)); - - if (child_plan != (Plan *) list_nth(dpns->subplans, - subplan->plan_id - 1)) - continue; - - /* No parameters to be had here. */ - Assert(subplan->parParam == NIL); - - /* We have emerged from an initplan. */ - in_same_plan_level = false; - break; - } - - /* No luck, crawl up to next ancestor */ - child_plan = (Plan *) ancestor; - } - } - - /* No referent found */ - return NULL; -} - -/* - * Display a Param appropriately. - */ -static void -get_parameter(Param *param, deparse_context *context) -{ - Node *expr; - deparse_namespace *dpns; - ListCell *ancestor_cell; - - /* - * If it's a PARAM_EXEC parameter, try to locate the expression from which - * the parameter was computed. Note that failing to find a referent isn't - * an error, since the Param might well be a subplan output rather than an - * input. - */ - expr = find_param_referent(param, context, &dpns, &ancestor_cell); - if (expr) - { - /* Found a match, so print it */ - deparse_namespace save_dpns; - bool save_varprefix; - bool need_paren; - - /* Switch attention to the ancestor plan node */ - push_ancestor_plan(dpns, ancestor_cell, &save_dpns); - - /* - * Force prefixing of Vars, since they won't belong to the relation - * being scanned in the original plan node. - */ - save_varprefix = context->varprefix; - context->varprefix = true; - - /* - * A Param's expansion is typically a Var, Aggref, or upper-level - * Param, which wouldn't need extra parentheses. Otherwise, insert - * parens to ensure the expression looks atomic. - */ - need_paren = !(IsA(expr, Var) || - IsA(expr, Aggref) || - IsA(expr, Param)); - if (need_paren) - appendStringInfoChar(context->buf, '('); - - get_rule_expr(expr, context, false); - - if (need_paren) - appendStringInfoChar(context->buf, ')'); - - context->varprefix = save_varprefix; - - pop_ancestor_plan(dpns, &save_dpns); - - return; - } - - /* - * Not PARAM_EXEC, or couldn't find referent: for base types just print $N. - * For composite types, add cast to the parameter to ease remote node detect - * the type. - */ - if (param->paramtype >= FirstNormalObjectId) - { - char *typeName = format_type_with_typemod(param->paramtype, param->paramtypmod); - - appendStringInfo(context->buf, "$%d::%s", param->paramid, typeName); - } - else - { - appendStringInfo(context->buf, "$%d", param->paramid); - } -} - -/* - * get_simple_binary_op_name - * - * helper function for isSimpleNode - * will return single char binary operator name, or NULL if it's not - */ -static const char * -get_simple_binary_op_name(OpExpr *expr) -{ - List *args = expr->args; - - if (list_length(args) == 2) - { - /* binary operator */ - Node *arg1 = (Node *) linitial(args); - Node *arg2 = (Node *) lsecond(args); - const char *op; - - op = generate_operator_name(expr->opno, exprType(arg1), exprType(arg2)); - if (strlen(op) == 1) - return op; - } - return NULL; -} - - -/* - * isSimpleNode - check if given node is simple (doesn't need parenthesizing) - * - * true : simple in the context of parent node's type - * false : not simple - */ -static bool -isSimpleNode(Node *node, Node *parentNode, int prettyFlags) -{ - if (!node) - return false; - - switch (nodeTag(node)) - { - case T_Var: - case T_Const: - case T_Param: - case T_CoerceToDomainValue: - case T_SetToDefault: - case T_CurrentOfExpr: - /* single words: always simple */ - return true; - - case T_SubscriptingRef: - case T_ArrayExpr: - case T_RowExpr: - case T_CoalesceExpr: - case T_MinMaxExpr: - case T_SQLValueFunction: - case T_XmlExpr: - case T_NextValueExpr: - case T_NullIfExpr: - case T_Aggref: - case T_WindowFunc: - case T_FuncExpr: - /* function-like: name(..) or name[..] */ - return true; - - /* CASE keywords act as parentheses */ - case T_CaseExpr: - return true; - - case T_FieldSelect: - - /* - * appears simple since . has top precedence, unless parent is - * T_FieldSelect itself! - */ - return (IsA(parentNode, FieldSelect) ? false : true); - - case T_FieldStore: - - /* - * treat like FieldSelect (probably doesn't matter) - */ - return (IsA(parentNode, FieldStore) ? false : true); - - case T_CoerceToDomain: - /* maybe simple, check args */ - return isSimpleNode((Node *) ((CoerceToDomain *) node)->arg, - node, prettyFlags); - case T_RelabelType: - return isSimpleNode((Node *) ((RelabelType *) node)->arg, - node, prettyFlags); - case T_CoerceViaIO: - return isSimpleNode((Node *) ((CoerceViaIO *) node)->arg, - node, prettyFlags); - case T_ArrayCoerceExpr: - return isSimpleNode((Node *) ((ArrayCoerceExpr *) node)->arg, - node, prettyFlags); - case T_ConvertRowtypeExpr: - return isSimpleNode((Node *) ((ConvertRowtypeExpr *) node)->arg, - node, prettyFlags); - - case T_OpExpr: - { - /* depends on parent node type; needs further checking */ - if (prettyFlags & PRETTYFLAG_PAREN && IsA(parentNode, OpExpr)) - { - const char *op; - const char *parentOp; - bool is_lopriop; - bool is_hipriop; - bool is_lopriparent; - bool is_hipriparent; - - op = get_simple_binary_op_name((OpExpr *) node); - if (!op) - return false; - - /* We know only the basic operators + - and * / % */ - is_lopriop = (strchr("+-", *op) != NULL); - is_hipriop = (strchr("*/%", *op) != NULL); - if (!(is_lopriop || is_hipriop)) - return false; - - parentOp = get_simple_binary_op_name((OpExpr *) parentNode); - if (!parentOp) - return false; - - is_lopriparent = (strchr("+-", *parentOp) != NULL); - is_hipriparent = (strchr("*/%", *parentOp) != NULL); - if (!(is_lopriparent || is_hipriparent)) - return false; - - if (is_hipriop && is_lopriparent) - return true; /* op binds tighter than parent */ - - if (is_lopriop && is_hipriparent) - return false; - - /* - * Operators are same priority --- can skip parens only if - * we have (a - b) - c, not a - (b - c). - */ - if (node == (Node *) linitial(((OpExpr *) parentNode)->args)) - return true; - - return false; - } - /* else do the same stuff as for T_SubLink et al. */ - } - /* FALLTHROUGH */ - - case T_SubLink: - case T_NullTest: - case T_BooleanTest: - case T_DistinctExpr: - switch (nodeTag(parentNode)) - { - case T_FuncExpr: - { - /* special handling for casts */ - CoercionForm type = ((FuncExpr *) parentNode)->funcformat; - - if (type == COERCE_EXPLICIT_CAST || - type == COERCE_IMPLICIT_CAST) - return false; - return true; /* own parentheses */ - } - case T_BoolExpr: /* lower precedence */ - case T_SubscriptingRef: /* other separators */ - case T_ArrayExpr: /* other separators */ - case T_RowExpr: /* other separators */ - case T_CoalesceExpr: /* own parentheses */ - case T_MinMaxExpr: /* own parentheses */ - case T_XmlExpr: /* own parentheses */ - case T_NullIfExpr: /* other separators */ - case T_Aggref: /* own parentheses */ - case T_WindowFunc: /* own parentheses */ - case T_CaseExpr: /* other separators */ - return true; - default: - return false; - } - - case T_BoolExpr: - switch (nodeTag(parentNode)) - { - case T_BoolExpr: - if (prettyFlags & PRETTYFLAG_PAREN) - { - BoolExprType type; - BoolExprType parentType; - - type = ((BoolExpr *) node)->boolop; - parentType = ((BoolExpr *) parentNode)->boolop; - switch (type) - { - case NOT_EXPR: - case AND_EXPR: - if (parentType == AND_EXPR || parentType == OR_EXPR) - return true; - break; - case OR_EXPR: - if (parentType == OR_EXPR) - return true; - break; - } - } - return false; - case T_FuncExpr: - { - /* special handling for casts */ - CoercionForm type = ((FuncExpr *) parentNode)->funcformat; - - if (type == COERCE_EXPLICIT_CAST || - type == COERCE_IMPLICIT_CAST) - return false; - return true; /* own parentheses */ - } - case T_SubscriptingRef: /* other separators */ - case T_ArrayExpr: /* other separators */ - case T_RowExpr: /* other separators */ - case T_CoalesceExpr: /* own parentheses */ - case T_MinMaxExpr: /* own parentheses */ - case T_XmlExpr: /* own parentheses */ - case T_NullIfExpr: /* other separators */ - case T_Aggref: /* own parentheses */ - case T_WindowFunc: /* own parentheses */ - case T_CaseExpr: /* other separators */ - return true; - default: - return false; - } - - default: - break; - } - /* those we don't know: in dubio complexo */ - return false; -} - - -/* - * appendContextKeyword - append a keyword to buffer - * - * If prettyPrint is enabled, perform a line break, and adjust indentation. - * Otherwise, just append the keyword. - */ -static void -appendContextKeyword(deparse_context *context, const char *str, - int indentBefore, int indentAfter, int indentPlus) -{ - StringInfo buf = context->buf; - - if (PRETTY_INDENT(context)) - { - int indentAmount; - - context->indentLevel += indentBefore; - - /* remove any trailing spaces currently in the buffer ... */ - removeStringInfoSpaces(buf); - /* ... then add a newline and some spaces */ - appendStringInfoChar(buf, '\n'); - - if (context->indentLevel < PRETTYINDENT_LIMIT) - indentAmount = Max(context->indentLevel, 0) + indentPlus; - else - { - /* - * If we're indented more than PRETTYINDENT_LIMIT characters, try - * to conserve horizontal space by reducing the per-level - * indentation. For best results the scale factor here should - * divide all the indent amounts that get added to indentLevel - * (PRETTYINDENT_STD, etc). It's important that the indentation - * not grow unboundedly, else deeply-nested trees use O(N^2) - * whitespace; so we also wrap modulo PRETTYINDENT_LIMIT. - */ - indentAmount = PRETTYINDENT_LIMIT + - (context->indentLevel - PRETTYINDENT_LIMIT) / - (PRETTYINDENT_STD / 2); - indentAmount %= PRETTYINDENT_LIMIT; - /* scale/wrap logic affects indentLevel, but not indentPlus */ - indentAmount += indentPlus; - } - appendStringInfoSpaces(buf, indentAmount); - - appendStringInfoString(buf, str); - - context->indentLevel += indentAfter; - if (context->indentLevel < 0) - context->indentLevel = 0; - } - else - appendStringInfoString(buf, str); -} - -/* - * removeStringInfoSpaces - delete trailing spaces from a buffer. - * - * Possibly this should move to stringinfo.c at some point. - */ -static void -removeStringInfoSpaces(StringInfo str) -{ - while (str->len > 0 && str->data[str->len - 1] == ' ') - str->data[--(str->len)] = '\0'; -} - - -/* - * get_rule_expr_paren - deparse expr using get_rule_expr, - * embracing the string with parentheses if necessary for prettyPrint. - * - * Never embrace if prettyFlags=0, because it's done in the calling node. - * - * Any node that does *not* embrace its argument node by sql syntax (with - * parentheses, non-operator keywords like CASE/WHEN/ON, or comma etc) should - * use get_rule_expr_paren instead of get_rule_expr so parentheses can be - * added. - */ -static void -get_rule_expr_paren(Node *node, deparse_context *context, - bool showimplicit, Node *parentNode) -{ - bool need_paren; - - need_paren = PRETTY_PAREN(context) && - !isSimpleNode(node, parentNode, context->prettyFlags); - - if (need_paren) - appendStringInfoChar(context->buf, '('); - - get_rule_expr(node, context, showimplicit); - - if (need_paren) - appendStringInfoChar(context->buf, ')'); -} - - -/* ---------- - * get_rule_expr - Parse back an expression - * - * Note: showimplicit determines whether we display any implicit cast that - * is present at the top of the expression tree. It is a passed argument, - * not a field of the context struct, because we change the value as we - * recurse down into the expression. In general we suppress implicit casts - * when the result type is known with certainty (eg, the arguments of an - * OR must be boolean). We display implicit casts for arguments of functions - * and operators, since this is needed to be certain that the same function - * or operator will be chosen when the expression is re-parsed. - * ---------- - */ -static void -get_rule_expr(Node *node, deparse_context *context, - bool showimplicit) -{ - StringInfo buf = context->buf; - - if (node == NULL) - return; - - /* Guard against excessively long or deeply-nested queries */ - CHECK_FOR_INTERRUPTS(); - check_stack_depth(); - - /* - * Each level of get_rule_expr must emit an indivisible term - * (parenthesized if necessary) to ensure result is reparsed into the same - * expression tree. The only exception is that when the input is a List, - * we emit the component items comma-separated with no surrounding - * decoration; this is convenient for most callers. - */ - switch (nodeTag(node)) - { - case T_Var: - (void) get_variable((Var *) node, 0, false, context); - break; - - case T_Const: - get_const_expr((Const *) node, context, 0); - break; - - case T_Param: - get_parameter((Param *) node, context); - break; - - case T_Aggref: - get_agg_expr((Aggref *) node, context, (Aggref *) node); - break; - - case T_GroupingFunc: - { - GroupingFunc *gexpr = (GroupingFunc *) node; - - appendStringInfoString(buf, "GROUPING("); - get_rule_expr((Node *) gexpr->args, context, true); - appendStringInfoChar(buf, ')'); - } - break; - - case T_WindowFunc: - get_windowfunc_expr((WindowFunc *) node, context); - break; - - case T_SubscriptingRef: - { - SubscriptingRef *sbsref = (SubscriptingRef *) node; - bool need_parens; - - /* - * If the argument is a CaseTestExpr, we must be inside a - * FieldStore, ie, we are assigning to an element of an array - * within a composite column. Since we already punted on - * displaying the FieldStore's target information, just punt - * here too, and display only the assignment source - * expression. - */ - if (IsA(sbsref->refexpr, CaseTestExpr)) - { - Assert(sbsref->refassgnexpr); - get_rule_expr((Node *) sbsref->refassgnexpr, - context, showimplicit); - break; - } - - /* - * Parenthesize the argument unless it's a simple Var or a - * FieldSelect. (In particular, if it's another - * SubscriptingRef, we *must* parenthesize to avoid - * confusion.) - */ - need_parens = !IsA(sbsref->refexpr, Var) && - !IsA(sbsref->refexpr, FieldSelect); - if (need_parens) - appendStringInfoChar(buf, '('); - get_rule_expr((Node *) sbsref->refexpr, context, showimplicit); - if (need_parens) - appendStringInfoChar(buf, ')'); - - /* - * If there's a refassgnexpr, we want to print the node in the - * format "container[subscripts] := refassgnexpr". This is - * not legal SQL, so decompilation of INSERT or UPDATE - * statements should always use processIndirection as part of - * the statement-level syntax. We should only see this when - * EXPLAIN tries to print the targetlist of a plan resulting - * from such a statement. - */ - if (sbsref->refassgnexpr) - { - Node *refassgnexpr; - - /* - * Use processIndirection to print this node's subscripts - * as well as any additional field selections or - * subscripting in immediate descendants. It returns the - * RHS expr that is actually being "assigned". - */ - refassgnexpr = processIndirection(node, context); - appendStringInfoString(buf, " := "); - get_rule_expr(refassgnexpr, context, showimplicit); - } - else - { - /* Just an ordinary container fetch, so print subscripts */ - printSubscripts(sbsref, context); - } - } - break; - - case T_FuncExpr: - get_func_expr((FuncExpr *) node, context, showimplicit); - break; - - case T_NamedArgExpr: - { - NamedArgExpr *na = (NamedArgExpr *) node; - - appendStringInfo(buf, "%s => ", quote_identifier(na->name)); - get_rule_expr((Node *) na->arg, context, showimplicit); - } - break; - - case T_OpExpr: - get_oper_expr((OpExpr *) node, context); - break; - - case T_DistinctExpr: - { - DistinctExpr *expr = (DistinctExpr *) node; - List *args = expr->args; - Node *arg1 = (Node *) linitial(args); - Node *arg2 = (Node *) lsecond(args); - - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, '('); - get_rule_expr_paren(arg1, context, true, node); - appendStringInfoString(buf, " IS DISTINCT FROM "); - get_rule_expr_paren(arg2, context, true, node); - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, ')'); - } - break; - - case T_NullIfExpr: - { - NullIfExpr *nullifexpr = (NullIfExpr *) node; - - appendStringInfoString(buf, "NULLIF("); - get_rule_expr((Node *) nullifexpr->args, context, true); - appendStringInfoChar(buf, ')'); - } - break; - - case T_ScalarArrayOpExpr: - { - ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; - List *args = expr->args; - Node *arg1 = (Node *) linitial(args); - Node *arg2 = (Node *) lsecond(args); - - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, '('); - get_rule_expr_paren(arg1, context, true, node); - appendStringInfo(buf, " %s %s (", - generate_operator_name(expr->opno, - exprType(arg1), - get_base_element_type(exprType(arg2))), - expr->useOr ? "ANY" : "ALL"); - get_rule_expr_paren(arg2, context, true, node); - - /* - * There's inherent ambiguity in "x op ANY/ALL (y)" when y is - * a bare sub-SELECT. Since we're here, the sub-SELECT must - * be meant as a scalar sub-SELECT yielding an array value to - * be used in ScalarArrayOpExpr; but the grammar will - * preferentially interpret such a construct as an ANY/ALL - * SubLink. To prevent misparsing the output that way, insert - * a dummy coercion (which will be stripped by parse analysis, - * so no inefficiency is added in dump and reload). This is - * indeed most likely what the user wrote to get the construct - * accepted in the first place. - */ - if (IsA(arg2, SubLink) && - ((SubLink *) arg2)->subLinkType == EXPR_SUBLINK) - appendStringInfo(buf, "::%s", - format_type_with_typemod(exprType(arg2), - exprTypmod(arg2))); - appendStringInfoChar(buf, ')'); - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, ')'); - } - break; - - case T_BoolExpr: - { - BoolExpr *expr = (BoolExpr *) node; - Node *first_arg = linitial(expr->args); - ListCell *arg = list_second_cell(expr->args); - - switch (expr->boolop) - { - case AND_EXPR: - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, '('); - get_rule_expr_paren(first_arg, context, - false, node); - while (arg) - { - appendStringInfoString(buf, " AND "); - get_rule_expr_paren((Node *) lfirst(arg), context, - false, node); - arg = lnext(expr->args, arg); - } - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, ')'); - break; - - case OR_EXPR: - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, '('); - get_rule_expr_paren(first_arg, context, - false, node); - while (arg) - { - appendStringInfoString(buf, " OR "); - get_rule_expr_paren((Node *) lfirst(arg), context, - false, node); - arg = lnext(expr->args, arg); - } - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, ')'); - break; - - case NOT_EXPR: - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, '('); - appendStringInfoString(buf, "NOT "); - get_rule_expr_paren(first_arg, context, - false, node); - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, ')'); - break; - - default: - elog(ERROR, "unrecognized boolop: %d", - (int) expr->boolop); - } - } - break; - - case T_SubLink: - get_sublink_expr((SubLink *) node, context); - break; - - case T_SubPlan: - { - SubPlan *subplan = (SubPlan *) node; - - /* - * We cannot see an already-planned subplan in rule deparsing, - * only while EXPLAINing a query plan. We don't try to - * reconstruct the original SQL, just reference the subplan - * that appears elsewhere in EXPLAIN's result. - */ - if (subplan->useHashTable) - appendStringInfo(buf, "(hashed %s)", subplan->plan_name); - else - appendStringInfo(buf, "(%s)", subplan->plan_name); - } - break; - - case T_AlternativeSubPlan: - { - AlternativeSubPlan *asplan = (AlternativeSubPlan *) node; - ListCell *lc; - - /* As above, this can only happen during EXPLAIN */ - appendStringInfoString(buf, "(alternatives: "); - foreach(lc, asplan->subplans) - { - SubPlan *splan = lfirst_node(SubPlan, lc); - - if (splan->useHashTable) - appendStringInfo(buf, "hashed %s", splan->plan_name); - else - appendStringInfoString(buf, splan->plan_name); - if (lnext(asplan->subplans, lc)) - appendStringInfoString(buf, " or "); - } - appendStringInfoChar(buf, ')'); - } - break; - - case T_FieldSelect: - { - FieldSelect *fselect = (FieldSelect *) node; - Node *arg = (Node *) fselect->arg; - int fno = fselect->fieldnum; - const char *fieldname; - bool need_parens; - - /* - * Parenthesize the argument unless it's an SubscriptingRef or - * another FieldSelect. Note in particular that it would be - * WRONG to not parenthesize a Var argument; simplicity is not - * the issue here, having the right number of names is. - */ - need_parens = !IsA(arg, SubscriptingRef) && - !IsA(arg, FieldSelect); - if (need_parens) - appendStringInfoChar(buf, '('); - get_rule_expr(arg, context, true); - if (need_parens) - appendStringInfoChar(buf, ')'); - - /* - * Get and print the field name. - */ - fieldname = get_name_for_var_field((Var *) arg, fno, - 0, context); - appendStringInfo(buf, ".%s", quote_identifier(fieldname)); - } - break; - - case T_FieldStore: - { - FieldStore *fstore = (FieldStore *) node; - bool need_parens; - - /* - * There is no good way to represent a FieldStore as real SQL, - * so decompilation of INSERT or UPDATE statements should - * always use processIndirection as part of the - * statement-level syntax. We should only get here when - * EXPLAIN tries to print the targetlist of a plan resulting - * from such a statement. The plan case is even harder than - * ordinary rules would be, because the planner tries to - * collapse multiple assignments to the same field or subfield - * into one FieldStore; so we can see a list of target fields - * not just one, and the arguments could be FieldStores - * themselves. We don't bother to try to print the target - * field names; we just print the source arguments, with a - * ROW() around them if there's more than one. This isn't - * terribly complete, but it's probably good enough for - * EXPLAIN's purposes; especially since anything more would be - * either hopelessly confusing or an even poorer - * representation of what the plan is actually doing. - */ - need_parens = (list_length(fstore->newvals) != 1); - if (need_parens) - appendStringInfoString(buf, "ROW("); - get_rule_expr((Node *) fstore->newvals, context, showimplicit); - if (need_parens) - appendStringInfoChar(buf, ')'); - } - break; - - case T_RelabelType: - { - RelabelType *relabel = (RelabelType *) node; - Node *arg = (Node *) relabel->arg; - - if (relabel->relabelformat == COERCE_IMPLICIT_CAST && - !showimplicit) - { - /* don't show the implicit cast */ - get_rule_expr_paren(arg, context, false, node); - } - else - { - get_coercion_expr(arg, context, - relabel->resulttype, - relabel->resulttypmod, - node); - } - } - break; - - case T_CoerceViaIO: - { - CoerceViaIO *iocoerce = (CoerceViaIO *) node; - Node *arg = (Node *) iocoerce->arg; - - if (iocoerce->coerceformat == COERCE_IMPLICIT_CAST && - !showimplicit) - { - /* don't show the implicit cast */ - get_rule_expr_paren(arg, context, false, node); - } - else - { - get_coercion_expr(arg, context, - iocoerce->resulttype, - -1, - node); - } - } - break; - - case T_ArrayCoerceExpr: - { - ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node; - Node *arg = (Node *) acoerce->arg; - - if (acoerce->coerceformat == COERCE_IMPLICIT_CAST && - !showimplicit) - { - /* don't show the implicit cast */ - get_rule_expr_paren(arg, context, false, node); - } - else - { - get_coercion_expr(arg, context, - acoerce->resulttype, - acoerce->resulttypmod, - node); - } - } - break; - - case T_ConvertRowtypeExpr: - { - ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node; - Node *arg = (Node *) convert->arg; - - if (convert->convertformat == COERCE_IMPLICIT_CAST && - !showimplicit) - { - /* don't show the implicit cast */ - get_rule_expr_paren(arg, context, false, node); - } - else - { - get_coercion_expr(arg, context, - convert->resulttype, -1, - node); - } - } - break; - - case T_CollateExpr: - { - CollateExpr *collate = (CollateExpr *) node; - Node *arg = (Node *) collate->arg; - - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, '('); - get_rule_expr_paren(arg, context, showimplicit, node); - appendStringInfo(buf, " COLLATE %s", - generate_collation_name(collate->collOid)); - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, ')'); - } - break; - - case T_CaseExpr: - { - CaseExpr *caseexpr = (CaseExpr *) node; - ListCell *temp; - - appendContextKeyword(context, "CASE", - 0, PRETTYINDENT_VAR, 0); - if (caseexpr->arg) - { - appendStringInfoChar(buf, ' '); - get_rule_expr((Node *) caseexpr->arg, context, true); - } - foreach(temp, caseexpr->args) - { - CaseWhen *when = (CaseWhen *) lfirst(temp); - Node *w = (Node *) when->expr; - - if (caseexpr->arg) - { - /* - * The parser should have produced WHEN clauses of the - * form "CaseTestExpr = RHS", possibly with an - * implicit coercion inserted above the CaseTestExpr. - * For accurate decompilation of rules it's essential - * that we show just the RHS. However in an - * expression that's been through the optimizer, the - * WHEN clause could be almost anything (since the - * equality operator could have been expanded into an - * inline function). If we don't recognize the form - * of the WHEN clause, just punt and display it as-is. - */ - if (IsA(w, OpExpr)) - { - List *args = ((OpExpr *) w)->args; - - if (list_length(args) == 2 && - IsA(strip_implicit_coercions(linitial(args)), - CaseTestExpr)) - w = (Node *) lsecond(args); - } - } - - if (!PRETTY_INDENT(context)) - appendStringInfoChar(buf, ' '); - appendContextKeyword(context, "WHEN ", - 0, 0, 0); - get_rule_expr(w, context, false); - appendStringInfoString(buf, " THEN "); - get_rule_expr((Node *) when->result, context, true); - } - if (!PRETTY_INDENT(context)) - appendStringInfoChar(buf, ' '); - appendContextKeyword(context, "ELSE ", - 0, 0, 0); - get_rule_expr((Node *) caseexpr->defresult, context, true); - if (!PRETTY_INDENT(context)) - appendStringInfoChar(buf, ' '); - appendContextKeyword(context, "END", - -PRETTYINDENT_VAR, 0, 0); - } - break; - - case T_CaseTestExpr: - { - /* - * Normally we should never get here, since for expressions - * that can contain this node type we attempt to avoid - * recursing to it. But in an optimized expression we might - * be unable to avoid that (see comments for CaseExpr). If we - * do see one, print it as CASE_TEST_EXPR. - */ - appendStringInfoString(buf, "CASE_TEST_EXPR"); - } - break; - - case T_ArrayExpr: - { - ArrayExpr *arrayexpr = (ArrayExpr *) node; - - appendStringInfoString(buf, "ARRAY["); - get_rule_expr((Node *) arrayexpr->elements, context, true); - appendStringInfoChar(buf, ']'); - - /* - * If the array isn't empty, we assume its elements are - * coerced to the desired type. If it's empty, though, we - * need an explicit coercion to the array type. - */ - if (arrayexpr->elements == NIL) - appendStringInfo(buf, "::%s", - format_type_with_typemod(arrayexpr->array_typeid, -1)); - } - break; - - case T_RowExpr: - { - RowExpr *rowexpr = (RowExpr *) node; - TupleDesc tupdesc = NULL; - ListCell *arg; - int i; - char *sep; - - /* - * If it's a named type and not RECORD, we may have to skip - * dropped columns and/or claim there are NULLs for added - * columns. - */ - if (rowexpr->row_typeid != RECORDOID) - { - tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1); - Assert(list_length(rowexpr->args) <= tupdesc->natts); - } - - /* - * SQL99 allows "ROW" to be omitted when there is more than - * one column, but for simplicity we always print it. - */ - appendStringInfoString(buf, "ROW("); - sep = ""; - i = 0; - foreach(arg, rowexpr->args) - { - Node *e = (Node *) lfirst(arg); - - if (tupdesc == NULL || - !TupleDescAttr(tupdesc, i)->attisdropped) - { - appendStringInfoString(buf, sep); - /* Whole-row Vars need special treatment here */ - get_rule_expr_toplevel(e, context, true); - sep = ", "; - } - i++; - } - if (tupdesc != NULL) - { - while (i < tupdesc->natts) - { - if (!TupleDescAttr(tupdesc, i)->attisdropped) - { - appendStringInfoString(buf, sep); - appendStringInfoString(buf, "NULL"); - sep = ", "; - } - i++; - } - - ReleaseTupleDesc(tupdesc); - } - appendStringInfoChar(buf, ')'); - if (rowexpr->row_format == COERCE_EXPLICIT_CAST) - appendStringInfo(buf, "::%s", - format_type_with_typemod(rowexpr->row_typeid, -1)); - } - break; - - case T_RowCompareExpr: - { - RowCompareExpr *rcexpr = (RowCompareExpr *) node; - ListCell *arg; - char *sep; - - /* - * SQL99 allows "ROW" to be omitted when there is more than - * one column, but for simplicity we always print it. - */ - appendStringInfoString(buf, "(ROW("); - sep = ""; - foreach(arg, rcexpr->largs) - { - Node *e = (Node *) lfirst(arg); - - appendStringInfoString(buf, sep); - get_rule_expr(e, context, true); - sep = ", "; - } - - /* - * We assume that the name of the first-column operator will - * do for all the rest too. This is definitely open to - * failure, eg if some but not all operators were renamed - * since the construct was parsed, but there seems no way to - * be perfect. - */ - appendStringInfo(buf, ") %s ROW(", - generate_operator_name(linitial_oid(rcexpr->opnos), - exprType(linitial(rcexpr->largs)), - exprType(linitial(rcexpr->rargs)))); - sep = ""; - foreach(arg, rcexpr->rargs) - { - Node *e = (Node *) lfirst(arg); - - appendStringInfoString(buf, sep); - get_rule_expr(e, context, true); - sep = ", "; - } - appendStringInfoString(buf, "))"); - } - break; - - case T_CoalesceExpr: - { - CoalesceExpr *coalesceexpr = (CoalesceExpr *) node; - - appendStringInfoString(buf, "COALESCE("); - get_rule_expr((Node *) coalesceexpr->args, context, true); - appendStringInfoChar(buf, ')'); - } - break; - - case T_MinMaxExpr: - { - MinMaxExpr *minmaxexpr = (MinMaxExpr *) node; - - switch (minmaxexpr->op) - { - case IS_GREATEST: - appendStringInfoString(buf, "GREATEST("); - break; - case IS_LEAST: - appendStringInfoString(buf, "LEAST("); - break; - } - get_rule_expr((Node *) minmaxexpr->args, context, true); - appendStringInfoChar(buf, ')'); - } - break; - - case T_SQLValueFunction: - { - SQLValueFunction *svf = (SQLValueFunction *) node; - - /* - * Note: this code knows that typmod for time, timestamp, and - * timestamptz just prints as integer. - */ - switch (svf->op) - { - case SVFOP_CURRENT_DATE: - appendStringInfoString(buf, "CURRENT_DATE"); - break; - case SVFOP_CURRENT_TIME: - appendStringInfoString(buf, "CURRENT_TIME"); - break; - case SVFOP_CURRENT_TIME_N: - appendStringInfo(buf, "CURRENT_TIME(%d)", svf->typmod); - break; - case SVFOP_CURRENT_TIMESTAMP: - appendStringInfoString(buf, "CURRENT_TIMESTAMP"); - break; - case SVFOP_CURRENT_TIMESTAMP_N: - appendStringInfo(buf, "CURRENT_TIMESTAMP(%d)", - svf->typmod); - break; - case SVFOP_LOCALTIME: - appendStringInfoString(buf, "LOCALTIME"); - break; - case SVFOP_LOCALTIME_N: - appendStringInfo(buf, "LOCALTIME(%d)", svf->typmod); - break; - case SVFOP_LOCALTIMESTAMP: - appendStringInfoString(buf, "LOCALTIMESTAMP"); - break; - case SVFOP_LOCALTIMESTAMP_N: - appendStringInfo(buf, "LOCALTIMESTAMP(%d)", - svf->typmod); - break; - case SVFOP_CURRENT_ROLE: - appendStringInfoString(buf, "CURRENT_ROLE"); - break; - case SVFOP_CURRENT_USER: - appendStringInfoString(buf, "CURRENT_USER"); - break; - case SVFOP_USER: - appendStringInfoString(buf, "USER"); - break; - case SVFOP_SESSION_USER: - appendStringInfoString(buf, "SESSION_USER"); - break; - case SVFOP_CURRENT_CATALOG: - appendStringInfoString(buf, "CURRENT_CATALOG"); - break; - case SVFOP_CURRENT_SCHEMA: - appendStringInfoString(buf, "CURRENT_SCHEMA"); - break; - } - } - break; - - case T_XmlExpr: - { - XmlExpr *xexpr = (XmlExpr *) node; - bool needcomma = false; - ListCell *arg; - ListCell *narg; - Const *con; - - switch (xexpr->op) - { - case IS_XMLCONCAT: - appendStringInfoString(buf, "XMLCONCAT("); - break; - case IS_XMLELEMENT: - appendStringInfoString(buf, "XMLELEMENT("); - break; - case IS_XMLFOREST: - appendStringInfoString(buf, "XMLFOREST("); - break; - case IS_XMLPARSE: - appendStringInfoString(buf, "XMLPARSE("); - break; - case IS_XMLPI: - appendStringInfoString(buf, "XMLPI("); - break; - case IS_XMLROOT: - appendStringInfoString(buf, "XMLROOT("); - break; - case IS_XMLSERIALIZE: - appendStringInfoString(buf, "XMLSERIALIZE("); - break; - case IS_DOCUMENT: - break; - } - if (xexpr->op == IS_XMLPARSE || xexpr->op == IS_XMLSERIALIZE) - { - if (xexpr->xmloption == XMLOPTION_DOCUMENT) - appendStringInfoString(buf, "DOCUMENT "); - else - appendStringInfoString(buf, "CONTENT "); - } - if (xexpr->name) - { - appendStringInfo(buf, "NAME %s", - quote_identifier(map_xml_name_to_sql_identifier(xexpr->name))); - needcomma = true; - } - if (xexpr->named_args) - { - if (xexpr->op != IS_XMLFOREST) - { - if (needcomma) - appendStringInfoString(buf, ", "); - appendStringInfoString(buf, "XMLATTRIBUTES("); - needcomma = false; - } - forboth(arg, xexpr->named_args, narg, xexpr->arg_names) - { - Node *e = (Node *) lfirst(arg); - char *argname = strVal(lfirst(narg)); - - if (needcomma) - appendStringInfoString(buf, ", "); - get_rule_expr((Node *) e, context, true); - appendStringInfo(buf, " AS %s", - quote_identifier(map_xml_name_to_sql_identifier(argname))); - needcomma = true; - } - if (xexpr->op != IS_XMLFOREST) - appendStringInfoChar(buf, ')'); - } - if (xexpr->args) - { - if (needcomma) - appendStringInfoString(buf, ", "); - switch (xexpr->op) - { - case IS_XMLCONCAT: - case IS_XMLELEMENT: - case IS_XMLFOREST: - case IS_XMLPI: - case IS_XMLSERIALIZE: - /* no extra decoration needed */ - get_rule_expr((Node *) xexpr->args, context, true); - break; - case IS_XMLPARSE: - Assert(list_length(xexpr->args) == 2); - - get_rule_expr((Node *) linitial(xexpr->args), - context, true); - - con = lsecond_node(Const, xexpr->args); - Assert(!con->constisnull); - if (DatumGetBool(con->constvalue)) - appendStringInfoString(buf, - " PRESERVE WHITESPACE"); - else - appendStringInfoString(buf, - " STRIP WHITESPACE"); - break; - case IS_XMLROOT: - Assert(list_length(xexpr->args) == 3); - - get_rule_expr((Node *) linitial(xexpr->args), - context, true); - - appendStringInfoString(buf, ", VERSION "); - con = (Const *) lsecond(xexpr->args); - if (IsA(con, Const) && - con->constisnull) - appendStringInfoString(buf, "NO VALUE"); - else - get_rule_expr((Node *) con, context, false); - - con = lthird_node(Const, xexpr->args); - if (con->constisnull) - /* suppress STANDALONE NO VALUE */ ; - else - { - switch (DatumGetInt32(con->constvalue)) - { - case XML_STANDALONE_YES: - appendStringInfoString(buf, - ", STANDALONE YES"); - break; - case XML_STANDALONE_NO: - appendStringInfoString(buf, - ", STANDALONE NO"); - break; - case XML_STANDALONE_NO_VALUE: - appendStringInfoString(buf, - ", STANDALONE NO VALUE"); - break; - default: - break; - } - } - break; - case IS_DOCUMENT: - get_rule_expr_paren((Node *) xexpr->args, context, false, node); - break; - } - - } - if (xexpr->op == IS_XMLSERIALIZE) - appendStringInfo(buf, " AS %s", - format_type_with_typemod(xexpr->type, - xexpr->typmod)); - if (xexpr->op == IS_DOCUMENT) - appendStringInfoString(buf, " IS DOCUMENT"); - else - appendStringInfoChar(buf, ')'); - } - break; - - case T_NullTest: - { - NullTest *ntest = (NullTest *) node; - - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, '('); - get_rule_expr_paren((Node *) ntest->arg, context, true, node); - - /* - * For scalar inputs, we prefer to print as IS [NOT] NULL, - * which is shorter and traditional. If it's a rowtype input - * but we're applying a scalar test, must print IS [NOT] - * DISTINCT FROM NULL to be semantically correct. - */ - if (ntest->argisrow || - !type_is_rowtype(exprType((Node *) ntest->arg))) - { - switch (ntest->nulltesttype) - { - case IS_NULL: - appendStringInfoString(buf, " IS NULL"); - break; - case IS_NOT_NULL: - appendStringInfoString(buf, " IS NOT NULL"); - break; - default: - elog(ERROR, "unrecognized nulltesttype: %d", - (int) ntest->nulltesttype); - } - } - else - { - switch (ntest->nulltesttype) - { - case IS_NULL: - appendStringInfoString(buf, " IS NOT DISTINCT FROM NULL"); - break; - case IS_NOT_NULL: - appendStringInfoString(buf, " IS DISTINCT FROM NULL"); - break; - default: - elog(ERROR, "unrecognized nulltesttype: %d", - (int) ntest->nulltesttype); - } - } - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, ')'); - } - break; - - case T_BooleanTest: - { - BooleanTest *btest = (BooleanTest *) node; - - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, '('); - get_rule_expr_paren((Node *) btest->arg, context, false, node); - switch (btest->booltesttype) - { - case IS_TRUE: - appendStringInfoString(buf, " IS TRUE"); - break; - case IS_NOT_TRUE: - appendStringInfoString(buf, " IS NOT TRUE"); - break; - case IS_FALSE: - appendStringInfoString(buf, " IS FALSE"); - break; - case IS_NOT_FALSE: - appendStringInfoString(buf, " IS NOT FALSE"); - break; - case IS_UNKNOWN: - appendStringInfoString(buf, " IS UNKNOWN"); - break; - case IS_NOT_UNKNOWN: - appendStringInfoString(buf, " IS NOT UNKNOWN"); - break; - default: - elog(ERROR, "unrecognized booltesttype: %d", - (int) btest->booltesttype); - } - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, ')'); - } - break; - - case T_CoerceToDomain: - { - CoerceToDomain *ctest = (CoerceToDomain *) node; - Node *arg = (Node *) ctest->arg; - - if (ctest->coercionformat == COERCE_IMPLICIT_CAST && - !showimplicit) - { - /* don't show the implicit cast */ - get_rule_expr(arg, context, false); - } - else - { - get_coercion_expr(arg, context, - ctest->resulttype, - ctest->resulttypmod, - node); - } - } - break; - - case T_CoerceToDomainValue: - appendStringInfoString(buf, "VALUE"); - break; - - case T_SetToDefault: - appendStringInfoString(buf, "DEFAULT"); - break; - - case T_CurrentOfExpr: - { - CurrentOfExpr *cexpr = (CurrentOfExpr *) node; - - if (cexpr->cursor_name) - appendStringInfo(buf, "CURRENT OF %s", - quote_identifier(cexpr->cursor_name)); - else - appendStringInfo(buf, "CURRENT OF $%d", - cexpr->cursor_param); - } - break; - - case T_NextValueExpr: - { - NextValueExpr *nvexpr = (NextValueExpr *) node; - - /* - * This isn't exactly nextval(), but that seems close enough - * for EXPLAIN's purposes. - */ - appendStringInfoString(buf, "nextval("); - simple_quote_literal(buf, - generate_relation_name(nvexpr->seqid, - NIL)); - appendStringInfoChar(buf, ')'); - } - break; - - case T_InferenceElem: - { - InferenceElem *iexpr = (InferenceElem *) node; - bool save_varprefix; - bool need_parens; - - /* - * InferenceElem can only refer to target relation, so a - * prefix is not useful, and indeed would cause parse errors. - */ - save_varprefix = context->varprefix; - context->varprefix = false; - - /* - * Parenthesize the element unless it's a simple Var or a bare - * function call. Follows pg_get_indexdef_worker(). - */ - need_parens = !IsA(iexpr->expr, Var); - if (IsA(iexpr->expr, FuncExpr) && - ((FuncExpr *) iexpr->expr)->funcformat == - COERCE_EXPLICIT_CALL) - need_parens = false; - - if (need_parens) - appendStringInfoChar(buf, '('); - get_rule_expr((Node *) iexpr->expr, - context, false); - if (need_parens) - appendStringInfoChar(buf, ')'); - - context->varprefix = save_varprefix; - - if (iexpr->infercollid) - appendStringInfo(buf, " COLLATE %s", - generate_collation_name(iexpr->infercollid)); - - /* Add the operator class name, if not default */ - if (iexpr->inferopclass) - { - Oid inferopclass = iexpr->inferopclass; - Oid inferopcinputtype = get_opclass_input_type(iexpr->inferopclass); - - get_opclass_name(inferopclass, inferopcinputtype, buf); - } - } - break; - - case T_PartitionBoundSpec: - { - PartitionBoundSpec *spec = (PartitionBoundSpec *) node; - ListCell *cell; - char *sep; - - if (spec->is_default) - { - appendStringInfoString(buf, "DEFAULT"); - break; - } - - switch (spec->strategy) - { - case PARTITION_STRATEGY_HASH: - Assert(spec->modulus > 0 && spec->remainder >= 0); - Assert(spec->modulus > spec->remainder); - - appendStringInfoString(buf, "FOR VALUES"); - appendStringInfo(buf, " WITH (modulus %d, remainder %d)", - spec->modulus, spec->remainder); - break; - - case PARTITION_STRATEGY_LIST: - Assert(spec->listdatums != NIL); - - appendStringInfoString(buf, "FOR VALUES IN ("); - sep = ""; - foreach(cell, spec->listdatums) - { - Const *val = castNode(Const, lfirst(cell)); - - appendStringInfoString(buf, sep); - get_const_expr(val, context, -1); - sep = ", "; - } - - appendStringInfoChar(buf, ')'); - break; - - case PARTITION_STRATEGY_RANGE: - Assert(spec->lowerdatums != NIL && - spec->upperdatums != NIL && - list_length(spec->lowerdatums) == - list_length(spec->upperdatums)); - - appendStringInfo(buf, "FOR VALUES FROM %s TO %s", - get_range_partbound_string(spec->lowerdatums), - get_range_partbound_string(spec->upperdatums)); - break; - - default: - elog(ERROR, "unrecognized partition strategy: %d", - (int) spec->strategy); - break; - } - } - break; - - case T_List: - { - char *sep; - ListCell *l; - - sep = ""; - foreach(l, (List *) node) - { - appendStringInfoString(buf, sep); - get_rule_expr((Node *) lfirst(l), context, showimplicit); - sep = ", "; - } - } - break; - - case T_TableFunc: - get_tablefunc((TableFunc *) node, context, showimplicit); - break; - - case T_CallStmt: - get_func_expr(((CallStmt *) node)->funcexpr, context, showimplicit); - break; - - default: - elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node)); - break; - } -} - -/* - * get_rule_expr_toplevel - Parse back a toplevel expression - * - * Same as get_rule_expr(), except that if the expr is just a Var, we pass - * istoplevel = true not false to get_variable(). This causes whole-row Vars - * to get printed with decoration that will prevent expansion of "*". - * We need to use this in contexts such as ROW() and VALUES(), where the - * parser would expand "foo.*" appearing at top level. (In principle we'd - * use this in get_target_list() too, but that has additional worries about - * whether to print AS, so it needs to invoke get_variable() directly anyway.) - */ -static void -get_rule_expr_toplevel(Node *node, deparse_context *context, - bool showimplicit) -{ - if (node && IsA(node, Var)) - (void) get_variable((Var *) node, 0, true, context); - else - get_rule_expr(node, context, showimplicit); -} - -/* - * get_rule_expr_funccall - Parse back a function-call expression - * - * Same as get_rule_expr(), except that we guarantee that the output will - * look like a function call, or like one of the things the grammar treats as - * equivalent to a function call (see the func_expr_windowless production). - * This is needed in places where the grammar uses func_expr_windowless and - * you can't substitute a parenthesized a_expr. If what we have isn't going - * to look like a function call, wrap it in a dummy CAST() expression, which - * will satisfy the grammar --- and, indeed, is likely what the user wrote to - * produce such a thing. - */ -static void -get_rule_expr_funccall(Node *node, deparse_context *context, - bool showimplicit) -{ - if (looks_like_function(node)) - get_rule_expr(node, context, showimplicit); - else - { - StringInfo buf = context->buf; - - appendStringInfoString(buf, "CAST("); - /* no point in showing any top-level implicit cast */ - get_rule_expr(node, context, false); - appendStringInfo(buf, " AS %s)", - format_type_with_typemod(exprType(node), - exprTypmod(node))); - } -} - -/* - * Helper function to identify node types that satisfy func_expr_windowless. - * If in doubt, "false" is always a safe answer. - */ -static bool -looks_like_function(Node *node) -{ - if (node == NULL) - return false; /* probably shouldn't happen */ - switch (nodeTag(node)) - { - case T_FuncExpr: - /* OK, unless it's going to deparse as a cast */ - return (((FuncExpr *) node)->funcformat == COERCE_EXPLICIT_CALL); - case T_NullIfExpr: - case T_CoalesceExpr: - case T_MinMaxExpr: - case T_SQLValueFunction: - case T_XmlExpr: - /* these are all accepted by func_expr_common_subexpr */ - return true; - default: - break; - } - return false; -} - - -/* - * get_oper_expr - Parse back an OpExpr node - */ -static void -get_oper_expr(OpExpr *expr, deparse_context *context) -{ - StringInfo buf = context->buf; - Oid opno = expr->opno; - List *args = expr->args; - - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, '('); - if (list_length(args) == 2) - { - /* binary operator */ - Node *arg1 = (Node *) linitial(args); - Node *arg2 = (Node *) lsecond(args); - - get_rule_expr_paren(arg1, context, true, (Node *) expr); - appendStringInfo(buf, " %s ", - generate_operator_name(opno, - exprType(arg1), - exprType(arg2))); - get_rule_expr_paren(arg2, context, true, (Node *) expr); - } - else - { - /* unary operator --- but which side? */ - Node *arg = (Node *) linitial(args); - HeapTuple tp; - Form_pg_operator optup; - - tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno)); - if (!HeapTupleIsValid(tp)) - elog(ERROR, "cache lookup failed for operator %u", opno); - optup = (Form_pg_operator) GETSTRUCT(tp); - switch (optup->oprkind) - { - case 'l': - appendStringInfo(buf, "%s ", - generate_operator_name(opno, - InvalidOid, - exprType(arg))); - get_rule_expr_paren(arg, context, true, (Node *) expr); - break; - case 'r': - get_rule_expr_paren(arg, context, true, (Node *) expr); - appendStringInfo(buf, " %s", - generate_operator_name(opno, - exprType(arg), - InvalidOid)); - break; - default: - elog(ERROR, "bogus oprkind: %d", optup->oprkind); - } - ReleaseSysCache(tp); - } - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, ')'); -} - -/* - * get_func_expr - Parse back a FuncExpr node - */ -static void -get_func_expr(FuncExpr *expr, deparse_context *context, - bool showimplicit) -{ - StringInfo buf = context->buf; - Oid funcoid = expr->funcid; - Oid argtypes[FUNC_MAX_ARGS]; - int nargs; - List *argnames; - bool use_variadic; - ListCell *l; - - /* - * If the function call came from an implicit coercion, then just show the - * first argument --- unless caller wants to see implicit coercions. - */ - if (expr->funcformat == COERCE_IMPLICIT_CAST && !showimplicit) - { - get_rule_expr_paren((Node *) linitial(expr->args), context, - false, (Node *) expr); - return; - } - - /* - * If the function call came from a cast, then show the first argument - * plus an explicit cast operation. - */ - if (expr->funcformat == COERCE_EXPLICIT_CAST || - expr->funcformat == COERCE_IMPLICIT_CAST) - { - Node *arg = linitial(expr->args); - Oid rettype = expr->funcresulttype; - int32 coercedTypmod; - - /* Get the typmod if this is a length-coercion function */ - (void) exprIsLengthCoercion((Node *) expr, &coercedTypmod); - - get_coercion_expr(arg, context, - rettype, coercedTypmod, - (Node *) expr); - - return; - } - - /* - * Normal function: display as proname(args). First we need to extract - * the argument datatypes. - */ - if (list_length(expr->args) > FUNC_MAX_ARGS) - ereport(ERROR, - (errcode(ERRCODE_TOO_MANY_ARGUMENTS), - errmsg("too many arguments"))); - nargs = 0; - argnames = NIL; - foreach(l, expr->args) - { - Node *arg = (Node *) lfirst(l); - - if (IsA(arg, NamedArgExpr)) - argnames = lappend(argnames, ((NamedArgExpr *) arg)->name); - argtypes[nargs] = exprType(arg); - nargs++; - } - - appendStringInfo(buf, "%s(", - generate_function_name(funcoid, nargs, - argnames, argtypes, - expr->funcvariadic, - &use_variadic, - context->special_exprkind)); - nargs = 0; - foreach(l, expr->args) - { - if (nargs++ > 0) - appendStringInfoString(buf, ", "); - if (use_variadic && lnext(expr->args, l) == NULL) - appendStringInfoString(buf, "VARIADIC "); - get_rule_expr((Node *) lfirst(l), context, true); - } - appendStringInfoChar(buf, ')'); -} - -/* - * get_agg_expr - Parse back an Aggref node - */ -static void -get_agg_expr(Aggref *aggref, deparse_context *context, - Aggref *original_aggref) -{ - StringInfo buf = context->buf; - Oid argtypes[FUNC_MAX_ARGS]; - int nargs; - bool use_variadic; - - /* - * For a combining aggregate, we look up and deparse the corresponding - * partial aggregate instead. This is necessary because our input - * argument list has been replaced; the new argument list always has just - * one element, which will point to a partial Aggref that supplies us with - * transition states to combine. - */ - if (DO_AGGSPLIT_COMBINE(aggref->aggsplit)) - { - TargetEntry *tle; - - - Assert(list_length(aggref->args) == 1); - tle = linitial_node(TargetEntry, aggref->args); - resolve_special_varno((Node *) tle->expr, context, - get_agg_combine_expr, original_aggref); - return; - } - - /* - * Mark as PARTIAL, if appropriate. We look to the original aggref so as - * to avoid printing this when recursing from the code just above. - */ - if (DO_AGGSPLIT_SKIPFINAL(original_aggref->aggsplit)) - appendStringInfoString(buf, "PARTIAL "); - - /* Extract the argument types as seen by the parser */ - nargs = get_aggregate_argtypes(aggref, argtypes); - - /* Print the aggregate name, schema-qualified if needed */ - appendStringInfo(buf, "%s(%s", - generate_function_name(aggref->aggfnoid, nargs, - NIL, argtypes, - aggref->aggvariadic, - &use_variadic, - context->special_exprkind), - (aggref->aggdistinct != NIL) ? "DISTINCT " : ""); - - if (AGGKIND_IS_ORDERED_SET(aggref->aggkind)) - { - /* - * Ordered-set aggregates do not use "*" syntax. Also, we needn't - * worry about inserting VARIADIC. So we can just dump the direct - * args as-is. - */ - Assert(!aggref->aggvariadic); - get_rule_expr((Node *) aggref->aggdirectargs, context, true); - Assert(aggref->aggorder != NIL); - appendStringInfoString(buf, ") WITHIN GROUP (ORDER BY "); - get_rule_orderby(aggref->aggorder, aggref->args, false, context); - } - else - { - /* aggstar can be set only in zero-argument aggregates */ - if (aggref->aggstar) - appendStringInfoChar(buf, '*'); - else - { - ListCell *l; - int i; - - i = 0; - foreach(l, aggref->args) - { - TargetEntry *tle = (TargetEntry *) lfirst(l); - Node *arg = (Node *) tle->expr; - - Assert(!IsA(arg, NamedArgExpr)); - if (tle->resjunk) - continue; - if (i++ > 0) - appendStringInfoString(buf, ", "); - if (use_variadic && i == nargs) - appendStringInfoString(buf, "VARIADIC "); - get_rule_expr(arg, context, true); - } - } - - if (aggref->aggorder != NIL) - { - appendStringInfoString(buf, " ORDER BY "); - get_rule_orderby(aggref->aggorder, aggref->args, false, context); - } - } - - if (aggref->aggfilter != NULL) - { - appendStringInfoString(buf, ") FILTER (WHERE "); - get_rule_expr((Node *) aggref->aggfilter, context, false); - } - - appendStringInfoChar(buf, ')'); -} - -/* - * This is a helper function for get_agg_expr(). It's used when we deparse - * a combining Aggref; resolve_special_varno locates the corresponding partial - * Aggref and then calls this. - */ -static void -get_agg_combine_expr(Node *node, deparse_context *context, void *callback_arg) -{ - Aggref *aggref; - Aggref *original_aggref = callback_arg; - - if (!IsA(node, Aggref)) - elog(ERROR, "combining Aggref does not point to an Aggref"); - - aggref = (Aggref *) node; - get_agg_expr(aggref, context, original_aggref); -} - -/* - * get_windowfunc_expr - Parse back a WindowFunc node - */ -static void -get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context) -{ - StringInfo buf = context->buf; - Oid argtypes[FUNC_MAX_ARGS]; - int nargs; - List *argnames; - ListCell *l; - - if (list_length(wfunc->args) > FUNC_MAX_ARGS) - ereport(ERROR, - (errcode(ERRCODE_TOO_MANY_ARGUMENTS), - errmsg("too many arguments"))); - nargs = 0; - argnames = NIL; - foreach(l, wfunc->args) - { - Node *arg = (Node *) lfirst(l); - - if (IsA(arg, NamedArgExpr)) - argnames = lappend(argnames, ((NamedArgExpr *) arg)->name); - argtypes[nargs] = exprType(arg); - nargs++; - } - - appendStringInfo(buf, "%s(", - generate_function_name(wfunc->winfnoid, nargs, - argnames, argtypes, - false, NULL, - context->special_exprkind)); - /* winstar can be set only in zero-argument aggregates */ - if (wfunc->winstar) - appendStringInfoChar(buf, '*'); - else - get_rule_expr((Node *) wfunc->args, context, true); - - if (wfunc->aggfilter != NULL) - { - appendStringInfoString(buf, ") FILTER (WHERE "); - get_rule_expr((Node *) wfunc->aggfilter, context, false); - } - - appendStringInfoString(buf, ") OVER "); - - foreach(l, context->windowClause) - { - WindowClause *wc = (WindowClause *) lfirst(l); - - if (wc->winref == wfunc->winref) - { - if (wc->name) - appendStringInfoString(buf, quote_identifier(wc->name)); - else - get_rule_windowspec(wc, context->windowTList, context); - break; - } - } - if (l == NULL) - { - if (context->windowClause) - elog(ERROR, "could not find window clause for winref %u", - wfunc->winref); - - /* - * In EXPLAIN, we don't have window context information available, so - * we have to settle for this: - */ - appendStringInfoString(buf, "(?)"); - } -} - -/* ---------- - * get_coercion_expr - * - * Make a string representation of a value coerced to a specific type - * ---------- - */ -static void -get_coercion_expr(Node *arg, deparse_context *context, - Oid resulttype, int32 resulttypmod, - Node *parentNode) -{ - StringInfo buf = context->buf; - - /* - * Since parse_coerce.c doesn't immediately collapse application of - * length-coercion functions to constants, what we'll typically see in - * such cases is a Const with typmod -1 and a length-coercion function - * right above it. Avoid generating redundant output. However, beware of - * suppressing casts when the user actually wrote something like - * 'foo'::text::char(3). - * - * Note: it might seem that we are missing the possibility of needing to - * print a COLLATE clause for such a Const. However, a Const could only - * have nondefault collation in a post-constant-folding tree, in which the - * length coercion would have been folded too. See also the special - * handling of CollateExpr in coerce_to_target_type(): any collation - * marking will be above the coercion node, not below it. - */ - if (arg && IsA(arg, Const) && - ((Const *) arg)->consttype == resulttype && - ((Const *) arg)->consttypmod == -1) - { - /* Show the constant without normal ::typename decoration */ - get_const_expr((Const *) arg, context, -1); - } - else - { - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, '('); - get_rule_expr_paren(arg, context, false, parentNode); - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, ')'); - } - appendStringInfo(buf, "::%s", - format_type_with_typemod(resulttype, resulttypmod)); -} - -/* ---------- - * get_const_expr - * - * Make a string representation of a Const - * - * showtype can be -1 to never show "::typename" decoration, or +1 to always - * show it, or 0 to show it only if the constant wouldn't be assumed to be - * the right type by default. - * - * If the Const's collation isn't default for its type, show that too. - * We mustn't do this when showtype is -1 (since that means the caller will - * print "::typename", and we can't put a COLLATE clause in between). It's - * caller's responsibility that collation isn't missed in such cases. - * ---------- - */ -static void -get_const_expr(Const *constval, deparse_context *context, int showtype) -{ - StringInfo buf = context->buf; - Oid typoutput; - bool typIsVarlena; - char *extval; - bool needlabel = false; - - if (constval->constisnull) - { - /* - * Always label the type of a NULL constant to prevent misdecisions - * about type when reparsing. - */ - appendStringInfoString(buf, "NULL"); - if (showtype >= 0) - { - appendStringInfo(buf, "::%s", - format_type_with_typemod(constval->consttype, - constval->consttypmod)); - get_const_collation(constval, context); - } - return; - } - - getTypeOutputInfo(constval->consttype, - &typoutput, &typIsVarlena); - - extval = OidOutputFunctionCall(typoutput, constval->constvalue); - - switch (constval->consttype) - { - case INT4OID: - - /* - * INT4 can be printed without any decoration, unless it is - * negative; in that case print it as '-nnn'::integer to ensure - * that the output will re-parse as a constant, not as a constant - * plus operator. In most cases we could get away with printing - * (-nnn) instead, because of the way that gram.y handles negative - * literals; but that doesn't work for INT_MIN, and it doesn't - * seem that much prettier anyway. - */ - if (extval[0] != '-') - appendStringInfoString(buf, extval); - else - { - appendStringInfo(buf, "'%s'", extval); - needlabel = true; /* we must attach a cast */ - } - break; - - case NUMERICOID: - - /* - * NUMERIC can be printed without quotes if it looks like a float - * constant (not an integer, and not Infinity or NaN) and doesn't - * have a leading sign (for the same reason as for INT4). - */ - if (isdigit((unsigned char) extval[0]) && - strcspn(extval, "eE.") != strlen(extval)) - { - appendStringInfoString(buf, extval); - } - else - { - appendStringInfo(buf, "'%s'", extval); - needlabel = true; /* we must attach a cast */ - } - break; - - case BITOID: - case VARBITOID: - appendStringInfo(buf, "B'%s'", extval); - break; - - case BOOLOID: - if (strcmp(extval, "t") == 0) - appendStringInfoString(buf, "true"); - else - appendStringInfoString(buf, "false"); - break; - - default: - simple_quote_literal(buf, extval); - break; - } - - pfree(extval); - - if (showtype < 0) - return; - - /* - * For showtype == 0, append ::typename unless the constant will be - * implicitly typed as the right type when it is read in. - * - * XXX this code has to be kept in sync with the behavior of the parser, - * especially make_const. - */ - switch (constval->consttype) - { - case BOOLOID: - case UNKNOWNOID: - /* These types can be left unlabeled */ - needlabel = false; - break; - case INT4OID: - /* We determined above whether a label is needed */ - break; - case NUMERICOID: - - /* - * Float-looking constants will be typed as numeric, which we - * checked above; but if there's a nondefault typmod we need to - * show it. - */ - needlabel |= (constval->consttypmod >= 0); - break; - default: - needlabel = true; - break; - } - if (needlabel || showtype > 0) - appendStringInfo(buf, "::%s", - format_type_with_typemod(constval->consttype, - constval->consttypmod)); - - get_const_collation(constval, context); -} - -/* - * helper for get_const_expr: append COLLATE if needed - */ -static void -get_const_collation(Const *constval, deparse_context *context) -{ - StringInfo buf = context->buf; - - if (OidIsValid(constval->constcollid)) - { - Oid typcollation = get_typcollation(constval->consttype); - - if (constval->constcollid != typcollation) - { - appendStringInfo(buf, " COLLATE %s", - generate_collation_name(constval->constcollid)); - } - } -} - -/* - * simple_quote_literal - Format a string as a SQL literal, append to buf - */ -static void -simple_quote_literal(StringInfo buf, const char *val) -{ - const char *valptr; - - /* - * We form the string literal according to the prevailing setting of - * standard_conforming_strings; we never use E''. User is responsible for - * making sure result is used correctly. - */ - appendStringInfoChar(buf, '\''); - for (valptr = val; *valptr; valptr++) - { - char ch = *valptr; - - if (SQL_STR_DOUBLE(ch, !standard_conforming_strings)) - appendStringInfoChar(buf, ch); - appendStringInfoChar(buf, ch); - } - appendStringInfoChar(buf, '\''); -} - - -/* ---------- - * get_sublink_expr - Parse back a sublink - * ---------- - */ -static void -get_sublink_expr(SubLink *sublink, deparse_context *context) -{ - StringInfo buf = context->buf; - Query *query = (Query *) (sublink->subselect); - char *opname = NULL; - bool need_paren; - - if (sublink->subLinkType == ARRAY_SUBLINK) - appendStringInfoString(buf, "ARRAY("); - else - appendStringInfoChar(buf, '('); - - /* - * Note that we print the name of only the first operator, when there are - * multiple combining operators. This is an approximation that could go - * wrong in various scenarios (operators in different schemas, renamed - * operators, etc) but there is not a whole lot we can do about it, since - * the syntax allows only one operator to be shown. - */ - if (sublink->testexpr) - { - if (IsA(sublink->testexpr, OpExpr)) - { - /* single combining operator */ - OpExpr *opexpr = (OpExpr *) sublink->testexpr; - - get_rule_expr(linitial(opexpr->args), context, true); - opname = generate_operator_name(opexpr->opno, - exprType(linitial(opexpr->args)), - exprType(lsecond(opexpr->args))); - } - else if (IsA(sublink->testexpr, BoolExpr)) - { - /* multiple combining operators, = or <> cases */ - char *sep; - ListCell *l; - - appendStringInfoChar(buf, '('); - sep = ""; - foreach(l, ((BoolExpr *) sublink->testexpr)->args) - { - OpExpr *opexpr = lfirst_node(OpExpr, l); - - appendStringInfoString(buf, sep); - get_rule_expr(linitial(opexpr->args), context, true); - if (!opname) - opname = generate_operator_name(opexpr->opno, - exprType(linitial(opexpr->args)), - exprType(lsecond(opexpr->args))); - sep = ", "; - } - appendStringInfoChar(buf, ')'); - } - else if (IsA(sublink->testexpr, RowCompareExpr)) - { - /* multiple combining operators, < <= > >= cases */ - RowCompareExpr *rcexpr = (RowCompareExpr *) sublink->testexpr; - - appendStringInfoChar(buf, '('); - get_rule_expr((Node *) rcexpr->largs, context, true); - opname = generate_operator_name(linitial_oid(rcexpr->opnos), - exprType(linitial(rcexpr->largs)), - exprType(linitial(rcexpr->rargs))); - appendStringInfoChar(buf, ')'); - } - else - elog(ERROR, "unrecognized testexpr type: %d", - (int) nodeTag(sublink->testexpr)); - } - - need_paren = true; - - switch (sublink->subLinkType) - { - case EXISTS_SUBLINK: - appendStringInfoString(buf, "EXISTS "); - break; - - case ANY_SUBLINK: - if (strcmp(opname, "=") == 0) /* Represent = ANY as IN */ - appendStringInfoString(buf, " IN "); - else - appendStringInfo(buf, " %s ANY ", opname); - break; - - case ALL_SUBLINK: - appendStringInfo(buf, " %s ALL ", opname); - break; - - case ROWCOMPARE_SUBLINK: - appendStringInfo(buf, " %s ", opname); - break; - - case EXPR_SUBLINK: - case MULTIEXPR_SUBLINK: - case ARRAY_SUBLINK: - need_paren = false; - break; - - case CTE_SUBLINK: /* shouldn't occur in a SubLink */ - default: - elog(ERROR, "unrecognized sublink type: %d", - (int) sublink->subLinkType); - break; - } - - if (need_paren) - appendStringInfoChar(buf, '('); - - get_query_def(query, buf, context->namespaces, NULL, - context->prettyFlags, context->wrapColumn, - context->indentLevel); - - if (need_paren) - appendStringInfoString(buf, "))"); - else - appendStringInfoChar(buf, ')'); -} - - -/* ---------- - * get_tablefunc - Parse back a table function - * ---------- - */ -static void -get_tablefunc(TableFunc *tf, deparse_context *context, bool showimplicit) -{ - StringInfo buf = context->buf; - - /* XMLTABLE is the only existing implementation. */ - - appendStringInfoString(buf, "XMLTABLE("); - - if (tf->ns_uris != NIL) - { - ListCell *lc1, - *lc2; - bool first = true; - - appendStringInfoString(buf, "XMLNAMESPACES ("); - forboth(lc1, tf->ns_uris, lc2, tf->ns_names) - { - Node *expr = (Node *) lfirst(lc1); - char *name = strVal(lfirst(lc2)); - - if (!first) - appendStringInfoString(buf, ", "); - else - first = false; - - if (name != NULL) - { - get_rule_expr(expr, context, showimplicit); - appendStringInfo(buf, " AS %s", name); - } - else - { - appendStringInfoString(buf, "DEFAULT "); - get_rule_expr(expr, context, showimplicit); - } - } - appendStringInfoString(buf, "), "); - } - - appendStringInfoChar(buf, '('); - get_rule_expr((Node *) tf->rowexpr, context, showimplicit); - appendStringInfoString(buf, ") PASSING ("); - get_rule_expr((Node *) tf->docexpr, context, showimplicit); - appendStringInfoChar(buf, ')'); - - if (tf->colexprs != NIL) - { - ListCell *l1; - ListCell *l2; - ListCell *l3; - ListCell *l4; - ListCell *l5; - int colnum = 0; - - appendStringInfoString(buf, " COLUMNS "); - forfive(l1, tf->colnames, l2, tf->coltypes, l3, tf->coltypmods, - l4, tf->colexprs, l5, tf->coldefexprs) - { - char *colname = strVal(lfirst(l1)); - Oid typid = lfirst_oid(l2); - int32 typmod = lfirst_int(l3); - Node *colexpr = (Node *) lfirst(l4); - Node *coldefexpr = (Node *) lfirst(l5); - bool ordinality = (tf->ordinalitycol == colnum); - bool notnull = bms_is_member(colnum, tf->notnulls); - - if (colnum > 0) - appendStringInfoString(buf, ", "); - colnum++; - - appendStringInfo(buf, "%s %s", quote_identifier(colname), - ordinality ? "FOR ORDINALITY" : - format_type_with_typemod(typid, typmod)); - if (ordinality) - continue; - - if (coldefexpr != NULL) - { - appendStringInfoString(buf, " DEFAULT ("); - get_rule_expr((Node *) coldefexpr, context, showimplicit); - appendStringInfoChar(buf, ')'); - } - if (colexpr != NULL) - { - appendStringInfoString(buf, " PATH ("); - get_rule_expr((Node *) colexpr, context, showimplicit); - appendStringInfoChar(buf, ')'); - } - if (notnull) - appendStringInfoString(buf, " NOT NULL"); - } - } - - appendStringInfoChar(buf, ')'); -} - -/* ---------- - * get_from_clause - Parse back a FROM clause - * - * "prefix" is the keyword that denotes the start of the list of FROM - * elements. It is FROM when used to parse back SELECT and UPDATE, but - * is USING when parsing back DELETE. - * ---------- - */ -static void -get_from_clause(Query *query, const char *prefix, deparse_context *context) -{ - StringInfo buf = context->buf; - bool first = true; - ListCell *l; - - /* - * We use the query's jointree as a guide to what to print. However, we - * must ignore auto-added RTEs that are marked not inFromCl. (These can - * only appear at the top level of the jointree, so it's sufficient to - * check here.) This check also ensures we ignore the rule pseudo-RTEs - * for NEW and OLD. - */ - foreach(l, query->jointree->fromlist) - { - Node *jtnode = (Node *) lfirst(l); - - if (IsA(jtnode, RangeTblRef)) - { - int varno = ((RangeTblRef *) jtnode)->rtindex; - RangeTblEntry *rte = rt_fetch(varno, query->rtable); - - if (!rte->inFromCl) - continue; - } - - if (first) - { - appendContextKeyword(context, prefix, - -PRETTYINDENT_STD, PRETTYINDENT_STD, 2); - first = false; - - get_from_clause_item(jtnode, query, context); - } - else - { - StringInfoData itembuf; - - appendStringInfoString(buf, ", "); - - /* - * Put the new FROM item's text into itembuf so we can decide - * after we've got it whether or not it needs to go on a new line. - */ - initStringInfo(&itembuf); - context->buf = &itembuf; - - get_from_clause_item(jtnode, query, context); - - /* Restore context's output buffer */ - context->buf = buf; - - /* Consider line-wrapping if enabled */ - if (PRETTY_INDENT(context) && context->wrapColumn >= 0) - { - /* Does the new item start with a new line? */ - if (itembuf.len > 0 && itembuf.data[0] == '\n') - { - /* If so, we shouldn't add anything */ - /* instead, remove any trailing spaces currently in buf */ - removeStringInfoSpaces(buf); - } - else - { - char *trailing_nl; - - /* Locate the start of the current line in the buffer */ - trailing_nl = strrchr(buf->data, '\n'); - if (trailing_nl == NULL) - trailing_nl = buf->data; - else - trailing_nl++; - - /* - * Add a newline, plus some indentation, if the new item - * would cause an overflow. - */ - if (strlen(trailing_nl) + itembuf.len > context->wrapColumn) - appendContextKeyword(context, "", -PRETTYINDENT_STD, - PRETTYINDENT_STD, - PRETTYINDENT_VAR); - } - } - - /* Add the new item */ - appendStringInfoString(buf, itembuf.data); - - /* clean up */ - pfree(itembuf.data); - } - } -} - -static void -get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) -{ - StringInfo buf = context->buf; - deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces); - - if (IsA(jtnode, RangeTblRef)) - { - int varno = ((RangeTblRef *) jtnode)->rtindex; - RangeTblEntry *rte = rt_fetch(varno, query->rtable); - char *refname = get_rtable_name(varno, context); - deparse_columns *colinfo = deparse_columns_fetch(varno, dpns); - RangeTblFunction *rtfunc1 = NULL; - bool printalias; - CitusRTEKind rteKind = GetRangeTblKind(rte); - - if (rte->lateral) - appendStringInfoString(buf, "LATERAL "); - - /* Print the FROM item proper */ - switch (rte->rtekind) - { - case RTE_RELATION: - /* Normal relation RTE */ - appendStringInfo(buf, "%s%s", - only_marker(rte), - generate_relation_or_shard_name(rte->relid, - context->distrelid, - context->shardid, - context->namespaces)); - break; - case RTE_SUBQUERY: - /* Subquery RTE */ - appendStringInfoChar(buf, '('); - get_query_def(rte->subquery, buf, context->namespaces, NULL, - context->prettyFlags, context->wrapColumn, - context->indentLevel); - appendStringInfoChar(buf, ')'); - break; - case RTE_FUNCTION: - /* if it's a shard, do differently */ - if (GetRangeTblKind(rte) == CITUS_RTE_SHARD) - { - char *fragmentSchemaName = NULL; - char *fragmentTableName = NULL; - - ExtractRangeTblExtraData(rte, NULL, &fragmentSchemaName, &fragmentTableName, NULL); - - /* use schema and table name from the remote alias */ - appendStringInfo(buf, "%s%s", - only_marker(rte), - generate_fragment_name(fragmentSchemaName, - fragmentTableName)); - break; - } - - /* Function RTE */ - rtfunc1 = (RangeTblFunction *) linitial(rte->functions); - - /* - * Omit ROWS FROM() syntax for just one function, unless it - * has both a coldeflist and WITH ORDINALITY. If it has both, - * we must use ROWS FROM() syntax to avoid ambiguity about - * whether the coldeflist includes the ordinality column. - */ - if (list_length(rte->functions) == 1 && - (rtfunc1->funccolnames == NIL || !rte->funcordinality)) - { - get_rule_expr_funccall(rtfunc1->funcexpr, context, true); - /* we'll print the coldeflist below, if it has one */ - } - else - { - bool all_unnest; - ListCell *lc; - - /* - * If all the function calls in the list are to unnest, - * and none need a coldeflist, then collapse the list back - * down to UNNEST(args). (If we had more than one - * built-in unnest function, this would get more - * difficult.) - * - * XXX This is pretty ugly, since it makes not-terribly- - * future-proof assumptions about what the parser would do - * with the output; but the alternative is to emit our - * nonstandard ROWS FROM() notation for what might have - * been a perfectly spec-compliant multi-argument - * UNNEST(). - */ - all_unnest = true; - foreach(lc, rte->functions) - { - RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc); - - if (!IsA(rtfunc->funcexpr, FuncExpr) || - ((FuncExpr *) rtfunc->funcexpr)->funcid != F_ARRAY_UNNEST || - rtfunc->funccolnames != NIL) - { - all_unnest = false; - break; - } - } - - if (all_unnest) - { - List *allargs = NIL; - - foreach(lc, rte->functions) - { - RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc); - List *args = ((FuncExpr *) rtfunc->funcexpr)->args; - - allargs = list_concat(allargs, args); - } - - appendStringInfoString(buf, "UNNEST("); - get_rule_expr((Node *) allargs, context, true); - appendStringInfoChar(buf, ')'); - } - else - { - int funcno = 0; - - appendStringInfoString(buf, "ROWS FROM("); - foreach(lc, rte->functions) - { - RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc); - - if (funcno > 0) - appendStringInfoString(buf, ", "); - get_rule_expr_funccall(rtfunc->funcexpr, context, true); - if (rtfunc->funccolnames != NIL) - { - /* Reconstruct the column definition list */ - appendStringInfoString(buf, " AS "); - get_from_clause_coldeflist(rtfunc, - NULL, - context); - } - funcno++; - } - appendStringInfoChar(buf, ')'); - } - /* prevent printing duplicate coldeflist below */ - rtfunc1 = NULL; - } - if (rte->funcordinality) - appendStringInfoString(buf, " WITH ORDINALITY"); - break; - case RTE_TABLEFUNC: - get_tablefunc(rte->tablefunc, context, true); - break; - case RTE_VALUES: - /* Values list RTE */ - appendStringInfoChar(buf, '('); - get_values_def(rte->values_lists, context); - appendStringInfoChar(buf, ')'); - break; - case RTE_CTE: - appendStringInfoString(buf, quote_identifier(rte->ctename)); - break; - default: - elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind); - break; - } - - /* Print the relation alias, if needed */ - printalias = false; - if (rte->alias != NULL) - { - /* Always print alias if user provided one */ - printalias = true; - } - else if (colinfo->printaliases) - { - /* Always print alias if we need to print column aliases */ - printalias = true; - } - else if (rte->rtekind == RTE_RELATION) - { - /* - * No need to print alias if it's same as relation name (this - * would normally be the case, but not if set_rtable_names had to - * resolve a conflict). - */ - if (strcmp(refname, get_relation_name(rte->relid)) != 0) - printalias = true; - } - else if (rte->rtekind == RTE_FUNCTION) - { - /* - * For a function RTE, always print alias. This covers possible - * renaming of the function and/or instability of the - * FigureColname rules for things that aren't simple functions. - * Note we'd need to force it anyway for the columndef list case. - */ - printalias = true; - } - else if (rte->rtekind == RTE_VALUES) - { - /* Alias is syntactically required for VALUES */ - printalias = true; - } - else if (rte->rtekind == RTE_CTE) - { - /* - * No need to print alias if it's same as CTE name (this would - * normally be the case, but not if set_rtable_names had to - * resolve a conflict). - */ - if (strcmp(refname, rte->ctename) != 0) - printalias = true; - } - else if (rte->rtekind == RTE_SUBQUERY) - { - /* subquery requires alias too */ - printalias = true; - } - if (printalias) - appendStringInfo(buf, " %s", quote_identifier(refname)); - - /* Print the column definitions or aliases, if needed */ - if (rtfunc1 && rtfunc1->funccolnames != NIL) - { - /* Reconstruct the columndef list, which is also the aliases */ - get_from_clause_coldeflist(rtfunc1, colinfo, context); - } - else if (GetRangeTblKind(rte) != CITUS_RTE_SHARD || - (rte->alias != NULL && rte->alias->colnames != NIL)) - { - /* Else print column aliases as needed */ - get_column_alias_list(colinfo, context); - } - /* check if column's are given aliases in distributed tables */ - else if (colinfo->parentUsing != NIL) - { - Assert(colinfo->printaliases); - get_column_alias_list(colinfo, context); - } - - /* Tablesample clause must go after any alias */ - if ((rteKind == CITUS_RTE_RELATION || rteKind == CITUS_RTE_SHARD) && - rte->tablesample) - { - get_tablesample_def(rte->tablesample, context); - } - } - else if (IsA(jtnode, JoinExpr)) - { - JoinExpr *j = (JoinExpr *) jtnode; - deparse_columns *colinfo = deparse_columns_fetch(j->rtindex, dpns); - bool need_paren_on_right; - - need_paren_on_right = PRETTY_PAREN(context) && - !IsA(j->rarg, RangeTblRef) && - !(IsA(j->rarg, JoinExpr) &&((JoinExpr *) j->rarg)->alias != NULL); - - if (!PRETTY_PAREN(context) || j->alias != NULL) - appendStringInfoChar(buf, '('); - - get_from_clause_item(j->larg, query, context); - - switch (j->jointype) - { - case JOIN_INNER: - if (j->quals) - appendContextKeyword(context, " JOIN ", - -PRETTYINDENT_STD, - PRETTYINDENT_STD, - PRETTYINDENT_JOIN); - else - appendContextKeyword(context, " CROSS JOIN ", - -PRETTYINDENT_STD, - PRETTYINDENT_STD, - PRETTYINDENT_JOIN); - break; - case JOIN_LEFT: - appendContextKeyword(context, " LEFT JOIN ", - -PRETTYINDENT_STD, - PRETTYINDENT_STD, - PRETTYINDENT_JOIN); - break; - case JOIN_FULL: - appendContextKeyword(context, " FULL JOIN ", - -PRETTYINDENT_STD, - PRETTYINDENT_STD, - PRETTYINDENT_JOIN); - break; - case JOIN_RIGHT: - appendContextKeyword(context, " RIGHT JOIN ", - -PRETTYINDENT_STD, - PRETTYINDENT_STD, - PRETTYINDENT_JOIN); - break; - default: - elog(ERROR, "unrecognized join type: %d", - (int) j->jointype); - } - - if (need_paren_on_right) - appendStringInfoChar(buf, '('); - get_from_clause_item(j->rarg, query, context); - if (need_paren_on_right) - appendStringInfoChar(buf, ')'); - - if (j->usingClause) - { - ListCell *lc; - bool first = true; - - appendStringInfoString(buf, " USING ("); - /* Use the assigned names, not what's in usingClause */ - foreach(lc, colinfo->usingNames) - { - char *colname = (char *) lfirst(lc); - - if (first) - first = false; - else - appendStringInfoString(buf, ", "); - appendStringInfoString(buf, quote_identifier(colname)); - } - appendStringInfoChar(buf, ')'); - } - else if (j->quals) - { - appendStringInfoString(buf, " ON "); - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, '('); - get_rule_expr(j->quals, context, false); - if (!PRETTY_PAREN(context)) - appendStringInfoChar(buf, ')'); - } - else if (j->jointype != JOIN_INNER) - { - /* If we didn't say CROSS JOIN above, we must provide an ON */ - appendStringInfoString(buf, " ON TRUE"); - } - - if (!PRETTY_PAREN(context) || j->alias != NULL) - appendStringInfoChar(buf, ')'); - - /* Yes, it's correct to put alias after the right paren ... */ - if (j->alias != NULL) - { - /* - * Note that it's correct to emit an alias clause if and only if - * there was one originally. Otherwise we'd be converting a named - * join to unnamed or vice versa, which creates semantic - * subtleties we don't want. However, we might print a different - * alias name than was there originally. - */ - appendStringInfo(buf, " %s", - quote_identifier(get_rtable_name(j->rtindex, - context))); - get_column_alias_list(colinfo, context); - } - } - else - elog(ERROR, "unrecognized node type: %d", - (int) nodeTag(jtnode)); -} - -/* - * get_column_alias_list - print column alias list for an RTE - * - * Caller must already have printed the relation's alias name. - */ -static void -get_column_alias_list(deparse_columns *colinfo, deparse_context *context) -{ - StringInfo buf = context->buf; - int i; - bool first = true; - - /* Don't print aliases if not needed */ - if (!colinfo->printaliases) - return; - - for (i = 0; i < colinfo->num_new_cols; i++) - { - char *colname = colinfo->new_colnames[i]; - - if (first) - { - appendStringInfoChar(buf, '('); - first = false; - } - else - appendStringInfoString(buf, ", "); - appendStringInfoString(buf, quote_identifier(colname)); - } - if (!first) - appendStringInfoChar(buf, ')'); -} - -/* - * get_from_clause_coldeflist - reproduce FROM clause coldeflist - * - * When printing a top-level coldeflist (which is syntactically also the - * relation's column alias list), use column names from colinfo. But when - * printing a coldeflist embedded inside ROWS FROM(), we prefer to use the - * original coldeflist's names, which are available in rtfunc->funccolnames. - * Pass NULL for colinfo to select the latter behavior. - * - * The coldeflist is appended immediately (no space) to buf. Caller is - * responsible for ensuring that an alias or AS is present before it. - */ -static void -get_from_clause_coldeflist(RangeTblFunction *rtfunc, - deparse_columns *colinfo, - deparse_context *context) -{ - StringInfo buf = context->buf; - ListCell *l1; - ListCell *l2; - ListCell *l3; - ListCell *l4; - int i; - - appendStringInfoChar(buf, '('); - - i = 0; - forfour(l1, rtfunc->funccoltypes, - l2, rtfunc->funccoltypmods, - l3, rtfunc->funccolcollations, - l4, rtfunc->funccolnames) - { - Oid atttypid = lfirst_oid(l1); - int32 atttypmod = lfirst_int(l2); - Oid attcollation = lfirst_oid(l3); - char *attname; - - if (colinfo) - attname = colinfo->colnames[i]; - else - attname = strVal(lfirst(l4)); - - Assert(attname); /* shouldn't be any dropped columns here */ - - if (i > 0) - appendStringInfoString(buf, ", "); - appendStringInfo(buf, "%s %s", - quote_identifier(attname), - format_type_with_typemod(atttypid, atttypmod)); - if (OidIsValid(attcollation) && - attcollation != get_typcollation(atttypid)) - appendStringInfo(buf, " COLLATE %s", - generate_collation_name(attcollation)); - - i++; - } - - appendStringInfoChar(buf, ')'); -} - -/* - * get_tablesample_def - print a TableSampleClause - */ -static void -get_tablesample_def(TableSampleClause *tablesample, deparse_context *context) -{ - StringInfo buf = context->buf; - Oid argtypes[1]; - int nargs; - ListCell *l; - - /* - * We should qualify the handler's function name if it wouldn't be - * resolved by lookup in the current search path. - */ - argtypes[0] = INTERNALOID; - appendStringInfo(buf, " TABLESAMPLE %s (", - generate_function_name(tablesample->tsmhandler, 1, - NIL, argtypes, - false, NULL, EXPR_KIND_NONE)); - - nargs = 0; - foreach(l, tablesample->args) - { - if (nargs++ > 0) - appendStringInfoString(buf, ", "); - get_rule_expr((Node *) lfirst(l), context, false); - } - appendStringInfoChar(buf, ')'); - - if (tablesample->repeatable != NULL) - { - appendStringInfoString(buf, " REPEATABLE ("); - get_rule_expr((Node *) tablesample->repeatable, context, false); - appendStringInfoChar(buf, ')'); - } -} - - -/* - * get_opclass_name - fetch name of an index operator class - * - * The opclass name is appended (after a space) to buf. - * - * Output is suppressed if the opclass is the default for the given - * actual_datatype. (If you don't want this behavior, just pass - * InvalidOid for actual_datatype.) - */ -static void -get_opclass_name(Oid opclass, Oid actual_datatype, - StringInfo buf) -{ - HeapTuple ht_opc; - Form_pg_opclass opcrec; - char *opcname; - char *nspname; - - ht_opc = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass)); - if (!HeapTupleIsValid(ht_opc)) - elog(ERROR, "cache lookup failed for opclass %u", opclass); - opcrec = (Form_pg_opclass) GETSTRUCT(ht_opc); - - if (!OidIsValid(actual_datatype) || - GetDefaultOpClass(actual_datatype, opcrec->opcmethod) != opclass) - { - /* Okay, we need the opclass name. Do we need to qualify it? */ - opcname = NameStr(opcrec->opcname); - if (OpclassIsVisible(opclass)) - appendStringInfo(buf, " %s", quote_identifier(opcname)); - else - { - nspname = get_namespace_name(opcrec->opcnamespace); - appendStringInfo(buf, " %s.%s", - quote_identifier(nspname), - quote_identifier(opcname)); - } - } - ReleaseSysCache(ht_opc); -} - -/* - * processIndirection - take care of array and subfield assignment - * - * We strip any top-level FieldStore or assignment SubscriptingRef nodes that - * appear in the input, printing them as decoration for the base column - * name (which we assume the caller just printed). We might also need to - * strip CoerceToDomain nodes, but only ones that appear above assignment - * nodes. - * - * Returns the subexpression that's to be assigned. - */ -static Node * -processIndirection(Node *node, deparse_context *context) -{ - StringInfo buf = context->buf; - CoerceToDomain *cdomain = NULL; - - for (;;) - { - if (node == NULL) - break; - if (IsA(node, FieldStore)) - { - FieldStore *fstore = (FieldStore *) node; - Oid typrelid; - char *fieldname; - - /* lookup tuple type */ - typrelid = get_typ_typrelid(fstore->resulttype); - if (!OidIsValid(typrelid)) - elog(ERROR, "argument type %s of FieldStore is not a tuple type", - format_type_be(fstore->resulttype)); - - /* - * Print the field name. There should only be one target field in - * stored rules. There could be more than that in executable - * target lists, but this function cannot be used for that case. - */ - Assert(list_length(fstore->fieldnums) == 1); - fieldname = get_attname(typrelid, - linitial_int(fstore->fieldnums), false); - appendStringInfo(buf, ".%s", quote_identifier(fieldname)); - - /* - * We ignore arg since it should be an uninteresting reference to - * the target column or subcolumn. - */ - node = (Node *) linitial(fstore->newvals); - } - else if (IsA(node, SubscriptingRef)) - { - SubscriptingRef *sbsref = (SubscriptingRef *) node; - - if (sbsref->refassgnexpr == NULL) - break; - printSubscripts(sbsref, context); - - /* - * We ignore refexpr since it should be an uninteresting reference - * to the target column or subcolumn. - */ - node = (Node *) sbsref->refassgnexpr; - } - else if (IsA(node, CoerceToDomain)) - { - cdomain = (CoerceToDomain *) node; - /* If it's an explicit domain coercion, we're done */ - if (cdomain->coercionformat != COERCE_IMPLICIT_CAST) - break; - /* Tentatively descend past the CoerceToDomain */ - node = (Node *) cdomain->arg; - } - else - break; - } - - /* - * If we descended past a CoerceToDomain whose argument turned out not to - * be a FieldStore or array assignment, back up to the CoerceToDomain. - * (This is not enough to be fully correct if there are nested implicit - * CoerceToDomains, but such cases shouldn't ever occur.) - */ - if (cdomain && node == (Node *) cdomain->arg) - node = (Node *) cdomain; - - return node; -} - -static void -printSubscripts(SubscriptingRef *sbsref, deparse_context *context) -{ - StringInfo buf = context->buf; - ListCell *lowlist_item; - ListCell *uplist_item; - - lowlist_item = list_head(sbsref->reflowerindexpr); /* could be NULL */ - foreach(uplist_item, sbsref->refupperindexpr) - { - appendStringInfoChar(buf, '['); - if (lowlist_item) - { - /* If subexpression is NULL, get_rule_expr prints nothing */ - get_rule_expr((Node *) lfirst(lowlist_item), context, false); - appendStringInfoChar(buf, ':'); - lowlist_item = lnext(sbsref->reflowerindexpr, lowlist_item); - } - /* If subexpression is NULL, get_rule_expr prints nothing */ - get_rule_expr((Node *) lfirst(uplist_item), context, false); - appendStringInfoChar(buf, ']'); - } -} - -/* - * get_relation_name - * Get the unqualified name of a relation specified by OID - * - * This differs from the underlying get_rel_name() function in that it will - * throw error instead of silently returning NULL if the OID is bad. - */ -static char * -get_relation_name(Oid relid) -{ - char *relname = get_rel_name(relid); - - if (!relname) - elog(ERROR, "cache lookup failed for relation %u", relid); - return relname; -} - -/* - * generate_relation_or_shard_name - * Compute the name to display for a relation or shard - * - * If the provided relid is equal to the provided distrelid, this function - * returns a shard-extended relation name; otherwise, it falls through to a - * simple generate_relation_name call. - */ -static char * -generate_relation_or_shard_name(Oid relid, Oid distrelid, int64 shardid, - List *namespaces) -{ - char *relname = NULL; - - if (relid == distrelid) - { - relname = get_relation_name(relid); - - if (shardid > 0) - { - Oid schemaOid = get_rel_namespace(relid); - char *schemaName = get_namespace_name(schemaOid); - - AppendShardIdToName(&relname, shardid); - - relname = quote_qualified_identifier(schemaName, relname); - } - } - else - { - relname = generate_relation_name(relid, namespaces); - } - - return relname; -} - -/* - * generate_relation_name - * Compute the name to display for a relation specified by OID - * - * The result includes all necessary quoting and schema-prefixing. - * - * If namespaces isn't NIL, it must be a list of deparse_namespace nodes. - * We will forcibly qualify the relation name if it equals any CTE name - * visible in the namespace list. - */ -char * -generate_relation_name(Oid relid, List *namespaces) -{ - HeapTuple tp; - Form_pg_class reltup; - bool need_qual; - ListCell *nslist; - char *relname; - char *nspname; - char *result; - - tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); - if (!HeapTupleIsValid(tp)) - elog(ERROR, "cache lookup failed for relation %u", relid); - reltup = (Form_pg_class) GETSTRUCT(tp); - relname = NameStr(reltup->relname); - - /* Check for conflicting CTE name */ - need_qual = false; - foreach(nslist, namespaces) - { - deparse_namespace *dpns = (deparse_namespace *) lfirst(nslist); - ListCell *ctlist; - - foreach(ctlist, dpns->ctes) - { - CommonTableExpr *cte = (CommonTableExpr *) lfirst(ctlist); - - if (strcmp(cte->ctename, relname) == 0) - { - need_qual = true; - break; - } - } - if (need_qual) - break; - } - - /* Otherwise, qualify the name if not visible in search path */ - if (!need_qual) - need_qual = !RelationIsVisible(relid); - - if (need_qual) - nspname = get_namespace_name(reltup->relnamespace); - else - nspname = NULL; - - result = quote_qualified_identifier(nspname, relname); - - ReleaseSysCache(tp); - - return result; -} - - -/* - * generate_rte_shard_name returns the qualified name of the shard given a - * CITUS_RTE_SHARD range table entry. - */ -static char * -generate_rte_shard_name(RangeTblEntry *rangeTableEntry) -{ - char *shardSchemaName = NULL; - char *shardTableName = NULL; - - Assert(GetRangeTblKind(rangeTableEntry) == CITUS_RTE_SHARD); - - ExtractRangeTblExtraData(rangeTableEntry, NULL, &shardSchemaName, &shardTableName, - NULL); - - return generate_fragment_name(shardSchemaName, shardTableName); -} - - -/* - * generate_fragment_name - * Compute the name to display for a shard or merged table - * - * The result includes all necessary quoting and schema-prefixing. The schema - * name can be NULL for regular shards. For merged tables, they are always - * declared within a job-specific schema, and therefore can't have null schema - * names. - */ -static char * -generate_fragment_name(char *schemaName, char *tableName) -{ - StringInfo fragmentNameString = makeStringInfo(); - - if (schemaName != NULL) - { - appendStringInfo(fragmentNameString, "%s.%s", quote_identifier(schemaName), - quote_identifier(tableName)); - } - else - { - appendStringInfoString(fragmentNameString, quote_identifier(tableName)); - } - - return fragmentNameString->data; -} - -/* - * generate_function_name - * Compute the name to display for a function specified by OID, - * given that it is being called with the specified actual arg names and - * types. (Those matter because of ambiguous-function resolution rules.) - * - * If we're dealing with a potentially variadic function (in practice, this - * means a FuncExpr or Aggref, not some other way of calling a function), then - * has_variadic must specify whether variadic arguments have been merged, - * and *use_variadic_p will be set to indicate whether to print VARIADIC in - * the output. For non-FuncExpr cases, has_variadic should be false and - * use_variadic_p can be NULL. - * - * The result includes all necessary quoting and schema-prefixing. - */ -static char * -generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes, - bool has_variadic, bool *use_variadic_p, - ParseExprKind special_exprkind) -{ - char *result; - HeapTuple proctup; - Form_pg_proc procform; - char *proname; - bool use_variadic; - char *nspname; - FuncDetailCode p_result; - Oid p_funcid; - Oid p_rettype; - bool p_retset; - int p_nvargs; - Oid p_vatype; - Oid *p_true_typeids; - bool force_qualify = false; - - proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); - if (!HeapTupleIsValid(proctup)) - elog(ERROR, "cache lookup failed for function %u", funcid); - procform = (Form_pg_proc) GETSTRUCT(proctup); - proname = NameStr(procform->proname); - - /* - * Due to parser hacks to avoid needing to reserve CUBE, we need to force - * qualification in some special cases. - */ - if (special_exprkind == EXPR_KIND_GROUP_BY) - { - if (strcmp(proname, "cube") == 0 || strcmp(proname, "rollup") == 0) - force_qualify = true; - } - - /* - * Determine whether VARIADIC should be printed. We must do this first - * since it affects the lookup rules in func_get_detail(). - * - * Currently, we always print VARIADIC if the function has a merged - * variadic-array argument. Note that this is always the case for - * functions taking a VARIADIC argument type other than VARIADIC ANY. - * - * In principle, if VARIADIC wasn't originally specified and the array - * actual argument is deconstructable, we could print the array elements - * separately and not print VARIADIC, thus more nearly reproducing the - * original input. For the moment that seems like too much complication - * for the benefit, and anyway we do not know whether VARIADIC was - * originally specified if it's a non-ANY type. - */ - if (use_variadic_p) - { - /* Parser should not have set funcvariadic unless fn is variadic */ - Assert(!has_variadic || OidIsValid(procform->provariadic)); - use_variadic = has_variadic; - *use_variadic_p = use_variadic; - } - else - { - Assert(!has_variadic); - use_variadic = false; - } - - /* - * The idea here is to schema-qualify only if the parser would fail to - * resolve the correct function given the unqualified func name with the - * specified argtypes and VARIADIC flag. But if we already decided to - * force qualification, then we can skip the lookup and pretend we didn't - * find it. - */ - if (!force_qualify) - p_result = func_get_detail(list_make1(makeString(proname)), - NIL, argnames, nargs, argtypes, - !use_variadic, true, - &p_funcid, &p_rettype, - &p_retset, &p_nvargs, &p_vatype, - &p_true_typeids, NULL); - else - { - p_result = FUNCDETAIL_NOTFOUND; - p_funcid = InvalidOid; - } - - if ((p_result == FUNCDETAIL_NORMAL || - p_result == FUNCDETAIL_AGGREGATE || - p_result == FUNCDETAIL_WINDOWFUNC) && - p_funcid == funcid) - nspname = NULL; - else - nspname = get_namespace_name(procform->pronamespace); - - result = quote_qualified_identifier(nspname, proname); - - ReleaseSysCache(proctup); - - return result; -} - -/* - * generate_operator_name - * Compute the name to display for an operator specified by OID, - * given that it is being called with the specified actual arg types. - * (Arg types matter because of ambiguous-operator resolution rules. - * Pass InvalidOid for unused arg of a unary operator.) - * - * The result includes all necessary quoting and schema-prefixing, - * plus the OPERATOR() decoration needed to use a qualified operator name - * in an expression. - */ -char * -generate_operator_name(Oid operid, Oid arg1, Oid arg2) -{ - StringInfoData buf; - HeapTuple opertup; - Form_pg_operator operform; - char *oprname; - char *nspname; - - initStringInfo(&buf); - - opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operid)); - if (!HeapTupleIsValid(opertup)) - elog(ERROR, "cache lookup failed for operator %u", operid); - operform = (Form_pg_operator) GETSTRUCT(opertup); - oprname = NameStr(operform->oprname); - - /* - * Unlike generate_operator_name() in postgres/src/backend/utils/adt/ruleutils.c, - * we don't check if the operator is in current namespace or not. This is - * because this check is costly when the operator is not in current namespace. - */ - nspname = get_namespace_name(operform->oprnamespace); - Assert(nspname != NULL); - appendStringInfo(&buf, "OPERATOR(%s.", quote_identifier(nspname)); - appendStringInfoString(&buf, oprname); - appendStringInfoChar(&buf, ')'); - - ReleaseSysCache(opertup); - - return buf.data; -} - -/* - * get_one_range_partition_bound_string - * A C string representation of one range partition bound - */ -char * -get_range_partbound_string(List *bound_datums) -{ - deparse_context context; - StringInfo buf = makeStringInfo(); - ListCell *cell; - char *sep; - - memset(&context, 0, sizeof(deparse_context)); - context.buf = buf; - - appendStringInfoString(buf, "("); - sep = ""; - foreach(cell, bound_datums) - { - PartitionRangeDatum *datum = - castNode(PartitionRangeDatum, lfirst(cell)); - - appendStringInfoString(buf, sep); - if (datum->kind == PARTITION_RANGE_DATUM_MINVALUE) - appendStringInfoString(buf, "MINVALUE"); - else if (datum->kind == PARTITION_RANGE_DATUM_MAXVALUE) - appendStringInfoString(buf, "MAXVALUE"); - else - { - Const *val = castNode(Const, datum->value); - - get_const_expr(val, &context, -1); - } - sep = ", "; - } - appendStringInfoChar(buf, ')'); - - return buf->data; -} - -/* - * Collect a list of OIDs of all sequences owned by the specified relation, - * and column if specified. If deptype is not zero, then only find sequences - * with the specified dependency type. - */ -List * -getOwnedSequences_internal(Oid relid, AttrNumber attnum, char deptype) -{ - List *result = NIL; - Relation depRel; - ScanKeyData key[3]; - SysScanDesc scan; - HeapTuple tup; - - depRel = table_open(DependRelationId, AccessShareLock); - - ScanKeyInit(&key[0], - Anum_pg_depend_refclassid, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(RelationRelationId)); - ScanKeyInit(&key[1], - Anum_pg_depend_refobjid, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(relid)); - if (attnum) - ScanKeyInit(&key[2], - Anum_pg_depend_refobjsubid, - BTEqualStrategyNumber, F_INT4EQ, - Int32GetDatum(attnum)); - - scan = systable_beginscan(depRel, DependReferenceIndexId, true, - NULL, attnum ? 3 : 2, key); - - while (HeapTupleIsValid(tup = systable_getnext(scan))) - { - Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup); - - /* - * We assume any auto or internal dependency of a sequence on a column - * must be what we are looking for. (We need the relkind test because - * indexes can also have auto dependencies on columns.) - */ - if (deprec->classid == RelationRelationId && - deprec->objsubid == 0 && - deprec->refobjsubid != 0 && - (deprec->deptype == DEPENDENCY_AUTO || deprec->deptype == DEPENDENCY_INTERNAL) && - get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE) - { - if (!deptype || deprec->deptype == deptype) - result = lappend_oid(result, deprec->objid); - } - } - - systable_endscan(scan); - - table_close(depRel, AccessShareLock); - - return result; -} - -#endif /* (PG_VERSION_NUM >= PG_VERSION_13) && (PG_VERSION_NUM < PG_VERSION_14) */ diff --git a/src/backend/distributed/executor/adaptive_executor.c b/src/backend/distributed/executor/adaptive_executor.c index 0c710909b..039475735 100644 --- a/src/backend/distributed/executor/adaptive_executor.c +++ b/src/backend/distributed/executor/adaptive_executor.c @@ -496,11 +496,7 @@ struct TaskPlacementExecution; /* GUC, determining whether Citus opens 1 connection per task */ bool ForceMaxQueryParallelization = false; int MaxAdaptiveExecutorPoolSize = 16; -#if PG_VERSION_NUM >= PG_VERSION_14 bool EnableBinaryProtocol = true; -#else -bool EnableBinaryProtocol = false; -#endif /* GUC, number of ms to wait between opening connections to the same worker */ int ExecutorSlowStartInterval = 10; diff --git a/src/backend/distributed/executor/multi_executor.c b/src/backend/distributed/executor/multi_executor.c index 04cb39a58..662eaaf97 100644 --- a/src/backend/distributed/executor/multi_executor.c +++ b/src/backend/distributed/executor/multi_executor.c @@ -455,9 +455,9 @@ ReadFileIntoTupleStore(char *fileName, char *copyFormat, TupleDesc tupleDescript location); copyOptions = lappend(copyOptions, copyOption); - CopyFromState copyState = BeginCopyFrom_compat(NULL, stubRelation, NULL, - fileName, false, NULL, - NULL, copyOptions); + CopyFromState copyState = BeginCopyFrom(NULL, stubRelation, NULL, + fileName, false, NULL, + NULL, copyOptions); while (true) { diff --git a/src/backend/distributed/executor/query_stats.c b/src/backend/distributed/executor/query_stats.c index 6dd5196f2..1ac70489c 100644 --- a/src/backend/distributed/executor/query_stats.c +++ b/src/backend/distributed/executor/query_stats.c @@ -797,11 +797,7 @@ BuildExistingQueryIdHash(void) { const int userIdAttributeNumber = 1; const int dbIdAttributeNumber = 2; -#if PG_VERSION_NUM >= PG_VERSION_14 const int queryIdAttributeNumber = 4; -#else - const int queryIdAttributeNumber = 3; -#endif Datum commandTypeDatum = (Datum) 0; bool missingOK = true; diff --git a/src/backend/distributed/metadata/dependency.c b/src/backend/distributed/metadata/dependency.c index adc3fc1ab..c307dc737 100644 --- a/src/backend/distributed/metadata/dependency.c +++ b/src/backend/distributed/metadata/dependency.c @@ -896,18 +896,11 @@ DeferErrorIfHasUnsupportedDependency(const ObjectAddress *objectAddress) return NULL; } - char *objectDescription = NULL; - char *dependencyDescription = NULL; StringInfo errorInfo = makeStringInfo(); StringInfo detailInfo = makeStringInfo(); - #if PG_VERSION_NUM >= PG_VERSION_14 - objectDescription = getObjectDescription(objectAddress, false); - dependencyDescription = getObjectDescription(undistributableDependency, false); - #else - objectDescription = getObjectDescription(objectAddress); - dependencyDescription = getObjectDescription(undistributableDependency); - #endif + char *objectDescription = getObjectDescription(objectAddress, false); + char *dependencyDescription = getObjectDescription(undistributableDependency, false); /* * We expect callers to interpret the error returned from this function diff --git a/src/backend/distributed/metadata/distobject.c b/src/backend/distributed/metadata/distobject.c index 55d7c9f33..5997480a0 100644 --- a/src/backend/distributed/metadata/distobject.c +++ b/src/backend/distributed/metadata/distobject.c @@ -85,12 +85,12 @@ citus_unmark_object_distributed(PG_FUNCTION_ARGS) { ereport(ERROR, (errmsg("object still exists"), errdetail("the %s \"%s\" still exists", - getObjectTypeDescription_compat(&address, + getObjectTypeDescription(&address, - /* missingOk: */ false), - getObjectIdentity_compat(&address, + /* missingOk: */ false), + getObjectIdentity(&address, - /* missingOk: */ false)), + /* missingOk: */ false)), errhint("drop the object via a DROP command"))); } diff --git a/src/backend/distributed/metadata/metadata_sync.c b/src/backend/distributed/metadata/metadata_sync.c index 5f8f76bd6..7dfc30f73 100644 --- a/src/backend/distributed/metadata/metadata_sync.c +++ b/src/backend/distributed/metadata/metadata_sync.c @@ -916,15 +916,9 @@ MarkObjectsDistributedCreateCommand(List *addresses, int forceDelegation = list_nth_int(forceDelegations, currentObjectCounter); List *names = NIL; List *args = NIL; - char *objectType = NULL; - #if PG_VERSION_NUM >= PG_VERSION_14 - objectType = getObjectTypeDescription(address, false); + char *objectType = getObjectTypeDescription(address, false); getObjectIdentityParts(address, &names, &args, false); - #else - objectType = getObjectTypeDescription(address); - getObjectIdentityParts(address, &names, &args); - #endif if (!isFirstObject) { diff --git a/src/backend/distributed/metadata/metadata_utility.c b/src/backend/distributed/metadata/metadata_utility.c index a6ff93220..53a963029 100644 --- a/src/backend/distributed/metadata/metadata_utility.c +++ b/src/backend/distributed/metadata/metadata_utility.c @@ -4031,11 +4031,7 @@ CancelTasksForJob(int64 jobid) errmsg("must be a superuser to cancel superuser tasks"))); } else if (!has_privs_of_role(GetUserId(), taskOwner) && -#if PG_VERSION_NUM >= 140000 !has_privs_of_role(GetUserId(), ROLE_PG_SIGNAL_BACKEND)) -#else - !has_privs_of_role(GetUserId(), DEFAULT_ROLE_SIGNAL_BACKENDID)) -#endif { /* user doesn't have the permissions to cancel this job */ ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), diff --git a/src/backend/distributed/operations/worker_node_manager.c b/src/backend/distributed/operations/worker_node_manager.c index 658c83269..76f2732ba 100644 --- a/src/backend/distributed/operations/worker_node_manager.c +++ b/src/backend/distributed/operations/worker_node_manager.c @@ -31,11 +31,7 @@ #include "utils/guc.h" #include "utils/hsearch.h" #include "utils/memutils.h" -#if PG_VERSION_NUM < PG_VERSION_13 -#include "utils/hashutils.h" -#else #include "common/hashfn.h" -#endif /* Config variables managed via guc.c */ diff --git a/src/backend/distributed/operations/worker_shard_copy.c b/src/backend/distributed/operations/worker_shard_copy.c index 00a5413c9..ba65635a7 100644 --- a/src/backend/distributed/operations/worker_shard_copy.c +++ b/src/backend/distributed/operations/worker_shard_copy.c @@ -527,13 +527,13 @@ LocalCopyToShard(ShardCopyDestReceiver *copyDest, CopyOutState localCopyOutState false /* inFromCl */); List *options = (isBinaryCopy) ? list_make1(binaryFormatOption) : NULL; - CopyFromState cstate = BeginCopyFrom_compat(pState, shard, - NULL /* whereClause */, - NULL /* fileName */, - false /* is_program */, - ReadFromLocalBufferCallback, - NULL /* attlist (NULL is all columns) */, - options); + CopyFromState cstate = BeginCopyFrom(pState, shard, + NULL /* whereClause */, + NULL /* fileName */, + false /* is_program */, + ReadFromLocalBufferCallback, + NULL /* attlist (NULL is all columns) */, + options); CopyFrom(cstate); EndCopyFrom(cstate); resetStringInfo(localCopyOutState->fe_msgbuf); diff --git a/src/backend/distributed/planner/insert_select_planner.c b/src/backend/distributed/planner/insert_select_planner.c index 84e76c6d4..c59e920b5 100644 --- a/src/backend/distributed/planner/insert_select_planner.c +++ b/src/backend/distributed/planner/insert_select_planner.c @@ -861,8 +861,8 @@ RouterModifyTaskForShardInterval(Query *originalQuery, * Note that this is only the case with PG14 as the parameter doesn't exist * prior to that. */ - shardRestrictionList = make_simple_restrictinfo_compat(NULL, - (Expr *) shardOpExpressions); + shardRestrictionList = make_simple_restrictinfo(NULL, + (Expr *) shardOpExpressions); extendedBaseRestrictInfo = lappend(extendedBaseRestrictInfo, shardRestrictionList); diff --git a/src/backend/distributed/planner/multi_explain.c b/src/backend/distributed/planner/multi_explain.c index 1cc3d4102..674077b46 100644 --- a/src/backend/distributed/planner/multi_explain.c +++ b/src/backend/distributed/planner/multi_explain.c @@ -1101,8 +1101,8 @@ worker_save_query_explain_analyze(PG_FUNCTION_ARGS) TupleDesc tupleDescriptor = NULL; Tuplestorestate *tupleStore = SetupTuplestore(fcinfo, &tupleDescriptor); DestReceiver *tupleStoreDest = CreateTuplestoreDestReceiver(); - SetTuplestoreDestReceiverParams_compat(tupleStoreDest, tupleStore, - CurrentMemoryContext, false, NULL, NULL); + SetTuplestoreDestReceiverParams(tupleStoreDest, tupleStore, + CurrentMemoryContext, false, NULL, NULL); List *parseTreeList = pg_parse_query(queryString); if (list_length(parseTreeList) != 1) @@ -1126,15 +1126,9 @@ worker_save_query_explain_analyze(PG_FUNCTION_ARGS) Query *analyzedQuery = parse_analyze_varparams_compat(parseTree, queryString, ¶mTypes, &numParams, NULL); -#if PG_VERSION_NUM >= PG_VERSION_14 - /* pg_rewrite_query is a wrapper around QueryRewrite with some debugging logic */ List *queryList = pg_rewrite_query(analyzedQuery); -#else - /* pg_rewrite_query is not yet public in PostgreSQL 13 */ - List *queryList = QueryRewrite(analyzedQuery); -#endif if (list_length(queryList) != 1) { ereport(ERROR, (errmsg("cannot EXPLAIN ANALYZE a query rewritten " diff --git a/src/backend/distributed/planner/multi_logical_optimizer.c b/src/backend/distributed/planner/multi_logical_optimizer.c index e0548049f..455f050a0 100644 --- a/src/backend/distributed/planner/multi_logical_optimizer.c +++ b/src/backend/distributed/planner/multi_logical_optimizer.c @@ -1855,11 +1855,7 @@ MasterAggregateExpression(Aggref *originalAggregate, { /* array_cat_agg() takes anyarray as input */ catAggregateName = ARRAY_CAT_AGGREGATE_NAME; -#if PG_VERSION_NUM >= PG_VERSION_14 catInputType = ANYCOMPATIBLEARRAYOID; -#else - catInputType = ANYARRAYOID; -#endif } else if (aggregateType == AGGREGATE_JSONB_AGG || aggregateType == AGGREGATE_JSONB_OBJECT_AGG) @@ -1897,8 +1893,6 @@ MasterAggregateExpression(Aggref *originalAggregate, if (aggregateType == AGGREGATE_ARRAY_AGG) { -#if PG_VERSION_NUM >= PG_VERSION_14 - /* * Postgres expects the type of the array here such as INT4ARRAYOID. * Hence we set it to workerReturnType. If we set this to @@ -1906,9 +1900,6 @@ MasterAggregateExpression(Aggref *originalAggregate, * "argument declared anycompatiblearray is not an array but type anycompatiblearray" */ newMasterAggregate->aggargtypes = list_make1_oid(workerReturnType); -#else - newMasterAggregate->aggargtypes = list_make1_oid(ANYARRAYOID); -#endif } else { @@ -3625,8 +3616,8 @@ static Oid CitusFunctionOidWithSignature(char *functionName, int numargs, Oid *argtypes) { List *aggregateName = list_make2(makeString("pg_catalog"), makeString(functionName)); - FuncCandidateList clist = FuncnameGetCandidates_compat(aggregateName, numargs, NIL, - false, false, false, true); + FuncCandidateList clist = FuncnameGetCandidates(aggregateName, numargs, NIL, + false, false, false, true); for (; clist; clist = clist->next) { diff --git a/src/backend/distributed/planner/multi_router_planner.c b/src/backend/distributed/planner/multi_router_planner.c index 41ae916ad..6ad51e0ae 100644 --- a/src/backend/distributed/planner/multi_router_planner.c +++ b/src/backend/distributed/planner/multi_router_planner.c @@ -152,10 +152,8 @@ static List * ExtractInsertValuesList(Query *query, Var *partitionColumn); static DeferredErrorMessage * DeferErrorIfUnsupportedRouterPlannableSelectQuery( Query *query); static DeferredErrorMessage * ErrorIfQueryHasUnroutableModifyingCTE(Query *queryTree); -#if PG_VERSION_NUM >= PG_VERSION_14 static DeferredErrorMessage * ErrorIfQueryHasCTEWithSearchClause(Query *queryTree); static bool ContainsSearchClauseWalker(Node *node, void *context); -#endif static bool SelectsFromDistributedTable(List *rangeTableList, Query *query); static ShardPlacement * CreateDummyPlacement(bool hasLocalRelation); static ShardPlacement * CreateLocalDummyPlacement(); @@ -1118,14 +1116,12 @@ ModifyQuerySupported(Query *queryTree, Query *originalQuery, bool multiShardQuer } } -#if PG_VERSION_NUM >= PG_VERSION_14 DeferredErrorMessage *CTEWithSearchClauseError = ErrorIfQueryHasCTEWithSearchClause(originalQuery); if (CTEWithSearchClauseError != NULL) { return CTEWithSearchClauseError; } -#endif return NULL; } @@ -3758,14 +3754,12 @@ DeferErrorIfUnsupportedRouterPlannableSelectQuery(Query *query) NULL, NULL); } -#if PG_VERSION_NUM >= PG_VERSION_14 DeferredErrorMessage *CTEWithSearchClauseError = ErrorIfQueryHasCTEWithSearchClause(query); if (CTEWithSearchClauseError != NULL) { return CTEWithSearchClauseError; } -#endif return ErrorIfQueryHasUnroutableModifyingCTE(query); } @@ -3900,8 +3894,6 @@ ErrorIfQueryHasUnroutableModifyingCTE(Query *queryTree) } -#if PG_VERSION_NUM >= PG_VERSION_14 - /* * ErrorIfQueryHasCTEWithSearchClause checks if the query contains any common table * expressions with search clause and errors out if it does. @@ -3948,9 +3940,6 @@ ContainsSearchClauseWalker(Node *node, void *context) } -#endif - - /* * get_all_actual_clauses * diff --git a/src/backend/distributed/planner/relation_restriction_equivalence.c b/src/backend/distributed/planner/relation_restriction_equivalence.c index ac36842de..b57e37735 100644 --- a/src/backend/distributed/planner/relation_restriction_equivalence.c +++ b/src/backend/distributed/planner/relation_restriction_equivalence.c @@ -2143,8 +2143,8 @@ GetRestrictInfoListForRelation(RangeTblEntry *rangeTblEntry, * If the restriction involves multiple tables, we cannot add it to * input relation's expression list. */ - Relids varnos = pull_varnos_compat(relationRestriction->plannerInfo, - (Node *) restrictionClause); + Relids varnos = pull_varnos(relationRestriction->plannerInfo, + (Node *) restrictionClause); if (bms_num_members(varnos) != 1) { continue; diff --git a/src/backend/distributed/replication/multi_logical_replication.c b/src/backend/distributed/replication/multi_logical_replication.c index e51329f22..550095875 100644 --- a/src/backend/distributed/replication/multi_logical_replication.c +++ b/src/backend/distributed/replication/multi_logical_replication.c @@ -1536,7 +1536,7 @@ CreateSubscriptions(MultiConnection *sourceConnection, quote_identifier(target->publication->name), quote_identifier(target->replicationSlot->name)); - if (EnableBinaryProtocol && PG_VERSION_NUM >= PG_VERSION_14) + if (EnableBinaryProtocol) { appendStringInfoString(createSubscriptionCommand, ", binary=true)"); } diff --git a/src/backend/distributed/shared_library_init.c b/src/backend/distributed/shared_library_init.c index a67e4c878..907d8e73e 100644 --- a/src/backend/distributed/shared_library_init.c +++ b/src/backend/distributed/shared_library_init.c @@ -1215,11 +1215,7 @@ RegisterCitusConfigVariables(void) "Enables communication between nodes using binary protocol when possible"), NULL, &EnableBinaryProtocol, -#if PG_VERSION_NUM >= PG_VERSION_14 true, -#else - false, -#endif PGC_USERSET, GUC_STANDARD, NULL, NULL, NULL); diff --git a/src/backend/distributed/test/fake_am.c b/src/backend/distributed/test/fake_am.c index 5a8ede316..1654bf095 100644 --- a/src/backend/distributed/test/fake_am.c +++ b/src/backend/distributed/test/fake_am.c @@ -169,7 +169,6 @@ fake_tuple_satisfies_snapshot(Relation rel, TupleTableSlot *slot, } -#if PG_VERSION_NUM >= PG_VERSION_14 static TransactionId fake_index_delete_tuples(Relation rel, TM_IndexDeleteOp *delstate) @@ -179,20 +178,6 @@ fake_index_delete_tuples(Relation rel, } -#else -static TransactionId -fake_compute_xid_horizon_for_tuples(Relation rel, - ItemPointerData *tids, - int nitems) -{ - elog(ERROR, "fake_compute_xid_horizon_for_tuples not implemented"); - return InvalidTransactionId; -} - - -#endif - - /* ---------------------------------------------------------------------------- * Functions for manipulations of physical tuples for fake AM. * ---------------------------------------------------------------------------- @@ -568,11 +553,7 @@ static const TableAmRoutine fake_methods = { .tuple_get_latest_tid = fake_get_latest_tid, .tuple_tid_valid = fake_tuple_tid_valid, .tuple_satisfies_snapshot = fake_tuple_satisfies_snapshot, -#if PG_VERSION_NUM >= PG_VERSION_14 .index_delete_tuples = fake_index_delete_tuples, -#else - .compute_xid_horizon_for_tuples = fake_compute_xid_horizon_for_tuples, -#endif .relation_set_new_filenode = fake_relation_set_new_filenode, .relation_nontransactional_truncate = fake_relation_nontransactional_truncate, diff --git a/src/backend/distributed/test/xact_stats.c b/src/backend/distributed/test/xact_stats.c index c31a17b7f..87e15aa64 100644 --- a/src/backend/distributed/test/xact_stats.c +++ b/src/backend/distributed/test/xact_stats.c @@ -48,8 +48,8 @@ MemoryContextTotalSpace(MemoryContext context) Size totalSpace = 0; MemoryContextCounters totals = { 0 }; - TopTransactionContext->methods->stats_compat(TopTransactionContext, NULL, NULL, - &totals, true); + TopTransactionContext->methods->stats(TopTransactionContext, NULL, NULL, + &totals, true); totalSpace += totals.totalspace; for (MemoryContext child = context->firstchild; diff --git a/src/backend/distributed/transaction/backend_data.c b/src/backend/distributed/transaction/backend_data.c index fc89fde9a..3e2ea5ca1 100644 --- a/src/backend/distributed/transaction/backend_data.c +++ b/src/backend/distributed/transaction/backend_data.c @@ -503,11 +503,7 @@ UserHasPermissionToViewStatsOf(Oid currentUserId, Oid backendOwnedId) } if (is_member_of_role(currentUserId, -#if PG_VERSION_NUM >= PG_VERSION_14 ROLE_PG_READ_ALL_STATS)) -#else - DEFAULT_ROLE_READ_ALL_STATS)) -#endif { return true; } diff --git a/src/backend/distributed/transaction/lock_graph.c b/src/backend/distributed/transaction/lock_graph.c index 8c09160b0..0be4bb2e9 100644 --- a/src/backend/distributed/transaction/lock_graph.c +++ b/src/backend/distributed/transaction/lock_graph.c @@ -664,7 +664,7 @@ IsProcessWaitingForSafeOperations(PGPROC *proc) return false; } - if (pgproc_statusflags_compat(proc) & PROC_IS_AUTOVACUUM) + if (proc->statusFlags & PROC_IS_AUTOVACUUM) { return true; } diff --git a/src/backend/distributed/utils/background_jobs.c b/src/backend/distributed/utils/background_jobs.c index 84ef4229f..2b5ce2dca 100644 --- a/src/backend/distributed/utils/background_jobs.c +++ b/src/backend/distributed/utils/background_jobs.c @@ -1436,13 +1436,11 @@ error_severity(int elevel) break; } -#if PG_VERSION_NUM >= PG_VERSION_14 case WARNING_CLIENT_ONLY: { prefix = gettext_noop("WARNING"); break; } -#endif case ERROR: { diff --git a/src/backend/distributed/utils/citus_clauses.c b/src/backend/distributed/utils/citus_clauses.c index c48239548..82900ea1a 100644 --- a/src/backend/distributed/utils/citus_clauses.c +++ b/src/backend/distributed/utils/citus_clauses.c @@ -528,9 +528,9 @@ FixFunctionArgumentsWalker(Node *expr, void *context) elog(ERROR, "cache lookup failed for function %u", funcExpr->funcid); } - funcExpr->args = expand_function_arguments_compat(funcExpr->args, false, - funcExpr->funcresulttype, - func_tuple); + funcExpr->args = expand_function_arguments(funcExpr->args, false, + funcExpr->funcresulttype, + func_tuple); ReleaseSysCache(func_tuple); } diff --git a/src/backend/distributed/utils/enable_ssl.c b/src/backend/distributed/utils/enable_ssl.c index b449fa3e4..cac32f74c 100644 --- a/src/backend/distributed/utils/enable_ssl.c +++ b/src/backend/distributed/utils/enable_ssl.c @@ -19,11 +19,6 @@ * done before including libpq.h. */ #include "distributed/pg_version_constants.h" -#if PG_VERSION_NUM < PG_VERSION_14 -#ifndef OPENSSL_API_COMPAT -#define OPENSSL_API_COMPAT 0x1000100L -#endif -#endif #include "distributed/connection_management.h" #include "distributed/memutils.h" diff --git a/src/backend/distributed/utils/function_utils.c b/src/backend/distributed/utils/function_utils.c index 04750b23f..006e29555 100644 --- a/src/backend/distributed/utils/function_utils.c +++ b/src/backend/distributed/utils/function_utils.c @@ -46,7 +46,7 @@ FunctionOidExtended(const char *schemaName, const char *functionName, int argume const bool findVariadics = false; const bool findDefaults = false; - FuncCandidateList functionList = FuncnameGetCandidates_compat( + FuncCandidateList functionList = FuncnameGetCandidates( qualifiedFunctionNameList, argumentCount, argumentList, diff --git a/src/backend/distributed/utils/listutils.c b/src/backend/distributed/utils/listutils.c index 3279193ef..dd54443c4 100644 --- a/src/backend/distributed/utils/listutils.c +++ b/src/backend/distributed/utils/listutils.c @@ -118,9 +118,7 @@ ListToHashSet(List *itemList, Size keySize, bool isStringList) if (isStringList) { -#if PG_VERSION_NUM >= PG_VERSION_14 flags |= HASH_STRINGS; -#endif } else { diff --git a/src/backend/distributed/utils/log_utils.c b/src/backend/distributed/utils/log_utils.c index ed463c40a..59a090a16 100644 --- a/src/backend/distributed/utils/log_utils.c +++ b/src/backend/distributed/utils/log_utils.c @@ -18,9 +18,7 @@ #include "utils/builtins.h" -#if PG_VERSION_NUM >= PG_VERSION_14 #include "common/cryptohash.h" -#endif /* diff --git a/src/backend/distributed/utils/multi_partitioning_utils.c b/src/backend/distributed/utils/multi_partitioning_utils.c index c5fcd2377..ab36483fd 100644 --- a/src/backend/distributed/utils/multi_partitioning_utils.c +++ b/src/backend/distributed/utils/multi_partitioning_utils.c @@ -1023,7 +1023,7 @@ IsParentTable(Oid relationId) Oid PartitionParentOid(Oid partitionOid) { - Oid partitionParentOid = get_partition_parent_compat(partitionOid, false); + Oid partitionParentOid = get_partition_parent(partitionOid, false); return partitionParentOid; } @@ -1074,7 +1074,7 @@ PartitionList(Oid parentRelationId) ereport(ERROR, (errmsg("\"%s\" is not a parent table", relationName))); } - PartitionDesc partDesc = RelationGetPartitionDesc_compat(rel, true); + PartitionDesc partDesc = RelationGetPartitionDesc(rel, true); Assert(partDesc != NULL); int partitionCount = partDesc->nparts; @@ -1107,7 +1107,7 @@ GenerateDetachPartitionCommand(Oid partitionTableId) ereport(ERROR, (errmsg("\"%s\" is not a partition", relationName))); } - Oid parentId = get_partition_parent_compat(partitionTableId, false); + Oid parentId = get_partition_parent(partitionTableId, false); char *tableQualifiedName = generate_qualified_relation_name(partitionTableId); char *parentTableQualifiedName = generate_qualified_relation_name(parentId); @@ -1221,7 +1221,7 @@ GenerateAlterTableAttachPartitionCommand(Oid partitionTableId) ereport(ERROR, (errmsg("\"%s\" is not a partition", relationName))); } - Oid parentId = get_partition_parent_compat(partitionTableId, false); + Oid parentId = get_partition_parent(partitionTableId, false); char *tableQualifiedName = generate_qualified_relation_name(partitionTableId); char *parentTableQualifiedName = generate_qualified_relation_name(parentId); diff --git a/src/include/columnar/columnar_version_compat.h b/src/include/columnar/columnar_version_compat.h index c40aa6236..0e0ae3112 100644 --- a/src/include/columnar/columnar_version_compat.h +++ b/src/include/columnar/columnar_version_compat.h @@ -22,29 +22,6 @@ ExecARDeleteTriggers(a, b, c, d, e) #endif -#if PG_VERSION_NUM >= PG_VERSION_14 -#define ColumnarProcessUtility_compat(a, b, c, d, e, f, g, h) \ - ColumnarProcessUtility(a, b, c, d, e, f, g, h) -#define PrevProcessUtilityHook_compat(a, b, c, d, e, f, g, h) \ - PrevProcessUtilityHook(a, b, c, d, e, f, g, h) -#define GetOldestNonRemovableTransactionId_compat(a, b) \ - GetOldestNonRemovableTransactionId(a) -#define ExecSimpleRelationInsert_compat(a, b, c) \ - ExecSimpleRelationInsert(a, b, c) -#define index_insert_compat(a, b, c, d, e, f, g, h) \ - index_insert(a, b, c, d, e, f, g, h) -#else -#define ColumnarProcessUtility_compat(a, b, c, d, e, f, g, h) \ - ColumnarProcessUtility(a, b, d, e, f, g, h) -#define PrevProcessUtilityHook_compat(a, b, c, d, e, f, g, h) \ - PrevProcessUtilityHook(a, b, d, e, f, g, h) -#define GetOldestNonRemovableTransactionId_compat(a, b) GetOldestXmin(a, b) -#define ExecSimpleRelationInsert_compat(a, b, c) \ - ExecSimpleRelationInsert(b, c) -#define index_insert_compat(a, b, c, d, e, f, g, h) \ - index_insert(a, b, c, d, e, f, h) -#endif - #define ACLCHECK_OBJECT_TABLE OBJECT_TABLE #define ExplainPropertyLong(qlabel, value, es) \ diff --git a/src/include/distributed/commands/multi_copy.h b/src/include/distributed/commands/multi_copy.h index 70f93cfb9..4255c952d 100644 --- a/src/include/distributed/commands/multi_copy.h +++ b/src/include/distributed/commands/multi_copy.h @@ -31,12 +31,7 @@ typedef enum CitusCopyDest { COPY_FILE, /* to/from file (or a piped program) */ -#if PG_VERSION_NUM >= PG_VERSION_14 COPY_FRONTEND, /* to frontend */ -#else - COPY_OLD_FE, /* to/from frontend (2.0 protocol) */ - COPY_NEW_FE, /* to/from frontend (3.0 protocol) */ -#endif COPY_CALLBACK /* to/from callback function */ } CitusCopyDest; diff --git a/src/include/distributed/commands/utility_hook.h b/src/include/distributed/commands/utility_hook.h index 7229f7c72..f02f83fe3 100644 --- a/src/include/distributed/commands/utility_hook.h +++ b/src/include/distributed/commands/utility_hook.h @@ -79,9 +79,7 @@ typedef struct DDLJob extern ProcessUtility_hook_type PrevProcessUtility; extern void multi_ProcessUtility(PlannedStmt *pstmt, const char *queryString, -#if PG_VERSION_NUM >= PG_VERSION_14 bool readOnlyTree, -#endif ProcessUtilityContext context, ParamListInfo params, struct QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *completionTag diff --git a/src/include/distributed/connection_management.h b/src/include/distributed/connection_management.h index 96bed3457..f08124123 100644 --- a/src/include/distributed/connection_management.h +++ b/src/include/distributed/connection_management.h @@ -353,7 +353,4 @@ extern bool CitusModifyWaitEvent(WaitEventSet *set, int pos, uint32 events, extern double MillisecondsPassedSince(instr_time moment); extern long MillisecondsToTimeout(instr_time start, long msAfterStart); -#if PG_VERSION_NUM < 140000 -extern void WarmUpConnParamsHash(void); -#endif #endif /* CONNECTION_MANAGMENT_H */ diff --git a/src/include/distributed/pg_version_constants.h b/src/include/distributed/pg_version_constants.h index 83b1071dd..a85d72d84 100644 --- a/src/include/distributed/pg_version_constants.h +++ b/src/include/distributed/pg_version_constants.h @@ -11,7 +11,6 @@ #ifndef PG_VERSION_CONSTANTS #define PG_VERSION_CONSTANTS -#define PG_VERSION_13 130000 #define PG_VERSION_14 140000 #define PG_VERSION_15 150000 #define PG_VERSION_16 160000 diff --git a/src/include/pg_version_compat.h b/src/include/pg_version_compat.h index eb81bca43..00c5e286b 100644 --- a/src/include/pg_version_compat.h +++ b/src/include/pg_version_compat.h @@ -61,8 +61,7 @@ pg_strtoint64(char *s) * We want to use it in all versions. So we backport it ourselves in earlier * versions, and rely on the Postgres provided version in the later versions. */ -#if PG_VERSION_NUM >= PG_VERSION_13 && PG_VERSION_NUM < 130010 \ - || PG_VERSION_NUM >= PG_VERSION_14 && PG_VERSION_NUM < 140007 +#if PG_VERSION_NUM < 140007 static inline SMgrRelation RelationGetSmgr(Relation rel) { @@ -84,67 +83,6 @@ RelationGetSmgr(Relation rel) #endif -#if PG_VERSION_NUM >= PG_VERSION_14 -#define AlterTableStmtObjType_compat(a) ((a)->objtype) -#define getObjectTypeDescription_compat(a, b) getObjectTypeDescription(a, b) -#define getObjectIdentity_compat(a, b) getObjectIdentity(a, b) - -/* for MemoryContextMethods->stats */ -#define stats_compat(a, b, c, d, e) stats(a, b, c, d, e) -#define FuncnameGetCandidates_compat(a, b, c, d, e, f, g) \ - FuncnameGetCandidates(a, b, c, d, e, f, g) -#define expand_function_arguments_compat(a, b, c, d) expand_function_arguments(a, b, c, d) -#define BeginCopyFrom_compat(a, b, c, d, e, f, g, h) BeginCopyFrom(a, b, c, d, e, f, g, h) -#define standard_ProcessUtility_compat(a, b, c, d, e, f, g, h) \ - standard_ProcessUtility(a, b, c, d, e, f, g, h) -#define ProcessUtility_compat(a, b, c, d, e, f, g, h) \ - ProcessUtility(a, b, c, d, e, f, g, h) -#define PrevProcessUtility_compat(a, b, c, d, e, f, g, h) \ - PrevProcessUtility(a, b, c, d, e, f, g, h) -#define SetTuplestoreDestReceiverParams_compat(a, b, c, d, e, f) \ - SetTuplestoreDestReceiverParams(a, b, c, d, e, f) -#define pgproc_statusflags_compat(pgproc) ((pgproc)->statusFlags) -#define get_partition_parent_compat(a, b) get_partition_parent(a, b) -#define RelationGetPartitionDesc_compat(a, b) RelationGetPartitionDesc(a, b) -#define make_simple_restrictinfo_compat(a, b) make_simple_restrictinfo(a, b) -#define pull_varnos_compat(a, b) pull_varnos(a, b) -#else -#define AlterTableStmtObjType_compat(a) ((a)->relkind) -#define F_NEXTVAL F_NEXTVAL_OID -#define ROLE_PG_MONITOR DEFAULT_ROLE_MONITOR -#define PROC_WAIT_STATUS_WAITING STATUS_WAITING -#define getObjectTypeDescription_compat(a, b) getObjectTypeDescription(a) -#define getObjectIdentity_compat(a, b) getObjectIdentity(a) - -/* for MemoryContextMethods->stats */ -#define stats_compat(a, b, c, d, e) stats(a, b, c, d) -#define FuncnameGetCandidates_compat(a, b, c, d, e, f, g) \ - FuncnameGetCandidates(a, b, c, d, e, g) -#define expand_function_arguments_compat(a, b, c, d) expand_function_arguments(a, c, d) -#define VacOptValue VacOptTernaryValue -#define VACOPTVALUE_UNSPECIFIED VACOPT_TERNARY_DEFAULT -#define VACOPTVALUE_DISABLED VACOPT_TERNARY_DISABLED -#define VACOPTVALUE_ENABLED VACOPT_TERNARY_ENABLED -#define CopyFromState CopyState -#define BeginCopyFrom_compat(a, b, c, d, e, f, g, h) BeginCopyFrom(a, b, d, e, f, g, h) -#define standard_ProcessUtility_compat(a, b, c, d, e, f, g, h) \ - standard_ProcessUtility(a, b, d, e, f, g, h) -#define ProcessUtility_compat(a, b, c, d, e, f, g, h) ProcessUtility(a, b, d, e, f, g, h) -#define PrevProcessUtility_compat(a, b, c, d, e, f, g, h) \ - PrevProcessUtility(a, b, d, e, f, g, h) -#define COPY_FRONTEND COPY_NEW_FE -#define SetTuplestoreDestReceiverParams_compat(a, b, c, d, e, f) \ - SetTuplestoreDestReceiverParams(a, b, c, d) -#define pgproc_statusflags_compat(pgproc) \ - ((&ProcGlobal->allPgXact[(pgproc)->pgprocno])->vacuumFlags) -#define get_partition_parent_compat(a, b) get_partition_parent(a) -#define RelationGetPartitionDesc_compat(a, b) RelationGetPartitionDesc(a) -#define PQ_LARGE_MESSAGE_LIMIT 0 -#define make_simple_restrictinfo_compat(a, b) make_simple_restrictinfo(b) -#define pull_varnos_compat(a, b) pull_varnos(b) -#define ROLE_PG_READ_ALL_STATS DEFAULT_ROLE_READ_ALL_STATS -#endif - #define SetListCellPtr(a, b) ((a)->ptr_value = (b)) #define RangeTableEntryFromNSItem(a) ((a)->p_rte) #define fcGetArgValue(fc, n) ((fc)->args[n].value) diff --git a/src/test/regress/bin/normalize.sed b/src/test/regress/bin/normalize.sed index 0370f4e98..a374c73f5 100644 --- a/src/test/regress/bin/normalize.sed +++ b/src/test/regress/bin/normalize.sed @@ -98,34 +98,7 @@ s/of relation ".*" violates not-null constraint/violates not-null constraint/g s/partition ".*" would be violated by some row/partition would be violated by some row/g s/of relation ".*" contains null values/contains null values/g -#if (PG_VERSION_NUM >= PG_VERSION_13) && (PG_VERSION_NUM < PG_VERSION_14) -# (This is not preprocessor directive, but a reminder for the developer that will drop PG13 support ) -# libpq message changes for minor versions of pg13 - -# We ignore multiline error messages, and substitute first line with a single line -# alternative that is used in some older libpq versions. -s/(ERROR: |WARNING: |error:) server closed the connection unexpectedly/\1 connection not open/g -/^\s*This probably means the server terminated abnormally$/d -/^\s*before or while processing the request.$/d -/^\s*connection not open$/d - -s/ERROR: fake_fetch_row_version not implemented/ERROR: fake_tuple_update not implemented/g -s/ERROR: COMMIT is not allowed in an SQL function/ERROR: COMMIT is not allowed in a SQL function/g -s/ERROR: ROLLBACK is not allowed in an SQL function/ERROR: ROLLBACK is not allowed in a SQL function/g -/.*Async-Capable.*/d -/.*Async Capable.*/d -/Parent Relationship/d -/Parent-Relationship/d -s/function array_cat_agg\(anyarray\) anyarray/function array_cat_agg\(anycompatiblearray\) anycompatiblearray/g -s/function array_cat_agg\(anyarray\)/function array_cat_agg\(anycompatiblearray\)/g -s/TRIM\(BOTH FROM value\)/btrim\(value\)/g -/DETAIL: Subqueries are not supported in policies on distributed tables/d -s/ERROR: unexpected non-SELECT command in SubLink/ERROR: cannot create policy/g - -# PG13 changes bgworker sigterm message, we can drop that line with PG13 drop -s/(FATAL: terminating).*Citus Background Task Queue Executor.*(due to administrator command)\+/\1 connection \2 \+/g - -#endif /* (PG_VERSION_NUM >= PG_VERSION_13) && (PG_VERSION_NUM < PG_VERSION_14) */ +s/(Citus Background Task Queue Executor: regression\/postgres for \()[0-9]+\/[0-9]+\)/\1xxxxx\/xxxxx\)/g # Changed outputs after minor bump to PG14.5 and PG13.8 s/(ERROR: |WARNING: |error:) invalid socket/\1 connection not open/g @@ -135,9 +108,18 @@ s/(ERROR: |WARNING: |error:) invalid socket/\1 connection not open/g # pg15 changes # can be removed when dropping PG13&14 support +#if (PG_VERSION_NUM >= PG_VERSION_14) && (PG_VERSION_NUM < PG_VERSION_15) +# (This is not preprocessor directive, but a reminder for the developer that will drop PG14 support ) s/is not a PostgreSQL server process/is not a PostgreSQL backend process/g s/ AS "\?column\?"//g s/".*\.(.*)": (found .* removable)/"\1": \2/g +# We ignore multiline error messages, and substitute first line with a single line +# alternative that is used in some older libpq versions. +s/(ERROR: |WARNING: |error:) server closed the connection unexpectedly/\1 connection not open/g +/^\s*This probably means the server terminated abnormally$/d +/^\s*before or while processing the request.$/d +/^\s*connection not open$/d +#endif /* (PG_VERSION_NUM >= PG_VERSION_13) && (PG_VERSION_NUM < PG_VERSION_14) */ # intermediate_results s/(ERROR.*)pgsql_job_cache\/([0-9]+_[0-9]+_[0-9]+)\/(.*).data/\1pgsql_job_cache\/xx_x_xxx\/\3.data/g diff --git a/src/test/regress/citus_tests/run_test.py b/src/test/regress/citus_tests/run_test.py index b41ba35cc..8e1e1c91e 100755 --- a/src/test/regress/citus_tests/run_test.py +++ b/src/test/regress/citus_tests/run_test.py @@ -166,6 +166,7 @@ DEPS = { "multi_table_ddl", ], ), + "grant_on_schema_propagation": TestDeps("minimal_schedule"), } diff --git a/src/test/regress/expected/background_task_queue_monitor.out b/src/test/regress/expected/background_task_queue_monitor.out index 2b4f7de37..1d4006377 100644 --- a/src/test/regress/expected/background_task_queue_monitor.out +++ b/src/test/regress/expected/background_task_queue_monitor.out @@ -495,11 +495,11 @@ SELECT task_id, status, retry_count, message FROM pg_dist_background_task ORDER BY task_id; -- show that all tasks are runnable by retry policy after termination signal task_id | status | retry_count | message --------------------------------------------------------------------- - 1450019 | runnable | 1 | FATAL: terminating connection due to administrator command + - | | | CONTEXT: Citus Background Task Queue Executor: regression/postgres for (1450011/1450019) + + 1450019 | runnable | 1 | FATAL: terminating background worker "Citus Background Task Queue Executor: regression/postgres for (xxxxx/xxxxx)" due to administrator command+ + | | | CONTEXT: Citus Background Task Queue Executor: regression/postgres for (xxxxx/xxxxx) + | | | - 1450020 | runnable | 1 | FATAL: terminating connection due to administrator command + - | | | CONTEXT: Citus Background Task Queue Executor: regression/postgres for (1450012/1450020) + + 1450020 | runnable | 1 | FATAL: terminating background worker "Citus Background Task Queue Executor: regression/postgres for (xxxxx/xxxxx)" due to administrator command+ + | | | CONTEXT: Citus Background Task Queue Executor: regression/postgres for (xxxxx/xxxxx) + | | | (2 rows) diff --git a/src/test/regress/expected/cpu_priority.out b/src/test/regress/expected/cpu_priority.out index ad05e09f5..04bd8e0f4 100644 --- a/src/test/regress/expected/cpu_priority.out +++ b/src/test/regress/expected/cpu_priority.out @@ -85,17 +85,14 @@ SET search_path TO cpu_priority; -- in their CREATE SUBSCRIPTION commands. SET citus.log_remote_commands TO ON; SET citus.grep_remote_commands = '%CREATE SUBSCRIPTION%'; --- We disable binary protocol, so we have consistent output between PG13 and --- PG14, beacuse PG13 doesn't support binary logical replication. -SET citus.enable_binary_protocol = false; SELECT master_move_shard_placement(11568900, 'localhost', :worker_1_port, 'localhost', :worker_2_port, 'force_logical'); -NOTICE: issuing CREATE SUBSCRIPTION citus_shard_move_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_move_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_move_slot_xxxxxxx_xxxxxxx_xxxxxxx) +NOTICE: issuing CREATE SUBSCRIPTION citus_shard_move_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_move_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_move_slot_xxxxxxx_xxxxxxx_xxxxxxx, binary=true) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing CREATE SUBSCRIPTION citus_shard_move_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_move_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_move_slot_xxxxxxx_xxxxxxx_xxxxxxx) +NOTICE: issuing CREATE SUBSCRIPTION citus_shard_move_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_move_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_move_slot_xxxxxxx_xxxxxxx_xxxxxxx, binary=true) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing CREATE SUBSCRIPTION citus_shard_move_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_move_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_move_slot_xxxxxxx_xxxxxxx_xxxxxxx) +NOTICE: issuing CREATE SUBSCRIPTION citus_shard_move_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_move_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_move_slot_xxxxxxx_xxxxxxx_xxxxxxx, binary=true) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing CREATE SUBSCRIPTION citus_shard_move_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_move_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_move_slot_xxxxxxx_xxxxxxx_xxxxxxx) +NOTICE: issuing CREATE SUBSCRIPTION citus_shard_move_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_move_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_move_slot_xxxxxxx_xxxxxxx_xxxxxxx, binary=true) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx master_move_shard_placement --------------------------------------------------------------------- @@ -104,13 +101,13 @@ DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx SET citus.cpu_priority_for_logical_replication_senders = 15; SELECT master_move_shard_placement(11568900, 'localhost', :worker_2_port, 'localhost', :worker_1_port, 'force_logical'); -NOTICE: issuing CREATE SUBSCRIPTION citus_shard_move_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_move_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_move_slot_xxxxxxx_xxxxxxx_xxxxxxx) +NOTICE: issuing CREATE SUBSCRIPTION citus_shard_move_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_move_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_move_slot_xxxxxxx_xxxxxxx_xxxxxxx, binary=true) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing CREATE SUBSCRIPTION citus_shard_move_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_move_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_move_slot_xxxxxxx_xxxxxxx_xxxxxxx) +NOTICE: issuing CREATE SUBSCRIPTION citus_shard_move_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_move_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_move_slot_xxxxxxx_xxxxxxx_xxxxxxx, binary=true) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing CREATE SUBSCRIPTION citus_shard_move_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_move_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_move_slot_xxxxxxx_xxxxxxx_xxxxxxx) +NOTICE: issuing CREATE SUBSCRIPTION citus_shard_move_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_move_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_move_slot_xxxxxxx_xxxxxxx_xxxxxxx, binary=true) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing CREATE SUBSCRIPTION citus_shard_move_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_move_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_move_slot_xxxxxxx_xxxxxxx_xxxxxxx) +NOTICE: issuing CREATE SUBSCRIPTION citus_shard_move_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_move_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_move_slot_xxxxxxx_xxxxxxx_xxxxxxx, binary=true) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx master_move_shard_placement --------------------------------------------------------------------- @@ -119,13 +116,13 @@ DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx SET citus.max_high_priority_background_processes = 3; SELECT master_move_shard_placement(11568900, 'localhost', :worker_1_port, 'localhost', :worker_2_port, 'force_logical'); -NOTICE: issuing CREATE SUBSCRIPTION citus_shard_move_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_move_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_move_slot_xxxxxxx_xxxxxxx_xxxxxxx) +NOTICE: issuing CREATE SUBSCRIPTION citus_shard_move_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_move_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_move_slot_xxxxxxx_xxxxxxx_xxxxxxx, binary=true) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing CREATE SUBSCRIPTION citus_shard_move_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_move_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_move_slot_xxxxxxx_xxxxxxx_xxxxxxx) +NOTICE: issuing CREATE SUBSCRIPTION citus_shard_move_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_move_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_move_slot_xxxxxxx_xxxxxxx_xxxxxxx, binary=true) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing CREATE SUBSCRIPTION citus_shard_move_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_move_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_move_slot_xxxxxxx_xxxxxxx_xxxxxxx) +NOTICE: issuing CREATE SUBSCRIPTION citus_shard_move_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_move_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_move_slot_xxxxxxx_xxxxxxx_xxxxxxx, binary=true) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing CREATE SUBSCRIPTION citus_shard_move_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_move_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_move_slot_xxxxxxx_xxxxxxx_xxxxxxx) +NOTICE: issuing CREATE SUBSCRIPTION citus_shard_move_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_move_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_move_slot_xxxxxxx_xxxxxxx_xxxxxxx, binary=true) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx master_move_shard_placement --------------------------------------------------------------------- @@ -145,21 +142,21 @@ SELECT pg_catalog.citus_split_shard_by_split_points( ARRAY['-1500000000'], ARRAY[:worker_1_node, :worker_2_node], 'force_logical'); -NOTICE: issuing CREATE SUBSCRIPTION citus_shard_split_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_split_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_split_slot_xxxxxxx_xxxxxxx_xxxxxxx) +NOTICE: issuing CREATE SUBSCRIPTION citus_shard_split_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_split_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_split_slot_xxxxxxx_xxxxxxx_xxxxxxx, binary=true) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing CREATE SUBSCRIPTION citus_shard_split_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_split_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_split_slot_xxxxxxx_xxxxxxx_xxxxxxx) +NOTICE: issuing CREATE SUBSCRIPTION citus_shard_split_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_split_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_split_slot_xxxxxxx_xxxxxxx_xxxxxxx, binary=true) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing CREATE SUBSCRIPTION citus_shard_split_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_split_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_split_slot_xxxxxxx_xxxxxxx_xxxxxxx) +NOTICE: issuing CREATE SUBSCRIPTION citus_shard_split_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_split_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_split_slot_xxxxxxx_xxxxxxx_xxxxxxx, binary=true) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing CREATE SUBSCRIPTION citus_shard_split_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_split_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_split_slot_xxxxxxx_xxxxxxx_xxxxxxx) +NOTICE: issuing CREATE SUBSCRIPTION citus_shard_split_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_split_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_split_slot_xxxxxxx_xxxxxxx_xxxxxxx, binary=true) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing CREATE SUBSCRIPTION citus_shard_split_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_split_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_split_slot_xxxxxxx_xxxxxxx_xxxxxxx) +NOTICE: issuing CREATE SUBSCRIPTION citus_shard_split_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_split_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_split_slot_xxxxxxx_xxxxxxx_xxxxxxx, binary=true) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing CREATE SUBSCRIPTION citus_shard_split_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_split_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_split_slot_xxxxxxx_xxxxxxx_xxxxxxx) +NOTICE: issuing CREATE SUBSCRIPTION citus_shard_split_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_split_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_split_slot_xxxxxxx_xxxxxxx_xxxxxxx, binary=true) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing CREATE SUBSCRIPTION citus_shard_split_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_split_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_split_slot_xxxxxxx_xxxxxxx_xxxxxxx) +NOTICE: issuing CREATE SUBSCRIPTION citus_shard_split_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_split_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_split_slot_xxxxxxx_xxxxxxx_xxxxxxx, binary=true) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx -NOTICE: issuing CREATE SUBSCRIPTION citus_shard_split_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_split_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_split_slot_xxxxxxx_xxxxxxx_xxxxxxx) +NOTICE: issuing CREATE SUBSCRIPTION citus_shard_split_subscription_xxxxxxx_xxxxxxx CONNECTION 'host=''localhost'' port=xxxxx user=''postgres'' dbname=''regression'' connect_timeout=20' PUBLICATION citus_shard_split_publication_xxxxxxx_xxxxxxx_xxxxxxx WITH (citus_use_authinfo=true, create_slot=false, copy_data=false, enabled=false, slot_name=citus_shard_split_slot_xxxxxxx_xxxxxxx_xxxxxxx, binary=true) DETAIL: on server postgres@localhost:xxxxx connectionId: xxxxxxx citus_split_shard_by_split_points --------------------------------------------------------------------- diff --git a/src/test/regress/expected/generated_identity.out b/src/test/regress/expected/generated_identity.out index 617a8fee6..8fe7a0dc6 100644 --- a/src/test/regress/expected/generated_identity.out +++ b/src/test/regress/expected/generated_identity.out @@ -1,11 +1,3 @@ --- This test file has an alternative output because of error messages vary for PG13 -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int <= 13 AS server_version_le_13; - server_version_le_13 ---------------------------------------------------------------------- - f -(1 row) - CREATE SCHEMA generated_identities; SET search_path TO generated_identities; SET client_min_messages to ERROR; diff --git a/src/test/regress/expected/generated_identity_0.out b/src/test/regress/expected/generated_identity_0.out deleted file mode 100644 index 1bff7f68f..000000000 --- a/src/test/regress/expected/generated_identity_0.out +++ /dev/null @@ -1,431 +0,0 @@ --- This test file has an alternative output because of error messages vary for PG13 -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int <= 13 AS server_version_le_13; - server_version_le_13 ---------------------------------------------------------------------- - t -(1 row) - -CREATE SCHEMA generated_identities; -SET search_path TO generated_identities; -SET client_min_messages to ERROR; -SET citus.shard_replication_factor TO 1; -SELECT 1 from citus_add_node('localhost', :master_port, groupId=>0); - ?column? ---------------------------------------------------------------------- - 1 -(1 row) - --- smallint identity column can not be distributed -CREATE TABLE smallint_identity_column ( - a smallint GENERATED BY DEFAULT AS IDENTITY -); -SELECT create_distributed_table('smallint_identity_column', 'a'); -ERROR: cannot complete operation on generated_identities.smallint_identity_column with smallint/int identity column -HINT: Use bigint identity column instead. -SELECT create_distributed_table_concurrently('smallint_identity_column', 'a'); -ERROR: cannot complete operation on generated_identities.smallint_identity_column with smallint/int identity column -HINT: Use bigint identity column instead. -SELECT create_reference_table('smallint_identity_column'); -ERROR: cannot complete operation on a table with identity column -SELECT citus_add_local_table_to_metadata('smallint_identity_column'); - citus_add_local_table_to_metadata ---------------------------------------------------------------------- - -(1 row) - -DROP TABLE smallint_identity_column; --- int identity column can not be distributed -CREATE TABLE int_identity_column ( - a int GENERATED BY DEFAULT AS IDENTITY -); -SELECT create_distributed_table('int_identity_column', 'a'); -ERROR: cannot complete operation on generated_identities.int_identity_column with smallint/int identity column -HINT: Use bigint identity column instead. -SELECT create_distributed_table_concurrently('int_identity_column', 'a'); -ERROR: cannot complete operation on generated_identities.int_identity_column with smallint/int identity column -HINT: Use bigint identity column instead. -SELECT create_reference_table('int_identity_column'); -ERROR: cannot complete operation on a table with identity column -SELECT citus_add_local_table_to_metadata('int_identity_column'); - citus_add_local_table_to_metadata ---------------------------------------------------------------------- - -(1 row) - -DROP TABLE int_identity_column; -RESET citus.shard_replication_factor; -CREATE TABLE bigint_identity_column ( - a bigint GENERATED BY DEFAULT AS IDENTITY, - b int -); -SELECT citus_add_local_table_to_metadata('bigint_identity_column'); - citus_add_local_table_to_metadata ---------------------------------------------------------------------- - -(1 row) - -DROP TABLE bigint_identity_column; -CREATE TABLE bigint_identity_column ( - a bigint GENERATED BY DEFAULT AS IDENTITY, - b int -); -SELECT create_distributed_table('bigint_identity_column', 'a'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -\d bigint_identity_column - Table "generated_identities.bigint_identity_column" - Column | Type | Collation | Nullable | Default ---------------------------------------------------------------------- - a | bigint | | not null | generated by default as identity - b | integer | | | - -\c - - - :worker_1_port -SET search_path TO generated_identities; -SET client_min_messages to ERROR; -INSERT INTO bigint_identity_column (b) -SELECT s FROM generate_series(1,10) s; -\d generated_identities.bigint_identity_column - Table "generated_identities.bigint_identity_column" - Column | Type | Collation | Nullable | Default ---------------------------------------------------------------------- - a | bigint | | not null | generated by default as identity - b | integer | | | - -\c - - - :master_port -SET search_path TO generated_identities; -SET client_min_messages to ERROR; -INSERT INTO bigint_identity_column (b) -SELECT s FROM generate_series(11,20) s; -SELECT * FROM bigint_identity_column ORDER BY B ASC; - a | b ---------------------------------------------------------------------- - 3940649673949185 | 1 - 3940649673949186 | 2 - 3940649673949187 | 3 - 3940649673949188 | 4 - 3940649673949189 | 5 - 3940649673949190 | 6 - 3940649673949191 | 7 - 3940649673949192 | 8 - 3940649673949193 | 9 - 3940649673949194 | 10 - 1 | 11 - 2 | 12 - 3 | 13 - 4 | 14 - 5 | 15 - 6 | 16 - 7 | 17 - 8 | 18 - 9 | 19 - 10 | 20 -(20 rows) - --- table with identity column cannot be altered. -SELECT alter_distributed_table('bigint_identity_column', 'b'); -ERROR: cannot complete operation on a table with identity column --- table with identity column cannot be undistributed. -SELECT undistribute_table('bigint_identity_column'); -ERROR: cannot complete operation on a table with identity column -DROP TABLE bigint_identity_column; --- create a partitioned table for testing. -CREATE TABLE partitioned_table ( - a bigint CONSTRAINT myconname GENERATED BY DEFAULT AS IDENTITY (START WITH 10 INCREMENT BY 10), - b bigint GENERATED ALWAYS AS IDENTITY (START WITH 10 INCREMENT BY 10), - c int -) -PARTITION BY RANGE (c); -CREATE TABLE partitioned_table_1_50 PARTITION OF partitioned_table FOR VALUES FROM (1) TO (50); -CREATE TABLE partitioned_table_50_500 PARTITION OF partitioned_table FOR VALUES FROM (50) TO (1000); -SELECT create_distributed_table('partitioned_table', 'a'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -\d partitioned_table - Partitioned table "generated_identities.partitioned_table" - Column | Type | Collation | Nullable | Default ---------------------------------------------------------------------- - a | bigint | | not null | generated by default as identity - b | bigint | | not null | generated always as identity - c | integer | | | -Partition key: RANGE (c) -Number of partitions: 2 (Use \d+ to list them.) - -\c - - - :worker_1_port -SET search_path TO generated_identities; -SET client_min_messages to ERROR; -\d generated_identities.partitioned_table - Partitioned table "generated_identities.partitioned_table" - Column | Type | Collation | Nullable | Default ---------------------------------------------------------------------- - a | bigint | | not null | generated by default as identity - b | bigint | | not null | generated always as identity - c | integer | | | -Partition key: RANGE (c) -Number of partitions: 2 (Use \d+ to list them.) - -insert into partitioned_table (c) values (1); -insert into partitioned_table (c) SELECT 2; -INSERT INTO partitioned_table (c) -SELECT s FROM generate_series(3,7) s; -\c - - - :master_port -SET search_path TO generated_identities; -SET client_min_messages to ERROR; -INSERT INTO partitioned_table (c) -SELECT s FROM generate_series(10,20) s; -INSERT INTO partitioned_table (a,c) VALUES (998,998); -INSERT INTO partitioned_table (a,b,c) OVERRIDING SYSTEM VALUE VALUES (999,999,999); -SELECT * FROM partitioned_table ORDER BY c ASC; - a | b | c ---------------------------------------------------------------------- - 3940649673949185 | 3940649673949185 | 1 - 3940649673949195 | 3940649673949195 | 2 - 3940649673949205 | 3940649673949205 | 3 - 3940649673949215 | 3940649673949215 | 4 - 3940649673949225 | 3940649673949225 | 5 - 3940649673949235 | 3940649673949235 | 6 - 3940649673949245 | 3940649673949245 | 7 - 10 | 10 | 10 - 20 | 20 | 11 - 30 | 30 | 12 - 40 | 40 | 13 - 50 | 50 | 14 - 60 | 60 | 15 - 70 | 70 | 16 - 80 | 80 | 17 - 90 | 90 | 18 - 100 | 100 | 19 - 110 | 110 | 20 - 998 | 120 | 998 - 999 | 999 | 999 -(20 rows) - --- alter table .. alter column .. add is unsupported -ALTER TABLE partitioned_table ALTER COLUMN g ADD GENERATED ALWAYS AS IDENTITY; -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. --- alter table .. alter column is unsupported -ALTER TABLE partitioned_table ALTER COLUMN b TYPE int; -ERROR: cannot execute ALTER COLUMN command involving identity column -DROP TABLE partitioned_table; --- create a table for reference table testing. -CREATE TABLE reference_table ( - a bigint CONSTRAINT myconname GENERATED BY DEFAULT AS IDENTITY (START WITH 10 INCREMENT BY 10), - b bigint GENERATED ALWAYS AS IDENTITY (START WITH 10 INCREMENT BY 10) UNIQUE, - c int -); -SELECT create_reference_table('reference_table'); - create_reference_table ---------------------------------------------------------------------- - -(1 row) - -\d reference_table - Table "generated_identities.reference_table" - Column | Type | Collation | Nullable | Default ---------------------------------------------------------------------- - a | bigint | | not null | generated by default as identity - b | bigint | | not null | generated always as identity - c | integer | | | -Indexes: - "reference_table_b_key" UNIQUE CONSTRAINT, btree (b) - -\c - - - :worker_1_port -SET search_path TO generated_identities; -\d generated_identities.reference_table - Table "generated_identities.reference_table" - Column | Type | Collation | Nullable | Default ---------------------------------------------------------------------- - a | bigint | | not null | generated by default as identity - b | bigint | | not null | generated always as identity - c | integer | | | -Indexes: - "reference_table_b_key" UNIQUE CONSTRAINT, btree (b) - -INSERT INTO reference_table (c) -SELECT s FROM generate_series(1,10) s; ---on master -select * from reference_table; - a | b | c ---------------------------------------------------------------------- - 3940649673949185 | 3940649673949185 | 1 - 3940649673949195 | 3940649673949195 | 2 - 3940649673949205 | 3940649673949205 | 3 - 3940649673949215 | 3940649673949215 | 4 - 3940649673949225 | 3940649673949225 | 5 - 3940649673949235 | 3940649673949235 | 6 - 3940649673949245 | 3940649673949245 | 7 - 3940649673949255 | 3940649673949255 | 8 - 3940649673949265 | 3940649673949265 | 9 - 3940649673949275 | 3940649673949275 | 10 -(10 rows) - -\c - - - :master_port -SET search_path TO generated_identities; -SET client_min_messages to ERROR; -INSERT INTO reference_table (c) -SELECT s FROM generate_series(11,20) s; -SELECT * FROM reference_table ORDER BY c ASC; - a | b | c ---------------------------------------------------------------------- - 3940649673949185 | 3940649673949185 | 1 - 3940649673949195 | 3940649673949195 | 2 - 3940649673949205 | 3940649673949205 | 3 - 3940649673949215 | 3940649673949215 | 4 - 3940649673949225 | 3940649673949225 | 5 - 3940649673949235 | 3940649673949235 | 6 - 3940649673949245 | 3940649673949245 | 7 - 3940649673949255 | 3940649673949255 | 8 - 3940649673949265 | 3940649673949265 | 9 - 3940649673949275 | 3940649673949275 | 10 - 10 | 10 | 11 - 20 | 20 | 12 - 30 | 30 | 13 - 40 | 40 | 14 - 50 | 50 | 15 - 60 | 60 | 16 - 70 | 70 | 17 - 80 | 80 | 18 - 90 | 90 | 19 - 100 | 100 | 20 -(20 rows) - -DROP TABLE reference_table; -CREATE TABLE color ( - color_id BIGINT GENERATED ALWAYS AS IDENTITY UNIQUE, - color_name VARCHAR NOT NULL -); --- https://github.com/citusdata/citus/issues/6694 -CREATE USER identity_test_user; -GRANT INSERT ON color TO identity_test_user; -GRANT USAGE ON SCHEMA generated_identities TO identity_test_user; -SET ROLE identity_test_user; -SELECT create_distributed_table('color', 'color_id'); -ERROR: must be owner of table color -SET ROLE postgres; -SET citus.shard_replication_factor TO 1; -SELECT create_distributed_table_concurrently('color', 'color_id'); - create_distributed_table_concurrently ---------------------------------------------------------------------- - -(1 row) - -RESET citus.shard_replication_factor; -\c - identity_test_user - :worker_1_port -SET search_path TO generated_identities; -SET client_min_messages to ERROR; -INSERT INTO color(color_name) VALUES ('Blue'); -\c - postgres - :master_port -SET search_path TO generated_identities; -SET client_min_messages to ERROR; -SET citus.next_shard_id TO 12400000; -DROP TABLE Color; -CREATE TABLE color ( - color_id BIGINT GENERATED ALWAYS AS IDENTITY UNIQUE, - color_name VARCHAR NOT NULL -) USING columnar; -SELECT create_distributed_table('color', 'color_id'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -INSERT INTO color(color_name) VALUES ('Blue'); -\d+ color - Table "generated_identities.color" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------------------------------------------------------------------- - color_id | bigint | | not null | generated always as identity | plain | | - color_name | character varying | | not null | | extended | | -Indexes: - "color_color_id_key" UNIQUE CONSTRAINT, btree (color_id) - -\c - - - :worker_1_port -SET search_path TO generated_identities; -\d+ color - Table "generated_identities.color" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------------------------------------------------------------------- - color_id | bigint | | not null | generated always as identity | plain | | - color_name | character varying | | not null | | extended | | -Indexes: - "color_color_id_key" UNIQUE CONSTRAINT, btree (color_id) - -INSERT INTO color(color_name) VALUES ('Red'); --- alter sequence .. restart -ALTER SEQUENCE color_color_id_seq RESTART WITH 1000; -ERROR: Altering a distributed sequence is currently not supported. --- override system value -INSERT INTO color(color_id, color_name) VALUES (1, 'Red'); -ERROR: cannot insert into column "color_id" -DETAIL: Column "color_id" is an identity column defined as GENERATED ALWAYS. -HINT: Use OVERRIDING SYSTEM VALUE to override. -INSERT INTO color(color_id, color_name) VALUES (NULL, 'Red'); -ERROR: cannot insert into column "color_id" -DETAIL: Column "color_id" is an identity column defined as GENERATED ALWAYS. -HINT: Use OVERRIDING SYSTEM VALUE to override. -INSERT INTO color(color_id, color_name) OVERRIDING SYSTEM VALUE VALUES (1, 'Red'); -ERROR: duplicate key value violates unique constraint "color_color_id_key_12400000" -DETAIL: Key (color_id)=(1) already exists. -CONTEXT: while executing command on localhost:xxxxx --- update null or custom value -UPDATE color SET color_id = NULL; -ERROR: column "color_id" can only be updated to DEFAULT -DETAIL: Column "color_id" is an identity column defined as GENERATED ALWAYS. -UPDATE color SET color_id = 1; -ERROR: column "color_id" can only be updated to DEFAULT -DETAIL: Column "color_id" is an identity column defined as GENERATED ALWAYS. -\c - postgres - :master_port -SET search_path TO generated_identities; -SET client_min_messages to ERROR; --- alter table .. add column .. GENERATED .. AS IDENTITY -ALTER TABLE color ADD COLUMN color_id BIGINT GENERATED ALWAYS AS IDENTITY; -ERROR: cannot execute ADD COLUMN commands involving identity columns when metadata is synchronized to workers --- alter sequence .. restart -ALTER SEQUENCE color_color_id_seq RESTART WITH 1000; -ERROR: Altering a distributed sequence is currently not supported. --- override system value -INSERT INTO color(color_id, color_name) VALUES (1, 'Red'); -ERROR: cannot insert into column "color_id" -DETAIL: Column "color_id" is an identity column defined as GENERATED ALWAYS. -HINT: Use OVERRIDING SYSTEM VALUE to override. -INSERT INTO color(color_id, color_name) VALUES (NULL, 'Red'); -ERROR: cannot insert into column "color_id" -DETAIL: Column "color_id" is an identity column defined as GENERATED ALWAYS. -HINT: Use OVERRIDING SYSTEM VALUE to override. -INSERT INTO color(color_id, color_name) OVERRIDING SYSTEM VALUE VALUES (1, 'Red'); -ERROR: duplicate key value violates unique constraint "color_color_id_key_12400000" -DETAIL: Key (color_id)=(1) already exists. -CONTEXT: while executing command on localhost:xxxxx --- update null or custom value -UPDATE color SET color_id = NULL; -ERROR: column "color_id" can only be updated to DEFAULT -DETAIL: Column "color_id" is an identity column defined as GENERATED ALWAYS. -UPDATE color SET color_id = 1; -ERROR: column "color_id" can only be updated to DEFAULT -DETAIL: Column "color_id" is an identity column defined as GENERATED ALWAYS. -DROP TABLE IF EXISTS test; -CREATE TABLE test (x int, y int, z bigint generated by default as identity); -SELECT create_distributed_table('test', 'x', colocate_with := 'none'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -INSERT INTO test VALUES (1,2); -INSERT INTO test SELECT x, y FROM test WHERE x = 1; -SELECT * FROM test; - x | y | z ---------------------------------------------------------------------- - 1 | 2 | 1 - 1 | 2 | 2 -(2 rows) - -DROP SCHEMA generated_identities CASCADE; -DROP USER identity_test_user; diff --git a/src/test/regress/expected/grant_on_schema_propagation.out b/src/test/regress/expected/grant_on_schema_propagation.out index 410865d49..77447c2dd 100644 --- a/src/test/regress/expected/grant_on_schema_propagation.out +++ b/src/test/regress/expected/grant_on_schema_propagation.out @@ -1,7 +1,7 @@ -- -- GRANT_ON_SCHEMA_PROPAGATION -- --- this test has different output for PG13/14 compared to PG15 +-- this test has different output for PG14 compared to PG15 -- In PG15, public schema is owned by pg_database_owner role -- Relevant PG commit: b073c3ccd06e4cb845e121387a43faa8c68a7b62 SHOW server_version \gset @@ -327,6 +327,8 @@ SELECT master_remove_node('localhost', :worker_2_port); (1 row) +-- to avoid different output in PG15 +GRANT CREATE ON SCHEMA public TO public; -- distribute the public schema (it has to be distributed by now but just in case) CREATE TABLE public_schema_table (id INT); SELECT create_distributed_table('public_schema_table', 'id'); diff --git a/src/test/regress/expected/grant_on_schema_propagation_0.out b/src/test/regress/expected/grant_on_schema_propagation_0.out index 6b8b782ca..9806a0dbd 100644 --- a/src/test/regress/expected/grant_on_schema_propagation_0.out +++ b/src/test/regress/expected/grant_on_schema_propagation_0.out @@ -1,7 +1,7 @@ -- -- GRANT_ON_SCHEMA_PROPAGATION -- --- this test has different output for PG13/14 compared to PG15 +-- this test has different output for PG14 compared to PG15 -- In PG15, public schema is owned by pg_database_owner role -- Relevant PG commit: b073c3ccd06e4cb845e121387a43faa8c68a7b62 SHOW server_version \gset @@ -327,6 +327,8 @@ SELECT master_remove_node('localhost', :worker_2_port); (1 row) +-- to avoid different output in PG15 +GRANT CREATE ON SCHEMA public TO public; -- distribute the public schema (it has to be distributed by now but just in case) CREATE TABLE public_schema_table (id INT); SELECT create_distributed_table('public_schema_table', 'id'); diff --git a/src/test/regress/expected/isolation_master_update_node_1.out b/src/test/regress/expected/isolation_master_update_node_1.out deleted file mode 100644 index 194299c4d..000000000 --- a/src/test/regress/expected/isolation_master_update_node_1.out +++ /dev/null @@ -1,66 +0,0 @@ -Parsed test spec with 2 sessions - -starting permutation: s1-begin s1-insert s2-begin s2-update-node-1 s1-abort s2-abort -create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -step s1-begin: BEGIN; -step s1-insert: INSERT INTO t1 SELECT generate_series(1, 100); -step s2-begin: BEGIN; -step s2-update-node-1: - -- update a specific node by address - SELECT master_update_node(nodeid, 'localhost', nodeport + 10) - FROM pg_dist_node - WHERE nodename = 'localhost' - AND nodeport = 57637; - -step s1-abort: ABORT; -step s2-update-node-1: <... completed> -master_update_node ---------------------------------------------------------------------- - -(1 row) - -step s2-abort: ABORT; -master_remove_node ---------------------------------------------------------------------- - - -(2 rows) - - -starting permutation: s1-begin s1-insert s2-begin s2-update-node-1-force s2-abort s1-abort -create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -step s1-begin: BEGIN; -step s1-insert: INSERT INTO t1 SELECT generate_series(1, 100); -step s2-begin: BEGIN; -step s2-update-node-1-force: - -- update a specific node by address (force) - SELECT master_update_node(nodeid, 'localhost', nodeport + 10, force => true, lock_cooldown => 100) - FROM pg_dist_node - WHERE nodename = 'localhost' - AND nodeport = 57637; - -step s2-update-node-1-force: <... completed> -master_update_node ---------------------------------------------------------------------- - -(1 row) - -step s2-abort: ABORT; -step s1-abort: ABORT; -FATAL: terminating connection due to administrator command -server closed the connection unexpectedly - -master_remove_node ---------------------------------------------------------------------- - - -(2 rows) - diff --git a/src/test/regress/expected/local_shard_execution.out b/src/test/regress/expected/local_shard_execution.out index f77af42da..e70dc1102 100644 --- a/src/test/regress/expected/local_shard_execution.out +++ b/src/test/regress/expected/local_shard_execution.out @@ -1200,7 +1200,7 @@ NOTICE: executing the command locally: SELECT count(*) AS count FROM local_shar EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 @@ -1209,7 +1209,7 @@ NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FR EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 @@ -1218,7 +1218,7 @@ NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FR EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 @@ -1227,7 +1227,7 @@ NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FR EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 @@ -1236,7 +1236,7 @@ NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FR EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 @@ -1245,7 +1245,7 @@ NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FR EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 @@ -1254,7 +1254,7 @@ NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FR EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 @@ -1263,7 +1263,7 @@ NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FR EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 diff --git a/src/test/regress/expected/local_shard_execution_0.out b/src/test/regress/expected/local_shard_execution_0.out index 5350728aa..c7f002cad 100644 --- a/src/test/regress/expected/local_shard_execution_0.out +++ b/src/test/regress/expected/local_shard_execution_0.out @@ -1200,7 +1200,7 @@ NOTICE: executing the command locally: SELECT count(*) AS count FROM local_shar EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 @@ -1209,7 +1209,7 @@ NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FR EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 @@ -1218,7 +1218,7 @@ NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FR EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 @@ -1227,7 +1227,7 @@ NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FR EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 @@ -1236,7 +1236,7 @@ NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FR EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 @@ -1245,7 +1245,7 @@ NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FR EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 @@ -1254,7 +1254,7 @@ NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FR EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 @@ -1263,7 +1263,7 @@ NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FR EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution.distributed_table_1470003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 diff --git a/src/test/regress/expected/local_shard_execution_replicated.out b/src/test/regress/expected/local_shard_execution_replicated.out index 7d36a5559..07da961c2 100644 --- a/src/test/regress/expected/local_shard_execution_replicated.out +++ b/src/test/regress/expected/local_shard_execution_replicated.out @@ -1187,7 +1187,7 @@ NOTICE: executing the command locally: SELECT count(*) AS count FROM local_shar EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 @@ -1196,7 +1196,7 @@ NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FR EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 @@ -1205,7 +1205,7 @@ NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FR EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 @@ -1214,7 +1214,7 @@ NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FR EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 @@ -1223,7 +1223,7 @@ NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FR EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 @@ -1232,7 +1232,7 @@ NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FR EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 @@ -1241,7 +1241,7 @@ NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FR EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 @@ -1250,7 +1250,7 @@ NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FR EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 diff --git a/src/test/regress/expected/local_shard_execution_replicated_0.out b/src/test/regress/expected/local_shard_execution_replicated_0.out index 759d842fd..c913bf628 100644 --- a/src/test/regress/expected/local_shard_execution_replicated_0.out +++ b/src/test/regress/expected/local_shard_execution_replicated_0.out @@ -1187,7 +1187,7 @@ NOTICE: executing the command locally: SELECT count(*) AS count FROM local_shar EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 @@ -1196,7 +1196,7 @@ NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FR EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 @@ -1205,7 +1205,7 @@ NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FR EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 @@ -1214,7 +1214,7 @@ NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FR EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 @@ -1223,7 +1223,7 @@ NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FR EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 @@ -1232,7 +1232,7 @@ NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FR EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 @@ -1241,7 +1241,7 @@ NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FR EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 @@ -1250,7 +1250,7 @@ NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FR EXECUTE local_prepare_no_param_subquery; NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500001 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint NOTICE: executing the command locally: SELECT worker_column_1 AS value FROM (SELECT distributed_table.value AS worker_column_1 FROM local_shard_execution_replicated.distributed_table_1500003 distributed_table WHERE ((distributed_table.key OPERATOR(pg_catalog.=) ANY (ARRAY[1, 6, 500, 701])) AND (((SELECT 2))::double precision OPERATOR(pg_catalog.>) random()))) worker_subquery ORDER BY worker_column_1 LIMIT '2'::bigint -NOTICE: executing the command locally: SELECT DISTINCT btrim(value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t +NOTICE: executing the command locally: SELECT DISTINCT TRIM(BOTH FROM value) AS btrim FROM (SELECT intermediate_result.value FROM read_intermediate_result('XXX_1'::text, 'binary'::citus_copy_format) intermediate_result(value text)) t btrim --------------------------------------------------------------------- 12 diff --git a/src/test/regress/expected/multi_alter_table_row_level_security.out b/src/test/regress/expected/multi_alter_table_row_level_security.out index 962b037fc..d82f76a25 100644 --- a/src/test/regress/expected/multi_alter_table_row_level_security.out +++ b/src/test/regress/expected/multi_alter_table_row_level_security.out @@ -538,6 +538,7 @@ CREATE POLICY fp_s ON information FOR SELECT -- this attempt for distribution fails because the table has a disallowed expression SELECT create_distributed_table('information', 'group_id'); ERROR: cannot create policy +DETAIL: Subqueries are not supported in policies on distributed tables -- DROP the expression so we can distribute the table DROP POLICY fp_s ON information; SELECT create_distributed_table('information', 'group_id'); @@ -549,7 +550,7 @@ SELECT create_distributed_table('information', 'group_id'); -- Try and create the expression on a distributed table, this should also fail CREATE POLICY fp_s ON information FOR SELECT USING (group_id <= (SELECT group_id FROM users WHERE user_name = current_user)); -ERROR: cannot create policy +ERROR: unexpected non-SELECT command in SubLink -- Clean up test DROP TABLE information, groups, users; SET citus.next_shard_id TO 1810000; diff --git a/src/test/regress/expected/multi_explain.out b/src/test/regress/expected/multi_explain.out index f9fc5a164..b3e47474f 100644 --- a/src/test/regress/expected/multi_explain.out +++ b/src/test/regress/expected/multi_explain.out @@ -98,19 +98,24 @@ EXPLAIN (COSTS FALSE, FORMAT JSON) "Plan": { "Node Type": "Sort", "Parallel Aware": false, + "Async Capable": false, "Sort Key": ["(COALESCE((pg_catalog.sum(remote_scan.count_quantity))::bigint, '0'::bigint))", "remote_scan.l_quantity"], "Plans": [ { "Node Type": "Aggregate", "Strategy": "Hashed", "Partial Mode": "Simple", + "Parent Relationship": "Outer", "Parallel Aware": false, + "Async Capable": false, "Group Key": ["remote_scan.l_quantity"], "Plans": [ { "Node Type": "Custom Scan", + "Parent Relationship": "Outer", "Custom Plan Provider": "Citus Adaptive", "Parallel Aware": false, + "Async Capable": false, "Distributed Query": { "Job": { "Task Count": 2, @@ -126,11 +131,14 @@ EXPLAIN (COSTS FALSE, FORMAT JSON) "Strategy": "Hashed", "Partial Mode": "Simple", "Parallel Aware": false, + "Async Capable": false, "Group Key": ["l_quantity"], "Plans": [ { "Node Type": "Seq Scan", + "Parent Relationship": "Outer", "Parallel Aware": false, + "Async Capable": false, "Relation Name": "lineitem_360000", "Alias": "lineitem" } @@ -172,6 +180,7 @@ EXPLAIN (COSTS FALSE, FORMAT XML) Sort false + false (COALESCE((pg_catalog.sum(remote_scan.count_quantity))::bigint, '0'::bigint)) remote_scan.l_quantity @@ -181,15 +190,19 @@ EXPLAIN (COSTS FALSE, FORMAT XML) Aggregate Hashed Simple + Outer false + false remote_scan.l_quantity Custom Scan + Outer Citus Adaptive false + false 2 @@ -205,13 +218,16 @@ EXPLAIN (COSTS FALSE, FORMAT XML) Hashed Simple false + false l_quantity Seq Scan + Outer false + false lineitem_360000 lineitem @@ -250,6 +266,7 @@ EXPLAIN (COSTS FALSE, FORMAT YAML) - Plan: Node Type: "Sort" Parallel Aware: false + Async Capable: false Sort Key: - "(COALESCE((pg_catalog.sum(remote_scan.count_quantity))::bigint, '0'::bigint))" - "remote_scan.l_quantity" @@ -257,13 +274,17 @@ EXPLAIN (COSTS FALSE, FORMAT YAML) - Node Type: "Aggregate" Strategy: "Hashed" Partial Mode: "Simple" + Parent Relationship: "Outer" Parallel Aware: false + Async Capable: false Group Key: - "remote_scan.l_quantity" Plans: - Node Type: "Custom Scan" + Parent Relationship: "Outer" Custom Plan Provider: "Citus Adaptive" Parallel Aware: false + Async Capable: false Distributed Query: Job: Task Count: 2 @@ -276,11 +297,14 @@ EXPLAIN (COSTS FALSE, FORMAT YAML) Strategy: "Hashed" Partial Mode: "Simple" Parallel Aware: false + Async Capable: false Group Key: - "l_quantity" Plans: - Node Type: "Seq Scan" + Parent Relationship: "Outer" Parallel Aware: false + Async Capable: false Relation Name: "lineitem_360000" Alias: "lineitem" @@ -1135,11 +1159,14 @@ EXPLAIN (COSTS FALSE, FORMAT JSON) "Strategy": "Plain", "Partial Mode": "Simple", "Parallel Aware": false, + "Async Capable": false, "Plans": [ { "Node Type": "Custom Scan", + "Parent Relationship": "Outer", "Custom Plan Provider": "Citus Adaptive", "Parallel Aware": false, + "Async Capable": false, "Distributed Query": { "Job": { "Task Count": 6, @@ -1191,11 +1218,14 @@ EXPLAIN (COSTS FALSE, FORMAT XML) Plain Simple false + false Custom Scan + Outer Citus Adaptive false + false 6 @@ -1258,10 +1288,13 @@ EXPLAIN (COSTS FALSE, FORMAT YAML) Strategy: "Plain" Partial Mode: "Simple" Parallel Aware: false + Async Capable: false Plans: - Node Type: "Custom Scan" + Parent Relationship: "Outer" Custom Plan Provider: "Citus Adaptive" Parallel Aware: false + Async Capable: false Distributed Query: Job: Task Count: 6 @@ -1684,6 +1717,7 @@ SELECT explain_analyze_output FROM worker_last_saved_explain_analyze(); "Plan": { + "Node Type": "Result", + "Parallel Aware": false,+ + "Async Capable": false, + "Actual Rows": 1, + "Actual Loops": 1 + }, + @@ -1707,6 +1741,7 @@ SELECT explain_analyze_output FROM worker_last_saved_explain_analyze(); + Result + false + + false + 1 + 1 + + @@ -1728,6 +1763,7 @@ SELECT explain_analyze_output FROM worker_last_saved_explain_analyze(); - Plan: + Node Type: "Result" + Parallel Aware: false+ + Async Capable: false + Actual Rows: 1 + Actual Loops: 1 + Triggers: @@ -2115,6 +2151,7 @@ EXPLAIN (COSTS off, ANALYZE on, TIMING off, SUMMARY off, FORMAT JSON) INSERT INT "Node Type": "Custom Scan", "Custom Plan Provider": "Citus Adaptive", "Parallel Aware": false, + "Async Capable": false, "Actual Rows": 0, "Actual Loops": 1, "Distributed Query": { @@ -2131,6 +2168,7 @@ EXPLAIN (COSTS off, ANALYZE on, TIMING off, SUMMARY off, FORMAT JSON) INSERT INT "Node Type": "ModifyTable", "Operation": "Insert", "Parallel Aware": false, + "Async Capable": false, "Relation Name": "explain_pk_570013", "Alias": "citus_table_alias", "Actual Rows": 0, @@ -2138,7 +2176,9 @@ EXPLAIN (COSTS off, ANALYZE on, TIMING off, SUMMARY off, FORMAT JSON) INSERT INT "Plans": [ { "Node Type": "Result", + "Parent Relationship": "Outer", "Parallel Aware": false, + "Async Capable": false, "Actual Rows": 1, "Actual Loops": 1 } @@ -2167,6 +2207,7 @@ EXPLAIN (COSTS off, ANALYZE on, TIMING off, SUMMARY off, FORMAT JSON) SELECT * F "Node Type": "Custom Scan", "Custom Plan Provider": "Citus Adaptive", "Parallel Aware": false, + "Async Capable": false, "Actual Rows": 0, "Actual Loops": 1, "Distributed Query": { @@ -2184,6 +2225,7 @@ EXPLAIN (COSTS off, ANALYZE on, TIMING off, SUMMARY off, FORMAT JSON) SELECT * F "Plan": { "Node Type": "Seq Scan", "Parallel Aware": false, + "Async Capable": false, "Relation Name": "explain_pk_570013", "Alias": "explain_pk", "Actual Rows": 0, @@ -2212,6 +2254,7 @@ EXPLAIN (COSTS off, ANALYZE on, TIMING off, SUMMARY off, FORMAT XML) INSERT INTO Custom Scan Citus Adaptive false + false 0 1 @@ -2228,6 +2271,7 @@ EXPLAIN (COSTS off, ANALYZE on, TIMING off, SUMMARY off, FORMAT XML) INSERT INTO ModifyTable Insert false + false explain_pk_570013 citus_table_alias 0 @@ -2235,7 +2279,9 @@ EXPLAIN (COSTS off, ANALYZE on, TIMING off, SUMMARY off, FORMAT XML) INSERT INTO Result + Outer false + false 1 1 @@ -2263,6 +2309,7 @@ EXPLAIN (COSTS off, ANALYZE on, TIMING off, SUMMARY off, FORMAT XML) SELECT * FR Custom Scan Citus Adaptive false + false 0 1 @@ -2280,6 +2327,7 @@ EXPLAIN (COSTS off, ANALYZE on, TIMING off, SUMMARY off, FORMAT XML) SELECT * FR Seq Scan false + false explain_pk_570013 explain_pk 0 diff --git a/src/test/regress/expected/multi_metadata_sync.out b/src/test/regress/expected/multi_metadata_sync.out index b74cdc179..e85253031 100644 --- a/src/test/regress/expected/multi_metadata_sync.out +++ b/src/test/regress/expected/multi_metadata_sync.out @@ -1,7 +1,7 @@ -- -- MULTI_METADATA_SYNC -- --- this test has different output for PG13/14 compared to PG15 +-- this test has different output for PG14 compared to PG15 -- In PG15, public schema is owned by pg_database_owner role -- Relevant PG commit: b073c3ccd06e4cb845e121387a43faa8c68a7b62 SHOW server_version \gset diff --git a/src/test/regress/expected/multi_metadata_sync_0.out b/src/test/regress/expected/multi_metadata_sync_0.out index 16c319d4e..6e1ba6525 100644 --- a/src/test/regress/expected/multi_metadata_sync_0.out +++ b/src/test/regress/expected/multi_metadata_sync_0.out @@ -1,7 +1,7 @@ -- -- MULTI_METADATA_SYNC -- --- this test has different output for PG13/14 compared to PG15 +-- this test has different output for PG14 compared to PG15 -- In PG15, public schema is owned by pg_database_owner role -- Relevant PG commit: b073c3ccd06e4cb845e121387a43faa8c68a7b62 SHOW server_version \gset diff --git a/src/test/regress/expected/multi_mx_explain.out b/src/test/regress/expected/multi_mx_explain.out index 1c585a027..beb374d23 100644 --- a/src/test/regress/expected/multi_mx_explain.out +++ b/src/test/regress/expected/multi_mx_explain.out @@ -85,19 +85,24 @@ EXPLAIN (COSTS FALSE, FORMAT JSON) "Plan": { "Node Type": "Sort", "Parallel Aware": false, + "Async Capable": false, "Sort Key": ["(COALESCE((pg_catalog.sum(remote_scan.count_quantity))::bigint, '0'::bigint))", "remote_scan.l_quantity"], "Plans": [ { "Node Type": "Aggregate", "Strategy": "Hashed", "Partial Mode": "Simple", + "Parent Relationship": "Outer", "Parallel Aware": false, + "Async Capable": false, "Group Key": ["remote_scan.l_quantity"], "Plans": [ { "Node Type": "Custom Scan", + "Parent Relationship": "Outer", "Custom Plan Provider": "Citus Adaptive", "Parallel Aware": false, + "Async Capable": false, "Distributed Query": { "Job": { "Task Count": 16, @@ -113,11 +118,14 @@ EXPLAIN (COSTS FALSE, FORMAT JSON) "Strategy": "Hashed", "Partial Mode": "Simple", "Parallel Aware": false, + "Async Capable": false, "Group Key": ["l_quantity"], "Plans": [ { "Node Type": "Seq Scan", + "Parent Relationship": "Outer", "Parallel Aware": false, + "Async Capable": false, "Relation Name": "lineitem_mx_1220052", "Alias": "lineitem_mx" } @@ -153,6 +161,7 @@ EXPLAIN (COSTS FALSE, FORMAT XML) Sort false + false (COALESCE((pg_catalog.sum(remote_scan.count_quantity))::bigint, '0'::bigint)) remote_scan.l_quantity @@ -162,15 +171,19 @@ EXPLAIN (COSTS FALSE, FORMAT XML) Aggregate Hashed Simple + Outer false + false remote_scan.l_quantity Custom Scan + Outer Citus Adaptive false + false 16 @@ -186,13 +199,16 @@ EXPLAIN (COSTS FALSE, FORMAT XML) Hashed Simple false + false l_quantity Seq Scan + Outer false + false lineitem_mx_1220052 lineitem_mx @@ -224,6 +240,7 @@ EXPLAIN (COSTS FALSE, FORMAT YAML) - Plan: Node Type: "Sort" Parallel Aware: false + Async Capable: false Sort Key: - "(COALESCE((pg_catalog.sum(remote_scan.count_quantity))::bigint, '0'::bigint))" - "remote_scan.l_quantity" @@ -231,13 +248,17 @@ EXPLAIN (COSTS FALSE, FORMAT YAML) - Node Type: "Aggregate" Strategy: "Hashed" Partial Mode: "Simple" + Parent Relationship: "Outer" Parallel Aware: false + Async Capable: false Group Key: - "remote_scan.l_quantity" Plans: - Node Type: "Custom Scan" + Parent Relationship: "Outer" Custom Plan Provider: "Citus Adaptive" Parallel Aware: false + Async Capable: false Distributed Query: Job: Task Count: 16 @@ -250,11 +271,14 @@ EXPLAIN (COSTS FALSE, FORMAT YAML) Strategy: "Hashed" Partial Mode: "Simple" Parallel Aware: false + Async Capable: false Group Key: - "l_quantity" Plans: - Node Type: "Seq Scan" + Parent Relationship: "Outer" Parallel Aware: false + Async Capable: false Relation Name: "lineitem_mx_1220052" Alias: "lineitem_mx" @@ -528,11 +552,14 @@ EXPLAIN (COSTS FALSE, FORMAT JSON) "Strategy": "Plain", "Partial Mode": "Simple", "Parallel Aware": false, + "Async Capable": false, "Plans": [ { "Node Type": "Custom Scan", + "Parent Relationship": "Outer", "Custom Plan Provider": "Citus Adaptive", "Parallel Aware": false, + "Async Capable": false, "Distributed Query": { "Job": { "Task Count": 16, @@ -548,34 +575,45 @@ EXPLAIN (COSTS FALSE, FORMAT JSON) "Strategy": "Plain", "Partial Mode": "Simple", "Parallel Aware": false, + "Async Capable": false, "Plans": [ { "Node Type": "Hash Join", + "Parent Relationship": "Outer", "Parallel Aware": false, + "Async Capable": false, "Join Type": "Inner", "Inner Unique": false, "Hash Cond": "(lineitem_mx.l_orderkey = orders_mx.o_orderkey)", "Plans": [ { "Node Type": "Hash Join", + "Parent Relationship": "Outer", "Parallel Aware": false, + "Async Capable": false, "Join Type": "Inner", "Inner Unique": false, "Hash Cond": "(supplier_mx.s_suppkey = lineitem_mx.l_suppkey)", "Plans": [ { "Node Type": "Seq Scan", + "Parent Relationship": "Outer", "Parallel Aware": false, + "Async Capable": false, "Relation Name": "supplier_mx_1220087", "Alias": "supplier_mx" }, { "Node Type": "Hash", + "Parent Relationship": "Inner", "Parallel Aware": false, + "Async Capable": false, "Plans": [ { "Node Type": "Seq Scan", + "Parent Relationship": "Outer", "Parallel Aware": false, + "Async Capable": false, "Relation Name": "lineitem_mx_1220052", "Alias": "lineitem_mx" } @@ -585,28 +623,38 @@ EXPLAIN (COSTS FALSE, FORMAT JSON) }, { "Node Type": "Hash", + "Parent Relationship": "Inner", "Parallel Aware": false, + "Async Capable": false, "Plans": [ { "Node Type": "Hash Join", + "Parent Relationship": "Outer", "Parallel Aware": false, + "Async Capable": false, "Join Type": "Inner", "Inner Unique": false, "Hash Cond": "(customer_mx.c_custkey = orders_mx.o_custkey)", "Plans": [ { "Node Type": "Seq Scan", + "Parent Relationship": "Outer", "Parallel Aware": false, + "Async Capable": false, "Relation Name": "customer_mx_1220084", "Alias": "customer_mx" }, { "Node Type": "Hash", + "Parent Relationship": "Inner", "Parallel Aware": false, + "Async Capable": false, "Plans": [ { "Node Type": "Seq Scan", + "Parent Relationship": "Outer", "Parallel Aware": false, + "Async Capable": false, "Relation Name": "orders_mx_1220068", "Alias": "orders_mx" } @@ -653,11 +701,14 @@ EXPLAIN (COSTS FALSE, FORMAT XML) Plain Simple false + false Custom Scan + Outer Citus Adaptive false + false 16 @@ -673,34 +724,45 @@ EXPLAIN (COSTS FALSE, FORMAT XML) Plain Simple false + false Hash Join + Outer false + false Inner false (lineitem_mx.l_orderkey = orders_mx.o_orderkey) Hash Join + Outer false + false Inner false (supplier_mx.s_suppkey = lineitem_mx.l_suppkey) Seq Scan + Outer false + false supplier_mx_1220087 supplier_mx Hash + Inner false + false Seq Scan + Outer false + false lineitem_mx_1220052 lineitem_mx @@ -710,28 +772,38 @@ EXPLAIN (COSTS FALSE, FORMAT XML) Hash + Inner false + false Hash Join + Outer false + false Inner false (customer_mx.c_custkey = orders_mx.o_custkey) Seq Scan + Outer false + false customer_mx_1220084 customer_mx Hash + Inner false + false Seq Scan + Outer false + false orders_mx_1220068 orders_mx @@ -775,10 +847,13 @@ EXPLAIN (COSTS FALSE, FORMAT YAML) Strategy: "Plain" Partial Mode: "Simple" Parallel Aware: false + Async Capable: false Plans: - Node Type: "Custom Scan" + Parent Relationship: "Outer" Custom Plan Provider: "Citus Adaptive" Parallel Aware: false + Async Capable: false Distributed Query: Job: Task Count: 16 @@ -791,48 +866,69 @@ EXPLAIN (COSTS FALSE, FORMAT YAML) Strategy: "Plain" Partial Mode: "Simple" Parallel Aware: false + Async Capable: false Plans: - Node Type: "Hash Join" + Parent Relationship: "Outer" Parallel Aware: false + Async Capable: false Join Type: "Inner" Inner Unique: false Hash Cond: "(lineitem_mx.l_orderkey = orders_mx.o_orderkey)" Plans: - Node Type: "Hash Join" + Parent Relationship: "Outer" Parallel Aware: false + Async Capable: false Join Type: "Inner" Inner Unique: false Hash Cond: "(supplier_mx.s_suppkey = lineitem_mx.l_suppkey)" Plans: - Node Type: "Seq Scan" + Parent Relationship: "Outer" Parallel Aware: false + Async Capable: false Relation Name: "supplier_mx_1220087" Alias: "supplier_mx" - Node Type: "Hash" + Parent Relationship: "Inner" Parallel Aware: false + Async Capable: false Plans: - Node Type: "Seq Scan" + Parent Relationship: "Outer" Parallel Aware: false + Async Capable: false Relation Name: "lineitem_mx_1220052" Alias: "lineitem_mx" - Node Type: "Hash" + Parent Relationship: "Inner" Parallel Aware: false + Async Capable: false Plans: - Node Type: "Hash Join" + Parent Relationship: "Outer" Parallel Aware: false + Async Capable: false Join Type: "Inner" Inner Unique: false Hash Cond: "(customer_mx.c_custkey = orders_mx.o_custkey)" Plans: - Node Type: "Seq Scan" + Parent Relationship: "Outer" Parallel Aware: false + Async Capable: false Relation Name: "customer_mx_1220084" Alias: "customer_mx" - Node Type: "Hash" + Parent Relationship: "Inner" Parallel Aware: false + Async Capable: false Plans: - Node Type: "Seq Scan" + Parent Relationship: "Outer" Parallel Aware: false + Async Capable: false Relation Name: "orders_mx_1220068" Alias: "orders_mx" diff --git a/src/test/regress/expected/pg14.out b/src/test/regress/expected/pg14.out index e4f94c053..8483a2891 100644 --- a/src/test/regress/expected/pg14.out +++ b/src/test/regress/expected/pg14.out @@ -1,10 +1,3 @@ -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int >= 14 AS server_version_ge_14 -\gset -\if :server_version_ge_14 -\else -\q -\endif create schema pg14; set search_path to pg14; SET citus.shard_replication_factor TO 1; diff --git a/src/test/regress/expected/pg14_0.out b/src/test/regress/expected/pg14_0.out deleted file mode 100644 index cff095489..000000000 --- a/src/test/regress/expected/pg14_0.out +++ /dev/null @@ -1,6 +0,0 @@ -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int >= 14 AS server_version_ge_14 -\gset -\if :server_version_ge_14 -\else -\q diff --git a/src/test/regress/expected/sql_procedure.out b/src/test/regress/expected/sql_procedure.out index 9e3aae5c7..63802b354 100644 --- a/src/test/regress/expected/sql_procedure.out +++ b/src/test/regress/expected/sql_procedure.out @@ -37,7 +37,7 @@ CREATE PROCEDURE test_procedure_commit(tt_id int, tt_org_id int) LANGUAGE SQL AS COMMIT; $$; CALL test_procedure_commit(2,5); -ERROR: COMMIT is not allowed in a SQL function +ERROR: COMMIT is not allowed in an SQL function CONTEXT: SQL function "test_procedure_commit" during startup SELECT * FROM test_table ORDER BY 1, 2; id | org_id @@ -52,7 +52,7 @@ CREATE PROCEDURE test_procedure_rollback(tt_id int, tt_org_id int) LANGUAGE SQL COMMIT; $$; CALL test_procedure_rollback(2,15); -ERROR: ROLLBACK is not allowed in a SQL function +ERROR: ROLLBACK is not allowed in an SQL function CONTEXT: SQL function "test_procedure_rollback" during startup SELECT * FROM test_table ORDER BY 1, 2; id | org_id diff --git a/src/test/regress/expected/stat_statements.out b/src/test/regress/expected/stat_statements.out index 537bb4e9b..a3e2f673f 100644 --- a/src/test/regress/expected/stat_statements.out +++ b/src/test/regress/expected/stat_statements.out @@ -2,12 +2,7 @@ -- stat_statements -- -- tests citus_stat_statements functionality -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int >= 14 AS server_version_ge_14 -\gset -\if :server_version_ge_14 SET compute_query_id = 'on'; -\endif -- check if pg_stat_statements is available SELECT name FROM pg_available_extensions WHERE name = 'pg_stat_statements'; name @@ -72,11 +67,7 @@ select query, calls from citus_stat_statements(); insert into test values($1) | 1 (1 row) -\if :server_version_ge_14 SET compute_query_id = 'off'; -\else -set citus.stat_statements_track = 'none'; -\endif -- for pg >= 14, since compute_query_id is off, this insert -- shouldn't be tracked -- for pg < 14, we disable it explicitly so that we don't need @@ -88,11 +79,7 @@ select query, calls from citus_stat_statements(); insert into test values($1) | 1 (1 row) -\if :server_version_ge_14 SET compute_query_id = 'on'; -\else -RESET citus.stat_statements_track; -\endif SELECT citus_stat_statements_reset(); citus_stat_statements_reset --------------------------------------------------------------------- @@ -646,6 +633,4 @@ CONTEXT: PL/pgSQL function citus_stat_statements() line XX at RAISE -- drop created tables DROP TABLE stat_test_text, stat_test_bigint, stat_test_bigint_other, stat_test_reference; DROP FUNCTION normalize_query_string(text); -\if :server_version_ge_14 SET compute_query_id = 'off'; -\endif diff --git a/src/test/regress/expected/tableam.out b/src/test/regress/expected/tableam.out index 47f4c241d..8e6fe5205 100644 --- a/src/test/regress/expected/tableam.out +++ b/src/test/regress/expected/tableam.out @@ -114,7 +114,7 @@ delete from test_ref; ERROR: fake_tuple_delete not implemented CONTEXT: while executing command on localhost:xxxxx update test_ref set a=2; -ERROR: fake_tuple_update not implemented +ERROR: fake_fetch_row_version not implemented CONTEXT: while executing command on localhost:xxxxx RESET client_min_messages; -- ddl events should include "USING fake_am" diff --git a/src/test/regress/expected/window_functions.out b/src/test/regress/expected/window_functions.out index 6f30a49e3..d4718c4dd 100644 --- a/src/test/regress/expected/window_functions.out +++ b/src/test/regress/expected/window_functions.out @@ -3,8 +3,6 @@ -- =================================================================== -- test top level window functions that are pushdownable -- =================================================================== --- This test file has an alternative output because of use of --- incremental sort in some explain outputs in PG13 -- -- a very simple window function with an aggregate and a window function -- distribution column is on the partition by clause diff --git a/src/test/regress/expected/window_functions_0.out b/src/test/regress/expected/window_functions_0.out deleted file mode 100644 index c5a132301..000000000 --- a/src/test/regress/expected/window_functions_0.out +++ /dev/null @@ -1,1657 +0,0 @@ --- --- WINDOW_FUNCTIONS --- =================================================================== --- test top level window functions that are pushdownable --- =================================================================== --- This test file has an alternative output because of use of --- incremental sort in some explain outputs in PG13 --- --- a very simple window function with an aggregate and a window function --- distribution column is on the partition by clause -SELECT - user_id, COUNT(*) OVER (PARTITION BY user_id), - rank() OVER (PARTITION BY user_id) -FROM - users_table -ORDER BY - 1 DESC, 2 DESC, 3 DESC -LIMIT 5; - user_id | count | rank ---------------------------------------------------------------------- - 6 | 10 | 1 - 6 | 10 | 1 - 6 | 10 | 1 - 6 | 10 | 1 - 6 | 10 | 1 -(5 rows) - --- a more complicated window clause, including an aggregate --- in both the window clause and the target entry -SELECT - user_id, avg(avg(value_3)) OVER (PARTITION BY user_id, MIN(value_2)) -FROM - users_table -GROUP BY - 1 -ORDER BY - 2 DESC NULLS LAST, 1 DESC; - user_id | avg ---------------------------------------------------------------------- - 2 | 3 - 4 | 2.82608695652174 - 3 | 2.70588235294118 - 6 | 2.6 - 1 | 2.57142857142857 - 5 | 2.46153846153846 -(6 rows) - --- window clause operates on the results of a subquery -SELECT - user_id, max(value_1) OVER (PARTITION BY user_id, MIN(value_2)) -FROM ( - SELECT - DISTINCT us.user_id, us.value_2, value_1, random() as r1 - FROM - users_table as us, events_table - WHERE - us.user_id = events_table.user_id AND event_type IN (1,2) - ORDER BY - user_id, value_2 - ) s -GROUP BY - 1, value_1 -ORDER BY - 2 DESC, 1; - user_id | max ---------------------------------------------------------------------- - 1 | 5 - 3 | 5 - 3 | 5 - 4 | 5 - 5 | 5 - 5 | 5 - 6 | 5 - 6 | 5 - 1 | 4 - 2 | 4 - 3 | 4 - 3 | 4 - 3 | 4 - 4 | 4 - 4 | 4 - 5 | 4 - 5 | 4 - 1 | 3 - 2 | 3 - 2 | 3 - 2 | 3 - 6 | 3 - 2 | 2 - 4 | 2 - 4 | 2 - 4 | 2 - 6 | 2 - 1 | 1 - 3 | 1 - 5 | 1 - 6 | 1 - 5 | 0 -(32 rows) - --- window function operates on the results of --- a join --- we also want to verify that this doesn't crash --- when the logging level is DEBUG4 -SET log_min_messages TO DEBUG4; -SELECT - us.user_id, - SUM(us.value_1) OVER (PARTITION BY us.user_id) -FROM - users_table us - JOIN - events_table ev - ON (us.user_id = ev.user_id) -GROUP BY - 1, - value_1 -ORDER BY - 1, - 2 -LIMIT 5; - user_id | sum ---------------------------------------------------------------------- - 1 | 13 - 1 | 13 - 1 | 13 - 1 | 13 - 2 | 10 -(5 rows) - --- the same query, but this time join with an alias -SELECT - user_id, value_1, SUM(j.value_1) OVER (PARTITION BY j.user_id) -FROM - (users_table us - JOIN - events_table ev - USING (user_id ) - ) j -GROUP BY - user_id, - value_1 -ORDER BY - 3 DESC, 2 DESC, 1 DESC -LIMIT 5; - user_id | value_1 | sum ---------------------------------------------------------------------- - 5 | 5 | 15 - 4 | 5 | 15 - 3 | 5 | 15 - 5 | 4 | 15 - 4 | 4 | 15 -(5 rows) - --- querying views that have window functions should be ok -CREATE VIEW window_view AS -SELECT - DISTINCT user_id, rank() OVER (PARTITION BY user_id ORDER BY value_1) -FROM - users_table -GROUP BY - user_id, value_1 -HAVING count(*) > 1; --- Window function in View works -SELECT * -FROM - window_view -ORDER BY - 2 DESC, 1 -LIMIT 10; - user_id | rank ---------------------------------------------------------------------- - 5 | 6 - 2 | 5 - 4 | 5 - 5 | 5 - 2 | 4 - 3 | 4 - 4 | 4 - 5 | 4 - 6 | 4 - 2 | 3 -(10 rows) - --- the other way around also should work fine --- query a view using window functions -CREATE VIEW users_view AS SELECT * FROM users_table; -SELECT - DISTINCT user_id, rank() OVER (PARTITION BY user_id ORDER BY value_1) -FROM - users_view -GROUP BY - user_id, value_1 -HAVING count(*) > 4 -ORDER BY - 2 DESC, 1; - user_id | rank ---------------------------------------------------------------------- - 4 | 2 - 5 | 2 - 2 | 1 - 3 | 1 - 4 | 1 - 5 | 1 -(6 rows) - -DROP VIEW users_view, window_view; --- window functions along with subquery in HAVING -SELECT - user_id, count (user_id) OVER (PARTITION BY user_id) -FROM - users_table -GROUP BY - user_id HAVING avg(value_1) < (SELECT min(k_no) FROM users_ref_test_table) -ORDER BY 1 DESC,2 DESC -LIMIT 1; - user_id | count ---------------------------------------------------------------------- - 6 | 1 -(1 row) - --- window function uses columns from two different tables -SELECT - DISTINCT ON (events_table.user_id, rnk) events_table.user_id, rank() OVER my_win AS rnk -FROM - events_table, users_table -WHERE - users_table.user_id = events_table.user_id -WINDOW - my_win AS (PARTITION BY events_table.user_id, users_table.value_1 ORDER BY events_table.time DESC) -ORDER BY - rnk DESC, 1 DESC -LIMIT 10; - user_id | rnk ---------------------------------------------------------------------- - 3 | 121 - 5 | 118 - 2 | 116 - 3 | 115 - 4 | 113 - 2 | 111 - 5 | 109 - 3 | 109 - 4 | 106 - 2 | 106 -(10 rows) - --- the same query with reference table column is also on the partition by clause -SELECT - DISTINCT ON (events_table.user_id, rnk) events_table.user_id, rank() OVER my_win AS rnk -FROM - events_table, users_ref_test_table uref -WHERE - uref.id = events_table.user_id -WINDOW - my_win AS (PARTITION BY events_table.user_id, uref.k_no ORDER BY events_table.time DESC) -ORDER BY - rnk DESC, 1 DESC -LIMIT 10; - user_id | rnk ---------------------------------------------------------------------- - 2 | 24 - 2 | 23 - 2 | 22 - 3 | 21 - 2 | 21 - 3 | 20 - 2 | 20 - 3 | 19 - 2 | 19 - 3 | 18 -(10 rows) - --- similar query with no distribution column on the partition by clause -SELECT - DISTINCT ON (events_table.user_id, rnk) events_table.user_id, rank() OVER my_win AS rnk -FROM - events_table, users_ref_test_table uref -WHERE - uref.id = events_table.user_id -WINDOW - my_win AS (PARTITION BY events_table.value_2, uref.k_no ORDER BY events_table.time DESC) -ORDER BY - rnk DESC, 1 DESC -LIMIT 10; - user_id | rnk ---------------------------------------------------------------------- - 3 | 7 - 2 | 7 - 3 | 6 - 2 | 6 - 4 | 5 - 3 | 5 - 2 | 5 - 1 | 5 - 6 | 4 - 5 | 4 -(10 rows) - --- ORDER BY in the window function is an aggregate -SELECT - user_id, rank() OVER my_win as rnk, avg(value_2) as avg_val_2 -FROM - events_table -GROUP BY - user_id, date_trunc('day', time) -WINDOW - my_win AS (PARTITION BY user_id ORDER BY avg(event_type) DESC) -ORDER BY - 3 DESC, 2 DESC, 1 DESC; - user_id | rnk | avg_val_2 ---------------------------------------------------------------------- - 1 | 1 | 3.3750000000000000 - 3 | 2 | 3.1666666666666667 - 5 | 1 | 2.6666666666666667 - 6 | 1 | 2.5000000000000000 - 4 | 1 | 2.5000000000000000 - 2 | 1 | 2.4736842105263158 - 4 | 2 | 2.4000000000000000 - 1 | 2 | 2.1428571428571429 - 5 | 2 | 2.0909090909090909 - 6 | 2 | 2.0000000000000000 - 2 | 2 | 2.0000000000000000 - 3 | 1 | 1.8000000000000000 -(12 rows) - --- lets push the limits of writing complex expressions aling with the window functions -SELECT - COUNT(*) OVER (PARTITION BY user_id, user_id + 1), - rank() OVER (PARTITION BY user_id) as cnt1, - COUNT(*) OVER (PARTITION BY user_id, abs(value_1 - value_2)) as cnt2, - date_trunc('min', lag(time) OVER (PARTITION BY user_id ORDER BY time)) as datee, - rank() OVER my_win as rnnk, - avg(CASE - WHEN user_id > 4 - THEN value_1 - ELSE value_2 - END) FILTER (WHERE user_id > 2) OVER my_win_2 as filtered_count, - sum(user_id * (5.0 / (value_1 + value_2 + 0.1)) * value_3) FILTER (WHERE value_1::text LIKE '%1%') OVER my_win_4 as cnt_with_filter_2 -FROM - users_table -WINDOW - my_win AS (PARTITION BY user_id, (value_1%3)::int ORDER BY time DESC), - my_win_2 AS (PARTITION BY user_id, (value_1)::int ORDER BY time DESC), - my_win_3 AS (PARTITION BY user_id, date_trunc('min', time)), - my_win_4 AS (my_win_3 ORDER BY value_2, value_3) -ORDER BY - cnt_with_filter_2 DESC NULLS LAST, filtered_count DESC NULLS LAST, datee DESC NULLS LAST, rnnk DESC, cnt2 DESC, cnt1 DESC, user_id DESC -LIMIT 5; - count | cnt1 | cnt2 | datee | rnnk | filtered_count | cnt_with_filter_2 ---------------------------------------------------------------------- - 23 | 1 | 7 | Thu Nov 23 02:14:00 2017 | 6 | 0.00000000000000000000 | 72.7272727272727 - 10 | 1 | 3 | Wed Nov 22 23:01:00 2017 | 1 | 1.00000000000000000000 | 57.1428571428571 - 17 | 1 | 5 | Wed Nov 22 23:24:00 2017 | 8 | 3.0000000000000000 | 28.5714285714286 - 17 | 1 | 5 | | 10 | 2.6666666666666667 | 28.5714285714286 - 17 | 1 | 5 | Thu Nov 23 00:15:00 2017 | 7 | 3.6666666666666667 | 24.1935483870968 -(5 rows) - --- some tests with GROUP BY along with PARTITION BY -SELECT - user_id, - rank() OVER my_win as my_rank, - avg(avg(event_type)) OVER my_win_2 as avg, - max(time) as mx_time -FROM - events_table -GROUP BY - user_id, - value_2 -WINDOW - my_win AS (PARTITION BY user_id, max(event_type) ORDER BY count(*) DESC), - my_win_2 AS (PARTITION BY user_id, avg(user_id) ORDER BY count(*) DESC) -ORDER BY - avg DESC, - mx_time DESC, - my_rank DESC, - user_id DESC; - user_id | my_rank | avg | mx_time ---------------------------------------------------------------------- - 6 | 1 | 3.0000000000000000 | Thu Nov 23 14:00:13.20013 2017 - 6 | 2 | 3.0000000000000000 | Thu Nov 23 11:16:13.106691 2017 - 6 | 1 | 3.0000000000000000 | Thu Nov 23 07:27:32.822068 2017 - 3 | 1 | 2.9857142857142857 | Thu Nov 23 16:31:56.219594 2017 - 4 | 2 | 2.9555555555555556 | Thu Nov 23 14:19:25.765876 2017 - 4 | 1 | 2.9555555555555556 | Thu Nov 23 08:36:53.871919 2017 - 1 | 4 | 2.8633333333333333 | Wed Nov 22 21:06:57.457147 2017 - 1 | 1 | 2.8250000000000000 | Thu Nov 23 21:54:46.924477 2017 - 2 | 2 | 2.7738095238095238 | Thu Nov 23 13:27:37.441959 2017 - 1 | 2 | 2.7722222222222222 | Thu Nov 23 09:23:30.994345 2017 - 3 | 1 | 2.7682539682539682 | Thu Nov 23 01:17:49.040685 2017 - 2 | 1 | 2.7142857142857143 | Thu Nov 23 15:58:49.273421 2017 - 1 | 3 | 2.5791666666666667 | Thu Nov 23 11:09:38.074595 2017 - 3 | 1 | 2.5714285714285714 | Thu Nov 23 16:44:41.903713 2017 - 2 | 1 | 2.5158730158730159 | Thu Nov 23 14:02:47.738901 2017 - 4 | 1 | 2.47777777777777778333 | Thu Nov 23 16:20:33.264457 2017 - 4 | 3 | 2.47777777777777778333 | Thu Nov 23 08:14:18.231273 2017 - 4 | 3 | 2.47777777777777778333 | Thu Nov 23 07:32:45.521278 2017 - 1 | 1 | 2.4000000000000000 | Thu Nov 23 10:23:27.617726 2017 - 2 | 1 | 2.3869047619047619 | Thu Nov 23 17:26:14.563216 2017 - 3 | 1 | 2.3841269841269841 | Thu Nov 23 18:08:26.550729 2017 - 3 | 1 | 2.3841269841269841 | Thu Nov 23 09:38:45.338008 2017 - 3 | 2 | 2.3841269841269841 | Thu Nov 23 06:44:50.887182 2017 - 2 | 2 | 2.3095238095238095 | Thu Nov 23 04:05:16.217731 2017 - 5 | 2 | 2.3000000000000000 | Thu Nov 23 14:28:51.833214 2017 - 5 | 2 | 2.3000000000000000 | Thu Nov 23 14:23:09.889786 2017 - 4 | 1 | 2.2000000000000000 | Thu Nov 23 18:10:21.338399 2017 - 2 | 1 | 2.09126984126984126667 | Thu Nov 23 03:35:04.321504 2017 - 5 | 1 | 2.0000000000000000 | Thu Nov 23 16:11:02.929469 2017 - 5 | 1 | 2.0000000000000000 | Thu Nov 23 14:40:40.467511 2017 - 5 | 1 | 2.0000000000000000 | Thu Nov 23 13:26:45.571108 2017 -(31 rows) - --- test for range and rows mode and different window functions --- mostly to make sure that deparsing works fine -SELECT - user_id, - rank() OVER (PARTITION BY user_id ROWS BETWEEN - UNBOUNDED PRECEDING AND CURRENT ROW), - dense_rank() OVER (PARTITION BY user_id RANGE BETWEEN - UNBOUNDED PRECEDING AND CURRENT ROW), - CUME_DIST() OVER (PARTITION BY user_id RANGE BETWEEN - UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING), - PERCENT_RANK() OVER (PARTITION BY user_id ORDER BY avg(value_1) RANGE BETWEEN - UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) -FROM - users_table -GROUP BY - 1 -ORDER BY - 4 DESC,3 DESC,2 DESC ,1 DESC; - user_id | rank | dense_rank | cume_dist | percent_rank ---------------------------------------------------------------------- - 6 | 1 | 1 | 1 | 0 - 5 | 1 | 1 | 1 | 0 - 4 | 1 | 1 | 1 | 0 - 3 | 1 | 1 | 1 | 0 - 2 | 1 | 1 | 1 | 0 - 1 | 1 | 1 | 1 | 0 -(6 rows) - --- test exclude supported -SELECT - user_id, - value_1, - array_agg(value_1) OVER (PARTITION BY user_id ORDER BY value_1 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), - array_agg(value_1) OVER (PARTITION BY user_id ORDER BY value_1 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE CURRENT ROW) -FROM - users_table -WHERE - user_id > 2 AND user_id < 6 -ORDER BY - user_id, value_1, 3, 4; - user_id | value_1 | array_agg | array_agg ---------------------------------------------------------------------- - 3 | 0 | {0} | - 3 | 1 | {0,1,1,1,1,1,1} | {0,1,1,1,1,1} - 3 | 1 | {0,1,1,1,1,1,1} | {0,1,1,1,1,1} - 3 | 1 | {0,1,1,1,1,1,1} | {0,1,1,1,1,1} - 3 | 1 | {0,1,1,1,1,1,1} | {0,1,1,1,1,1} - 3 | 1 | {0,1,1,1,1,1,1} | {0,1,1,1,1,1} - 3 | 1 | {0,1,1,1,1,1,1} | {0,1,1,1,1,1} - 3 | 2 | {0,1,1,1,1,1,1,2,2} | {0,1,1,1,1,1,1,2} - 3 | 2 | {0,1,1,1,1,1,1,2,2} | {0,1,1,1,1,1,1,2} - 3 | 3 | {0,1,1,1,1,1,1,2,2,3,3,3} | {0,1,1,1,1,1,1,2,2,3,3} - 3 | 3 | {0,1,1,1,1,1,1,2,2,3,3,3} | {0,1,1,1,1,1,1,2,2,3,3} - 3 | 3 | {0,1,1,1,1,1,1,2,2,3,3,3} | {0,1,1,1,1,1,1,2,2,3,3} - 3 | 4 | {0,1,1,1,1,1,1,2,2,3,3,3,4,4,4,4} | {0,1,1,1,1,1,1,2,2,3,3,3,4,4,4} - 3 | 4 | {0,1,1,1,1,1,1,2,2,3,3,3,4,4,4,4} | {0,1,1,1,1,1,1,2,2,3,3,3,4,4,4} - 3 | 4 | {0,1,1,1,1,1,1,2,2,3,3,3,4,4,4,4} | {0,1,1,1,1,1,1,2,2,3,3,3,4,4,4} - 3 | 4 | {0,1,1,1,1,1,1,2,2,3,3,3,4,4,4,4} | {0,1,1,1,1,1,1,2,2,3,3,3,4,4,4} - 3 | 5 | {0,1,1,1,1,1,1,2,2,3,3,3,4,4,4,4,5} | {0,1,1,1,1,1,1,2,2,3,3,3,4,4,4,4} - 4 | 0 | {0,0,0,0} | {0,0,0} - 4 | 0 | {0,0,0,0} | {0,0,0} - 4 | 0 | {0,0,0,0} | {0,0,0} - 4 | 0 | {0,0,0,0} | {0,0,0} - 4 | 1 | {0,0,0,0,1} | {0,0,0,0} - 4 | 2 | {0,0,0,0,1,2,2,2} | {0,0,0,0,1,2,2} - 4 | 2 | {0,0,0,0,1,2,2,2} | {0,0,0,0,1,2,2} - 4 | 2 | {0,0,0,0,1,2,2,2} | {0,0,0,0,1,2,2} - 4 | 3 | {0,0,0,0,1,2,2,2,3,3,3,3,3,3} | {0,0,0,0,1,2,2,2,3,3,3,3,3} - 4 | 3 | {0,0,0,0,1,2,2,2,3,3,3,3,3,3} | {0,0,0,0,1,2,2,2,3,3,3,3,3} - 4 | 3 | {0,0,0,0,1,2,2,2,3,3,3,3,3,3} | {0,0,0,0,1,2,2,2,3,3,3,3,3} - 4 | 3 | {0,0,0,0,1,2,2,2,3,3,3,3,3,3} | {0,0,0,0,1,2,2,2,3,3,3,3,3} - 4 | 3 | {0,0,0,0,1,2,2,2,3,3,3,3,3,3} | {0,0,0,0,1,2,2,2,3,3,3,3,3} - 4 | 3 | {0,0,0,0,1,2,2,2,3,3,3,3,3,3} | {0,0,0,0,1,2,2,2,3,3,3,3,3} - 4 | 4 | {0,0,0,0,1,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4,4} | {0,0,0,0,1,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4} - 4 | 4 | {0,0,0,0,1,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4,4} | {0,0,0,0,1,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4} - 4 | 4 | {0,0,0,0,1,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4,4} | {0,0,0,0,1,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4} - 4 | 4 | {0,0,0,0,1,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4,4} | {0,0,0,0,1,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4} - 4 | 4 | {0,0,0,0,1,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4,4} | {0,0,0,0,1,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4} - 4 | 4 | {0,0,0,0,1,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4,4} | {0,0,0,0,1,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4} - 4 | 4 | {0,0,0,0,1,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4,4} | {0,0,0,0,1,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4} - 4 | 5 | {0,0,0,0,1,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4,4,5,5} | {0,0,0,0,1,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4,4,5} - 4 | 5 | {0,0,0,0,1,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4,4,5,5} | {0,0,0,0,1,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4,4,5} - 5 | 0 | {0,0} | {0} - 5 | 0 | {0,0} | {0} - 5 | 1 | {0,0,1,1,1} | {0,0,1,1} - 5 | 1 | {0,0,1,1,1} | {0,0,1,1} - 5 | 1 | {0,0,1,1,1} | {0,0,1,1} - 5 | 2 | {0,0,1,1,1,2,2,2,2,2,2} | {0,0,1,1,1,2,2,2,2,2} - 5 | 2 | {0,0,1,1,1,2,2,2,2,2,2} | {0,0,1,1,1,2,2,2,2,2} - 5 | 2 | {0,0,1,1,1,2,2,2,2,2,2} | {0,0,1,1,1,2,2,2,2,2} - 5 | 2 | {0,0,1,1,1,2,2,2,2,2,2} | {0,0,1,1,1,2,2,2,2,2} - 5 | 2 | {0,0,1,1,1,2,2,2,2,2,2} | {0,0,1,1,1,2,2,2,2,2} - 5 | 2 | {0,0,1,1,1,2,2,2,2,2,2} | {0,0,1,1,1,2,2,2,2,2} - 5 | 3 | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3} | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3} - 5 | 3 | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3} | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3} - 5 | 3 | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3} | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3} - 5 | 3 | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3} | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3} - 5 | 3 | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3} | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3} - 5 | 3 | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3} | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3} - 5 | 3 | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3} | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3} - 5 | 3 | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3} | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3} - 5 | 3 | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3} | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3} - 5 | 4 | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,4,4,4} | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,4,4} - 5 | 4 | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,4,4,4} | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,4,4} - 5 | 4 | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,4,4,4} | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,4,4} - 5 | 5 | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,4,4,4,5,5,5} | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,4,4,4,5,5} - 5 | 5 | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,4,4,4,5,5,5} | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,4,4,4,5,5} - 5 | 5 | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,4,4,4,5,5,5} | {0,0,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,4,4,4,5,5} -(66 rows) - --- test preceding and following on RANGE window -SELECT - user_id, - value_1, - array_agg(value_1) OVER range_window, - array_agg(value_1) OVER range_window_exclude -FROM - users_table -WHERE - user_id > 2 AND user_id < 6 -WINDOW - range_window as (PARTITION BY user_id ORDER BY value_1 RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING), - range_window_exclude as (PARTITION BY user_id ORDER BY value_1 RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) -ORDER BY - user_id, value_1, 3, 4; - user_id | value_1 | array_agg | array_agg ---------------------------------------------------------------------- - 3 | 0 | {0,1,1,1,1,1,1} | {1,1,1,1,1,1} - 3 | 1 | {0,1,1,1,1,1,1,2,2} | {0,1,1,1,1,1,2,2} - 3 | 1 | {0,1,1,1,1,1,1,2,2} | {0,1,1,1,1,1,2,2} - 3 | 1 | {0,1,1,1,1,1,1,2,2} | {0,1,1,1,1,1,2,2} - 3 | 1 | {0,1,1,1,1,1,1,2,2} | {0,1,1,1,1,1,2,2} - 3 | 1 | {0,1,1,1,1,1,1,2,2} | {0,1,1,1,1,1,2,2} - 3 | 1 | {0,1,1,1,1,1,1,2,2} | {0,1,1,1,1,1,2,2} - 3 | 2 | {1,1,1,1,1,1,2,2,3,3,3} | {1,1,1,1,1,1,2,3,3,3} - 3 | 2 | {1,1,1,1,1,1,2,2,3,3,3} | {1,1,1,1,1,1,2,3,3,3} - 3 | 3 | {2,2,3,3,3,4,4,4,4} | {2,2,3,3,4,4,4,4} - 3 | 3 | {2,2,3,3,3,4,4,4,4} | {2,2,3,3,4,4,4,4} - 3 | 3 | {2,2,3,3,3,4,4,4,4} | {2,2,3,3,4,4,4,4} - 3 | 4 | {3,3,3,4,4,4,4,5} | {3,3,3,4,4,4,5} - 3 | 4 | {3,3,3,4,4,4,4,5} | {3,3,3,4,4,4,5} - 3 | 4 | {3,3,3,4,4,4,4,5} | {3,3,3,4,4,4,5} - 3 | 4 | {3,3,3,4,4,4,4,5} | {3,3,3,4,4,4,5} - 3 | 5 | {4,4,4,4,5} | {4,4,4,4} - 4 | 0 | {0,0,0,0,1} | {0,0,0,1} - 4 | 0 | {0,0,0,0,1} | {0,0,0,1} - 4 | 0 | {0,0,0,0,1} | {0,0,0,1} - 4 | 0 | {0,0,0,0,1} | {0,0,0,1} - 4 | 1 | {0,0,0,0,1,2,2,2} | {0,0,0,0,2,2,2} - 4 | 2 | {1,2,2,2,3,3,3,3,3,3} | {1,2,2,3,3,3,3,3,3} - 4 | 2 | {1,2,2,2,3,3,3,3,3,3} | {1,2,2,3,3,3,3,3,3} - 4 | 2 | {1,2,2,2,3,3,3,3,3,3} | {1,2,2,3,3,3,3,3,3} - 4 | 3 | {2,2,2,3,3,3,3,3,3,4,4,4,4,4,4,4} | {2,2,2,3,3,3,3,3,4,4,4,4,4,4,4} - 4 | 3 | {2,2,2,3,3,3,3,3,3,4,4,4,4,4,4,4} | {2,2,2,3,3,3,3,3,4,4,4,4,4,4,4} - 4 | 3 | {2,2,2,3,3,3,3,3,3,4,4,4,4,4,4,4} | {2,2,2,3,3,3,3,3,4,4,4,4,4,4,4} - 4 | 3 | {2,2,2,3,3,3,3,3,3,4,4,4,4,4,4,4} | {2,2,2,3,3,3,3,3,4,4,4,4,4,4,4} - 4 | 3 | {2,2,2,3,3,3,3,3,3,4,4,4,4,4,4,4} | {2,2,2,3,3,3,3,3,4,4,4,4,4,4,4} - 4 | 3 | {2,2,2,3,3,3,3,3,3,4,4,4,4,4,4,4} | {2,2,2,3,3,3,3,3,4,4,4,4,4,4,4} - 4 | 4 | {3,3,3,3,3,3,4,4,4,4,4,4,4,5,5} | {3,3,3,3,3,3,4,4,4,4,4,4,5,5} - 4 | 4 | {3,3,3,3,3,3,4,4,4,4,4,4,4,5,5} | {3,3,3,3,3,3,4,4,4,4,4,4,5,5} - 4 | 4 | {3,3,3,3,3,3,4,4,4,4,4,4,4,5,5} | {3,3,3,3,3,3,4,4,4,4,4,4,5,5} - 4 | 4 | {3,3,3,3,3,3,4,4,4,4,4,4,4,5,5} | {3,3,3,3,3,3,4,4,4,4,4,4,5,5} - 4 | 4 | {3,3,3,3,3,3,4,4,4,4,4,4,4,5,5} | {3,3,3,3,3,3,4,4,4,4,4,4,5,5} - 4 | 4 | {3,3,3,3,3,3,4,4,4,4,4,4,4,5,5} | {3,3,3,3,3,3,4,4,4,4,4,4,5,5} - 4 | 4 | {3,3,3,3,3,3,4,4,4,4,4,4,4,5,5} | {3,3,3,3,3,3,4,4,4,4,4,4,5,5} - 4 | 5 | {4,4,4,4,4,4,4,5,5} | {4,4,4,4,4,4,4,5} - 4 | 5 | {4,4,4,4,4,4,4,5,5} | {4,4,4,4,4,4,4,5} - 5 | 0 | {0,0,1,1,1} | {0,1,1,1} - 5 | 0 | {0,0,1,1,1} | {0,1,1,1} - 5 | 1 | {0,0,1,1,1,2,2,2,2,2,2} | {0,0,1,1,2,2,2,2,2,2} - 5 | 1 | {0,0,1,1,1,2,2,2,2,2,2} | {0,0,1,1,2,2,2,2,2,2} - 5 | 1 | {0,0,1,1,1,2,2,2,2,2,2} | {0,0,1,1,2,2,2,2,2,2} - 5 | 2 | {1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3} | {1,1,1,2,2,2,2,2,3,3,3,3,3,3,3,3,3} - 5 | 2 | {1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3} | {1,1,1,2,2,2,2,2,3,3,3,3,3,3,3,3,3} - 5 | 2 | {1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3} | {1,1,1,2,2,2,2,2,3,3,3,3,3,3,3,3,3} - 5 | 2 | {1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3} | {1,1,1,2,2,2,2,2,3,3,3,3,3,3,3,3,3} - 5 | 2 | {1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3} | {1,1,1,2,2,2,2,2,3,3,3,3,3,3,3,3,3} - 5 | 2 | {1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3} | {1,1,1,2,2,2,2,2,3,3,3,3,3,3,3,3,3} - 5 | 3 | {2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,4,4,4} | {2,2,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4} - 5 | 3 | {2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,4,4,4} | {2,2,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4} - 5 | 3 | {2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,4,4,4} | {2,2,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4} - 5 | 3 | {2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,4,4,4} | {2,2,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4} - 5 | 3 | {2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,4,4,4} | {2,2,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4} - 5 | 3 | {2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,4,4,4} | {2,2,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4} - 5 | 3 | {2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,4,4,4} | {2,2,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4} - 5 | 3 | {2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,4,4,4} | {2,2,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4} - 5 | 3 | {2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,4,4,4} | {2,2,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4} - 5 | 4 | {3,3,3,3,3,3,3,3,3,4,4,4,5,5,5} | {3,3,3,3,3,3,3,3,3,4,4,5,5,5} - 5 | 4 | {3,3,3,3,3,3,3,3,3,4,4,4,5,5,5} | {3,3,3,3,3,3,3,3,3,4,4,5,5,5} - 5 | 4 | {3,3,3,3,3,3,3,3,3,4,4,4,5,5,5} | {3,3,3,3,3,3,3,3,3,4,4,5,5,5} - 5 | 5 | {4,4,4,5,5,5} | {4,4,4,5,5} - 5 | 5 | {4,4,4,5,5,5} | {4,4,4,5,5} - 5 | 5 | {4,4,4,5,5,5} | {4,4,4,5,5} -(66 rows) - --- test preceding and following on ROW window -SELECT - user_id, - value_1, - array_agg(value_1) OVER row_window, - array_agg(value_1) OVER row_window_exclude -FROM - users_table -WHERE - user_id > 2 and user_id < 6 -WINDOW - row_window as (PARTITION BY user_id ORDER BY value_1 ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING), - row_window_exclude as (PARTITION BY user_id ORDER BY value_1 ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) -ORDER BY - user_id, value_1, 3, 4; - user_id | value_1 | array_agg | array_agg ---------------------------------------------------------------------- - 3 | 0 | {0,1} | {1} - 3 | 1 | {0,1,1} | {0,1} - 3 | 1 | {1,1,1} | {1,1} - 3 | 1 | {1,1,1} | {1,1} - 3 | 1 | {1,1,1} | {1,1} - 3 | 1 | {1,1,1} | {1,1} - 3 | 1 | {1,1,2} | {1,2} - 3 | 2 | {1,2,2} | {1,2} - 3 | 2 | {2,2,3} | {2,3} - 3 | 3 | {2,3,3} | {2,3} - 3 | 3 | {3,3,3} | {3,3} - 3 | 3 | {3,3,4} | {3,4} - 3 | 4 | {3,4,4} | {3,4} - 3 | 4 | {4,4,4} | {4,4} - 3 | 4 | {4,4,4} | {4,4} - 3 | 4 | {4,4,5} | {4,5} - 3 | 5 | {4,5} | {4} - 4 | 0 | {0,0} | {0} - 4 | 0 | {0,0,0} | {0,0} - 4 | 0 | {0,0,0} | {0,0} - 4 | 0 | {0,0,1} | {0,1} - 4 | 1 | {0,1,2} | {0,2} - 4 | 2 | {1,2,2} | {1,2} - 4 | 2 | {2,2,2} | {2,2} - 4 | 2 | {2,2,3} | {2,3} - 4 | 3 | {2,3,3} | {2,3} - 4 | 3 | {3,3,3} | {3,3} - 4 | 3 | {3,3,3} | {3,3} - 4 | 3 | {3,3,3} | {3,3} - 4 | 3 | {3,3,3} | {3,3} - 4 | 3 | {3,3,4} | {3,4} - 4 | 4 | {3,4,4} | {3,4} - 4 | 4 | {4,4,4} | {4,4} - 4 | 4 | {4,4,4} | {4,4} - 4 | 4 | {4,4,4} | {4,4} - 4 | 4 | {4,4,4} | {4,4} - 4 | 4 | {4,4,4} | {4,4} - 4 | 4 | {4,4,5} | {4,5} - 4 | 5 | {4,5,5} | {4,5} - 4 | 5 | {5,5} | {5} - 5 | 0 | {0,0} | {0} - 5 | 0 | {0,0,1} | {0,1} - 5 | 1 | {0,1,1} | {0,1} - 5 | 1 | {1,1,1} | {1,1} - 5 | 1 | {1,1,2} | {1,2} - 5 | 2 | {1,2,2} | {1,2} - 5 | 2 | {2,2,2} | {2,2} - 5 | 2 | {2,2,2} | {2,2} - 5 | 2 | {2,2,2} | {2,2} - 5 | 2 | {2,2,2} | {2,2} - 5 | 2 | {2,2,3} | {2,3} - 5 | 3 | {2,3,3} | {2,3} - 5 | 3 | {3,3,3} | {3,3} - 5 | 3 | {3,3,3} | {3,3} - 5 | 3 | {3,3,3} | {3,3} - 5 | 3 | {3,3,3} | {3,3} - 5 | 3 | {3,3,3} | {3,3} - 5 | 3 | {3,3,3} | {3,3} - 5 | 3 | {3,3,3} | {3,3} - 5 | 3 | {3,3,4} | {3,4} - 5 | 4 | {3,4,4} | {3,4} - 5 | 4 | {4,4,4} | {4,4} - 5 | 4 | {4,4,5} | {4,5} - 5 | 5 | {4,5,5} | {4,5} - 5 | 5 | {5,5} | {5} - 5 | 5 | {5,5,5} | {5,5} -(66 rows) - --- repeat above 3 tests without grouping by distribution column -SELECT - value_2, - rank() OVER (PARTITION BY value_2 ROWS BETWEEN - UNBOUNDED PRECEDING AND CURRENT ROW), - dense_rank() OVER (PARTITION BY value_2 RANGE BETWEEN - UNBOUNDED PRECEDING AND CURRENT ROW), - CUME_DIST() OVER (PARTITION BY value_2 RANGE BETWEEN - UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING), - PERCENT_RANK() OVER (PARTITION BY value_2 ORDER BY avg(value_1) RANGE BETWEEN - UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) -FROM - users_table -GROUP BY - 1 -ORDER BY - 4 DESC,3 DESC,2 DESC ,1 DESC; - value_2 | rank | dense_rank | cume_dist | percent_rank ---------------------------------------------------------------------- - 5 | 1 | 1 | 1 | 0 - 4 | 1 | 1 | 1 | 0 - 3 | 1 | 1 | 1 | 0 - 2 | 1 | 1 | 1 | 0 - 1 | 1 | 1 | 1 | 0 - 0 | 1 | 1 | 1 | 0 -(6 rows) - --- test exclude supported -SELECT - value_2, - value_1, - array_agg(value_1) OVER (PARTITION BY value_2 ORDER BY value_1 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), - array_agg(value_1) OVER (PARTITION BY value_2 ORDER BY value_1 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE CURRENT ROW) -FROM - users_table -WHERE - value_2 > 2 AND value_2 < 6 -ORDER BY - value_2, value_1, 3, 4; - value_2 | value_1 | array_agg | array_agg ---------------------------------------------------------------------- - 3 | 0 | {0,0,0} | {0,0} - 3 | 0 | {0,0,0} | {0,0} - 3 | 0 | {0,0,0} | {0,0} - 3 | 1 | {0,0,0,1,1,1,1} | {0,0,0,1,1,1} - 3 | 1 | {0,0,0,1,1,1,1} | {0,0,0,1,1,1} - 3 | 1 | {0,0,0,1,1,1,1} | {0,0,0,1,1,1} - 3 | 1 | {0,0,0,1,1,1,1} | {0,0,0,1,1,1} - 3 | 2 | {0,0,0,1,1,1,1,2,2} | {0,0,0,1,1,1,1,2} - 3 | 2 | {0,0,0,1,1,1,1,2,2} | {0,0,0,1,1,1,1,2} - 3 | 3 | {0,0,0,1,1,1,1,2,2,3,3} | {0,0,0,1,1,1,1,2,2,3} - 3 | 3 | {0,0,0,1,1,1,1,2,2,3,3} | {0,0,0,1,1,1,1,2,2,3} - 3 | 4 | {0,0,0,1,1,1,1,2,2,3,3,4,4,4,4,4} | {0,0,0,1,1,1,1,2,2,3,3,4,4,4,4} - 3 | 4 | {0,0,0,1,1,1,1,2,2,3,3,4,4,4,4,4} | {0,0,0,1,1,1,1,2,2,3,3,4,4,4,4} - 3 | 4 | {0,0,0,1,1,1,1,2,2,3,3,4,4,4,4,4} | {0,0,0,1,1,1,1,2,2,3,3,4,4,4,4} - 3 | 4 | {0,0,0,1,1,1,1,2,2,3,3,4,4,4,4,4} | {0,0,0,1,1,1,1,2,2,3,3,4,4,4,4} - 3 | 4 | {0,0,0,1,1,1,1,2,2,3,3,4,4,4,4,4} | {0,0,0,1,1,1,1,2,2,3,3,4,4,4,4} - 3 | 5 | {0,0,0,1,1,1,1,2,2,3,3,4,4,4,4,4,5} | {0,0,0,1,1,1,1,2,2,3,3,4,4,4,4,4} - 4 | 0 | {0,0} | {0} - 4 | 0 | {0,0} | {0} - 4 | 1 | {0,0,1,1} | {0,0,1} - 4 | 1 | {0,0,1,1} | {0,0,1} - 4 | 2 | {0,0,1,1,2,2,2} | {0,0,1,1,2,2} - 4 | 2 | {0,0,1,1,2,2,2} | {0,0,1,1,2,2} - 4 | 2 | {0,0,1,1,2,2,2} | {0,0,1,1,2,2} - 4 | 3 | {0,0,1,1,2,2,2,3,3,3,3,3,3,3} | {0,0,1,1,2,2,2,3,3,3,3,3,3} - 4 | 3 | {0,0,1,1,2,2,2,3,3,3,3,3,3,3} | {0,0,1,1,2,2,2,3,3,3,3,3,3} - 4 | 3 | {0,0,1,1,2,2,2,3,3,3,3,3,3,3} | {0,0,1,1,2,2,2,3,3,3,3,3,3} - 4 | 3 | {0,0,1,1,2,2,2,3,3,3,3,3,3,3} | {0,0,1,1,2,2,2,3,3,3,3,3,3} - 4 | 3 | {0,0,1,1,2,2,2,3,3,3,3,3,3,3} | {0,0,1,1,2,2,2,3,3,3,3,3,3} - 4 | 3 | {0,0,1,1,2,2,2,3,3,3,3,3,3,3} | {0,0,1,1,2,2,2,3,3,3,3,3,3} - 4 | 3 | {0,0,1,1,2,2,2,3,3,3,3,3,3,3} | {0,0,1,1,2,2,2,3,3,3,3,3,3} - 4 | 4 | {0,0,1,1,2,2,2,3,3,3,3,3,3,3,4,4,4,4} | {0,0,1,1,2,2,2,3,3,3,3,3,3,3,4,4,4} - 4 | 4 | {0,0,1,1,2,2,2,3,3,3,3,3,3,3,4,4,4,4} | {0,0,1,1,2,2,2,3,3,3,3,3,3,3,4,4,4} - 4 | 4 | {0,0,1,1,2,2,2,3,3,3,3,3,3,3,4,4,4,4} | {0,0,1,1,2,2,2,3,3,3,3,3,3,3,4,4,4} - 4 | 4 | {0,0,1,1,2,2,2,3,3,3,3,3,3,3,4,4,4,4} | {0,0,1,1,2,2,2,3,3,3,3,3,3,3,4,4,4} - 4 | 5 | {0,0,1,1,2,2,2,3,3,3,3,3,3,3,4,4,4,4,5,5} | {0,0,1,1,2,2,2,3,3,3,3,3,3,3,4,4,4,4,5} - 4 | 5 | {0,0,1,1,2,2,2,3,3,3,3,3,3,3,4,4,4,4,5,5} | {0,0,1,1,2,2,2,3,3,3,3,3,3,3,4,4,4,4,5} - 5 | 0 | {0,0} | {0} - 5 | 0 | {0,0} | {0} - 5 | 1 | {0,0,1} | {0,0} - 5 | 2 | {0,0,1,2,2} | {0,0,1,2} - 5 | 2 | {0,0,1,2,2} | {0,0,1,2} - 5 | 3 | {0,0,1,2,2,3,3,3,3} | {0,0,1,2,2,3,3,3} - 5 | 3 | {0,0,1,2,2,3,3,3,3} | {0,0,1,2,2,3,3,3} - 5 | 3 | {0,0,1,2,2,3,3,3,3} | {0,0,1,2,2,3,3,3} - 5 | 3 | {0,0,1,2,2,3,3,3,3} | {0,0,1,2,2,3,3,3} - 5 | 4 | {0,0,1,2,2,3,3,3,3,4,4} | {0,0,1,2,2,3,3,3,3,4} - 5 | 4 | {0,0,1,2,2,3,3,3,3,4,4} | {0,0,1,2,2,3,3,3,3,4} - 5 | 5 | {0,0,1,2,2,3,3,3,3,4,4,5,5} | {0,0,1,2,2,3,3,3,3,4,4,5} - 5 | 5 | {0,0,1,2,2,3,3,3,3,4,4,5,5} | {0,0,1,2,2,3,3,3,3,4,4,5} -(50 rows) - --- test preceding and following on RANGE window -SELECT - value_2, - value_1, - array_agg(value_1) OVER range_window, - array_agg(value_1) OVER range_window_exclude -FROM - users_table -WHERE - value_2 > 2 AND value_2 < 6 -WINDOW - range_window as (PARTITION BY value_2 ORDER BY value_1 RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING), - range_window_exclude as (PARTITION BY value_2 ORDER BY value_1 RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) -ORDER BY - value_2, value_1, 3, 4; - value_2 | value_1 | array_agg | array_agg ---------------------------------------------------------------------- - 3 | 0 | {0,0,0,1,1,1,1} | {0,0,1,1,1,1} - 3 | 0 | {0,0,0,1,1,1,1} | {0,0,1,1,1,1} - 3 | 0 | {0,0,0,1,1,1,1} | {0,0,1,1,1,1} - 3 | 1 | {0,0,0,1,1,1,1,2,2} | {0,0,0,1,1,1,2,2} - 3 | 1 | {0,0,0,1,1,1,1,2,2} | {0,0,0,1,1,1,2,2} - 3 | 1 | {0,0,0,1,1,1,1,2,2} | {0,0,0,1,1,1,2,2} - 3 | 1 | {0,0,0,1,1,1,1,2,2} | {0,0,0,1,1,1,2,2} - 3 | 2 | {1,1,1,1,2,2,3,3} | {1,1,1,1,2,3,3} - 3 | 2 | {1,1,1,1,2,2,3,3} | {1,1,1,1,2,3,3} - 3 | 3 | {2,2,3,3,4,4,4,4,4} | {2,2,3,4,4,4,4,4} - 3 | 3 | {2,2,3,3,4,4,4,4,4} | {2,2,3,4,4,4,4,4} - 3 | 4 | {3,3,4,4,4,4,4,5} | {3,3,4,4,4,4,5} - 3 | 4 | {3,3,4,4,4,4,4,5} | {3,3,4,4,4,4,5} - 3 | 4 | {3,3,4,4,4,4,4,5} | {3,3,4,4,4,4,5} - 3 | 4 | {3,3,4,4,4,4,4,5} | {3,3,4,4,4,4,5} - 3 | 4 | {3,3,4,4,4,4,4,5} | {3,3,4,4,4,4,5} - 3 | 5 | {4,4,4,4,4,5} | {4,4,4,4,4} - 4 | 0 | {0,0,1,1} | {0,1,1} - 4 | 0 | {0,0,1,1} | {0,1,1} - 4 | 1 | {0,0,1,1,2,2,2} | {0,0,1,2,2,2} - 4 | 1 | {0,0,1,1,2,2,2} | {0,0,1,2,2,2} - 4 | 2 | {1,1,2,2,2,3,3,3,3,3,3,3} | {1,1,2,2,3,3,3,3,3,3,3} - 4 | 2 | {1,1,2,2,2,3,3,3,3,3,3,3} | {1,1,2,2,3,3,3,3,3,3,3} - 4 | 2 | {1,1,2,2,2,3,3,3,3,3,3,3} | {1,1,2,2,3,3,3,3,3,3,3} - 4 | 3 | {2,2,2,3,3,3,3,3,3,3,4,4,4,4} | {2,2,2,3,3,3,3,3,3,4,4,4,4} - 4 | 3 | {2,2,2,3,3,3,3,3,3,3,4,4,4,4} | {2,2,2,3,3,3,3,3,3,4,4,4,4} - 4 | 3 | {2,2,2,3,3,3,3,3,3,3,4,4,4,4} | {2,2,2,3,3,3,3,3,3,4,4,4,4} - 4 | 3 | {2,2,2,3,3,3,3,3,3,3,4,4,4,4} | {2,2,2,3,3,3,3,3,3,4,4,4,4} - 4 | 3 | {2,2,2,3,3,3,3,3,3,3,4,4,4,4} | {2,2,2,3,3,3,3,3,3,4,4,4,4} - 4 | 3 | {2,2,2,3,3,3,3,3,3,3,4,4,4,4} | {2,2,2,3,3,3,3,3,3,4,4,4,4} - 4 | 3 | {2,2,2,3,3,3,3,3,3,3,4,4,4,4} | {2,2,2,3,3,3,3,3,3,4,4,4,4} - 4 | 4 | {3,3,3,3,3,3,3,4,4,4,4,5,5} | {3,3,3,3,3,3,3,4,4,4,5,5} - 4 | 4 | {3,3,3,3,3,3,3,4,4,4,4,5,5} | {3,3,3,3,3,3,3,4,4,4,5,5} - 4 | 4 | {3,3,3,3,3,3,3,4,4,4,4,5,5} | {3,3,3,3,3,3,3,4,4,4,5,5} - 4 | 4 | {3,3,3,3,3,3,3,4,4,4,4,5,5} | {3,3,3,3,3,3,3,4,4,4,5,5} - 4 | 5 | {4,4,4,4,5,5} | {4,4,4,4,5} - 4 | 5 | {4,4,4,4,5,5} | {4,4,4,4,5} - 5 | 0 | {0,0,1} | {0,1} - 5 | 0 | {0,0,1} | {0,1} - 5 | 1 | {0,0,1,2,2} | {0,0,2,2} - 5 | 2 | {1,2,2,3,3,3,3} | {1,2,3,3,3,3} - 5 | 2 | {1,2,2,3,3,3,3} | {1,2,3,3,3,3} - 5 | 3 | {2,2,3,3,3,3,4,4} | {2,2,3,3,3,4,4} - 5 | 3 | {2,2,3,3,3,3,4,4} | {2,2,3,3,3,4,4} - 5 | 3 | {2,2,3,3,3,3,4,4} | {2,2,3,3,3,4,4} - 5 | 3 | {2,2,3,3,3,3,4,4} | {2,2,3,3,3,4,4} - 5 | 4 | {3,3,3,3,4,4,5,5} | {3,3,3,3,4,5,5} - 5 | 4 | {3,3,3,3,4,4,5,5} | {3,3,3,3,4,5,5} - 5 | 5 | {4,4,5,5} | {4,4,5} - 5 | 5 | {4,4,5,5} | {4,4,5} -(50 rows) - --- test preceding and following on ROW window -SELECT - value_2, - value_1, - array_agg(value_1) OVER row_window, - array_agg(value_1) OVER row_window_exclude -FROM - users_table -WHERE - value_2 > 2 and value_2 < 6 -WINDOW - row_window as (PARTITION BY value_2 ORDER BY value_1 ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING), - row_window_exclude as (PARTITION BY value_2 ORDER BY value_1 ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) -ORDER BY - value_2, value_1, 3, 4; - value_2 | value_1 | array_agg | array_agg ---------------------------------------------------------------------- - 3 | 0 | {0,0} | {0} - 3 | 0 | {0,0,0} | {0,0} - 3 | 0 | {0,0,1} | {0,1} - 3 | 1 | {0,1,1} | {0,1} - 3 | 1 | {1,1,1} | {1,1} - 3 | 1 | {1,1,1} | {1,1} - 3 | 1 | {1,1,2} | {1,2} - 3 | 2 | {1,2,2} | {1,2} - 3 | 2 | {2,2,3} | {2,3} - 3 | 3 | {2,3,3} | {2,3} - 3 | 3 | {3,3,4} | {3,4} - 3 | 4 | {3,4,4} | {3,4} - 3 | 4 | {4,4,4} | {4,4} - 3 | 4 | {4,4,4} | {4,4} - 3 | 4 | {4,4,4} | {4,4} - 3 | 4 | {4,4,5} | {4,5} - 3 | 5 | {4,5} | {4} - 4 | 0 | {0,0} | {0} - 4 | 0 | {0,0,1} | {0,1} - 4 | 1 | {0,1,1} | {0,1} - 4 | 1 | {1,1,2} | {1,2} - 4 | 2 | {1,2,2} | {1,2} - 4 | 2 | {2,2,2} | {2,2} - 4 | 2 | {2,2,3} | {2,3} - 4 | 3 | {2,3,3} | {2,3} - 4 | 3 | {3,3,3} | {3,3} - 4 | 3 | {3,3,3} | {3,3} - 4 | 3 | {3,3,3} | {3,3} - 4 | 3 | {3,3,3} | {3,3} - 4 | 3 | {3,3,3} | {3,3} - 4 | 3 | {3,3,4} | {3,4} - 4 | 4 | {3,4,4} | {3,4} - 4 | 4 | {4,4,4} | {4,4} - 4 | 4 | {4,4,4} | {4,4} - 4 | 4 | {4,4,5} | {4,5} - 4 | 5 | {4,5,5} | {4,5} - 4 | 5 | {5,5} | {5} - 5 | 0 | {0,0} | {0} - 5 | 0 | {0,0,1} | {0,1} - 5 | 1 | {0,1,2} | {0,2} - 5 | 2 | {1,2,2} | {1,2} - 5 | 2 | {2,2,3} | {2,3} - 5 | 3 | {2,3,3} | {2,3} - 5 | 3 | {3,3,3} | {3,3} - 5 | 3 | {3,3,3} | {3,3} - 5 | 3 | {3,3,4} | {3,4} - 5 | 4 | {3,4,4} | {3,4} - 5 | 4 | {4,4,5} | {4,5} - 5 | 5 | {4,5,5} | {4,5} - 5 | 5 | {5,5} | {5} -(50 rows) - --- some tests with GROUP BY, HAVING and LIMIT -SELECT - user_id, sum(event_type) OVER my_win , event_type -FROM - events_table -GROUP BY - user_id, event_type -HAVING count(*) > 2 - WINDOW my_win AS (PARTITION BY user_id, max(event_type) ORDER BY count(*) DESC) -ORDER BY - 2 DESC, 3 DESC, 1 DESC -LIMIT - 5; - user_id | sum | event_type ---------------------------------------------------------------------- - 4 | 4 | 4 - 3 | 4 | 4 - 2 | 4 | 4 - 1 | 4 | 4 - 5 | 3 | 3 -(5 rows) - --- test PARTITION BY avg(...) ORDER BY avg(...) -SELECT - value_1, - avg(value_3), - dense_rank() OVER (PARTITION BY avg(value_3) ORDER BY avg(value_2)) -FROM - users_table -GROUP BY - 1 -ORDER BY - 1; - value_1 | avg | dense_rank ---------------------------------------------------------------------- - 0 | 3.08333333333333 | 1 - 1 | 2.93333333333333 | 1 - 2 | 2.22222222222222 | 1 - 3 | 2.73076923076923 | 1 - 4 | 2.9047619047619 | 1 - 5 | 2.22222222222222 | 2 -(6 rows) - --- Group by has more columns than partition by -SELECT - DISTINCT user_id, SUM(value_2) OVER (PARTITION BY user_id) -FROM - users_table -GROUP BY - user_id, value_1, value_2 -HAVING count(*) > 2 -ORDER BY - 2 DESC, 1 -LIMIT - 10; - user_id | sum ---------------------------------------------------------------------- - 5 | 3 - 4 | 2 -(2 rows) - -SELECT - DISTINCT ON (user_id) user_id, SUM(value_2) OVER (PARTITION BY user_id) -FROM - users_table -GROUP BY - user_id, value_1, value_2 -HAVING count(*) > 2 -ORDER BY - 1, 2 DESC -LIMIT - 10; - user_id | sum ---------------------------------------------------------------------- - 4 | 2 - 5 | 3 -(2 rows) - -SELECT - DISTINCT ON (SUM(value_1) OVER (PARTITION BY user_id)) user_id, SUM(value_2) OVER (PARTITION BY user_id) -FROM - users_table -GROUP BY - user_id, value_1, value_2 -HAVING count(*) > 2 -ORDER BY - (SUM(value_1) OVER (PARTITION BY user_id)) , 2 DESC, 1 -LIMIT - 10; - user_id | sum ---------------------------------------------------------------------- - 5 | 3 - 4 | 2 -(2 rows) - --- not a meaningful query, with interesting syntax -SELECT - user_id, - AVG(avg(value_1)) OVER (PARTITION BY user_id, max(user_id), MIN(value_2)), - AVG(avg(user_id)) OVER (PARTITION BY user_id, min(user_id), AVG(value_1)) -FROM - users_table -GROUP BY - 1 -ORDER BY - 3 DESC, 2 DESC, 1 DESC; - user_id | avg | avg ---------------------------------------------------------------------- - 6 | 2.1000000000000000 | 6.0000000000000000 - 5 | 2.6538461538461538 | 5.0000000000000000 - 4 | 2.7391304347826087 | 4.0000000000000000 - 3 | 2.3529411764705882 | 3.0000000000000000 - 2 | 2.3333333333333333 | 2.0000000000000000 - 1 | 3.2857142857142857 | 1.00000000000000000000 -(6 rows) - -SELECT coordinator_plan($Q$ -EXPLAIN (COSTS FALSE) -SELECT - user_id, - AVG(avg(value_1)) OVER (PARTITION BY user_id, max(user_id), MIN(value_2)), - AVG(avg(user_id)) OVER (PARTITION BY user_id, min(user_id), AVG(value_1)) -FROM - users_table -GROUP BY - 1 -ORDER BY - 3 DESC, 2 DESC, 1 DESC; -$Q$); - coordinator_plan ---------------------------------------------------------------------- - Sort - Sort Key: remote_scan.avg_1 DESC, remote_scan.avg DESC, remote_scan.user_id DESC - -> Custom Scan (Citus Adaptive) - Task Count: 4 -(4 rows) - -SELECT - value_2, - AVG(avg(value_1)) OVER (PARTITION BY value_2, max(value_2), MIN(value_2)), - AVG(avg(value_2)) OVER (PARTITION BY value_2, min(value_2), AVG(value_1)) -FROM - users_table -GROUP BY - 1 -ORDER BY - 3 DESC, 2 DESC, 1 DESC; - value_2 | avg | avg ---------------------------------------------------------------------- - 5 | 2.6923076923076923 | 5.0000000000000000 - 4 | 2.7500000000000000 | 4.0000000000000000 - 3 | 2.2941176470588235 | 3.0000000000000000 - 2 | 2.7619047619047619 | 2.0000000000000000 - 1 | 2.4285714285714286 | 1.00000000000000000000 - 0 | 2.2222222222222222 | 0.00000000000000000000 -(6 rows) - -SELECT - value_2, user_id, - AVG(avg(value_1)) OVER (PARTITION BY value_2, max(value_2), MIN(value_2)), - AVG(avg(value_2)) OVER (PARTITION BY user_id, min(value_2), AVG(value_1)) -FROM - users_table -GROUP BY - 1, 2 -ORDER BY - 3 DESC, 2 DESC, 1 DESC; - value_2 | user_id | avg | avg ---------------------------------------------------------------------- - 5 | 5 | 2.6666666666666667 | 5.0000000000000000 - 5 | 4 | 2.6666666666666667 | 5.0000000000000000 - 5 | 3 | 2.6666666666666667 | 5.0000000000000000 - 5 | 2 | 2.6666666666666667 | 5.0000000000000000 - 2 | 6 | 2.54583333333333333333 | 2.0000000000000000 - 2 | 5 | 2.54583333333333333333 | 2.0000000000000000 - 2 | 4 | 2.54583333333333333333 | 2.0000000000000000 - 2 | 3 | 2.54583333333333333333 | 2.0000000000000000 - 2 | 2 | 2.54583333333333333333 | 2.0000000000000000 - 2 | 1 | 2.54583333333333333333 | 2.0000000000000000 - 0 | 6 | 2.50000000000000000000 | 0.00000000000000000000 - 0 | 5 | 2.50000000000000000000 | 0.00000000000000000000 - 0 | 4 | 2.50000000000000000000 | 0.00000000000000000000 - 0 | 2 | 2.50000000000000000000 | 0.00000000000000000000 - 0 | 1 | 2.50000000000000000000 | 0.00000000000000000000 - 4 | 6 | 2.45555555555555555000 | 4.0000000000000000 - 4 | 5 | 2.45555555555555555000 | 4.0000000000000000 - 4 | 4 | 2.45555555555555555000 | 4.0000000000000000 - 4 | 3 | 2.45555555555555555000 | 4.0000000000000000 - 4 | 2 | 2.45555555555555555000 | 4.0000000000000000 - 4 | 1 | 2.45555555555555555000 | 4.0000000000000000 - 3 | 6 | 2.3500000000000000 | 3.0000000000000000 - 3 | 5 | 2.3500000000000000 | 3.0000000000000000 - 3 | 4 | 2.3500000000000000 | 3.0000000000000000 - 3 | 3 | 2.3500000000000000 | 3.0000000000000000 - 3 | 2 | 2.3500000000000000 | 3.0000000000000000 - 3 | 1 | 2.3500000000000000 | 3.0000000000000000 - 1 | 6 | 1.90666666666666666000 | 1.00000000000000000000 - 1 | 5 | 1.90666666666666666000 | 1.00000000000000000000 - 1 | 4 | 1.90666666666666666000 | 1.00000000000000000000 - 1 | 3 | 1.90666666666666666000 | 1.00000000000000000000 - 1 | 2 | 1.90666666666666666000 | 1.00000000000000000000 -(32 rows) - -SELECT user_id, sum(avg(user_id)) OVER () -FROM users_table -GROUP BY user_id -ORDER BY 1 -LIMIT 10; - user_id | sum ---------------------------------------------------------------------- - 1 | 21.00000000000000000000 - 2 | 21.00000000000000000000 - 3 | 21.00000000000000000000 - 4 | 21.00000000000000000000 - 5 | 21.00000000000000000000 - 6 | 21.00000000000000000000 -(6 rows) - -SELECT - user_id, - 1 + sum(value_1), - 1 + AVG(value_2) OVER (partition by user_id) -FROM - users_table -GROUP BY - user_id, value_2 -ORDER BY - user_id, value_2; - user_id | ?column? | ?column? ---------------------------------------------------------------------- - 1 | 5 | 3.2500000000000000 - 1 | 4 | 3.2500000000000000 - 1 | 6 | 3.2500000000000000 - 1 | 12 | 3.2500000000000000 - 2 | 3 | 3.5000000000000000 - 2 | 5 | 3.5000000000000000 - 2 | 13 | 3.5000000000000000 - 2 | 6 | 3.5000000000000000 - 2 | 17 | 3.5000000000000000 - 2 | 4 | 3.5000000000000000 - 3 | 3 | 4.0000000000000000 - 3 | 13 | 4.0000000000000000 - 3 | 10 | 4.0000000000000000 - 3 | 2 | 4.0000000000000000 - 3 | 17 | 4.0000000000000000 - 4 | 4 | 3.5000000000000000 - 4 | 28 | 3.5000000000000000 - 4 | 1 | 3.5000000000000000 - 4 | 11 | 3.5000000000000000 - 4 | 17 | 3.5000000000000000 - 4 | 8 | 3.5000000000000000 - 5 | 7 | 3.5000000000000000 - 5 | 17 | 3.5000000000000000 - 5 | 24 | 3.5000000000000000 - 5 | 9 | 3.5000000000000000 - 5 | 8 | 3.5000000000000000 - 5 | 10 | 3.5000000000000000 - 6 | 6 | 3.0000000000000000 - 6 | 3 | 3.0000000000000000 - 6 | 9 | 3.0000000000000000 - 6 | 3 | 3.0000000000000000 - 6 | 5 | 3.0000000000000000 -(32 rows) - -SELECT - user_id, - 1 + sum(value_1), - 1 + AVG(value_2) OVER (partition by user_id) -FROM - users_table -GROUP BY - user_id, value_2 -ORDER BY - 2 DESC, 1 -LIMIT 5; - user_id | ?column? | ?column? ---------------------------------------------------------------------- - 4 | 28 | 3.5000000000000000 - 5 | 24 | 3.5000000000000000 - 2 | 17 | 3.5000000000000000 - 3 | 17 | 4.0000000000000000 - 4 | 17 | 3.5000000000000000 -(5 rows) - --- rank and ordering in the reverse order -SELECT - user_id, - avg(value_1), - RANK() OVER (partition by user_id order by value_2) -FROM - users_table -GROUP BY user_id, value_2 -ORDER BY user_id, value_2 DESC; - user_id | avg | rank ---------------------------------------------------------------------- - 1 | 3.6666666666666667 | 4 - 1 | 2.5000000000000000 | 3 - 1 | 3.0000000000000000 | 2 - 1 | 4.0000000000000000 | 1 - 2 | 1.5000000000000000 | 6 - 2 | 3.2000000000000000 | 5 - 2 | 1.6666666666666667 | 4 - 2 | 3.0000000000000000 | 3 - 2 | 1.3333333333333333 | 2 - 2 | 2.0000000000000000 | 1 - 3 | 2.6666666666666667 | 5 - 3 | 1.00000000000000000000 | 4 - 3 | 3.0000000000000000 | 3 - 3 | 2.4000000000000000 | 2 - 3 | 1.00000000000000000000 | 1 - 4 | 3.5000000000000000 | 6 - 4 | 3.2000000000000000 | 5 - 4 | 3.3333333333333333 | 4 - 4 | 0.00000000000000000000 | 3 - 4 | 3.0000000000000000 | 2 - 4 | 1.00000000000000000000 | 1 - 5 | 3.0000000000000000 | 6 - 5 | 2.3333333333333333 | 5 - 5 | 1.6000000000000000 | 4 - 5 | 2.8750000000000000 | 3 - 5 | 3.2000000000000000 | 2 - 5 | 3.0000000000000000 | 1 - 6 | 1.3333333333333333 | 5 - 6 | 2.0000000000000000 | 4 - 6 | 4.0000000000000000 | 3 - 6 | 1.00000000000000000000 | 2 - 6 | 2.5000000000000000 | 1 -(32 rows) - --- order by in the window function is same as avg(value_1) DESC -SELECT - user_id, - avg(value_1), - RANK() OVER (partition by user_id order by 1 / (1 + avg(value_1))) -FROM - users_table -GROUP BY user_id, value_2 -ORDER BY user_id, avg(value_1) DESC; - user_id | avg | rank ---------------------------------------------------------------------- - 1 | 4.0000000000000000 | 1 - 1 | 3.6666666666666667 | 2 - 1 | 3.0000000000000000 | 3 - 1 | 2.5000000000000000 | 4 - 2 | 3.2000000000000000 | 1 - 2 | 3.0000000000000000 | 2 - 2 | 2.0000000000000000 | 3 - 2 | 1.6666666666666667 | 4 - 2 | 1.5000000000000000 | 5 - 2 | 1.3333333333333333 | 6 - 3 | 3.0000000000000000 | 1 - 3 | 2.6666666666666667 | 2 - 3 | 2.4000000000000000 | 3 - 3 | 1.00000000000000000000 | 4 - 3 | 1.00000000000000000000 | 4 - 4 | 3.5000000000000000 | 1 - 4 | 3.3333333333333333 | 2 - 4 | 3.2000000000000000 | 3 - 4 | 3.0000000000000000 | 4 - 4 | 1.00000000000000000000 | 5 - 4 | 0.00000000000000000000 | 6 - 5 | 3.2000000000000000 | 1 - 5 | 3.0000000000000000 | 2 - 5 | 3.0000000000000000 | 2 - 5 | 2.8750000000000000 | 4 - 5 | 2.3333333333333333 | 5 - 5 | 1.6000000000000000 | 6 - 6 | 4.0000000000000000 | 1 - 6 | 2.5000000000000000 | 2 - 6 | 2.0000000000000000 | 3 - 6 | 1.3333333333333333 | 4 - 6 | 1.00000000000000000000 | 5 -(32 rows) - -EXPLAIN (COSTS FALSE) -SELECT - user_id, - avg(value_1), - RANK() OVER (partition by user_id order by 1 / (1 + avg(value_1))) -FROM - users_table -GROUP BY user_id, value_2 -ORDER BY user_id, avg(value_1) DESC; - QUERY PLAN ---------------------------------------------------------------------- - Sort - Sort Key: remote_scan.user_id, remote_scan.avg DESC - -> Custom Scan (Citus Adaptive) - Task Count: 4 - Tasks Shown: One of 4 - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> WindowAgg - -> Sort - Sort Key: users_table.user_id, (('1'::numeric / ('1'::numeric + avg(users_table.value_1)))) - -> HashAggregate - Group Key: users_table.user_id, users_table.value_2 - -> Seq Scan on users_table_1400256 users_table -(13 rows) - --- order by in the window function is same as avg(value_1) DESC -SELECT - user_id, - avg(value_1), - RANK() OVER (partition by user_id order by 1 / (1 + avg(value_1))) -FROM - users_table -GROUP BY user_id, value_2 -ORDER BY user_id, avg(value_1) DESC; - user_id | avg | rank ---------------------------------------------------------------------- - 1 | 4.0000000000000000 | 1 - 1 | 3.6666666666666667 | 2 - 1 | 3.0000000000000000 | 3 - 1 | 2.5000000000000000 | 4 - 2 | 3.2000000000000000 | 1 - 2 | 3.0000000000000000 | 2 - 2 | 2.0000000000000000 | 3 - 2 | 1.6666666666666667 | 4 - 2 | 1.5000000000000000 | 5 - 2 | 1.3333333333333333 | 6 - 3 | 3.0000000000000000 | 1 - 3 | 2.6666666666666667 | 2 - 3 | 2.4000000000000000 | 3 - 3 | 1.00000000000000000000 | 4 - 3 | 1.00000000000000000000 | 4 - 4 | 3.5000000000000000 | 1 - 4 | 3.3333333333333333 | 2 - 4 | 3.2000000000000000 | 3 - 4 | 3.0000000000000000 | 4 - 4 | 1.00000000000000000000 | 5 - 4 | 0.00000000000000000000 | 6 - 5 | 3.2000000000000000 | 1 - 5 | 3.0000000000000000 | 2 - 5 | 3.0000000000000000 | 2 - 5 | 2.8750000000000000 | 4 - 5 | 2.3333333333333333 | 5 - 5 | 1.6000000000000000 | 6 - 6 | 4.0000000000000000 | 1 - 6 | 2.5000000000000000 | 2 - 6 | 2.0000000000000000 | 3 - 6 | 1.3333333333333333 | 4 - 6 | 1.00000000000000000000 | 5 -(32 rows) - --- limit is not pushed down to worker !! -EXPLAIN (COSTS FALSE) -SELECT - user_id, - avg(value_1), - RANK() OVER (partition by user_id order by 1 / (1 + avg(value_1))) -FROM - users_table -GROUP BY user_id, value_2 -ORDER BY user_id, avg(value_1) DESC -LIMIT 5; - QUERY PLAN ---------------------------------------------------------------------- - Limit - -> Sort - Sort Key: remote_scan.user_id, remote_scan.avg DESC - -> Custom Scan (Citus Adaptive) - Task Count: 4 - Tasks Shown: One of 4 - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> Limit - -> Incremental Sort - Sort Key: users_table.user_id, (avg(users_table.value_1)) DESC - Presorted Key: users_table.user_id - -> WindowAgg - -> Sort - Sort Key: users_table.user_id, (('1'::numeric / ('1'::numeric + avg(users_table.value_1)))) - -> HashAggregate - Group Key: users_table.user_id, users_table.value_2 - -> Seq Scan on users_table_1400256 users_table -(18 rows) - -EXPLAIN (COSTS FALSE) -SELECT - user_id, - avg(value_1), - RANK() OVER (partition by user_id order by 1 / (1 + avg(value_1))) -FROM - users_table -GROUP BY user_id, value_2 -ORDER BY user_id, avg(value_1) DESC -LIMIT 5; - QUERY PLAN ---------------------------------------------------------------------- - Limit - -> Sort - Sort Key: remote_scan.user_id, remote_scan.avg DESC - -> Custom Scan (Citus Adaptive) - Task Count: 4 - Tasks Shown: One of 4 - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> Limit - -> Incremental Sort - Sort Key: users_table.user_id, (avg(users_table.value_1)) DESC - Presorted Key: users_table.user_id - -> WindowAgg - -> Sort - Sort Key: users_table.user_id, (('1'::numeric / ('1'::numeric + avg(users_table.value_1)))) - -> HashAggregate - Group Key: users_table.user_id, users_table.value_2 - -> Seq Scan on users_table_1400256 users_table -(18 rows) - -EXPLAIN (COSTS FALSE) -SELECT - user_id, - avg(value_1), - RANK() OVER (partition by user_id order by 1 / (1 + sum(value_2))) -FROM - users_table -GROUP BY user_id, value_2 -ORDER BY user_id, avg(value_1) DESC -LIMIT 5; - QUERY PLAN ---------------------------------------------------------------------- - Limit - -> Sort - Sort Key: remote_scan.user_id, remote_scan.avg DESC - -> Custom Scan (Citus Adaptive) - Task Count: 4 - Tasks Shown: One of 4 - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> Limit - -> Incremental Sort - Sort Key: users_table.user_id, (avg(users_table.value_1)) DESC - Presorted Key: users_table.user_id - -> WindowAgg - -> Sort - Sort Key: users_table.user_id, ((1 / (1 + sum(users_table.value_2)))) - -> HashAggregate - Group Key: users_table.user_id, users_table.value_2 - -> Seq Scan on users_table_1400256 users_table -(18 rows) - -EXPLAIN (COSTS FALSE) -SELECT - user_id, - avg(value_1), - RANK() OVER (partition by user_id order by sum(value_2)) -FROM - users_table -GROUP BY user_id, value_2 -ORDER BY user_id, avg(value_1) DESC -LIMIT 5; - QUERY PLAN ---------------------------------------------------------------------- - Limit - -> Sort - Sort Key: remote_scan.user_id, remote_scan.avg DESC - -> Custom Scan (Citus Adaptive) - Task Count: 4 - Tasks Shown: One of 4 - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> Limit - -> Incremental Sort - Sort Key: users_table.user_id, (avg(users_table.value_1)) DESC - Presorted Key: users_table.user_id - -> WindowAgg - -> Sort - Sort Key: users_table.user_id, (sum(users_table.value_2)) - -> HashAggregate - Group Key: users_table.user_id, users_table.value_2 - -> Seq Scan on users_table_1400256 users_table -(18 rows) - --- Grouping can be pushed down with aggregates even when window function can't -EXPLAIN (COSTS FALSE) -SELECT user_id, count(value_1), stddev(value_1), count(user_id) OVER (PARTITION BY random()) -FROM users_table GROUP BY user_id HAVING avg(value_1) > 2 LIMIT 1; - QUERY PLAN ---------------------------------------------------------------------- - Limit - -> WindowAgg - -> Sort - Sort Key: remote_scan.worker_column_5 - -> Custom Scan (Citus Adaptive) - Task Count: 4 - Tasks Shown: One of 4 - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> HashAggregate - Group Key: user_id - Filter: (avg(value_1) > '2'::numeric) - -> Seq Scan on users_table_1400256 users_table -(13 rows) - --- Window function with inlined CTE -WITH cte as ( - SELECT uref.id user_id, events_table.value_2, count(*) c - FROM events_table - JOIN users_ref_test_table uref ON uref.id = events_table.user_id - GROUP BY 1, 2 -) -SELECT DISTINCT cte.value_2, cte.c, sum(cte.value_2) OVER (PARTITION BY cte.c) -FROM cte JOIN events_table et ON et.value_2 = cte.value_2 and et.value_2 = cte.c -ORDER BY 1; - value_2 | c | sum ---------------------------------------------------------------------- - 3 | 3 | 108 - 4 | 4 | 56 -(2 rows) - --- There was a strange bug where this wouldn't have window functions being pushed down --- Bug dependent on column ordering -CREATE TABLE daily_uniques (value_2 float, user_id bigint); -SELECT create_distributed_table('daily_uniques', 'user_id'); - create_distributed_table ---------------------------------------------------------------------- - -(1 row) - -EXPLAIN (COSTS FALSE) SELECT - user_id, - sum(value_2) AS commits, - RANK () OVER ( - PARTITION BY user_id - ORDER BY - sum(value_2) DESC - ) -FROM daily_uniques -GROUP BY user_id -HAVING - sum(value_2) > 0 -ORDER BY commits DESC -LIMIT 10; - QUERY PLAN ---------------------------------------------------------------------- - Limit - -> Sort - Sort Key: remote_scan.commits DESC - -> Custom Scan (Citus Adaptive) - Task Count: 4 - Tasks Shown: One of 4 - -> Task - Node: host=localhost port=xxxxx dbname=regression - -> Limit - -> Sort - Sort Key: (sum(daily_uniques.value_2)) DESC - -> WindowAgg - -> Sort - Sort Key: daily_uniques.user_id, (sum(daily_uniques.value_2)) DESC - -> HashAggregate - Group Key: daily_uniques.user_id - Filter: (sum(daily_uniques.value_2) > '0'::double precision) - -> Seq Scan on daily_uniques_xxxxxxx daily_uniques -(18 rows) - -DROP TABLE daily_uniques; --- Partition by reference table column joined to distribution column -SELECT DISTINCT value_2, array_agg(rnk ORDER BY rnk) FROM ( -SELECT events_table.value_2, sum(uref.k_no) OVER (PARTITION BY uref.id) AS rnk -FROM events_table -JOIN users_ref_test_table uref ON uref.id = events_table.user_id) sq -GROUP BY 1 ORDER BY 1; - value_2 | array_agg ---------------------------------------------------------------------- - 0 | {686,686,816,816,987,987,1104} - 1 | {500,500,675,675,675,686,686,816,816,816,987,987,987,987,987,1104,1104,1104,1104,1104,1104,1104} - 2 | {500,500,500,500,675,675,675,675,675,686,686,686,686,816,816,816,816,816,987,987,987,987,987,987,987,1104,1104,1104,1104,1104,1104} - 3 | {500,500,500,500,675,686,686,686,816,816,987,987,987,1104,1104,1104,1104,1104} - 4 | {675,675,675,675,686,686,686,816,816,816,987,987,1104,1104} - 5 | {675,675,816,816,987,987,1104,1104,1104} -(6 rows) - --- https://github.com/citusdata/citus/issues/3754 -select null = sum(null::int2) over () -from public.users_table as ut limit 1; - ?column? ---------------------------------------------------------------------- - -(1 row) - --- verify that this doesn't crash with DEBUG4 -SET log_min_messages TO DEBUG4; -SELECT - user_id, max(value_1) OVER (PARTITION BY user_id, MIN(value_2)) -FROM ( - SELECT - DISTINCT us.user_id, us.value_2, value_1, random() as r1 - FROM - users_table as us, events_table - WHERE - us.user_id = events_table.user_id AND event_type IN (1,2) - ORDER BY - user_id, value_2 - ) s -GROUP BY - 1, value_1 -ORDER BY - 2 DESC, 1; - user_id | max ---------------------------------------------------------------------- - 1 | 5 - 3 | 5 - 3 | 5 - 4 | 5 - 5 | 5 - 5 | 5 - 6 | 5 - 6 | 5 - 1 | 4 - 2 | 4 - 3 | 4 - 3 | 4 - 3 | 4 - 4 | 4 - 4 | 4 - 5 | 4 - 5 | 4 - 1 | 3 - 2 | 3 - 2 | 3 - 2 | 3 - 6 | 3 - 2 | 2 - 4 | 2 - 4 | 2 - 4 | 2 - 6 | 2 - 1 | 1 - 3 | 1 - 5 | 1 - 6 | 1 - 5 | 0 -(32 rows) - diff --git a/src/test/regress/spec/isolation_master_update_node.spec b/src/test/regress/spec/isolation_master_update_node.spec index 5c5a1bb48..3715b6afd 100644 --- a/src/test/regress/spec/isolation_master_update_node.spec +++ b/src/test/regress/spec/isolation_master_update_node.spec @@ -1,7 +1,6 @@ -// Three alternative test outputs: +// Two alternative test outputs: // isolation_master_update_node.out for PG15 // isolation_master_update_node_0.out for PG14 -// isolation_master_update_node_1.out for PG13 setup { diff --git a/src/test/regress/sql/cpu_priority.sql b/src/test/regress/sql/cpu_priority.sql index beb156fa8..ec2206f6f 100644 --- a/src/test/regress/sql/cpu_priority.sql +++ b/src/test/regress/sql/cpu_priority.sql @@ -63,9 +63,6 @@ SET search_path TO cpu_priority; -- in their CREATE SUBSCRIPTION commands. SET citus.log_remote_commands TO ON; SET citus.grep_remote_commands = '%CREATE SUBSCRIPTION%'; --- We disable binary protocol, so we have consistent output between PG13 and --- PG14, beacuse PG13 doesn't support binary logical replication. -SET citus.enable_binary_protocol = false; SELECT master_move_shard_placement(11568900, 'localhost', :worker_1_port, 'localhost', :worker_2_port, 'force_logical'); SET citus.cpu_priority_for_logical_replication_senders = 15; SELECT master_move_shard_placement(11568900, 'localhost', :worker_2_port, 'localhost', :worker_1_port, 'force_logical'); diff --git a/src/test/regress/sql/generated_identity.sql b/src/test/regress/sql/generated_identity.sql index 40021f8d3..df967ddd0 100644 --- a/src/test/regress/sql/generated_identity.sql +++ b/src/test/regress/sql/generated_identity.sql @@ -1,7 +1,3 @@ --- This test file has an alternative output because of error messages vary for PG13 -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int <= 13 AS server_version_le_13; - CREATE SCHEMA generated_identities; SET search_path TO generated_identities; SET client_min_messages to ERROR; diff --git a/src/test/regress/sql/grant_on_schema_propagation.sql b/src/test/regress/sql/grant_on_schema_propagation.sql index 1cb601ad6..f0bd233a2 100644 --- a/src/test/regress/sql/grant_on_schema_propagation.sql +++ b/src/test/regress/sql/grant_on_schema_propagation.sql @@ -1,7 +1,7 @@ -- -- GRANT_ON_SCHEMA_PROPAGATION -- --- this test has different output for PG13/14 compared to PG15 +-- this test has different output for PG14 compared to PG15 -- In PG15, public schema is owned by pg_database_owner role -- Relevant PG commit: b073c3ccd06e4cb845e121387a43faa8c68a7b62 SHOW server_version \gset @@ -189,6 +189,9 @@ DROP SCHEMA dist_schema CASCADE; SET citus.shard_replication_factor TO 1; SELECT master_remove_node('localhost', :worker_2_port); +-- to avoid different output in PG15 +GRANT CREATE ON SCHEMA public TO public; + -- distribute the public schema (it has to be distributed by now but just in case) CREATE TABLE public_schema_table (id INT); SELECT create_distributed_table('public_schema_table', 'id'); diff --git a/src/test/regress/sql/multi_metadata_sync.sql b/src/test/regress/sql/multi_metadata_sync.sql index 0529e1e1d..1b8043cdd 100644 --- a/src/test/regress/sql/multi_metadata_sync.sql +++ b/src/test/regress/sql/multi_metadata_sync.sql @@ -1,7 +1,7 @@ -- -- MULTI_METADATA_SYNC -- --- this test has different output for PG13/14 compared to PG15 +-- this test has different output for PG14 compared to PG15 -- In PG15, public schema is owned by pg_database_owner role -- Relevant PG commit: b073c3ccd06e4cb845e121387a43faa8c68a7b62 SHOW server_version \gset diff --git a/src/test/regress/sql/pg14.sql b/src/test/regress/sql/pg14.sql index be4d2f72d..afac00174 100644 --- a/src/test/regress/sql/pg14.sql +++ b/src/test/regress/sql/pg14.sql @@ -1,11 +1,3 @@ -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int >= 14 AS server_version_ge_14 -\gset -\if :server_version_ge_14 -\else -\q -\endif - create schema pg14; set search_path to pg14; SET citus.shard_replication_factor TO 1; diff --git a/src/test/regress/sql/stat_statements.sql b/src/test/regress/sql/stat_statements.sql index 546a5aefa..5afed9215 100644 --- a/src/test/regress/sql/stat_statements.sql +++ b/src/test/regress/sql/stat_statements.sql @@ -3,12 +3,7 @@ -- -- tests citus_stat_statements functionality -SHOW server_version \gset -SELECT substring(:'server_version', '\d+')::int >= 14 AS server_version_ge_14 -\gset -\if :server_version_ge_14 SET compute_query_id = 'on'; -\endif -- check if pg_stat_statements is available SELECT name FROM pg_available_extensions WHERE name = 'pg_stat_statements'; @@ -50,11 +45,7 @@ SELECT create_distributed_table('test','a'); insert into test values(1); select query, calls from citus_stat_statements(); -\if :server_version_ge_14 SET compute_query_id = 'off'; -\else -set citus.stat_statements_track = 'none'; -\endif -- for pg >= 14, since compute_query_id is off, this insert -- shouldn't be tracked @@ -64,11 +55,7 @@ insert into test values(1); select query, calls from citus_stat_statements(); -\if :server_version_ge_14 SET compute_query_id = 'on'; -\else -RESET citus.stat_statements_track; -\endif SELECT citus_stat_statements_reset(); @@ -290,6 +277,4 @@ DROP TABLE stat_test_text, stat_test_bigint, stat_test_bigint_other, stat_test_r DROP FUNCTION normalize_query_string(text); -\if :server_version_ge_14 SET compute_query_id = 'off'; -\endif diff --git a/src/test/regress/sql/window_functions.sql b/src/test/regress/sql/window_functions.sql index de936c95c..2f7ea18d2 100644 --- a/src/test/regress/sql/window_functions.sql +++ b/src/test/regress/sql/window_functions.sql @@ -3,8 +3,6 @@ -- =================================================================== -- test top level window functions that are pushdownable -- =================================================================== --- This test file has an alternative output because of use of --- incremental sort in some explain outputs in PG13 -- -- a very simple window function with an aggregate and a window function From 565c5260fd4589d8a3603794af98f6b7eb9a67b6 Mon Sep 17 00:00:00 2001 From: aykut-bozkurt <51649454+aykut-bozkurt@users.noreply.github.com> Date: Wed, 21 Jun 2023 14:50:01 +0300 Subject: [PATCH 111/118] Properly handle error at owner check (#6984) We did not properly handle the error at ownership check method, which causes `max stack depth for errors` as in https://github.com/citusdata/citus/issues/6980. **Fix:** In case of an error, we should rollback subtransaction and throw the message with log level to `LOG_SERVER_ONLY`. Note: We prevent logs from the client to prevent pg vanilla test failures due to Citus logs which differs from the actual Postgres logs. (For context: https://github.com/citusdata/citus/pull/6130) I also needed to fix a flaky test: `multi_schema_support` DESCRIPTION: Fixes a bug related to non-existent objects in DDL commands. Fixes https://github.com/citusdata/citus/issues/6980 --- .../distributed/utils/citus_depended_object.c | 52 ++++++++++++------- .../regress/expected/multi_schema_support.out | 5 +- src/test/regress/sql/multi_schema_support.sql | 5 +- 3 files changed, 41 insertions(+), 21 deletions(-) diff --git a/src/backend/distributed/utils/citus_depended_object.c b/src/backend/distributed/utils/citus_depended_object.c index 191bbb844..3b5a34b54 100644 --- a/src/backend/distributed/utils/citus_depended_object.c +++ b/src/backend/distributed/utils/citus_depended_object.c @@ -462,21 +462,25 @@ HasDropCommandViolatesOwnership(Node *node) static bool AnyObjectViolatesOwnership(DropStmt *dropStmt) { + bool hasOwnershipViolation = false; volatile ObjectAddress objectAddress = { 0 }; Relation relation = NULL; - bool objectViolatesOwnership = false; ObjectType objectType = dropStmt->removeType; bool missingOk = dropStmt->missing_ok; - Node *object = NULL; - foreach_ptr(object, dropStmt->objects) + MemoryContext savedContext = CurrentMemoryContext; + ResourceOwner savedOwner = CurrentResourceOwner; + BeginInternalSubTransaction(NULL); + MemoryContextSwitchTo(savedContext); + + PG_TRY(); { - PG_TRY(); + Node *object = NULL; + foreach_ptr(object, dropStmt->objects) { objectAddress = get_object_address(objectType, object, &relation, AccessShareLock, missingOk); - if (OidIsValid(objectAddress.objectId)) { /* @@ -487,29 +491,39 @@ AnyObjectViolatesOwnership(DropStmt *dropStmt) objectAddress, object, relation); } - } - PG_CATCH(); - { - if (OidIsValid(objectAddress.objectId)) + + if (relation != NULL) { - /* ownership violation */ - objectViolatesOwnership = true; + relation_close(relation, NoLock); + relation = NULL; } } - PG_END_TRY(); + ReleaseCurrentSubTransaction(); + MemoryContextSwitchTo(savedContext); + CurrentResourceOwner = savedOwner; + } + PG_CATCH(); + { + MemoryContextSwitchTo(savedContext); + ErrorData *edata = CopyErrorData(); + FlushErrorState(); + + hasOwnershipViolation = true; if (relation != NULL) { - relation_close(relation, AccessShareLock); + relation_close(relation, NoLock); relation = NULL; } + RollbackAndReleaseCurrentSubTransaction(); + MemoryContextSwitchTo(savedContext); + CurrentResourceOwner = savedOwner; - /* we found ownership violation, so can return here */ - if (objectViolatesOwnership) - { - return true; - } + /* Rethrow error with LOG_SERVER_ONLY to prevent log to be sent to client */ + edata->elevel = LOG_SERVER_ONLY; + ThrowErrorData(edata); } + PG_END_TRY(); - return false; + return hasOwnershipViolation; } diff --git a/src/test/regress/expected/multi_schema_support.out b/src/test/regress/expected/multi_schema_support.out index 9adb0c211..f39f5f2b1 100644 --- a/src/test/regress/expected/multi_schema_support.out +++ b/src/test/regress/expected/multi_schema_support.out @@ -1095,6 +1095,9 @@ ALTER TABLE IF EXISTS non_existent_table SET SCHEMA non_existent_schema; NOTICE: relation "non_existent_table" does not exist, skipping DROP SCHEMA existing_schema, another_existing_schema CASCADE; NOTICE: drop cascades to table existing_schema.table_set_schema +-- test DROP SCHEMA with nonexisting schemas +DROP SCHEMA ax, bx, cx, dx, ex, fx, gx, jx; +ERROR: schema "ax" does not exist -- test ALTER TABLE SET SCHEMA with interesting names CREATE SCHEMA "cItuS.T E E N'sSchema"; CREATE SCHEMA "citus-teen's scnd schm."; @@ -1390,6 +1393,7 @@ SELECT pg_identify_object_as_address(classid, objid, objsubid) FROM pg_catalog.p (schema,{run_test_schema},{}) (1 row) +DROP TABLE public.nation_local; DROP SCHEMA run_test_schema, test_schema_support_join_1, test_schema_support_join_2, "Citus'Teen123", "CiTUS.TEEN2", bar, test_schema_support CASCADE; -- verify that the dropped schema is removed from worker's pg_dist_object SELECT pg_identify_object_as_address(classid, objid, objsubid) FROM pg_catalog.pg_dist_object @@ -1398,4 +1402,3 @@ SELECT pg_identify_object_as_address(classid, objid, objsubid) FROM pg_catalog.p --------------------------------------------------------------------- (0 rows) -DROP TABLE public.nation_local CASCADE; diff --git a/src/test/regress/sql/multi_schema_support.sql b/src/test/regress/sql/multi_schema_support.sql index 1116cabac..7ca60162e 100644 --- a/src/test/regress/sql/multi_schema_support.sql +++ b/src/test/regress/sql/multi_schema_support.sql @@ -802,6 +802,9 @@ ALTER TABLE IF EXISTS non_existent_table SET SCHEMA non_existent_schema; DROP SCHEMA existing_schema, another_existing_schema CASCADE; +-- test DROP SCHEMA with nonexisting schemas +DROP SCHEMA ax, bx, cx, dx, ex, fx, gx, jx; + -- test ALTER TABLE SET SCHEMA with interesting names CREATE SCHEMA "cItuS.T E E N'sSchema"; CREATE SCHEMA "citus-teen's scnd schm."; @@ -985,8 +988,8 @@ SET client_min_messages TO WARNING; SELECT pg_identify_object_as_address(classid, objid, objsubid) FROM pg_catalog.pg_dist_object WHERE classid=2615 and objid IN (select oid from pg_namespace where nspname='run_test_schema'); +DROP TABLE public.nation_local; DROP SCHEMA run_test_schema, test_schema_support_join_1, test_schema_support_join_2, "Citus'Teen123", "CiTUS.TEEN2", bar, test_schema_support CASCADE; -- verify that the dropped schema is removed from worker's pg_dist_object SELECT pg_identify_object_as_address(classid, objid, objsubid) FROM pg_catalog.pg_dist_object WHERE classid=2615 and objid IN (select oid from pg_namespace where nspname='run_test_schema'); -DROP TABLE public.nation_local CASCADE; From 44e3c3b9c6923d4e2c22a9838d96f088cac410e4 Mon Sep 17 00:00:00 2001 From: Ahmet Gedemenli Date: Wed, 21 Jun 2023 15:24:09 +0300 Subject: [PATCH 112/118] Improve error message for CREATE SCHEMA .. CREATE TABLE (#7024) Improve error message for CREATE SCHEMA .. CREATE TABLE when enable_schema_based_sharding is enabled. --- src/backend/distributed/commands/schema.c | 9 +++++---- src/test/regress/expected/schema_based_sharding.out | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/backend/distributed/commands/schema.c b/src/backend/distributed/commands/schema.c index c16b87fa2..966a264d6 100644 --- a/src/backend/distributed/commands/schema.c +++ b/src/backend/distributed/commands/schema.c @@ -95,10 +95,11 @@ PostprocessCreateSchemaStmt(Node *node, const char *queryString) if (CreateSchemaStmtCreatesTable(createSchemaStmt)) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot create tenant table in CREATE " - "SCHEMA statement"), - errhint("Use CREATE TABLE statement to create " - "tenant tables."))); + errmsg("cannot create distributed schema and table in a " + "single statement"), + errhint("SET citus.enable_schema_based_sharding TO off, " + "or create the schema and table in separate " + "commands."))); } /* diff --git a/src/test/regress/expected/schema_based_sharding.out b/src/test/regress/expected/schema_based_sharding.out index ad456ad8e..00198d7dc 100644 --- a/src/test/regress/expected/schema_based_sharding.out +++ b/src/test/regress/expected/schema_based_sharding.out @@ -314,8 +314,8 @@ WHERE schemaid::regnamespace::text IN ('tenant_1', 'tenant_2'); -- verify that we don't allow creating tenant tables via CREATE SCHEMA command CREATE SCHEMA schema_using_schema_elements CREATE TABLE test_table(a int, b text); -ERROR: cannot create tenant table in CREATE SCHEMA statement -HINT: Use CREATE TABLE statement to create tenant tables. +ERROR: cannot create distributed schema and table in a single statement +HINT: SET citus.enable_schema_based_sharding TO off, or create the schema and table in separate commands. CREATE SCHEMA tenant_4; CREATE TABLE tenant_4.tbl_1(a int, b text); CREATE TABLE tenant_4.tbl_2(a int, b text); From 99edb2675ffd1c6a1424a01bed54af7b6fed4752 Mon Sep 17 00:00:00 2001 From: Ahmet Gedemenli Date: Thu, 22 Jun 2023 18:10:12 +0300 Subject: [PATCH 113/118] Improve error/hint messages related to schema-based sharding (#7027) Improve error/hint messages related to schema-based sharding --- .../distributed/commands/alter_table.c | 3 +- src/backend/distributed/commands/table.c | 16 +++++----- .../distributed/utils/colocation_utils.c | 8 ++--- .../expected/schema_based_sharding.out | 30 +++++++++---------- 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/backend/distributed/commands/alter_table.c b/src/backend/distributed/commands/alter_table.c index 5fa670ab2..788a3b8b0 100644 --- a/src/backend/distributed/commands/alter_table.c +++ b/src/backend/distributed/commands/alter_table.c @@ -457,7 +457,8 @@ EnsureUndistributeTenantTableSafe(Oid relationId, const char *operationName) ereport(ERROR, (errmsg("%s is not allowed for partition table %s in distributed " "schema %s", operationName, tableName, schemaName), errdetail("partition table should be under the same distributed " - "schema as its parent and be a tenant table."))); + "schema as its parent and be a " + "distributed schema table."))); } /* diff --git a/src/backend/distributed/commands/table.c b/src/backend/distributed/commands/table.c index 174c34946..cd26a741f 100644 --- a/src/backend/distributed/commands/table.c +++ b/src/backend/distributed/commands/table.c @@ -237,8 +237,8 @@ PostprocessCreateTableStmt(CreateStmt *createStatement, const char *queryString) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot create a tenant table by using CREATE TABLE " - "OF syntax"))); + errmsg("cannot create tables in a distributed schema using " + "CREATE TABLE OF syntax"))); } if (createStatement->inhRelations != NIL) @@ -254,8 +254,8 @@ PostprocessCreateTableStmt(CreateStmt *createStatement, const char *queryString) if (IsTenantSchema(schemaId)) { - ereport(ERROR, (errmsg("tenant tables cannot inherit or " - "be inherited"))); + ereport(ERROR, (errmsg("tables in a distributed schema cannot inherit " + "or be inherited"))); } RangeVar *parentRelation = NULL; @@ -272,8 +272,8 @@ PostprocessCreateTableStmt(CreateStmt *createStatement, const char *queryString) */ if (IsTenantSchema(get_rel_namespace(parentRelationId))) { - ereport(ERROR, (errmsg("tenant tables cannot inherit or " - "be inherited"))); + ereport(ERROR, (errmsg("tables in a distributed schema cannot " + "inherit or be inherited"))); } else if (IsCitusTable(parentRelationId)) { @@ -4257,8 +4257,8 @@ ConvertToTenantTableIfNecessary(AlterObjectSchemaStmt *stmt) char *schemaName = get_namespace_name(schemaId); char *tableName = stmt->relation->relname; - ereport(NOTICE, (errmsg("converting table %s to a tenant table in distributed " - "schema %s", tableName, schemaName))); + ereport(NOTICE, (errmsg("Moving %s into distributed schema %s", + tableName, schemaName))); CreateTenantSchemaTable(relationId); } diff --git a/src/backend/distributed/utils/colocation_utils.c b/src/backend/distributed/utils/colocation_utils.c index 62a13af3a..c386e9fcf 100644 --- a/src/backend/distributed/utils/colocation_utils.c +++ b/src/backend/distributed/utils/colocation_utils.c @@ -1446,11 +1446,11 @@ EnsureTableCanBeColocatedWith(Oid relationId, char replicationModel, ereport(ERROR, (errmsg("cannot colocate tables %s and %s", sourceRelationName, relationName), - errdetail("Cannot colocate tables with tenant tables " - "by using colocate_with option."), + errdetail("Cannot colocate tables with distributed schema tables" + " by using colocate_with option."), errhint("Consider using \"CREATE TABLE\" statement " - "to create this table as a tenant table in " - "the same schema to automatically colocate " + "to create this table as a single-shard distributed " + "table in the same schema to automatically colocate " "it with %s.%s", sourceRelationSchemaName, sourceRelationName))); } diff --git a/src/test/regress/expected/schema_based_sharding.out b/src/test/regress/expected/schema_based_sharding.out index 00198d7dc..d7b7b4710 100644 --- a/src/test/regress/expected/schema_based_sharding.out +++ b/src/test/regress/expected/schema_based_sharding.out @@ -109,7 +109,7 @@ ERROR: relation "tenant_2.test_table2" does not exist -- verify we can set regular table's schema to distributed schema CREATE TABLE regular_schema.test_table3(id int); ALTER TABLE regular_schema.test_table3 SET SCHEMA tenant_2; -NOTICE: converting table test_table3 to a tenant table in distributed schema tenant_2 +NOTICE: Moving test_table3 into distributed schema tenant_2 -- verify that tenant_2.test_table3 is recorded in pg_dist_partition as a single-shard table. SELECT COUNT(*)=1 FROM pg_dist_partition WHERE logicalrelid = 'tenant_2.test_table3'::regclass AND @@ -126,7 +126,7 @@ ERROR: relation "regular_schema.test_table3" does not exist CREATE TABLE tenant_2.test_table4(id int); ALTER TABLE tenant_2.test_table4 SET SCHEMA tenant_3; NOTICE: undistributing table test_table4 in distributed schema tenant_2 before altering its schema -NOTICE: converting table test_table4 to a tenant table in distributed schema tenant_3 +NOTICE: Moving test_table4 into distributed schema tenant_3 -- verify that tenant_3.test_table4 is recorded in pg_dist_partition as a single-shard table. SELECT COUNT(*)=1 FROM pg_dist_partition WHERE logicalrelid = 'tenant_3.test_table4'::regclass AND @@ -142,7 +142,7 @@ ERROR: relation "tenant_2.test_table4" does not exist -- verify that we can put a local table in regular schema into distributed schema CREATE TABLE regular_schema.pg_local_tbl(id int); ALTER TABLE regular_schema.pg_local_tbl SET SCHEMA tenant_2; -NOTICE: converting table pg_local_tbl to a tenant table in distributed schema tenant_2 +NOTICE: Moving pg_local_tbl into distributed schema tenant_2 -- verify that we can put a Citus local table in regular schema into distributed schema CREATE TABLE regular_schema.citus_local_tbl(id int); SELECT citus_add_local_table_to_metadata('regular_schema.citus_local_tbl'); @@ -152,7 +152,7 @@ SELECT citus_add_local_table_to_metadata('regular_schema.citus_local_tbl'); (1 row) ALTER TABLE regular_schema.citus_local_tbl SET SCHEMA tenant_2; -NOTICE: converting table citus_local_tbl to a tenant table in distributed schema tenant_2 +NOTICE: Moving citus_local_tbl into distributed schema tenant_2 -- verify that we do not allow a hash distributed table in regular schema into distributed schema CREATE TABLE regular_schema.hash_dist_tbl(id int); SELECT create_distributed_table('regular_schema.hash_dist_tbl', 'id'); @@ -183,7 +183,7 @@ NOTICE: undistributing table tenant_tbl in distributed schema tenant_2 before a CREATE TABLE tenant_2.tenant_tbl2(id int); ALTER TABLE tenant_2.tenant_tbl2 SET SCHEMA tenant_3; NOTICE: undistributing table tenant_tbl2 in distributed schema tenant_2 before altering its schema -NOTICE: converting table tenant_tbl2 to a tenant table in distributed schema tenant_3 +NOTICE: Moving tenant_tbl2 into distributed schema tenant_3 -- verify that we do not allow a local table in regular schema into distributed schema if it has foreign key to a non-reference table in another schema CREATE TABLE regular_schema.pg_local_tbl1(id int PRIMARY KEY); CREATE TABLE regular_schema.pg_local_tbl2(id int REFERENCES regular_schema.pg_local_tbl1(id)); @@ -193,7 +193,7 @@ DETAIL: "tenant_2.pg_local_tbl2" references "regular_schema.pg_local_tbl1" via -- verify that we allow a local table in regular schema into distributed schema if it has foreign key to a reference table in another schema CREATE TABLE regular_schema.pg_local_tbl3(id int REFERENCES regular_schema.ref_tbl(id)); ALTER TABLE regular_schema.pg_local_tbl3 SET SCHEMA tenant_2; -NOTICE: converting table pg_local_tbl3 to a tenant table in distributed schema tenant_2 +NOTICE: Moving pg_local_tbl3 into distributed schema tenant_2 -- verify that we do not allow a table in tenant schema into regular schema if it has foreign key to/from another table in the same schema CREATE TABLE tenant_2.tenant_tbl1(id int PRIMARY KEY); CREATE TABLE tenant_2.tenant_tbl2(id int REFERENCES tenant_2.tenant_tbl1(id)); @@ -550,7 +550,7 @@ CREATE TABLE tenant_4.employees OF employee_type ( PRIMARY KEY (name), salary WITH OPTIONS DEFAULT 1000 ); -ERROR: cannot create a tenant table by using CREATE TABLE OF syntax +ERROR: cannot create tables in a distributed schema using CREATE TABLE OF syntax -- verify that we act accordingly when if not exists is used CREATE TABLE IF NOT EXISTS tenant_4.tbl_6(a int, b text); CREATE TABLE IF NOT EXISTS tenant_4.tbl_6(a int, b text); @@ -614,18 +614,18 @@ WHERE logicalrelid = 'regular_schema.local_table_using_like'::regclass; -- verify that INHERITS syntax is not supported when creating a tenant table CREATE TABLE tenant_5.test_table_inherits_1(x int) INHERITS (tenant_5.tbl_1); -- using a table from the same schema -ERROR: tenant tables cannot inherit or be inherited +ERROR: tables in a distributed schema cannot inherit or be inherited CREATE TABLE tenant_5.test_table_inherits_2(x int) INHERITS (tenant_4.tbl_1); -- using a table from another schema -ERROR: tenant tables cannot inherit or be inherited +ERROR: tables in a distributed schema cannot inherit or be inherited CREATE TABLE tenant_5.test_table_inherits_3(x int) INHERITS (regular_schema.local); -- using a local table -ERROR: tenant tables cannot inherit or be inherited +ERROR: tables in a distributed schema cannot inherit or be inherited CREATE TABLE tenant_5.test_table_inherits_4(x int) INHERITS (regular_schema.citus_local); -- using a citus local table -ERROR: tenant tables cannot inherit or be inherited +ERROR: tables in a distributed schema cannot inherit or be inherited CREATE TABLE tenant_5.test_table_inherits_5(x int) INHERITS (regular_schema.dist); -- using a distributed table -ERROR: tenant tables cannot inherit or be inherited +ERROR: tables in a distributed schema cannot inherit or be inherited -- verify that INHERITS syntax is not supported when creating a local table based on a tenant table CREATE TABLE regular_schema.local_table_using_inherits(x int) INHERITS (tenant_5.tbl_1); -ERROR: tenant tables cannot inherit or be inherited +ERROR: tables in a distributed schema cannot inherit or be inherited CREATE TABLE tenant_5.tbl_2(a int, b text); CREATE SCHEMA "CiTuS.TeeN_108"; ALTER SCHEMA "CiTuS.TeeN_108" RENAME TO citus_teen_proper; @@ -814,8 +814,8 @@ $$); CREATE TABLE regular_schema.null_shard_key_1(a int, b text); SELECT create_distributed_table('regular_schema.null_shard_key_1', null, colocate_with => 'tenant_5.tbl_2'); ERROR: cannot colocate tables tbl_2 and null_shard_key_1 -DETAIL: Cannot colocate tables with tenant tables by using colocate_with option. -HINT: Consider using "CREATE TABLE" statement to create this table as a tenant table in the same schema to automatically colocate it with tenant_5.tbl_2 +DETAIL: Cannot colocate tables with distributed schema tables by using colocate_with option. +HINT: Consider using "CREATE TABLE" statement to create this table as a single-shard distributed table in the same schema to automatically colocate it with tenant_5.tbl_2 SELECT create_distributed_table('regular_schema.null_shard_key_1', 'a', colocate_with => 'tenant_5.tbl_2'); ERROR: cannot colocate tables tbl_2 and null_shard_key_1 DETAIL: Distribution column types don't match for tbl_2 and null_shard_key_1. From 387b5f80f94dfc78f47a58018a03909a34ebefbe Mon Sep 17 00:00:00 2001 From: Teja Mupparti Date: Thu, 25 May 2023 17:13:04 -0700 Subject: [PATCH 114/118] Fixes the bug#6785 --- .../distributed/planner/merge_planner.c | 62 +++++- src/test/regress/expected/merge.out | 199 ++++++++++++++++-- .../regress/expected/merge_repartition1.out | 167 ++++++++++++--- src/test/regress/sql/merge.sql | 96 +++++++++ src/test/regress/sql/merge_repartition1.sql | 65 ++++++ 5 files changed, 535 insertions(+), 54 deletions(-) diff --git a/src/backend/distributed/planner/merge_planner.c b/src/backend/distributed/planner/merge_planner.c index 13d0b84d6..6a80a7c33 100644 --- a/src/backend/distributed/planner/merge_planner.c +++ b/src/backend/distributed/planner/merge_planner.c @@ -33,6 +33,7 @@ #include "distributed/query_colocation_checker.h" #include "distributed/repartition_executor.h" #include "distributed/shared_library_init.h" +#include "distributed/shard_pruning.h" #if PG_VERSION_NUM >= PG_VERSION_15 @@ -40,6 +41,9 @@ static int SourceResultPartitionColumnIndex(Query *mergeQuery, List *sourceTargetList, CitusTableCacheEntry *targetRelation); static Var * ValidateAndReturnVarIfSupported(Node *entryExpr); +static DeferredErrorMessage * DeferErrorIfTargetHasFalseClause(Oid targetRelationId, + PlannerRestrictionContext * + plannerRestrictionContext); static void ErrorIfMergeQueryQualAndTargetListNotSupported(Oid targetRelationId, Query *originalQuery); static void ErrorIfMergeNotSupported(Query *query, Oid targetRelationId, @@ -51,7 +55,8 @@ static DeferredErrorMessage * DeferErrorIfRoutableMergeNotSupported(Query *query List *rangeTableList, PlannerRestrictionContext * - plannerRestrictionContext); + plannerRestrictionContext, + Oid targetRelationId); static DeferredErrorMessage * MergeQualAndTargetListFunctionsSupported(Oid resultRelationId, Query *query, @@ -164,7 +169,8 @@ CreateRouterMergePlan(Oid targetRelationId, Query *originalQuery, Query *query, distributedPlan->planningError = DeferErrorIfRoutableMergeNotSupported(originalQuery, rangeTableList, - plannerRestrictionContext); + plannerRestrictionContext, + targetRelationId); if (distributedPlan->planningError != NULL) { return distributedPlan; @@ -926,13 +932,52 @@ ErrorIfMergeNotSupported(Query *query, Oid targetRelationId, List *rangeTableLis } +/* + * DeferErrorIfTargetHasFalseClause checks for the presence of a false clause in the + * target relation and throws an exception if found. Router planner prunes all the shards + * for relations with such clauses, resulting in no task generation for the job. However, + * in the case of a MERGE query, tasks still need to be generated for the shards of the + * source relation. + */ +static DeferredErrorMessage * +DeferErrorIfTargetHasFalseClause(Oid targetRelationId, + PlannerRestrictionContext *plannerRestrictionContext) +{ + ListCell *restrictionCell = NULL; + foreach(restrictionCell, + plannerRestrictionContext->relationRestrictionContext->relationRestrictionList) + { + RelationRestriction *relationRestriction = + (RelationRestriction *) lfirst(restrictionCell); + Oid relationId = relationRestriction->relationId; + + /* Check only for target relation */ + if (relationId != targetRelationId) + { + continue; + } + + List *baseRestrictionList = relationRestriction->relOptInfo->baserestrictinfo; + List *restrictClauseList = get_all_actual_clauses(baseRestrictionList); + if (ContainsFalseClause(restrictClauseList)) + { + return DeferredError(ERRCODE_FEATURE_NOT_SUPPORTED, + "Routing query is not possible with " + "no shards for target", NULL, NULL); + } + } + return NULL; +} + + /* * DeferErrorIfRoutableMergeNotSupported Checks for conditions that prevent pushable planning, if * found, raises a deferred error, which then continues to try repartitioning strategy. */ static DeferredErrorMessage * DeferErrorIfRoutableMergeNotSupported(Query *query, List *rangeTableList, - PlannerRestrictionContext *plannerRestrictionContext) + PlannerRestrictionContext *plannerRestrictionContext, + Oid targetRelationId) { List *distTablesList = NIL; List *refTablesList = NIL; @@ -1020,6 +1065,17 @@ DeferErrorIfRoutableMergeNotSupported(Query *query, List *rangeTableList, "conflict, use ON instead", NULL, NULL); } + deferredError = DeferErrorIfTargetHasFalseClause(targetRelationId, + plannerRestrictionContext); + if (deferredError) + { + ereport(DEBUG1, (errmsg("Target relation has a filter of the " + "form: false (AND ..), which results " + "in empty shards, but we still need " + "to evaluate NOT-MATCHED clause, try " + "repartitioning"))); + return deferredError; + } return NULL; } diff --git a/src/test/regress/expected/merge.out b/src/test/regress/expected/merge.out index 882a22091..3cb69936c 100644 --- a/src/test/regress/expected/merge.out +++ b/src/test/regress/expected/merge.out @@ -2973,6 +2973,164 @@ SELECT COUNT(*) FROM demo_distributed where id1 = 2; 7 (1 row) +-- +-- Test FALSE filters +-- +CREATE TABLE source_filter(order_id INT, customer_id INT, order_center VARCHAR, order_time timestamp); +CREATE TABLE target_filter(customer_id INT, last_order_id INT, order_center VARCHAR, order_count INT, last_order timestamp); +SELECT create_distributed_table('source_filter', 'customer_id'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('target_filter', 'customer_id', colocate_with => 'source_filter'); + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +CREATE FUNCTION load_filter() RETURNS VOID AS $$ + +TRUNCATE target_filter; +TRUNCATE source_filter; + +INSERT INTO target_filter VALUES(100, 11, 'trg', -1, '2022-01-01 00:00:00'); -- Match UPDATE +INSERT INTO target_filter VALUES(200, 11, 'trg', -1, '2022-01-01 00:00:00'); -- Match DELETE + +INSERT INTO source_filter VALUES(12, 100, 'src', '2022-01-01 00:00:00'); +INSERT INTO source_filter VALUES(12, 200, 'src', '2022-01-01 00:00:00'); +INSERT INTO source_filter VALUES(12, 300, 'src', '2022-01-01 00:00:00'); + +$$ +LANGUAGE SQL; +--WHEN MATCH and FALSE +SELECT load_filter(); + load_filter +--------------------------------------------------------------------- + +(1 row) + +MERGE INTO target_filter t +USING source_filter s +ON s.customer_id = t.customer_id +WHEN MATCHED AND t.customer_id = 100 AND (FALSE) THEN + UPDATE SET order_count = 999 +WHEN MATCHED AND t.customer_id = 200 THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.customer_id, s.order_id, s.order_center, 1, s.order_time); +SELECT * FROM target_filter ORDER BY 1, 2; + customer_id | last_order_id | order_center | order_count | last_order +--------------------------------------------------------------------- + 100 | 11 | trg | -1 | Sat Jan 01 00:00:00 2022 + 300 | 12 | src | 1 | Sat Jan 01 00:00:00 2022 +(2 rows) + +--WHEN NOT MATCH and 1=0 +SELECT load_filter(); + load_filter +--------------------------------------------------------------------- + +(1 row) + +MERGE INTO target_filter t +USING source_filter s +ON s.customer_id = t.customer_id +WHEN MATCHED AND t.customer_id = 100 THEN + UPDATE SET order_count = 999 +WHEN MATCHED AND t.customer_id = 200 THEN + DELETE +WHEN NOT MATCHED AND (1=0) THEN + INSERT VALUES(s.customer_id, s.order_id, s.order_center, 1, s.order_time); +SELECT * FROM target_filter ORDER BY 1, 2; + customer_id | last_order_id | order_center | order_count | last_order +--------------------------------------------------------------------- + 100 | 11 | trg | 999 | Sat Jan 01 00:00:00 2022 +(1 row) + +--ON t.key = s.key AND 1 < 0 +SELECT load_filter(); + load_filter +--------------------------------------------------------------------- + +(1 row) + +MERGE INTO target_filter t +USING source_filter s +ON s.customer_id = t.customer_id AND 1 < 0 +WHEN MATCHED AND t.customer_id = 100 THEN + UPDATE SET order_count = 999 +WHEN MATCHED AND t.customer_id = 200 THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.customer_id, s.order_id, s.order_center, 1, s.order_time); +ERROR: The required join operation is missing between the target's distribution column and any expression originating from the source. The issue may arise from either a non-equi-join or a mismatch in the datatypes of the columns being joined. +DETAIL: Without a equi-join condition on the target's distribution column, the source rows cannot be efficiently redistributed, and the NOT-MATCHED condition cannot be evaluated unambiguously. This can result in incorrect or unexpected results when attempting to merge tables in a distributed setting +SELECT * FROM target_filter ORDER BY 1, 2; + customer_id | last_order_id | order_center | order_count | last_order +--------------------------------------------------------------------- + 100 | 11 | trg | -1 | Sat Jan 01 00:00:00 2022 + 200 | 11 | trg | -1 | Sat Jan 01 00:00:00 2022 +(2 rows) + +--(SELECT * FROM source_filter WHERE false) as source_filter +SELECT load_filter(); + load_filter +--------------------------------------------------------------------- + +(1 row) + +MERGE INTO target_filter t +USING (SELECT * FROM source_filter WHERE false) s +ON s.customer_id = t.customer_id +WHEN MATCHED AND t.customer_id = 100 THEN + UPDATE SET order_count = 999 +WHEN MATCHED AND t.customer_id = 200 THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.customer_id, s.order_id, s.order_center, 1, s.order_time); +SELECT * FROM target_filter ORDER BY 1, 2; + customer_id | last_order_id | order_center | order_count | last_order +--------------------------------------------------------------------- + 100 | 11 | trg | -1 | Sat Jan 01 00:00:00 2022 + 200 | 11 | trg | -1 | Sat Jan 01 00:00:00 2022 +(2 rows) + +-- Bug 6785 +CREATE TABLE source_6785( id integer, z int, d jsonb); +CREATE TABLE target_6785( id integer, z int, d jsonb); +SELECT create_distributed_table('target_6785','id'), create_distributed_table('source_6785', 'id'); + create_distributed_table | create_distributed_table +--------------------------------------------------------------------- + | +(1 row) + +INSERT INTO source_6785 SELECT i,i FROM generate_series(0,5)i; +SET client_min_messages TO DEBUG1; +MERGE INTO target_6785 sda +USING (SELECT * FROM source_6785 WHERE id = 1) sdn +ON sda.id = sdn.id AND sda.id = 2 +WHEN NOT matched THEN + INSERT (id, z) VALUES (sdn.id, 5); +DEBUG: Target relation has a filter of the form: false (AND ..), which results in empty shards, but we still need to evaluate NOT-MATCHED clause, try repartitioning +DEBUG: Routing query is not possible with no shards for target +DEBUG: Creating MERGE repartition plan +DEBUG: Using column - index:0 from the source list to redistribute +DEBUG: Collect source query results on coordinator +DEBUG: Create a MERGE task list that needs to be routed +DEBUG: +DEBUG: +DEBUG: +DEBUG: +DEBUG: Execute MERGE task list +RESET client_min_messages; +SELECT * FROM target_6785 ORDER BY 1; + id | z | d +--------------------------------------------------------------------- + 1 | 5 | +(1 row) + -- -- Error and Unsupported scenarios -- @@ -3725,21 +3883,21 @@ INSERT INTO postgres_local_table SELECT i, i FROM generate_series(5, 10) i; -- with a colocated table MERGE INTO nullkey_c1_t1 USING nullkey_c1_t2 ON (nullkey_c1_t1.a = nullkey_c1_t2.a) WHEN MATCHED THEN UPDATE SET b = nullkey_c1_t2.b; -DEBUG: +DEBUG: DEBUG: Creating MERGE router plan MERGE INTO nullkey_c1_t1 USING nullkey_c1_t2 ON (nullkey_c1_t1.a = nullkey_c1_t2.a) WHEN MATCHED THEN DELETE; -DEBUG: +DEBUG: DEBUG: Creating MERGE router plan MERGE INTO nullkey_c1_t1 USING nullkey_c1_t2 ON (nullkey_c1_t1.a = nullkey_c1_t2.a) WHEN MATCHED THEN UPDATE SET b = nullkey_c1_t2.b WHEN NOT MATCHED THEN INSERT VALUES (nullkey_c1_t2.a, nullkey_c1_t2.b); -DEBUG: +DEBUG: DEBUG: Creating MERGE router plan MERGE INTO nullkey_c1_t1 USING nullkey_c1_t2 ON (nullkey_c1_t1.a = nullkey_c1_t2.a) WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT VALUES (nullkey_c1_t2.a, nullkey_c1_t2.b); -DEBUG: +DEBUG: DEBUG: Creating MERGE router plan -- with non-colocated single-shard table MERGE INTO nullkey_c1_t1 USING nullkey_c2_t1 ON (nullkey_c1_t1.a = nullkey_c2_t1.a) @@ -3774,14 +3932,14 @@ DEBUG: Distributed planning for a fast-path router query DEBUG: Creating router plan DEBUG: Collect source query results on coordinator DEBUG: Create a MERGE task list that needs to be routed -DEBUG: -DEBUG: distributed statement: MERGE INTO query_single_shard_table.distributed_table_4000173 citus_table_alias USING (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('merge_into_XXX_4000173'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) nullkey_c1_t1 ON (nullkey_c1_t1.a OPERATOR(pg_catalog.=) citus_table_alias.a) WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (a, b) VALUES (nullkey_c1_t1.a, nullkey_c1_t1.b) -DEBUG: -DEBUG: distributed statement: MERGE INTO query_single_shard_table.distributed_table_4000174 citus_table_alias USING (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('merge_into_XXX_4000174'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) nullkey_c1_t1 ON (nullkey_c1_t1.a OPERATOR(pg_catalog.=) citus_table_alias.a) WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (a, b) VALUES (nullkey_c1_t1.a, nullkey_c1_t1.b) -DEBUG: -DEBUG: distributed statement: MERGE INTO query_single_shard_table.distributed_table_4000175 citus_table_alias USING (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('merge_into_XXX_4000175'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) nullkey_c1_t1 ON (nullkey_c1_t1.a OPERATOR(pg_catalog.=) citus_table_alias.a) WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (a, b) VALUES (nullkey_c1_t1.a, nullkey_c1_t1.b) -DEBUG: -DEBUG: distributed statement: MERGE INTO query_single_shard_table.distributed_table_4000176 citus_table_alias USING (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('merge_into_XXX_4000176'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) nullkey_c1_t1 ON (nullkey_c1_t1.a OPERATOR(pg_catalog.=) citus_table_alias.a) WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (a, b) VALUES (nullkey_c1_t1.a, nullkey_c1_t1.b) +DEBUG: +DEBUG: distributed statement: MERGE INTO query_single_shard_table.distributed_table_4000189 citus_table_alias USING (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('merge_into_XXX_4000189'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) nullkey_c1_t1 ON (nullkey_c1_t1.a OPERATOR(pg_catalog.=) citus_table_alias.a) WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (a, b) VALUES (nullkey_c1_t1.a, nullkey_c1_t1.b) +DEBUG: +DEBUG: distributed statement: MERGE INTO query_single_shard_table.distributed_table_4000190 citus_table_alias USING (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('merge_into_XXX_4000190'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) nullkey_c1_t1 ON (nullkey_c1_t1.a OPERATOR(pg_catalog.=) citus_table_alias.a) WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (a, b) VALUES (nullkey_c1_t1.a, nullkey_c1_t1.b) +DEBUG: +DEBUG: distributed statement: MERGE INTO query_single_shard_table.distributed_table_4000191 citus_table_alias USING (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('merge_into_XXX_4000191'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) nullkey_c1_t1 ON (nullkey_c1_t1.a OPERATOR(pg_catalog.=) citus_table_alias.a) WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (a, b) VALUES (nullkey_c1_t1.a, nullkey_c1_t1.b) +DEBUG: +DEBUG: distributed statement: MERGE INTO query_single_shard_table.distributed_table_4000192 citus_table_alias USING (SELECT intermediate_result.a, intermediate_result.b FROM read_intermediate_result('merge_into_XXX_4000192'::text, 'binary'::citus_copy_format) intermediate_result(a integer, b integer)) nullkey_c1_t1 ON (nullkey_c1_t1.a OPERATOR(pg_catalog.=) citus_table_alias.a) WHEN MATCHED THEN DELETE WHEN NOT MATCHED THEN INSERT (a, b) VALUES (nullkey_c1_t1.a, nullkey_c1_t1.b) -- with a reference table MERGE INTO nullkey_c1_t1 USING reference_table ON (nullkey_c1_t1.a = reference_table.a) WHEN MATCHED THEN UPDATE SET b = reference_table.b; @@ -3824,7 +3982,7 @@ WITH cte AS ( ) MERGE INTO nullkey_c1_t1 USING cte ON (nullkey_c1_t1.a = cte.a) WHEN MATCHED THEN UPDATE SET b = cte.b; -DEBUG: +DEBUG: DEBUG: Creating MERGE router plan WITH cte AS ( SELECT * FROM distributed_table @@ -3989,7 +4147,7 @@ CONTEXT: SQL statement "SELECT citus_drop_all_shards(v_obj.objid, v_obj.schema_ PL/pgSQL function citus_drop_trigger() line XX at PERFORM DROP FUNCTION merge_when_and_write(); DROP SCHEMA merge_schema CASCADE; -NOTICE: drop cascades to 98 other objects +NOTICE: drop cascades to 103 other objects DETAIL: drop cascades to function insert_data() drop cascades to table local_local drop cascades to table target @@ -4076,15 +4234,18 @@ drop cascades to table demo_distributed drop cascades to table demo_source_table drop cascades to table pg_demo_result drop cascades to table dist_demo_result +drop cascades to table source_filter +drop cascades to table target_filter +drop cascades to function load_filter() +drop cascades to table source_6785 +drop cascades to table target_6785 drop cascades to function add_s(integer,integer) drop cascades to table pg -drop cascades to table t1_4000158 -drop cascades to table s1_4000159 +drop cascades to table t1_4000174 +drop cascades to table s1_4000175 drop cascades to table t1 drop cascades to table s1 drop cascades to table dist_target drop cascades to table dist_source drop cascades to view show_tables -drop cascades to table target_columnar -drop cascades to table target_1 -drop cascades to table source_2 +and 3 other objects (see server log for list) diff --git a/src/test/regress/expected/merge_repartition1.out b/src/test/regress/expected/merge_repartition1.out index 0c3c47389..279358e30 100644 --- a/src/test/regress/expected/merge_repartition1.out +++ b/src/test/regress/expected/merge_repartition1.out @@ -1000,28 +1000,14 @@ SQL function "compare_data" statement 2 (1 row) -- Test source-query that requires repartitioning on top of MERGE repartitioning +SET client_min_messages TO WARNING; SELECT cleanup_data(); -NOTICE: creating a new table for merge_repartition1_schema.citus_target -CONTEXT: SQL function "cleanup_data" statement 5 -NOTICE: moving the data of merge_repartition1_schema.citus_target -CONTEXT: SQL function "cleanup_data" statement 5 -NOTICE: dropping the old merge_repartition1_schema.citus_target -CONTEXT: SQL function "cleanup_data" statement 5 -NOTICE: renaming the new table to merge_repartition1_schema.citus_target -CONTEXT: SQL function "cleanup_data" statement 5 -NOTICE: creating a new table for merge_repartition1_schema.citus_source -CONTEXT: SQL function "cleanup_data" statement 6 -NOTICE: moving the data of merge_repartition1_schema.citus_source -CONTEXT: SQL function "cleanup_data" statement 6 -NOTICE: dropping the old merge_repartition1_schema.citus_source -CONTEXT: SQL function "cleanup_data" statement 6 -NOTICE: renaming the new table to merge_repartition1_schema.citus_source -CONTEXT: SQL function "cleanup_data" statement 6 cleanup_data --------------------------------------------------------------------- (1 row) +RESET client_min_messages; SELECT setup_data(); setup_data --------------------------------------------------------------------- @@ -1159,28 +1145,14 @@ NOTICE: renaming the new table to merge_repartition1_schema.citus_source (1 row) -- Test CTE/Subquery in merge-actions (works only for router query) +SET client_min_messages TO WARNING; SELECT cleanup_data(); -NOTICE: creating a new table for merge_repartition1_schema.citus_target -CONTEXT: SQL function "cleanup_data" statement 5 -NOTICE: moving the data of merge_repartition1_schema.citus_target -CONTEXT: SQL function "cleanup_data" statement 5 -NOTICE: dropping the old merge_repartition1_schema.citus_target -CONTEXT: SQL function "cleanup_data" statement 5 -NOTICE: renaming the new table to merge_repartition1_schema.citus_target -CONTEXT: SQL function "cleanup_data" statement 5 -NOTICE: creating a new table for merge_repartition1_schema.citus_source -CONTEXT: SQL function "cleanup_data" statement 6 -NOTICE: moving the data of merge_repartition1_schema.citus_source -CONTEXT: SQL function "cleanup_data" statement 6 -NOTICE: dropping the old merge_repartition1_schema.citus_source -CONTEXT: SQL function "cleanup_data" statement 6 -NOTICE: renaming the new table to merge_repartition1_schema.citus_source -CONTEXT: SQL function "cleanup_data" statement 6 cleanup_data --------------------------------------------------------------------- (1 row) +RESET client_min_messages; SELECT setup_data(); setup_data --------------------------------------------------------------------- @@ -1233,6 +1205,137 @@ SQL function "compare_data" statement 2 (1 row) +-- +-- Test target with false clause +-- +SET client_min_messages TO WARNING; +SELECT cleanup_data(); + cleanup_data +--------------------------------------------------------------------- + +(1 row) + +RESET client_min_messages; +SELECT setup_data(); + setup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_target', 'id'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_target$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_source', 'id', colocate_with => 'citus_target'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_source$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +MERGE INTO pg_target t +USING (SELECT * FROM pg_source WHERE id > 2500) AS s +ON t.id = s.id AND t.id < 2500 +WHEN MATCHED AND t.id <= 5500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); +MERGE INTO citus_target t +USING (SELECT * FROM citus_source WHERE id > 2500) AS s +ON t.id = s.id AND t.id < 2500 +WHEN MATCHED AND t.id <= 5500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); +SELECT compare_data(); +NOTICE: The average of pg_target.id is equal to citus_target.id +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 1 +NOTICE: The average of pg_target.val is equal to citus_target.val +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 2 + compare_data +--------------------------------------------------------------------- + +(1 row) + +SET client_min_messages TO WARNING; +SELECT cleanup_data(); + cleanup_data +--------------------------------------------------------------------- + +(1 row) + +RESET client_min_messages; +SELECT setup_data(); + setup_data +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_target', 'id'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_target$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +SELECT create_distributed_table('citus_source', 'id', colocate_with => 'citus_target'); +NOTICE: Copying data from local table... +NOTICE: copying the data has completed +DETAIL: The local data in the table is no longer visible, but is still on disk. +HINT: To remove the local data, run: SELECT truncate_local_data_after_distributing_table($$merge_repartition1_schema.citus_source$$) + create_distributed_table +--------------------------------------------------------------------- + +(1 row) + +MERGE INTO pg_target t +USING (SELECT * FROM pg_source WHERE id = 2500) AS s +ON t.id = s.id AND t.id = 5000 +WHEN MATCHED AND t.id <= 5500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); +MERGE INTO citus_target t +USING (SELECT * FROM citus_source WHERE id = 2500) AS s +ON t.id = s.id AND t.id = 5000 +WHEN MATCHED AND t.id <= 5500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); +SELECT compare_data(); +NOTICE: The average of pg_target.id is equal to citus_target.id +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 1 +NOTICE: The average of pg_target.val is equal to citus_target.val +CONTEXT: PL/pgSQL function check_data(text,text,text,text) line XX at RAISE +SQL function "compare_data" statement 2 + compare_data +--------------------------------------------------------------------- + +(1 row) + DROP SCHEMA merge_repartition1_schema CASCADE; NOTICE: drop cascades to 8 other objects DETAIL: drop cascades to table pg_target diff --git a/src/test/regress/sql/merge.sql b/src/test/regress/sql/merge.sql index db3a76fb6..4fb911736 100644 --- a/src/test/regress/sql/merge.sql +++ b/src/test/regress/sql/merge.sql @@ -1855,6 +1855,102 @@ WHEN MATCHED THEN UPDATE SET val1 = 150; SELECT COUNT(*) FROM demo_distributed where val1 = 150; SELECT COUNT(*) FROM demo_distributed where id1 = 2; +-- +-- Test FALSE filters +-- +CREATE TABLE source_filter(order_id INT, customer_id INT, order_center VARCHAR, order_time timestamp); +CREATE TABLE target_filter(customer_id INT, last_order_id INT, order_center VARCHAR, order_count INT, last_order timestamp); + +SELECT create_distributed_table('source_filter', 'customer_id'); +SELECT create_distributed_table('target_filter', 'customer_id', colocate_with => 'source_filter'); + +CREATE FUNCTION load_filter() RETURNS VOID AS $$ + +TRUNCATE target_filter; +TRUNCATE source_filter; + +INSERT INTO target_filter VALUES(100, 11, 'trg', -1, '2022-01-01 00:00:00'); -- Match UPDATE +INSERT INTO target_filter VALUES(200, 11, 'trg', -1, '2022-01-01 00:00:00'); -- Match DELETE + +INSERT INTO source_filter VALUES(12, 100, 'src', '2022-01-01 00:00:00'); +INSERT INTO source_filter VALUES(12, 200, 'src', '2022-01-01 00:00:00'); +INSERT INTO source_filter VALUES(12, 300, 'src', '2022-01-01 00:00:00'); + +$$ +LANGUAGE SQL; + +--WHEN MATCH and FALSE +SELECT load_filter(); +MERGE INTO target_filter t +USING source_filter s +ON s.customer_id = t.customer_id +WHEN MATCHED AND t.customer_id = 100 AND (FALSE) THEN + UPDATE SET order_count = 999 +WHEN MATCHED AND t.customer_id = 200 THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.customer_id, s.order_id, s.order_center, 1, s.order_time); + +SELECT * FROM target_filter ORDER BY 1, 2; + +--WHEN NOT MATCH and 1=0 +SELECT load_filter(); +MERGE INTO target_filter t +USING source_filter s +ON s.customer_id = t.customer_id +WHEN MATCHED AND t.customer_id = 100 THEN + UPDATE SET order_count = 999 +WHEN MATCHED AND t.customer_id = 200 THEN + DELETE +WHEN NOT MATCHED AND (1=0) THEN + INSERT VALUES(s.customer_id, s.order_id, s.order_center, 1, s.order_time); + +SELECT * FROM target_filter ORDER BY 1, 2; + +--ON t.key = s.key AND 1 < 0 +SELECT load_filter(); +MERGE INTO target_filter t +USING source_filter s +ON s.customer_id = t.customer_id AND 1 < 0 +WHEN MATCHED AND t.customer_id = 100 THEN + UPDATE SET order_count = 999 +WHEN MATCHED AND t.customer_id = 200 THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.customer_id, s.order_id, s.order_center, 1, s.order_time); + +SELECT * FROM target_filter ORDER BY 1, 2; + +--(SELECT * FROM source_filter WHERE false) as source_filter +SELECT load_filter(); +MERGE INTO target_filter t +USING (SELECT * FROM source_filter WHERE false) s +ON s.customer_id = t.customer_id +WHEN MATCHED AND t.customer_id = 100 THEN + UPDATE SET order_count = 999 +WHEN MATCHED AND t.customer_id = 200 THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.customer_id, s.order_id, s.order_center, 1, s.order_time); + +SELECT * FROM target_filter ORDER BY 1, 2; + +-- Bug 6785 +CREATE TABLE source_6785( id integer, z int, d jsonb); +CREATE TABLE target_6785( id integer, z int, d jsonb); +SELECT create_distributed_table('target_6785','id'), create_distributed_table('source_6785', 'id'); +INSERT INTO source_6785 SELECT i,i FROM generate_series(0,5)i; + +SET client_min_messages TO DEBUG1; +MERGE INTO target_6785 sda +USING (SELECT * FROM source_6785 WHERE id = 1) sdn +ON sda.id = sdn.id AND sda.id = 2 +WHEN NOT matched THEN + INSERT (id, z) VALUES (sdn.id, 5); +RESET client_min_messages; + +SELECT * FROM target_6785 ORDER BY 1; + -- -- Error and Unsupported scenarios -- diff --git a/src/test/regress/sql/merge_repartition1.sql b/src/test/regress/sql/merge_repartition1.sql index 4d73e999d..858f4710c 100644 --- a/src/test/regress/sql/merge_repartition1.sql +++ b/src/test/regress/sql/merge_repartition1.sql @@ -434,7 +434,9 @@ WHEN NOT MATCHED THEN SELECT compare_data(); -- Test source-query that requires repartitioning on top of MERGE repartitioning +SET client_min_messages TO WARNING; SELECT cleanup_data(); +RESET client_min_messages; SELECT setup_data(); SELECT create_distributed_table('citus_target', 'id'); SELECT create_distributed_table('citus_source', 'id', colocate_with=>'none'); @@ -489,7 +491,9 @@ SELECT compare_data(); SELECT alter_table_set_access_method('citus_source', 'heap'); -- Test CTE/Subquery in merge-actions (works only for router query) +SET client_min_messages TO WARNING; SELECT cleanup_data(); +RESET client_min_messages; SELECT setup_data(); SELECT create_distributed_table('citus_target', 'id'); SELECT create_distributed_table('citus_source', 'id', colocate_with=>'citus_target'); @@ -512,4 +516,65 @@ WHEN NOT MATCHED AND (SELECT max_a < 5001 FROM (SELECT max(id) as max_a, max(val SELECT compare_data(); +-- +-- Test target with false clause +-- +SET client_min_messages TO WARNING; +SELECT cleanup_data(); +RESET client_min_messages; +SELECT setup_data(); +SELECT create_distributed_table('citus_target', 'id'); +SELECT create_distributed_table('citus_source', 'id', colocate_with => 'citus_target'); + +MERGE INTO pg_target t +USING (SELECT * FROM pg_source WHERE id > 2500) AS s +ON t.id = s.id AND t.id < 2500 +WHEN MATCHED AND t.id <= 5500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); + +MERGE INTO citus_target t +USING (SELECT * FROM citus_source WHERE id > 2500) AS s +ON t.id = s.id AND t.id < 2500 +WHEN MATCHED AND t.id <= 5500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); + +SELECT compare_data(); + +SET client_min_messages TO WARNING; +SELECT cleanup_data(); +RESET client_min_messages; +SELECT setup_data(); +SELECT create_distributed_table('citus_target', 'id'); +SELECT create_distributed_table('citus_source', 'id', colocate_with => 'citus_target'); + +MERGE INTO pg_target t +USING (SELECT * FROM pg_source WHERE id = 2500) AS s +ON t.id = s.id AND t.id = 5000 +WHEN MATCHED AND t.id <= 5500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); + +MERGE INTO citus_target t +USING (SELECT * FROM citus_source WHERE id = 2500) AS s +ON t.id = s.id AND t.id = 5000 +WHEN MATCHED AND t.id <= 5500 THEN + UPDATE SET val = s.val + 1 +WHEN MATCHED THEN + DELETE +WHEN NOT MATCHED THEN + INSERT VALUES(s.id, s.val); + +SELECT compare_data(); + DROP SCHEMA merge_repartition1_schema CASCADE; From 03a4769c3aa181cdbe9963cf3ee94592020cb926 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20Ozan=20Akg=C3=BCl?= Date: Fri, 23 Jun 2023 16:37:35 +0300 Subject: [PATCH 115/118] Fix Reference Table Check for CDC (#7025) Previously reference table check only looked at `partition method = 'n'`. This PR adds `replication model = 't'` to that. --- src/backend/distributed/cdc/cdc_decoder.c | 5 ++-- .../distributed/cdc/cdc_decoder_utils.c | 27 +++++++++++++------ .../distributed/cdc/cdc_decoder_utils.h | 2 +- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/backend/distributed/cdc/cdc_decoder.c b/src/backend/distributed/cdc/cdc_decoder.c index 9dfb8bc12..2beb27772 100644 --- a/src/backend/distributed/cdc/cdc_decoder.c +++ b/src/backend/distributed/cdc/cdc_decoder.c @@ -203,8 +203,7 @@ AddShardIdToHashTable(uint64 shardId, ShardIdHashEntry *entry) { entry->shardId = shardId; entry->distributedTableId = CdcLookupShardRelationFromCatalog(shardId, true); - entry->isReferenceTable = CdcPartitionMethodViaCatalog(entry->distributedTableId) == - 'n'; + entry->isReferenceTable = CdcIsReferenceTableViaCatalog(entry->distributedTableId); return entry->distributedTableId; } @@ -361,12 +360,14 @@ GetTupleForTargetSchemaForCdc(HeapTuple sourceRelationTuple, targetNulls[targetIndex] = true; targetIndex++; } + /* If this source attribute has been dropped, just skip this source attribute.*/ else if (TupleDescAttr(sourceRelDesc, sourceIndex)->attisdropped) { sourceIndex++; continue; } + /* If both source and target attributes are not dropped, add the attribute field to targetValues. */ else if (sourceIndex < sourceRelDesc->natts) { diff --git a/src/backend/distributed/cdc/cdc_decoder_utils.c b/src/backend/distributed/cdc/cdc_decoder_utils.c index 272221a5f..a69f307ba 100644 --- a/src/backend/distributed/cdc/cdc_decoder_utils.c +++ b/src/backend/distributed/cdc/cdc_decoder_utils.c @@ -331,16 +331,16 @@ CdcPgDistPartitionTupleViaCatalog(Oid relationId) /* - * CdcPartitionMethodViaCatalog gets a relationId and returns the partition - * method column from pg_dist_partition via reading from catalog. + * CdcIsReferenceTableViaCatalog gets a relationId and returns true if the relation + * is a reference table and false otherwise. */ char -CdcPartitionMethodViaCatalog(Oid relationId) +CdcIsReferenceTableViaCatalog(Oid relationId) { HeapTuple partitionTuple = CdcPgDistPartitionTupleViaCatalog(relationId); if (!HeapTupleIsValid(partitionTuple)) { - return DISTRIBUTE_BY_INVALID; + return false; } Datum datumArray[Natts_pg_dist_partition]; @@ -351,21 +351,32 @@ CdcPartitionMethodViaCatalog(Oid relationId) TupleDesc tupleDescriptor = RelationGetDescr(pgDistPartition); heap_deform_tuple(partitionTuple, tupleDescriptor, datumArray, isNullArray); - if (isNullArray[Anum_pg_dist_partition_partmethod - 1]) + if (isNullArray[Anum_pg_dist_partition_partmethod - 1] || + isNullArray[Anum_pg_dist_partition_repmodel - 1]) { - /* partition method cannot be NULL, still let's make sure */ + /* + * partition method and replication model cannot be NULL, + * still let's make sure + */ heap_freetuple(partitionTuple); table_close(pgDistPartition, NoLock); - return DISTRIBUTE_BY_INVALID; + return false; } Datum partitionMethodDatum = datumArray[Anum_pg_dist_partition_partmethod - 1]; char partitionMethodChar = DatumGetChar(partitionMethodDatum); + Datum replicationModelDatum = datumArray[Anum_pg_dist_partition_repmodel - 1]; + char replicationModelChar = DatumGetChar(replicationModelDatum); + heap_freetuple(partitionTuple); table_close(pgDistPartition, NoLock); - return partitionMethodChar; + /* + * A table is a reference table when its partition method is 'none' + * and replication model is 'two phase commit' + */ + return partitionMethodChar == 'n' && replicationModelChar == 't'; } diff --git a/src/backend/distributed/cdc/cdc_decoder_utils.h b/src/backend/distributed/cdc/cdc_decoder_utils.h index d30500de4..46d1e4ae5 100644 --- a/src/backend/distributed/cdc/cdc_decoder_utils.h +++ b/src/backend/distributed/cdc/cdc_decoder_utils.h @@ -25,7 +25,7 @@ uint64 CdcExtractShardIdFromTableName(const char *tableName, bool missingOk); Oid CdcLookupShardRelationFromCatalog(int64 shardId, bool missingOk); -char CdcPartitionMethodViaCatalog(Oid relationId); +char CdcIsReferenceTableViaCatalog(Oid relationId); bool CdcCitusHasBeenLoaded(void); From fd1427de2c5e9c77e2ffd850741351f90bd9d27f Mon Sep 17 00:00:00 2001 From: Jelte Fennema Date: Tue, 27 Jun 2023 16:37:09 +0200 Subject: [PATCH 116/118] Change by_disk_size rebalance strategy to have a base size (#7035) One problem with rebalancing by disk size is that shards in newly created collocation groups are considered extremely small. This can easily result in bad balances if there are some other collocation groups that do have some data. One extremely bad example of this is: 1. You have 2 workers 2. Both contain about 100GB of data, but there's a 70MB difference. 3. You create 100 new distributed schemas with a few empty tables in them 4. You run the rebalancer 5. Now all new distributed schemas are placed on the node with that had 70MB less. 6. You start loading some data in these shards and quickly the balance is completely off To address this edge case, this PR changes the by_disk_size rebalance strategy to add a a base size of 100MB to the actual size of each shard group. This can still result in a bad balance when shard groups are empty, but it solves some of the worst cases. --- .../distributed/operations/shard_rebalancer.c | 3 +++ src/backend/distributed/shared_library_init.c | 17 +++++++++++++++++ src/include/distributed/shard_rebalancer.h | 1 + src/test/regress/citus_tests/run_test.py | 1 + .../regress/expected/multi_partitioning.out | 2 +- .../expected/shard_move_deferred_delete.out | 2 +- src/test/regress/expected/shard_rebalancer.out | 18 +++++++++++++++++- src/test/regress/sql/shard_rebalancer.sql | 9 ++++++++- 8 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/backend/distributed/operations/shard_rebalancer.c b/src/backend/distributed/operations/shard_rebalancer.c index 5750b9e8d..20004f5fb 100644 --- a/src/backend/distributed/operations/shard_rebalancer.c +++ b/src/backend/distributed/operations/shard_rebalancer.c @@ -319,6 +319,7 @@ PG_FUNCTION_INFO_V1(citus_rebalance_wait); bool RunningUnderIsolationTest = false; int MaxRebalancerLoggedIgnoredMoves = 5; +int RebalancerByDiskSizeBaseCost = 100 * 1024 * 1024; bool PropagateSessionSettingsForLoopbackConnection = false; static const char *PlacementUpdateTypeNames[] = { @@ -677,6 +678,8 @@ citus_shard_cost_by_disk_size(PG_FUNCTION_ARGS) MemoryContextSwitchTo(oldContext); MemoryContextReset(localContext); + colocationSizeInBytes += RebalancerByDiskSizeBaseCost; + if (colocationSizeInBytes <= 0) { PG_RETURN_FLOAT4(1); diff --git a/src/backend/distributed/shared_library_init.c b/src/backend/distributed/shared_library_init.c index 907d8e73e..2fbe2f5f5 100644 --- a/src/backend/distributed/shared_library_init.c +++ b/src/backend/distributed/shared_library_init.c @@ -2194,6 +2194,23 @@ RegisterCitusConfigVariables(void) GUC_STANDARD, NULL, NULL, NULL); + DefineCustomIntVariable( + "citus.rebalancer_by_disk_size_base_cost", + gettext_noop( + "When using the by_disk_size rebalance strategy each shard group " + "will get this cost in bytes added to its actual disk size. This " + "is used to avoid creating a bad balance when there's very little " + "data in some of the shards. The assumption is that even empty " + "shards have some cost, because of parallelism and because empty " + "shard groups will likely grow in the future."), + gettext_noop( + "The main reason this is configurable, is so it can be lowered for Citus its regression tests."), + &RebalancerByDiskSizeBaseCost, + 100 * 1024 * 1024, 0, INT_MAX, + PGC_USERSET, + GUC_UNIT_BYTE | GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE, + NULL, NULL, NULL); + DefineCustomIntVariable( "citus.recover_2pc_interval", gettext_noop("Sets the time to wait between recovering 2PCs."), diff --git a/src/include/distributed/shard_rebalancer.h b/src/include/distributed/shard_rebalancer.h index 705196ad4..38ce4f485 100644 --- a/src/include/distributed/shard_rebalancer.h +++ b/src/include/distributed/shard_rebalancer.h @@ -188,6 +188,7 @@ typedef struct RebalancePlanFunctions extern char *VariablesToBePassedToNewConnections; extern int MaxRebalancerLoggedIgnoredMoves; +extern int RebalancerByDiskSizeBaseCost; extern bool RunningUnderIsolationTest; extern bool PropagateSessionSettingsForLoopbackConnection; extern int MaxBackgroundTaskExecutorsPerNode; diff --git a/src/test/regress/citus_tests/run_test.py b/src/test/regress/citus_tests/run_test.py index 8e1e1c91e..00d5638d9 100755 --- a/src/test/regress/citus_tests/run_test.py +++ b/src/test/regress/citus_tests/run_test.py @@ -118,6 +118,7 @@ DEPS = { "multi_extension": TestDeps(None, repeatable=False), "multi_test_helpers": TestDeps(None), "multi_insert_select": TestDeps("base_schedule"), + "multi_partitioning": TestDeps("base_schedule"), "multi_mx_create_table": TestDeps( None, [ diff --git a/src/test/regress/expected/multi_partitioning.out b/src/test/regress/expected/multi_partitioning.out index c0a21d4d5..64a4fd4dc 100644 --- a/src/test/regress/expected/multi_partitioning.out +++ b/src/test/regress/expected/multi_partitioning.out @@ -2042,7 +2042,7 @@ SELECT citus_shard_cost_by_disk_size(shardid) FROM pg_dist_shard WHERE logicalre DEBUG: skipping child tables for relation named: events.Energy Added citus_shard_cost_by_disk_size --------------------------------------------------------------------- - 16384 + 1.04874e+08 (1 row) RESET client_min_messages; diff --git a/src/test/regress/expected/shard_move_deferred_delete.out b/src/test/regress/expected/shard_move_deferred_delete.out index e87cd0f97..6a0c4fcd1 100644 --- a/src/test/regress/expected/shard_move_deferred_delete.out +++ b/src/test/regress/expected/shard_move_deferred_delete.out @@ -226,7 +226,7 @@ SET search_path TO shard_move_deferred_delete; SELECT citus_shard_cost_by_disk_size(20000001); citus_shard_cost_by_disk_size --------------------------------------------------------------------- - 8192 + 1.04866e+08 (1 row) -- When there's not enough space the move should fail diff --git a/src/test/regress/expected/shard_rebalancer.out b/src/test/regress/expected/shard_rebalancer.out index 0ae2193e5..62ae17487 100644 --- a/src/test/regress/expected/shard_rebalancer.out +++ b/src/test/regress/expected/shard_rebalancer.out @@ -3,6 +3,15 @@ -- SET citus.next_shard_id TO 433000; SET citus.propagate_session_settings_for_loopback_connection TO ON; +-- Lower the minimum disk size that a shard group is considered as. Otherwise +-- we need to create shards of more than 100MB. +ALTER SYSTEM SET citus.rebalancer_by_disk_size_base_cost = 0; +SELECT pg_reload_conf(); + pg_reload_conf +--------------------------------------------------------------------- + t +(1 row) + CREATE TABLE ref_table_test(a int primary key); SELECT create_reference_table('ref_table_test'); create_reference_table @@ -2182,7 +2191,7 @@ SELECT 1 FROM master_remove_node('localhost', :master_port); 1 (1 row) -SELECT public.wait_until_metadata_sync(30000); +SELECT public.wait_until_metadata_sync(60000); wait_until_metadata_sync --------------------------------------------------------------------- @@ -2854,6 +2863,13 @@ select 1 from citus_add_node('localhost', :worker_2_port); select rebalance_table_shards(); ERROR: cannot use logical replication to transfer shards of the relation table_without_primary_key since it doesn't have a REPLICA IDENTITY or PRIMARY KEY DROP TABLE table_with_primary_key, table_without_primary_key; +ALTER SYSTEM RESET citus.rebalancer_by_disk_size_base_cost; +SELECT pg_reload_conf(); + pg_reload_conf +--------------------------------------------------------------------- + t +(1 row) + \c - - - :worker_1_port SET citus.enable_ddl_propagation TO OFF; REVOKE ALL ON SCHEMA public FROM testrole; diff --git a/src/test/regress/sql/shard_rebalancer.sql b/src/test/regress/sql/shard_rebalancer.sql index f524212bf..ba22a8abd 100644 --- a/src/test/regress/sql/shard_rebalancer.sql +++ b/src/test/regress/sql/shard_rebalancer.sql @@ -5,6 +5,11 @@ SET citus.next_shard_id TO 433000; SET citus.propagate_session_settings_for_loopback_connection TO ON; +-- Lower the minimum disk size that a shard group is considered as. Otherwise +-- we need to create shards of more than 100MB. +ALTER SYSTEM SET citus.rebalancer_by_disk_size_base_cost = 0; +SELECT pg_reload_conf(); + CREATE TABLE ref_table_test(a int primary key); SELECT create_reference_table('ref_table_test'); CREATE TABLE dist_table_test(a int primary key); @@ -1228,7 +1233,7 @@ DROP TABLE tab; -- we don't need the coordinator on pg_dist_node anymore SELECT 1 FROM master_remove_node('localhost', :master_port); -SELECT public.wait_until_metadata_sync(30000); +SELECT public.wait_until_metadata_sync(60000); -- -- Make sure that rebalance_table_shards() and replicate_table_shards() replicate @@ -1569,6 +1574,8 @@ select 1 from citus_add_node('localhost', :worker_2_port); select rebalance_table_shards(); DROP TABLE table_with_primary_key, table_without_primary_key; +ALTER SYSTEM RESET citus.rebalancer_by_disk_size_base_cost; +SELECT pg_reload_conf(); \c - - - :worker_1_port SET citus.enable_ddl_propagation TO OFF; REVOKE ALL ON SCHEMA public FROM testrole; From ac24e1198682ed02caea5ec6b36b01984350b134 Mon Sep 17 00:00:00 2001 From: Jelte Fennema Date: Mon, 3 Jul 2023 11:08:24 +0200 Subject: [PATCH 117/118] Change default rebalance strategy to by_disk_size (#7033) DESCRIPTION: Change default rebalance strategy to by_disk_size When introducing rebalancing by disk size we didn't make it the default initially. The main reason was, because we expected some problems with it. We have indeed had some problems/bugs with it over the years, and have fixed all of them. By now we're quite confident in its stability, and that it pretty much always gives better results than by_shard_count. So this PR makes by_disk_size the new default. We don't change the default when some other strategy than by_shard_count is the current default. This is in case someone defined their own rebalance strategy and marked this as the default themselves. Note: It explicitly does nothing during a downgrade, because there's no way of knowing if the rebalance strategy before the upgrade was by_disk_size or by_shard_count. And even in previous versions by_disk_size is considered superior for quite some time. --- .../distributed/sql/citus--11.3-1--12.0-1.sql | 7 +++++++ .../sql/downgrades/citus--12.0-1--11.3-1.sql | 5 +++++ src/test/regress/expected/shard_rebalancer.out | 14 ++++++++++++++ .../regress/expected/single_shard_table_udfs.out | 2 +- src/test/regress/sql/shard_rebalancer.sql | 4 ++++ src/test/regress/sql/single_shard_table_udfs.sql | 2 +- 6 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql b/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql index 998ffc2be..a35d772b7 100644 --- a/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql +++ b/src/backend/distributed/sql/citus--11.3-1--12.0-1.sql @@ -42,3 +42,10 @@ DROP FUNCTION citus_shard_sizes; #include "udfs/drop_old_time_partitions/12.0-1.sql" #include "udfs/get_missing_time_partition_ranges/12.0-1.sql" + +-- Update the default rebalance strategy to 'by_disk_size', but only if the +-- default is currently 'by_shard_count' +SELECT citus_set_default_rebalance_strategy(name) +FROM pg_dist_rebalance_strategy +WHERE name = 'by_disk_size' + AND (SELECT default_strategy FROM pg_dist_rebalance_strategy WHERE name = 'by_shard_count'); diff --git a/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql b/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql index 1adb4cb72..4a25cfda0 100644 --- a/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql +++ b/src/backend/distributed/sql/downgrades/citus--12.0-1--11.3-1.sql @@ -76,3 +76,8 @@ DROP FUNCTION pg_catalog.citus_stat_tenants_local_internal( #include "../udfs/drop_old_time_partitions/10.2-1.sql" #include "../udfs/get_missing_time_partition_ranges/10.2-1.sql" + +-- This explicitly does not reset the rebalance strategy to by_shard_count, +-- because there's no way of knowing if the rebalance strategy before the +-- upgrade was by_disk_size or by_shard_count. And even in previous versions +-- by_disk_size is considered superior for quite some time. diff --git a/src/test/regress/expected/shard_rebalancer.out b/src/test/regress/expected/shard_rebalancer.out index 62ae17487..b8f4010b1 100644 --- a/src/test/regress/expected/shard_rebalancer.out +++ b/src/test/regress/expected/shard_rebalancer.out @@ -3,6 +3,14 @@ -- SET citus.next_shard_id TO 433000; SET citus.propagate_session_settings_for_loopback_connection TO ON; +-- Because of historic reasons this test was written in a way that assumes that +-- by_shard_count is the default strategy. +SELECT citus_set_default_rebalance_strategy('by_shard_count'); + citus_set_default_rebalance_strategy +--------------------------------------------------------------------- + +(1 row) + -- Lower the minimum disk size that a shard group is considered as. Otherwise -- we need to create shards of more than 100MB. ALTER SYSTEM SET citus.rebalancer_by_disk_size_base_cost = 0; @@ -2863,6 +2871,12 @@ select 1 from citus_add_node('localhost', :worker_2_port); select rebalance_table_shards(); ERROR: cannot use logical replication to transfer shards of the relation table_without_primary_key since it doesn't have a REPLICA IDENTITY or PRIMARY KEY DROP TABLE table_with_primary_key, table_without_primary_key; +SELECT citus_set_default_rebalance_strategy('by_disk_size'); + citus_set_default_rebalance_strategy +--------------------------------------------------------------------- + +(1 row) + ALTER SYSTEM RESET citus.rebalancer_by_disk_size_base_cost; SELECT pg_reload_conf(); pg_reload_conf diff --git a/src/test/regress/expected/single_shard_table_udfs.out b/src/test/regress/expected/single_shard_table_udfs.out index 3b73473b0..d49027b60 100644 --- a/src/test/regress/expected/single_shard_table_udfs.out +++ b/src/test/regress/expected/single_shard_table_udfs.out @@ -334,7 +334,7 @@ ERROR: Table 'single_shard_table_col2_1' is streaming replicated. Shards of str SELECT citus_copy_shard_placement(1820005, :worker_1_node, :worker_2_node); ERROR: Table 'single_shard_table_col2_1' is streaming replicated. Shards of streaming replicated tables cannot be copied -- no changes because it's already balanced -SELECT rebalance_table_shards(); +SELECT rebalance_table_shards(rebalance_strategy := 'by_shard_count'); rebalance_table_shards --------------------------------------------------------------------- diff --git a/src/test/regress/sql/shard_rebalancer.sql b/src/test/regress/sql/shard_rebalancer.sql index ba22a8abd..d64fb6826 100644 --- a/src/test/regress/sql/shard_rebalancer.sql +++ b/src/test/regress/sql/shard_rebalancer.sql @@ -5,6 +5,9 @@ SET citus.next_shard_id TO 433000; SET citus.propagate_session_settings_for_loopback_connection TO ON; +-- Because of historic reasons this test was written in a way that assumes that +-- by_shard_count is the default strategy. +SELECT citus_set_default_rebalance_strategy('by_shard_count'); -- Lower the minimum disk size that a shard group is considered as. Otherwise -- we need to create shards of more than 100MB. ALTER SYSTEM SET citus.rebalancer_by_disk_size_base_cost = 0; @@ -1574,6 +1577,7 @@ select 1 from citus_add_node('localhost', :worker_2_port); select rebalance_table_shards(); DROP TABLE table_with_primary_key, table_without_primary_key; +SELECT citus_set_default_rebalance_strategy('by_disk_size'); ALTER SYSTEM RESET citus.rebalancer_by_disk_size_base_cost; SELECT pg_reload_conf(); \c - - - :worker_1_port diff --git a/src/test/regress/sql/single_shard_table_udfs.sql b/src/test/regress/sql/single_shard_table_udfs.sql index 615e566db..7566f53e3 100644 --- a/src/test/regress/sql/single_shard_table_udfs.sql +++ b/src/test/regress/sql/single_shard_table_udfs.sql @@ -160,7 +160,7 @@ SELECT master_copy_shard_placement(1820005, 'localhost', :worker_1_port, 'localh SELECT citus_copy_shard_placement(1820005, :worker_1_node, :worker_2_node); -- no changes because it's already balanced -SELECT rebalance_table_shards(); +SELECT rebalance_table_shards(rebalance_strategy := 'by_shard_count'); -- same placements SELECT shardid, nodeport FROM pg_dist_shard_placement WHERE shardid > 1820000 ORDER BY shardid; From e0d347652650ec75afdfe4683aaca8a481cacd1a Mon Sep 17 00:00:00 2001 From: Gokhan Gulbiz Date: Mon, 3 Jul 2023 13:08:03 +0300 Subject: [PATCH 118/118] Add locking mechanism for tenant monitoring probabilistic approach (#7026) This PR * Addresses a concurrency issue in the probabilistic approach of tenant monitoring by acquiring a shared lock for tenant existence checks. * Changes `citus.stat_tenants_sample_rate_for_new_tenants` type to double * Renames `citus.stat_tenants_sample_rate_for_new_tenants` to `citus.stat_tenants_untracked_sample_rate` --- src/backend/distributed/shared_library_init.c | 21 ++-- .../distributed/utils/citus_stat_tenants.c | 22 +++- .../distributed/utils/citus_stat_tenants.h | 2 +- .../regress/expected/citus_stat_tenants.out | 106 +++++++++++++++++- src/test/regress/sql/citus_stat_tenants.sql | 45 +++++++- 5 files changed, 177 insertions(+), 19 deletions(-) diff --git a/src/backend/distributed/shared_library_init.c b/src/backend/distributed/shared_library_init.c index 2fbe2f5f5..2493a8ea9 100644 --- a/src/backend/distributed/shared_library_init.c +++ b/src/backend/distributed/shared_library_init.c @@ -2489,17 +2489,6 @@ RegisterCitusConfigVariables(void) GUC_STANDARD, NULL, NULL, NULL); - - DefineCustomIntVariable( - "citus.stat_tenants_sample_rate_for_new_tenants", - gettext_noop("Sampling rate for new tenants in citus_stat_tenants."), - NULL, - &StatTenantsSampleRateForNewTenants, - 100, 1, 100, - PGC_USERSET, - GUC_STANDARD, - NULL, NULL, NULL); - DefineCustomEnumVariable( "citus.stat_tenants_track", gettext_noop("Enables/Disables the stats collection for citus_stat_tenants."), @@ -2513,6 +2502,16 @@ RegisterCitusConfigVariables(void) GUC_STANDARD, NULL, NULL, NULL); + DefineCustomRealVariable( + "citus.stat_tenants_untracked_sample_rate", + gettext_noop("Sampling rate for new tenants in citus_stat_tenants."), + NULL, + &StatTenantsSampleRateForNewTenants, + 1, 0, 1, + PGC_USERSET, + GUC_STANDARD, + NULL, NULL, NULL); + DefineCustomBoolVariable( "citus.subquery_pushdown", gettext_noop("Usage of this GUC is highly discouraged, please read the long " diff --git a/src/backend/distributed/utils/citus_stat_tenants.c b/src/backend/distributed/utils/citus_stat_tenants.c index 2f174f764..aa813e152 100644 --- a/src/backend/distributed/utils/citus_stat_tenants.c +++ b/src/backend/distributed/utils/citus_stat_tenants.c @@ -36,6 +36,10 @@ #include +#if (PG_VERSION_NUM >= PG_VERSION_15) + #include "common/pg_prng.h" +#endif + static void AttributeMetricsIfApplicable(void); ExecutorEnd_hook_type prev_ExecutorEnd = NULL; @@ -80,7 +84,7 @@ int StatTenantsLogLevel = CITUS_LOG_LEVEL_OFF; int StatTenantsPeriod = (time_t) 60; int StatTenantsLimit = 100; int StatTenantsTrack = STAT_TENANTS_TRACK_NONE; -int StatTenantsSampleRateForNewTenants = 100; +double StatTenantsSampleRateForNewTenants = 1; PG_FUNCTION_INFO_V1(citus_stat_tenants_local); PG_FUNCTION_INFO_V1(citus_stat_tenants_local_reset); @@ -281,13 +285,25 @@ AttributeTask(char *tenantId, int colocationId, CmdType commandType) MultiTenantMonitor *monitor = GetMultiTenantMonitor(); bool found = false; + + /* Acquire the lock in shared mode to check if the tenant is already in the hash table. */ + LWLockAcquire(&monitor->lock, LW_SHARED); + hash_search(monitor->tenants, &key, HASH_FIND, &found); + LWLockRelease(&monitor->lock); + /* If the tenant is not found in the hash table, we will track the query with a probability of StatTenantsSampleRateForNewTenants. */ if (!found) { - int randomValue = rand() % 100; - bool shouldTrackQuery = randomValue < StatTenantsSampleRateForNewTenants; +#if (PG_VERSION_NUM >= PG_VERSION_15) + double randomValue = pg_prng_double(&pg_global_prng_state); +#else + + /* Generate a random double between 0 and 1 */ + double randomValue = (double) random() / MAX_RANDOM_VALUE; +#endif + bool shouldTrackQuery = randomValue <= StatTenantsSampleRateForNewTenants; if (!shouldTrackQuery) { return; diff --git a/src/include/distributed/utils/citus_stat_tenants.h b/src/include/distributed/utils/citus_stat_tenants.h index 8dac393a8..0a482b241 100644 --- a/src/include/distributed/utils/citus_stat_tenants.h +++ b/src/include/distributed/utils/citus_stat_tenants.h @@ -121,6 +121,6 @@ extern int StatTenantsLogLevel; extern int StatTenantsPeriod; extern int StatTenantsLimit; extern int StatTenantsTrack; -extern int StatTenantsSampleRateForNewTenants; +extern double StatTenantsSampleRateForNewTenants; #endif /*CITUS_ATTRIBUTE_H */ diff --git a/src/test/regress/expected/citus_stat_tenants.out b/src/test/regress/expected/citus_stat_tenants.out index e7ddadb71..14582e55b 100644 --- a/src/test/regress/expected/citus_stat_tenants.out +++ b/src/test/regress/expected/citus_stat_tenants.out @@ -240,12 +240,21 @@ SELECT tenant_attribute, query_count_in_this_period, score FROM citus_stat_tenan (5 rows) -- test period passing +\c - - - :worker_1_port +SET search_path TO citus_stat_tenants; +SET citus.stat_tenants_period TO 2; SELECT citus_stat_tenants_reset(); citus_stat_tenants_reset --------------------------------------------------------------------- (1 row) +SELECT sleep_until_next_period(); + sleep_until_next_period +--------------------------------------------------------------------- + +(1 row) + SELECT count(*)>=0 FROM dist_tbl WHERE a = 1; ?column? --------------------------------------------------------------------- @@ -253,7 +262,6 @@ SELECT count(*)>=0 FROM dist_tbl WHERE a = 1; (1 row) INSERT INTO dist_tbl VALUES (5, 'abcd'); -\c - - - :worker_1_port SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period, (cpu_usage_in_this_period>0) AS cpu_is_used_in_this_period, (cpu_usage_in_last_period>0) AS cpu_is_used_in_last_period FROM citus_stat_tenants_local @@ -265,13 +273,18 @@ ORDER BY tenant_attribute; (2 rows) -- simulate passing the period -SET citus.stat_tenants_period TO 5; SELECT sleep_until_next_period(); sleep_until_next_period --------------------------------------------------------------------- (1 row) +SELECT pg_sleep(1); + pg_sleep +--------------------------------------------------------------------- + +(1 row) + SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period, (cpu_usage_in_this_period>0) AS cpu_is_used_in_this_period, (cpu_usage_in_last_period>0) AS cpu_is_used_in_last_period FROM citus_stat_tenants_local @@ -288,6 +301,12 @@ SELECT sleep_until_next_period(); (1 row) +SELECT pg_sleep(1); + pg_sleep +--------------------------------------------------------------------- + +(1 row) + SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period, (cpu_usage_in_this_period>0) AS cpu_is_used_in_this_period, (cpu_usage_in_last_period>0) AS cpu_is_used_in_last_period FROM citus_stat_tenants_local @@ -1009,6 +1028,89 @@ SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, q \c - - - :master_port SET search_path TO citus_stat_tenants; +SET citus.enable_schema_based_sharding TO OFF; +SELECT citus_stat_tenants_reset(); + citus_stat_tenants_reset +--------------------------------------------------------------------- + +(1 row) + +-- test sampling +-- set rate to 0 to disable sampling +SELECT result FROM run_command_on_all_nodes('ALTER SYSTEM set citus.stat_tenants_untracked_sample_rate to 0;'); + result +--------------------------------------------------------------------- + ALTER SYSTEM + ALTER SYSTEM + ALTER SYSTEM +(3 rows) + +SELECT result FROM run_command_on_all_nodes('SELECT pg_reload_conf()'); + result +--------------------------------------------------------------------- + t + t + t +(3 rows) + +INSERT INTO dist_tbl VALUES (1, 'abcd'); +INSERT INTO dist_tbl VALUES (2, 'abcd'); +UPDATE dist_tbl SET b = a + 1 WHERE a = 3; +UPDATE dist_tbl SET b = a + 1 WHERE a = 4; +DELETE FROM dist_tbl WHERE a = 5; +SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants ORDER BY tenant_attribute; + tenant_attribute | read_count_in_this_period | read_count_in_last_period | query_count_in_this_period | query_count_in_last_period +--------------------------------------------------------------------- +(0 rows) + +-- test sampling +-- set rate to 1 to track all tenants +SELECT result FROM run_command_on_all_nodes('ALTER SYSTEM set citus.stat_tenants_untracked_sample_rate to 1;'); + result +--------------------------------------------------------------------- + ALTER SYSTEM + ALTER SYSTEM + ALTER SYSTEM +(3 rows) + +SELECT result FROM run_command_on_all_nodes('SELECT pg_reload_conf()'); + result +--------------------------------------------------------------------- + t + t + t +(3 rows) + +SELECT sleep_until_next_period(); + sleep_until_next_period +--------------------------------------------------------------------- + +(1 row) + +SELECT pg_sleep(0.1); + pg_sleep +--------------------------------------------------------------------- + +(1 row) + +INSERT INTO dist_tbl VALUES (1, 'abcd'); +INSERT INTO dist_tbl VALUES (2, 'abcd'); +UPDATE dist_tbl SET b = a + 1 WHERE a = 3; +UPDATE dist_tbl SET b = a + 1 WHERE a = 4; +DELETE FROM dist_tbl WHERE a = 5; +SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period, + (cpu_usage_in_this_period>0) AS cpu_is_used_in_this_period, (cpu_usage_in_last_period>0) AS cpu_is_used_in_last_period +FROM citus_stat_tenants(true) +ORDER BY tenant_attribute; + tenant_attribute | read_count_in_this_period | read_count_in_last_period | query_count_in_this_period | query_count_in_last_period | cpu_is_used_in_this_period | cpu_is_used_in_last_period +--------------------------------------------------------------------- + 1 | 0 | 0 | 1 | 0 | t | f + 2 | 0 | 0 | 1 | 0 | t | f + 3 | 0 | 0 | 1 | 0 | t | f + 4 | 0 | 0 | 1 | 0 | t | f + 5 | 0 | 0 | 1 | 0 | t | f +(5 rows) + SET client_min_messages TO ERROR; DROP SCHEMA citus_stat_tenants CASCADE; DROP SCHEMA citus_stat_tenants_t1 CASCADE; diff --git a/src/test/regress/sql/citus_stat_tenants.sql b/src/test/regress/sql/citus_stat_tenants.sql index 9160b3499..2e4ed1450 100644 --- a/src/test/regress/sql/citus_stat_tenants.sql +++ b/src/test/regress/sql/citus_stat_tenants.sql @@ -83,20 +83,24 @@ SELECT count(*)>=0 FROM dist_tbl_text WHERE a = 'defg'; SELECT tenant_attribute, query_count_in_this_period, score FROM citus_stat_tenants(true) WHERE nodeid = :worker_2_nodeid ORDER BY score DESC, tenant_attribute; -- test period passing +\c - - - :worker_1_port + +SET search_path TO citus_stat_tenants; +SET citus.stat_tenants_period TO 2; SELECT citus_stat_tenants_reset(); +SELECT sleep_until_next_period(); SELECT count(*)>=0 FROM dist_tbl WHERE a = 1; INSERT INTO dist_tbl VALUES (5, 'abcd'); -\c - - - :worker_1_port SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period, (cpu_usage_in_this_period>0) AS cpu_is_used_in_this_period, (cpu_usage_in_last_period>0) AS cpu_is_used_in_last_period FROM citus_stat_tenants_local ORDER BY tenant_attribute; -- simulate passing the period -SET citus.stat_tenants_period TO 5; SELECT sleep_until_next_period(); +SELECT pg_sleep(1); SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period, (cpu_usage_in_this_period>0) AS cpu_is_used_in_this_period, (cpu_usage_in_last_period>0) AS cpu_is_used_in_last_period @@ -104,6 +108,7 @@ FROM citus_stat_tenants_local ORDER BY tenant_attribute; SELECT sleep_until_next_period(); +SELECT pg_sleep(1); SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period, (cpu_usage_in_this_period>0) AS cpu_is_used_in_this_period, (cpu_usage_in_last_period>0) AS cpu_is_used_in_last_period @@ -377,6 +382,42 @@ SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, q \c - - - :master_port SET search_path TO citus_stat_tenants; +SET citus.enable_schema_based_sharding TO OFF; + +SELECT citus_stat_tenants_reset(); + +-- test sampling +-- set rate to 0 to disable sampling +SELECT result FROM run_command_on_all_nodes('ALTER SYSTEM set citus.stat_tenants_untracked_sample_rate to 0;'); +SELECT result FROM run_command_on_all_nodes('SELECT pg_reload_conf()'); + +INSERT INTO dist_tbl VALUES (1, 'abcd'); +INSERT INTO dist_tbl VALUES (2, 'abcd'); +UPDATE dist_tbl SET b = a + 1 WHERE a = 3; +UPDATE dist_tbl SET b = a + 1 WHERE a = 4; +DELETE FROM dist_tbl WHERE a = 5; + +SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period FROM citus_stat_tenants ORDER BY tenant_attribute; + +-- test sampling +-- set rate to 1 to track all tenants +SELECT result FROM run_command_on_all_nodes('ALTER SYSTEM set citus.stat_tenants_untracked_sample_rate to 1;'); +SELECT result FROM run_command_on_all_nodes('SELECT pg_reload_conf()'); + +SELECT sleep_until_next_period(); +SELECT pg_sleep(0.1); + +INSERT INTO dist_tbl VALUES (1, 'abcd'); +INSERT INTO dist_tbl VALUES (2, 'abcd'); +UPDATE dist_tbl SET b = a + 1 WHERE a = 3; +UPDATE dist_tbl SET b = a + 1 WHERE a = 4; +DELETE FROM dist_tbl WHERE a = 5; + +SELECT tenant_attribute, read_count_in_this_period, read_count_in_last_period, query_count_in_this_period, query_count_in_last_period, + (cpu_usage_in_this_period>0) AS cpu_is_used_in_this_period, (cpu_usage_in_last_period>0) AS cpu_is_used_in_last_period +FROM citus_stat_tenants(true) +ORDER BY tenant_attribute; + SET client_min_messages TO ERROR; DROP SCHEMA citus_stat_tenants CASCADE; DROP SCHEMA citus_stat_tenants_t1 CASCADE;