From 09961e10daf72e2c1fbbdddb05b2940b2da14df0 Mon Sep 17 00:00:00 2001 From: Dian M Fay Date: Mon, 1 Mar 2021 00:44:15 -0500 Subject: [PATCH] postgres_fdw: column option to override foreign types Enabling the use_local_type option on a foreign table column will deparse it with a cast to the locally-declared type, allowing overrides of foreign types not known to the local database. --- contrib/postgres_fdw/deparse.c | 15 ++++++++++++--- contrib/postgres_fdw/expected/postgres_fdw.out | 17 +++++++++++++++++ contrib/postgres_fdw/option.c | 1 + contrib/postgres_fdw/sql/postgres_fdw.sql | 10 ++++++++++ doc/src/sgml/postgres-fdw.sgml | 13 +++++++++++++ 5 files changed, 53 insertions(+), 3 deletions(-) diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c index 6faf499f9a..c722d49316 100644 --- a/contrib/postgres_fdw/deparse.c +++ b/contrib/postgres_fdw/deparse.c @@ -2264,6 +2264,7 @@ deparseColumnRef(StringInfo buf, int varno, int varattno, RangeTblEntry *rte, else { char *colname = NULL; + bool uselocaltype = NULL; List *options; ListCell *lc; @@ -2280,10 +2281,9 @@ deparseColumnRef(StringInfo buf, int varno, int varattno, RangeTblEntry *rte, DefElem *def = (DefElem *) lfirst(lc); if (strcmp(def->defname, "column_name") == 0) - { colname = defGetString(def); - break; - } + else if (strcmp(def->defname, "use_local_type") == 0) + uselocaltype = defGetBoolean(def); } /* @@ -2297,6 +2297,15 @@ deparseColumnRef(StringInfo buf, int varno, int varattno, RangeTblEntry *rte, ADD_REL_QUALIFIER(buf, varno); appendStringInfoString(buf, quote_identifier(colname)); + + if (uselocaltype) + { + Oid coltype = get_atttype(rte->relid, varattno); + char *typname = deparse_type_name(coltype, -1); + + appendStringInfoString(buf, "::"); + appendStringInfoString(buf, typname); + } } } diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out index 0649b6b81c..c3271bac8f 100644 --- a/contrib/postgres_fdw/expected/postgres_fdw.out +++ b/contrib/postgres_fdw/expected/postgres_fdw.out @@ -4105,6 +4105,23 @@ ERROR: invalid input syntax for type integer: "foo" CONTEXT: processing expression at position 2 in select list ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE user_enum; -- =================================================================== +-- conversion with use_local_type +-- =================================================================== +ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE text; +SELECT * FROM ft1 WHERE c8 = 'foo' LIMIT 1; -- ERROR +ERROR: operator does not exist: public.user_enum = text +HINT: No operator matches the given name and argument types. You might need to add explicit type casts. +CONTEXT: remote SQL command: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE ((c8 = 'foo'::text)) LIMIT 1::bigint +ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 OPTIONS (use_local_type 'true'); +SELECT * FROM ft1 WHERE c8 = 'foo' LIMIT 1; + c1 | c2 | c3 | c4 | c5 | c6 | c7 | c8 +----+----+-------+------------------------------+--------------------------+----+------------+----- + 1 | 1 | 00001 | Fri Jan 02 00:00:00 1970 PST | Fri Jan 02 00:00:00 1970 | 1 | 1 | foo +(1 row) + +ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 OPTIONS (SET use_local_type 'false'); +ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE user_enum; +-- =================================================================== -- subtransaction -- + local/remote error doesn't break cursor -- =================================================================== diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c index 64698c4da3..4d9ed3fc0f 100644 --- a/contrib/postgres_fdw/option.c +++ b/contrib/postgres_fdw/option.c @@ -200,6 +200,7 @@ InitPgFdwOptions(void) {"schema_name", ForeignTableRelationId, false}, {"table_name", ForeignTableRelationId, false}, {"column_name", AttributeRelationId, false}, + {"use_local_type", AttributeRelationId, false}, /* use_remote_estimate is available on both server and table */ {"use_remote_estimate", ForeignServerRelationId, false}, {"use_remote_estimate", ForeignTableRelationId, false}, diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql index 2b525ea44a..5db2e2b575 100644 --- a/contrib/postgres_fdw/sql/postgres_fdw.sql +++ b/contrib/postgres_fdw/sql/postgres_fdw.sql @@ -1133,6 +1133,16 @@ SELECT ft1.c1, ft2.c2, ft1 FROM ft1, ft2 WHERE ft1.c1 = ft2.c1 AND ft1.c1 = 1; SELECT sum(c2), array_agg(c8) FROM ft1 GROUP BY c8; -- ERROR ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE user_enum; +-- =================================================================== +-- conversion with use_local_type +-- =================================================================== +ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE text; +SELECT * FROM ft1 WHERE c8 = 'foo' LIMIT 1; -- ERROR +ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 OPTIONS (use_local_type 'true'); +SELECT * FROM ft1 WHERE c8 = 'foo' LIMIT 1; +ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 OPTIONS (SET use_local_type 'false'); +ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE user_enum; + -- =================================================================== -- subtransaction -- + local/remote error doesn't break cursor diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml index 07aa25799d..8486799e19 100644 --- a/doc/src/sgml/postgres-fdw.sgml +++ b/doc/src/sgml/postgres-fdw.sgml @@ -219,6 +219,19 @@ OPTIONS (ADD password_required 'false'); + + use_local_type + + + A foreign table column with this option set to true + will attempt to cast values and references to the type declared in + CREATE FOREIGN TABLE. This allows, for example, + a text column to represent an enum column in the remote table without + maintaining the enum type in the local database as well. + + + + -- 2.30.1