From 2e06e624769eea1b45058d2d2d91b6f0609609b8 Mon Sep 17 00:00:00 2001 From: Naisila Puka <37271756+naisila@users.noreply.github.com> Date: Tue, 27 Sep 2022 13:40:48 +0300 Subject: [PATCH] Adds tests for suppressed constants in postgres_fdw queries (#6370) PG15 has suppressed some casts on constants when querying foreign tables. For example, we can use text to represent a type that's an enum on the remote side. A comparison on such a column will get shipped as "var = 'foo'::text". But there's no enum = text operator on the remote side. If we leave off the explicit cast, the comparison will work. Test we behave in the same way with a Citus foreign table Reminder: foreign tables cannot be distributed/reference, can only be Citus local Relevant PG commit: https://github.com/postgres/postgres/commit/f8abb0f5e114d8c309239f0faa277b97f696d829 (cherry picked from commit 1b26d57288ac3b501c1cb488effb7d1dcc65a8ab) --- src/test/regress/expected/pg15.out | 116 +++++++++++++++++++++++++++++ src/test/regress/sql/pg15.sql | 65 ++++++++++++++++ 2 files changed, 181 insertions(+) diff --git a/src/test/regress/expected/pg15.out b/src/test/regress/expected/pg15.out index 1ef480084..57b3c6a59 100644 --- a/src/test/regress/expected/pg15.out +++ b/src/test/regress/expected/pg15.out @@ -1249,12 +1249,128 @@ CREATE TABLE set_on_default_test_referencing( ON DELETE SET DEFAULT (col_3) ); ERROR: cannot create foreign key constraint since Citus does not support ON DELETE / UPDATE SET DEFAULT actions on the columns that default to sequences +-- +-- PG15 has suppressed some casts on constants when querying foreign tables +-- For example, we can use text to represent a type that's an enum on the remote side +-- A comparison on such a column will get shipped as "var = 'foo'::text" +-- But there's no enum = text operator on the remote side +-- If we leave off the explicit cast, the comparison will work +-- Test we behave in the same way with a Citus foreign table +-- Reminder: foreign tables cannot be distributed/reference, can only be Citus local +-- Relevant PG commit: +-- f8abb0f5e114d8c309239f0faa277b97f696d829 +-- +\set VERBOSITY terse +SET citus.next_shard_id TO 960200; +SET citus.enable_local_execution TO ON; +-- add the foreign table to metadata with the guc +SET citus.use_citus_managed_tables TO ON; +CREATE TYPE user_enum AS ENUM ('foo', 'bar', 'buz'); +CREATE TABLE foreign_table_test (c0 integer NOT NULL, c1 user_enum); +INSERT INTO foreign_table_test VALUES (1, 'foo'); +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 foreign_table ( + c0 integer NOT NULL, + c1 text +) + SERVER foreign_server + OPTIONS (schema_name 'pg15', table_name 'foreign_table_test'); +-- check that the foreign table is a citus local table +SELECT partmethod, repmodel FROM pg_dist_partition WHERE logicalrelid = 'foreign_table'::regclass ORDER BY logicalrelid; + partmethod | repmodel +--------------------------------------------------------------------- + n | s +(1 row) + +-- same tests as in the relevant PG commit +-- Check that Remote SQL in the EXPLAIN doesn't contain casting +EXPLAIN (VERBOSE, COSTS OFF) +SELECT * FROM foreign_table WHERE c1 = 'foo' LIMIT 1; + QUERY PLAN +--------------------------------------------------------------------- + Custom Scan (Citus Adaptive) + Output: remote_scan.c0, remote_scan.c1 + Task Count: 1 + Tasks Shown: All + -> Task + Query: SELECT c0, c1 FROM pg15.foreign_table_960201 foreign_table WHERE (c1 OPERATOR(pg_catalog.=) 'foo'::text) LIMIT 1 + Node: host=localhost port=xxxxx dbname=regression + -> Foreign Scan on pg15.foreign_table_960201 foreign_table + Output: c0, c1 + Remote SQL: SELECT c0, c1 FROM pg15.foreign_table_test WHERE ((c1 = 'foo')) LIMIT 1::bigint +(10 rows) + +SELECT * FROM foreign_table WHERE c1 = 'foo' LIMIT 1; + c0 | c1 +--------------------------------------------------------------------- + 1 | foo +(1 row) + +-- Check that Remote SQL in the EXPLAIN doesn't contain casting +EXPLAIN (VERBOSE, COSTS OFF) +SELECT * FROM foreign_table WHERE 'foo' = c1 LIMIT 1; + QUERY PLAN +--------------------------------------------------------------------- + Custom Scan (Citus Adaptive) + Output: remote_scan.c0, remote_scan.c1 + Task Count: 1 + Tasks Shown: All + -> Task + Query: SELECT c0, c1 FROM pg15.foreign_table_960201 foreign_table WHERE ('foo'::text OPERATOR(pg_catalog.=) c1) LIMIT 1 + Node: host=localhost port=xxxxx dbname=regression + -> Foreign Scan on pg15.foreign_table_960201 foreign_table + Output: c0, c1 + Remote SQL: SELECT c0, c1 FROM pg15.foreign_table_test WHERE (('foo' = c1)) LIMIT 1::bigint +(10 rows) + +SELECT * FROM foreign_table WHERE 'foo' = c1 LIMIT 1; + c0 | c1 +--------------------------------------------------------------------- + 1 | foo +(1 row) + +-- we declared c1 to be text locally, but it's still the same type on +-- the remote which will balk if we try to do anything incompatible +-- with that remote type +SELECT * FROM foreign_table WHERE c1 LIKE 'foo' LIMIT 1; -- ERROR +ERROR: operator does not exist: pg15.user_enum ~~ unknown +SELECT * FROM foreign_table WHERE c1::text LIKE 'foo' LIMIT 1; -- ERROR; cast not pushed down +ERROR: operator does not exist: pg15.user_enum ~~ unknown +-- Clean up foreign table test +RESET citus.use_citus_managed_tables; +SELECT undistribute_table('foreign_table'); +NOTICE: creating a new table for pg15.foreign_table +NOTICE: dropping the old pg15.foreign_table +NOTICE: renaming the new table to pg15.foreign_table + undistribute_table +--------------------------------------------------------------------- + +(1 row) + +SELECT undistribute_table('foreign_table_test'); +NOTICE: creating a new table for pg15.foreign_table_test +NOTICE: moving the data of pg15.foreign_table_test +NOTICE: dropping the old pg15.foreign_table_test +NOTICE: renaming the new table to pg15.foreign_table_test + undistribute_table +--------------------------------------------------------------------- + +(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 -- Clean up \set VERBOSITY terse SET client_min_messages TO ERROR; diff --git a/src/test/regress/sql/pg15.sql b/src/test/regress/sql/pg15.sql index 9dedc149b..84367fbb1 100644 --- a/src/test/regress/sql/pg15.sql +++ b/src/test/regress/sql/pg15.sql @@ -806,7 +806,72 @@ CREATE TABLE set_on_default_test_referencing( ON DELETE SET DEFAULT (col_3) ); +-- +-- PG15 has suppressed some casts on constants when querying foreign tables +-- For example, we can use text to represent a type that's an enum on the remote side +-- A comparison on such a column will get shipped as "var = 'foo'::text" +-- But there's no enum = text operator on the remote side +-- If we leave off the explicit cast, the comparison will work +-- Test we behave in the same way with a Citus foreign table +-- Reminder: foreign tables cannot be distributed/reference, can only be Citus local +-- Relevant PG commit: +-- f8abb0f5e114d8c309239f0faa277b97f696d829 +-- + +\set VERBOSITY terse +SET citus.next_shard_id TO 960200; +SET citus.enable_local_execution TO ON; +-- add the foreign table to metadata with the guc +SET citus.use_citus_managed_tables TO ON; + +CREATE TYPE user_enum AS ENUM ('foo', 'bar', 'buz'); + +CREATE TABLE foreign_table_test (c0 integer NOT NULL, c1 user_enum); +INSERT INTO foreign_table_test VALUES (1, 'foo'); + +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 foreign_table ( + c0 integer NOT NULL, + c1 text +) + SERVER foreign_server + OPTIONS (schema_name 'pg15', table_name 'foreign_table_test'); + +-- check that the foreign table is a citus local table +SELECT partmethod, repmodel FROM pg_dist_partition WHERE logicalrelid = 'foreign_table'::regclass ORDER BY logicalrelid; + +-- same tests as in the relevant PG commit +-- Check that Remote SQL in the EXPLAIN doesn't contain casting +EXPLAIN (VERBOSE, COSTS OFF) +SELECT * FROM foreign_table WHERE c1 = 'foo' LIMIT 1; +SELECT * FROM foreign_table WHERE c1 = 'foo' LIMIT 1; + +-- Check that Remote SQL in the EXPLAIN doesn't contain casting +EXPLAIN (VERBOSE, COSTS OFF) +SELECT * FROM foreign_table WHERE 'foo' = c1 LIMIT 1; +SELECT * FROM foreign_table WHERE 'foo' = c1 LIMIT 1; + +-- we declared c1 to be text locally, but it's still the same type on +-- the remote which will balk if we try to do anything incompatible +-- with that remote type +SELECT * FROM foreign_table WHERE c1 LIKE 'foo' LIMIT 1; -- ERROR +SELECT * FROM foreign_table WHERE c1::text LIKE 'foo' LIMIT 1; -- ERROR; cast not pushed down + +-- Clean up foreign table test +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; -- Clean up \set VERBOSITY terse