From 3f5b45b606a781a603578ac73dc79e67da38b275 Mon Sep 17 00:00:00 2001 From: Paul Ramsey Date: Sat, 3 Oct 2015 08:53:09 -0700 Subject: [PATCH 1/7] Patch status at 20151001 --- contrib/postgres_fdw/Makefile | 6 +- contrib/postgres_fdw/deparse.c | 30 ++- contrib/postgres_fdw/expected/shippable.out | 139 ++++++++++++++ contrib/postgres_fdw/option.c | 55 ++++++ contrib/postgres_fdw/postgres_fdw.c | 37 +--- contrib/postgres_fdw/postgres_fdw.h | 42 +++++ contrib/postgres_fdw/shippable.c | 198 ++++++++++++++++++++ contrib/postgres_fdw/sql/shippable.sql | 76 ++++++++ doc/src/sgml/postgres-fdw.sgml | 32 ++++ 9 files changed, 574 insertions(+), 41 deletions(-) create mode 100644 contrib/postgres_fdw/expected/shippable.out create mode 100644 contrib/postgres_fdw/shippable.c create mode 100644 contrib/postgres_fdw/sql/shippable.sql diff --git a/contrib/postgres_fdw/Makefile b/contrib/postgres_fdw/Makefile index d2b98e10f3a5e..f78fc64c06919 100644 --- a/contrib/postgres_fdw/Makefile +++ b/contrib/postgres_fdw/Makefile @@ -1,7 +1,7 @@ # contrib/postgres_fdw/Makefile MODULE_big = postgres_fdw -OBJS = postgres_fdw.o option.o deparse.o connection.o $(WIN32RES) +OBJS = postgres_fdw.o option.o deparse.o connection.o shippable.o $(WIN32RES) PGFILEDESC = "postgres_fdw - foreign data wrapper for PostgreSQL" PG_CPPFLAGS = -I$(libpq_srcdir) @@ -10,7 +10,9 @@ SHLIB_LINK = $(libpq) EXTENSION = postgres_fdw DATA = postgres_fdw--1.0.sql -REGRESS = postgres_fdw +# Note: shippable tests depend on postgres_fdw tests setup +REGRESS = postgres_fdw shippable +EXTRA_INSTALL = contrib/cube ifdef USE_PGXS PG_CONFIG = pg_config diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c index 697de60dfe51b..4c06e66692c4e 100644 --- a/contrib/postgres_fdw/deparse.c +++ b/contrib/postgres_fdw/deparse.c @@ -233,6 +233,9 @@ foreign_expr_walker(Node *node, Oid collation; FDWCollateState state; + /* Access extension metadata from fpinfo on baserel */ + PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *)(glob_cxt->foreignrel->fdw_private); + /* Need do nothing for empty subexpressions */ if (node == NULL) return true; @@ -378,7 +381,8 @@ foreign_expr_walker(Node *node, * can't be sent to remote because it might have incompatible * semantics on remote side. */ - if (!is_builtin(fe->funcid)) + if (!is_builtin(fe->funcid) && + !is_shippable(fe->funcid, fpinfo->extensions)) return false; /* @@ -426,7 +430,8 @@ foreign_expr_walker(Node *node, * (If the operator is, surely its underlying function is * too.) */ - if (!is_builtin(oe->opno)) + if (!is_builtin(oe->opno) && + !is_shippable(oe->opno, fpinfo->extensions)) return false; /* @@ -466,7 +471,8 @@ foreign_expr_walker(Node *node, /* * Again, only built-in operators can be sent to remote. */ - if (!is_builtin(oe->opno)) + if (!is_builtin(oe->opno) && + !is_shippable(oe->opno, fpinfo->extensions)) return false; /* @@ -616,7 +622,9 @@ foreign_expr_walker(Node *node, * If result type of given expression is not built-in, it can't be sent to * remote because it might have incompatible semantics on remote side. */ - if (check_type && !is_builtin(exprType(node))) + if (check_type && + !is_builtin(exprType(node)) && + !is_shippable(exprType(node), fpinfo->extensions)) return false; /* @@ -1351,6 +1359,9 @@ deparseConst(Const *node, deparse_expr_cxt *context) bool isfloat = false; bool needlabel; + /* Access extension metadata from fpinfo on baserel */ + PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *)(context->foreignrel->fdw_private); + if (node->constisnull) { appendStringInfoString(buf, "NULL"); @@ -1428,9 +1439,16 @@ deparseConst(Const *node, deparse_expr_cxt *context) break; } if (needlabel) + { + /* + * References to extension types need to be fully qualified, + * but references to built-in types shouldn't be. + */ appendStringInfo(buf, "::%s", - format_type_with_typemod(node->consttype, - node->consttypmod)); + is_shippable(node->consttype, fpinfo->extensions) ? + format_type_be_qualified(node->consttype) : + format_type_with_typemod(node->consttype, node->consttypmod)); + } } /* diff --git a/contrib/postgres_fdw/expected/shippable.out b/contrib/postgres_fdw/expected/shippable.out new file mode 100644 index 0000000000000..73afbea0c0e94 --- /dev/null +++ b/contrib/postgres_fdw/expected/shippable.out @@ -0,0 +1,139 @@ +-- =================================================================== +-- create FDW objects +-- =================================================================== +-- Error, extension isn't installed yet +ALTER SERVER loopback OPTIONS (ADD extensions 'cube'); +ERROR: required extension "cube" is not installed +HINT: Extension must be installed locally before it can be used on a remote server. +-- Try again +CREATE EXTENSION cube; +ALTER SERVER loopback OPTIONS (ADD extensions 'cube'); +ALTER SERVER loopback OPTIONS (DROP extensions); +-- =================================================================== +-- create objects used through FDW loopback server +-- =================================================================== +CREATE SCHEMA "SH 1"; +CREATE TABLE "SH 1"."TBL 1" ( + "C 1" int NOT NULL, + c2 int NOT NULL, + c3 cube, + c4 timestamptz +); +INSERT INTO "SH 1"."TBL 1" + SELECT id, + 2 * id, + cube(id,2*id), + '1970-01-01'::timestamptz + ((id % 100) || ' days')::interval + FROM generate_series(1, 1000) id; +ANALYZE "SH 1"."TBL 1"; +-- =================================================================== +-- create foreign table +-- =================================================================== +CREATE FOREIGN TABLE shft1 ( + "C 1" int NOT NULL, + c2 int NOT NULL, + c3 cube, + c4 timestamptz +) SERVER loopback +OPTIONS (schema_name 'SH 1', table_name 'TBL 1'); +-- =================================================================== +-- simple queries +-- =================================================================== +-- without operator shipping +EXPLAIN (COSTS false) SELECT * FROM shft1 LIMIT 1; + QUERY PLAN +----------------------------- + Limit + -> Foreign Scan on shft1 +(2 rows) + +EXPLAIN VERBOSE SELECT c2 FROM shft1 WHERE c3 && cube(1.5,2.5); + QUERY PLAN +--------------------------------------------------------------------- + Foreign Scan on public.shft1 (cost=100.00..205.06 rows=15 width=4) + Output: c2 + Filter: (shft1.c3 && '(1.5),(2.5)'::cube) + Remote SQL: SELECT c2, c3 FROM "SH 1"."TBL 1" +(4 rows) + +SELECT c2 FROM shft1 WHERE c3 && cube(1.5,2.5); + c2 +---- + 2 + 4 +(2 rows) + +EXPLAIN VERBOSE SELECT c2 FROM shft1 WHERE c3 && '(1.5),(2.5)'::cube; + QUERY PLAN +--------------------------------------------------------------------- + Foreign Scan on public.shft1 (cost=100.00..205.06 rows=15 width=4) + Output: c2 + Filter: (shft1.c3 && '(1.5),(2.5)'::cube) + Remote SQL: SELECT c2, c3 FROM "SH 1"."TBL 1" +(4 rows) + +-- with operator shipping +ALTER SERVER loopback OPTIONS (ADD extensions 'cube'); +EXPLAIN VERBOSE SELECT c2 FROM shft1 WHERE c3 && cube(1.5,2.5); + QUERY PLAN +--------------------------------------------------------------------------------------------------------- + Foreign Scan on public.shft1 (cost=100.00..146.86 rows=15 width=4) + Output: c2 + Remote SQL: SELECT c2 FROM "SH 1"."TBL 1" WHERE ((c3 OPERATOR(public.&&) '(1.5),(2.5)'::public.cube)) +(3 rows) + +SELECT c2 FROM shft1 WHERE c3 && cube(1.5,2.5); + c2 +---- + 2 + 4 +(2 rows) + +EXPLAIN VERBOSE SELECT c2 FROM shft1 WHERE c3 && '(1.5),(2.5)'::cube; + QUERY PLAN +--------------------------------------------------------------------------------------------------------- + Foreign Scan on public.shft1 (cost=100.00..146.86 rows=15 width=4) + Output: c2 + Remote SQL: SELECT c2 FROM "SH 1"."TBL 1" WHERE ((c3 OPERATOR(public.&&) '(1.5),(2.5)'::public.cube)) +(3 rows) + +EXPLAIN VERBOSE SELECT cube_dim(c3) FROM shft1 WHERE c3 && '(1.5),(2.5)'::cube; + QUERY PLAN +--------------------------------------------------------------------------------------------------------- + Foreign Scan on public.shft1 (cost=100.00..128.43 rows=7 width=32) + Output: cube_dim(c3) + Remote SQL: SELECT c3 FROM "SH 1"."TBL 1" WHERE ((c3 OPERATOR(public.&&) '(1.5),(2.5)'::public.cube)) +(3 rows) + +SELECT cube_dim(c3) FROM shft1 WHERE c3 && '(1.5),(2.5)'::cube; + cube_dim +---------- + 1 + 1 +(2 rows) + +EXPLAIN VERBOSE SELECT c2 FROM shft1 WHERE cube_dim(c3) = 1 LIMIT 2; + QUERY PLAN +------------------------------------------------------------------------------------- + Limit (cost=100.00..107.22 rows=2 width=4) + Output: c2 + -> Foreign Scan on public.shft1 (cost=100.00..154.18 rows=15 width=4) + Output: c2 + Remote SQL: SELECT c2 FROM "SH 1"."TBL 1" WHERE ((public.cube_dim(c3) = 1)) +(5 rows) + +SELECT c2 FROM shft1 WHERE cube_dim(c3) = 1 LIMIT 2; + c2 +---- + 2 + 4 +(2 rows) + +-- =================================================================== +-- clean up +-- =================================================================== +DROP FOREIGN TABLE shft1; +DROP TABLE "SH 1"."TBL 1"; +DROP SCHEMA "SH 1"; +DROP EXTENSION cube; +ALTER SERVER loopback OPTIONS (DROP extensions); diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c index 7547ec28172e0..864bf53ce838d 100644 --- a/contrib/postgres_fdw/option.c +++ b/contrib/postgres_fdw/option.c @@ -19,6 +19,8 @@ #include "catalog/pg_foreign_table.h" #include "catalog/pg_user_mapping.h" #include "commands/defrem.h" +#include "commands/extension.h" +#include "utils/builtins.h" /* @@ -124,6 +126,11 @@ postgres_fdw_validator(PG_FUNCTION_ARGS) errmsg("%s requires a non-negative numeric value", def->defname))); } + else if (strcmp(def->defname, "extensions") == 0) + { + /* this must have already-installed extensions */ + (void) ExtractExtensionList(defGetString(def), false); + } } PG_RETURN_VOID(); @@ -153,6 +160,8 @@ InitPgFdwOptions(void) /* updatable is available on both server and table */ {"updatable", ForeignServerRelationId, false}, {"updatable", ForeignTableRelationId, false}, + /* extensions is available on server */ + {"extensions", ForeignServerRelationId, false}, {NULL, InvalidOid, false} }; @@ -293,3 +302,49 @@ ExtractConnectionOptions(List *defelems, const char **keywords, } return i; } + +/* + * Parse a comma-separated string and return a List of the Oids of the + * extensions in the string. If an extension provided cannot be looked + * up in the catalog (it hasn't been installed or doesn't exist) then + * throw up an error. + */ +List * +ExtractExtensionList(char *extensionString, bool populateList) +{ + List *extlist; + List *extensionOids = NIL; + ListCell *l; + + if (!SplitIdentifierString(extensionString, ',', &extlist)) + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid extension list syntax"))); + } + + foreach(l, extlist) + { + const char *extension_name = (const char *) lfirst(l); + Oid extension_oid = get_extension_oid(extension_name, true); + + if (!OidIsValid(extension_oid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("required extension \"%s\" is not installed", + extension_name), + errhint("Extension must be installed locally before it can be used on a remote server."))); + else if (populateList) + { + /* + * Only add this extension OID to the list if it is not already + * in included. + */ + if (!list_member_oid(extensionOids, extension_oid)) + extensionOids = lappend_oid(extensionOids, extension_oid); + } + } + + list_free(extlist); + return extensionOids; +} diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index e4d799cecd541..26147771199d7 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -47,39 +47,6 @@ PG_MODULE_MAGIC; /* Default CPU cost to process 1 row (above and beyond cpu_tuple_cost). */ #define DEFAULT_FDW_TUPLE_COST 0.01 -/* - * FDW-specific planner information kept in RelOptInfo.fdw_private for a - * foreign table. This information is collected by postgresGetForeignRelSize. - */ -typedef struct PgFdwRelationInfo -{ - /* baserestrictinfo clauses, broken down into safe and unsafe subsets. */ - List *remote_conds; - List *local_conds; - - /* Bitmap of attr numbers we need to fetch from the remote server. */ - Bitmapset *attrs_used; - - /* Cost and selectivity of local_conds. */ - QualCost local_conds_cost; - Selectivity local_conds_sel; - - /* Estimated size and cost for a scan with baserestrictinfo quals. */ - double rows; - int width; - Cost startup_cost; - Cost total_cost; - - /* Options extracted from catalogs. */ - bool use_remote_estimate; - Cost fdw_startup_cost; - Cost fdw_tuple_cost; - - /* Cached catalog information. */ - ForeignTable *table; - ForeignServer *server; - UserMapping *user; /* only set in use_remote_estimate mode */ -} PgFdwRelationInfo; /* * Indexes of FDW-private information stored in fdw_private lists. @@ -405,6 +372,7 @@ postgresGetForeignRelSize(PlannerInfo *root, fpinfo->use_remote_estimate = false; fpinfo->fdw_startup_cost = DEFAULT_FDW_STARTUP_COST; fpinfo->fdw_tuple_cost = DEFAULT_FDW_TUPLE_COST; + fpinfo->extensions = NIL; foreach(lc, fpinfo->server->options) { @@ -416,6 +384,9 @@ postgresGetForeignRelSize(PlannerInfo *root, fpinfo->fdw_startup_cost = strtod(defGetString(def), NULL); else if (strcmp(def->defname, "fdw_tuple_cost") == 0) fpinfo->fdw_tuple_cost = strtod(defGetString(def), NULL); + else if (strcmp(def->defname, "extensions") == 0) + fpinfo->extensions = + ExtractExtensionList(defGetString(def), true); } foreach(lc, fpinfo->table->options) { diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h index 3835ddb79ac61..27f6dad092892 100644 --- a/contrib/postgres_fdw/postgres_fdw.h +++ b/contrib/postgres_fdw/postgres_fdw.h @@ -20,6 +20,43 @@ #include "libpq-fe.h" +/* + * FDW-specific planner information kept in RelOptInfo.fdw_private for a + * foreign table. This information is collected by postgresGetForeignRelSize. + */ +typedef struct PgFdwRelationInfo +{ + /* baserestrictinfo clauses, broken down into safe and unsafe subsets. */ + List *remote_conds; + List *local_conds; + + /* Bitmap of attr numbers we need to fetch from the remote server. */ + Bitmapset *attrs_used; + + /* Cost and selectivity of local_conds. */ + QualCost local_conds_cost; + Selectivity local_conds_sel; + + /* Estimated size and cost for a scan with baserestrictinfo quals. */ + double rows; + int width; + Cost startup_cost; + Cost total_cost; + + /* Options extracted from catalogs. */ + bool use_remote_estimate; + Cost fdw_startup_cost; + Cost fdw_tuple_cost; + + /* Optional extensions to support (list of oid) */ + List *extensions; + + /* Cached catalog information. */ + ForeignTable *table; + ForeignServer *server; + UserMapping *user; /* only set in use_remote_estimate mode */ +} PgFdwRelationInfo; + /* in postgres_fdw.c */ extern int set_transmission_modes(void); extern void reset_transmission_modes(int nestlevel); @@ -37,6 +74,11 @@ extern void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, extern int ExtractConnectionOptions(List *defelems, const char **keywords, const char **values); +extern List *ExtractExtensionList(char *extensionString, + bool populateList); + +/* in shippable.c */ +extern bool is_shippable(Oid procnumber, List *extension_list); /* in deparse.c */ extern void classifyConditions(PlannerInfo *root, diff --git a/contrib/postgres_fdw/shippable.c b/contrib/postgres_fdw/shippable.c new file mode 100644 index 0000000000000..c8895075adad4 --- /dev/null +++ b/contrib/postgres_fdw/shippable.c @@ -0,0 +1,198 @@ +/*------------------------------------------------------------------------- + * + * shippable.c + * Facility to track database objects shippable to a foreign server. + * + * Determine if functions and operators for non-built-in types/functions/ops + * are shippable to the remote server. + * + * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group + * + * IDENTIFICATION + * contrib/postgres_fdw/shippable.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "postgres_fdw.h" + +#include "access/genam.h" +#include "access/heapam.h" +#include "access/htup_details.h" +#include "catalog/dependency.h" +#include "catalog/indexing.h" +#include "catalog/pg_depend.h" +#include "utils/fmgroids.h" +#include "utils/hsearch.h" +#include "utils/inval.h" +#include "utils/rel.h" +#include "utils/snapmgr.h" +#include "utils/syscache.h" + +/* Hash table for informations about remote objects we'll call */ +static HTAB *ShippableCacheHash = NULL; + +/* objid is the lookup key, must appear first */ +typedef struct +{ + /* extension the object appears within, or InvalidOid if none */ + Oid objid; +} ShippableCacheKey; + +typedef struct +{ + /* lookup key - must be first */ + ShippableCacheKey key; + bool shippable; +} ShippableCacheEntry; + +/* + * Flush all cache entries when pg_foreign_server is updated. + */ +static void +InvalidateShippableCacheCallback(Datum arg, int cacheid, uint32 hashvalue) +{ + HASH_SEQ_STATUS status; + ShippableCacheEntry *entry; + + hash_seq_init(&status, ShippableCacheHash); + while ((entry = (ShippableCacheEntry *) hash_seq_search(&status)) != NULL) + { + if (hash_search(ShippableCacheHash, + (void *) &entry->key, + HASH_REMOVE, + NULL) == NULL) + elog(ERROR, "hash table corrupted"); + } +} + +/* + * Initialize the cache of functions we can ship to remote server. + */ +static void +InitializeShippableCache(void) +{ + HASHCTL ctl; + + /* Initialize the hash table. */ + MemSet(&ctl, 0, sizeof(ctl)); + ctl.keysize = sizeof(ShippableCacheKey); + ctl.entrysize = sizeof(ShippableCacheEntry); + ShippableCacheHash = + hash_create("Shippable cache", 256, &ctl, HASH_ELEM); + + CacheRegisterSyscacheCallback(FOREIGNSERVEROID, + InvalidateShippableCacheCallback, + (Datum) 0); +} + +/* + * Returns true if given operator/function is part of an extension declared in + * the server options. + */ +static bool +lookup_shippable(Oid objnumber, List *extension_list) +{ + static int nkeys = 1; + ScanKeyData key[nkeys]; + HeapTuple tup; + Relation depRel; + SysScanDesc scan; + bool is_shippable = false; + + /* Always return false if we don't have any declared extensions */ + if (extension_list == NIL) + return false; + + /* We need this relation to scan */ + depRel = heap_open(DependRelationId, RowExclusiveLock); + + /* + * Scan the system dependency table for all entries this object + * depends on, then iterate through and see if one of them + * is an extension declared by the user in the options + */ + ScanKeyInit(&key[0], + Anum_pg_depend_objid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(objnumber)); + + scan = systable_beginscan(depRel, DependDependerIndexId, true, + GetCatalogSnapshot(depRel->rd_id), nkeys, key); + + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup); + + if (foundDep->deptype == DEPENDENCY_EXTENSION && + list_member_oid(extension_list, foundDep->refobjid)) + { + is_shippable = true; + break; + } + } + + systable_endscan(scan); + relation_close(depRel, RowExclusiveLock); + + return is_shippable; +} + +/* + * is_shippable + * Is this object (proc/op/type) shippable to foreign server? + * Check cache first, then look-up whether (proc/op/type) is + * part of a declared extension if it is not cached. + */ +bool +is_shippable(Oid objnumber, List *extension_list) +{ + ShippableCacheKey key; + ShippableCacheEntry *entry; + + /* Always return false if we don't have any declared extensions */ + if (extension_list == NIL) + return false; + + /* Find existing cache, if any. */ + if (!ShippableCacheHash) + InitializeShippableCache(); + + /* Zero out the key */ + memset(&key, 0, sizeof(key)); + + key.objid = objnumber; + + entry = (ShippableCacheEntry *) + hash_search(ShippableCacheHash, + (void *) &key, + HASH_FIND, + NULL); + + /* Not found in ShippableCacheHash cache. Construct new entry. */ + if (!entry) + { + /* + * Right now "shippability" is exclusively a function of whether + * the obj (proc/op/type) is in an extension declared by the user. + * In the future we could additionally have a whitelist of functions + * declared one at a time. + */ + bool shippable = lookup_shippable(objnumber, extension_list); + + entry = (ShippableCacheEntry *) + hash_search(ShippableCacheHash, + (void *) &key, + HASH_ENTER, + NULL); + + entry->shippable = shippable; + } + + if (!entry) + return false; + else + return entry->shippable; +} diff --git a/contrib/postgres_fdw/sql/shippable.sql b/contrib/postgres_fdw/sql/shippable.sql new file mode 100644 index 0000000000000..83ee38c021bab --- /dev/null +++ b/contrib/postgres_fdw/sql/shippable.sql @@ -0,0 +1,76 @@ +-- =================================================================== +-- create FDW objects +-- =================================================================== + +-- Error, extension isn't installed yet +ALTER SERVER loopback OPTIONS (ADD extensions 'cube'); + +-- Try again +CREATE EXTENSION cube; +ALTER SERVER loopback OPTIONS (ADD extensions 'cube'); +ALTER SERVER loopback OPTIONS (DROP extensions); + + +-- =================================================================== +-- create objects used through FDW loopback server +-- =================================================================== + +CREATE SCHEMA "SH 1"; +CREATE TABLE "SH 1"."TBL 1" ( + "C 1" int NOT NULL, + c2 int NOT NULL, + c3 cube, + c4 timestamptz +); + +INSERT INTO "SH 1"."TBL 1" + SELECT id, + 2 * id, + cube(id,2*id), + '1970-01-01'::timestamptz + ((id % 100) || ' days')::interval + FROM generate_series(1, 1000) id; + +ANALYZE "SH 1"."TBL 1"; + +-- =================================================================== +-- create foreign table +-- =================================================================== + +CREATE FOREIGN TABLE shft1 ( + "C 1" int NOT NULL, + c2 int NOT NULL, + c3 cube, + c4 timestamptz +) SERVER loopback +OPTIONS (schema_name 'SH 1', table_name 'TBL 1'); + +-- =================================================================== +-- simple queries +-- =================================================================== + +-- without operator shipping +EXPLAIN (COSTS false) SELECT * FROM shft1 LIMIT 1; +EXPLAIN VERBOSE SELECT c2 FROM shft1 WHERE c3 && cube(1.5,2.5); +SELECT c2 FROM shft1 WHERE c3 && cube(1.5,2.5); +EXPLAIN VERBOSE SELECT c2 FROM shft1 WHERE c3 && '(1.5),(2.5)'::cube; + +-- with operator shipping +ALTER SERVER loopback OPTIONS (ADD extensions 'cube'); +EXPLAIN VERBOSE SELECT c2 FROM shft1 WHERE c3 && cube(1.5,2.5); +SELECT c2 FROM shft1 WHERE c3 && cube(1.5,2.5); +EXPLAIN VERBOSE SELECT c2 FROM shft1 WHERE c3 && '(1.5),(2.5)'::cube; +EXPLAIN VERBOSE SELECT cube_dim(c3) FROM shft1 WHERE c3 && '(1.5),(2.5)'::cube; +SELECT cube_dim(c3) FROM shft1 WHERE c3 && '(1.5),(2.5)'::cube; + +EXPLAIN VERBOSE SELECT c2 FROM shft1 WHERE cube_dim(c3) = 1 LIMIT 2; +SELECT c2 FROM shft1 WHERE cube_dim(c3) = 1 LIMIT 2; + +-- =================================================================== +-- clean up +-- =================================================================== + +DROP FOREIGN TABLE shft1; +DROP TABLE "SH 1"."TBL 1"; +DROP SCHEMA "SH 1"; +DROP EXTENSION cube; +ALTER SERVER loopback OPTIONS (DROP extensions); diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml index 7c922821e988f..1e7ec08c9b9db 100644 --- a/doc/src/sgml/postgres-fdw.sgml +++ b/doc/src/sgml/postgres-fdw.sgml @@ -373,6 +373,38 @@ foreign tables, see . + + + Extension Options + + + By default only built-in operators and functions will be sent from the + local to the foreign server. This may be overridden using the following + option: + + + + + + extensions + + + This option controls the list of extensions that are expected to be + installed on the foreign server, using a comma-separated list of + extension names. Those extensions are also expected to be installed + on the local server too. This option is available for servers. + + +CREATE SERVER foreign_server + FOREIGN DATA WRAPPER postgres_fdw + OPTIONS (host '127.0.0.1', port '5432', dbname 'my_db', extensions 'cube, seg'); + + + + + + + From ed33e7489601e659f436d6afda3cce28304eba50 Mon Sep 17 00:00:00 2001 From: Paul Ramsey Date: Sat, 3 Oct 2015 18:54:22 -0700 Subject: [PATCH 2/7] Fixes from Andres Freund patch review --- contrib/postgres_fdw/deparse.c | 10 +++++----- contrib/postgres_fdw/option.c | 8 ++++---- contrib/postgres_fdw/postgres_fdw.h | 4 ++-- contrib/postgres_fdw/shippable.c | 22 ++++++++++++++-------- 4 files changed, 25 insertions(+), 19 deletions(-) diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c index 4c06e66692c4e..1f79344fc98c8 100644 --- a/contrib/postgres_fdw/deparse.c +++ b/contrib/postgres_fdw/deparse.c @@ -382,7 +382,7 @@ foreign_expr_walker(Node *node, * semantics on remote side. */ if (!is_builtin(fe->funcid) && - !is_shippable(fe->funcid, fpinfo->extensions)) + !is_shippable(fe->funcid, ProcedureRelationId, fpinfo->extensions)) return false; /* @@ -431,7 +431,7 @@ foreign_expr_walker(Node *node, * too.) */ if (!is_builtin(oe->opno) && - !is_shippable(oe->opno, fpinfo->extensions)) + !is_shippable(oe->opno, OperatorRelationId, fpinfo->extensions)) return false; /* @@ -472,7 +472,7 @@ foreign_expr_walker(Node *node, * Again, only built-in operators can be sent to remote. */ if (!is_builtin(oe->opno) && - !is_shippable(oe->opno, fpinfo->extensions)) + !is_shippable(oe->opno, OperatorRelationId, fpinfo->extensions)) return false; /* @@ -624,7 +624,7 @@ foreign_expr_walker(Node *node, */ if (check_type && !is_builtin(exprType(node)) && - !is_shippable(exprType(node), fpinfo->extensions)) + !is_shippable(exprType(node), TypeRelationId, fpinfo->extensions)) return false; /* @@ -1445,7 +1445,7 @@ deparseConst(Const *node, deparse_expr_cxt *context) * but references to built-in types shouldn't be. */ appendStringInfo(buf, "::%s", - is_shippable(node->consttype, fpinfo->extensions) ? + is_shippable(node->consttype, TypeRelationId, fpinfo->extensions) ? format_type_be_qualified(node->consttype) : format_type_with_typemod(node->consttype, node->consttypmod)); } diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c index 864bf53ce838d..30290b0dfa97d 100644 --- a/contrib/postgres_fdw/option.c +++ b/contrib/postgres_fdw/option.c @@ -128,7 +128,7 @@ postgres_fdw_validator(PG_FUNCTION_ARGS) } else if (strcmp(def->defname, "extensions") == 0) { - /* this must have already-installed extensions */ + /* check that the requested extensions are actually installed */ (void) ExtractExtensionList(defGetString(def), false); } } @@ -157,10 +157,10 @@ InitPgFdwOptions(void) /* cost factors */ {"fdw_startup_cost", ForeignServerRelationId, false}, {"fdw_tuple_cost", ForeignServerRelationId, false}, - /* updatable is available on both server and table */ + /* updatable option is available on both server and table */ {"updatable", ForeignServerRelationId, false}, {"updatable", ForeignTableRelationId, false}, - /* extensions is available on server */ + /* "extensions" option is available on server */ {"extensions", ForeignServerRelationId, false}, {NULL, InvalidOid, false} }; @@ -307,7 +307,7 @@ ExtractConnectionOptions(List *defelems, const char **keywords, * Parse a comma-separated string and return a List of the Oids of the * extensions in the string. If an extension provided cannot be looked * up in the catalog (it hasn't been installed or doesn't exist) then - * throw up an error. + * raise an error. */ List * ExtractExtensionList(char *extensionString, bool populateList) diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h index 27f6dad092892..300bfb359ce16 100644 --- a/contrib/postgres_fdw/postgres_fdw.h +++ b/contrib/postgres_fdw/postgres_fdw.h @@ -48,7 +48,7 @@ typedef struct PgFdwRelationInfo Cost fdw_startup_cost; Cost fdw_tuple_cost; - /* Optional extensions to support (list of oid) */ + /* Optional extensions to support (list of Oids). */ List *extensions; /* Cached catalog information. */ @@ -78,7 +78,7 @@ extern List *ExtractExtensionList(char *extensionString, bool populateList); /* in shippable.c */ -extern bool is_shippable(Oid procnumber, List *extension_list); +extern bool is_shippable(Oid objnumber, Oid classnumber, List *extension_list); /* in deparse.c */ extern void classifyConditions(PlannerInfo *root, diff --git a/contrib/postgres_fdw/shippable.c b/contrib/postgres_fdw/shippable.c index c8895075adad4..2ec3038f78b4d 100644 --- a/contrib/postgres_fdw/shippable.c +++ b/contrib/postgres_fdw/shippable.c @@ -39,6 +39,7 @@ typedef struct { /* extension the object appears within, or InvalidOid if none */ Oid objid; + Oid classid; } ShippableCacheKey; typedef struct @@ -89,24 +90,23 @@ InitializeShippableCache(void) } /* - * Returns true if given operator/function is part of an extension declared in + * Returns true if given operator/function is part of an extension listed in * the server options. */ static bool -lookup_shippable(Oid objnumber, List *extension_list) +lookup_shippable(Oid objnumber, Oid classnumber, List *extension_list) { - static int nkeys = 1; + static int nkeys = 2; ScanKeyData key[nkeys]; HeapTuple tup; Relation depRel; SysScanDesc scan; bool is_shippable = false; - /* Always return false if we don't have any declared extensions */ + /* Always return false if the user hasn't set the "extensions" option */ if (extension_list == NIL) return false; - /* We need this relation to scan */ depRel = heap_open(DependRelationId, RowExclusiveLock); /* @@ -115,6 +115,11 @@ lookup_shippable(Oid objnumber, List *extension_list) * is an extension declared by the user in the options */ ScanKeyInit(&key[0], + Anum_pg_depend_classid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(classnumber)); + + ScanKeyInit(&key[1], Anum_pg_depend_objid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(objnumber)); @@ -147,12 +152,12 @@ lookup_shippable(Oid objnumber, List *extension_list) * part of a declared extension if it is not cached. */ bool -is_shippable(Oid objnumber, List *extension_list) +is_shippable(Oid objnumber, Oid classnumber, List *extension_list) { ShippableCacheKey key; ShippableCacheEntry *entry; - /* Always return false if we don't have any declared extensions */ + /* Always return false if the user hasn't set the "extensions" option */ if (extension_list == NIL) return false; @@ -164,6 +169,7 @@ is_shippable(Oid objnumber, List *extension_list) memset(&key, 0, sizeof(key)); key.objid = objnumber; + key.classid = classnumber; entry = (ShippableCacheEntry *) hash_search(ShippableCacheHash, @@ -180,7 +186,7 @@ is_shippable(Oid objnumber, List *extension_list) * In the future we could additionally have a whitelist of functions * declared one at a time. */ - bool shippable = lookup_shippable(objnumber, extension_list); + bool shippable = lookup_shippable(objnumber, classnumber, extension_list); entry = (ShippableCacheEntry *) hash_search(ShippableCacheHash, From 5e495def5b3fe626c4051f0c7a7862abb3145d1e Mon Sep 17 00:00:00 2001 From: Paul Ramsey Date: Tue, 6 Oct 2015 06:22:33 -0700 Subject: [PATCH 3/7] Comment use of hash vis-a-vis cache invalidation --- contrib/postgres_fdw/shippable.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/contrib/postgres_fdw/shippable.c b/contrib/postgres_fdw/shippable.c index 2ec3038f78b4d..4ed37f46ac131 100644 --- a/contrib/postgres_fdw/shippable.c +++ b/contrib/postgres_fdw/shippable.c @@ -188,6 +188,11 @@ is_shippable(Oid objnumber, Oid classnumber, List *extension_list) */ bool shippable = lookup_shippable(objnumber, classnumber, extension_list); + /* + * Don't create a new hash entry until *after* we have the shippable + * result in hand, as the shippable lookup might trigger a cache + * invalidation. + */ entry = (ShippableCacheEntry *) hash_search(ShippableCacheHash, (void *) &key, From 37970c2cf5e8d897afa91bd7a4456e481d79343e Mon Sep 17 00:00:00 2001 From: Paul Ramsey Date: Tue, 6 Oct 2015 06:25:50 -0700 Subject: [PATCH 4/7] Remove comment change --- contrib/postgres_fdw/option.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c index 30290b0dfa97d..a20f626f62dfa 100644 --- a/contrib/postgres_fdw/option.c +++ b/contrib/postgres_fdw/option.c @@ -157,7 +157,7 @@ InitPgFdwOptions(void) /* cost factors */ {"fdw_startup_cost", ForeignServerRelationId, false}, {"fdw_tuple_cost", ForeignServerRelationId, false}, - /* updatable option is available on both server and table */ + /* updatable is available on both server and table */ {"updatable", ForeignServerRelationId, false}, {"updatable", ForeignTableRelationId, false}, /* "extensions" option is available on server */ From d77489f0ca7bec2c7c0f190dbbadd8cbf223d9b9 Mon Sep 17 00:00:00 2001 From: Paul Ramsey Date: Tue, 6 Oct 2015 07:00:06 -0700 Subject: [PATCH 5/7] Ensure shippability info is cached per-server --- contrib/postgres_fdw/deparse.c | 10 +++++----- contrib/postgres_fdw/postgres_fdw.h | 2 +- contrib/postgres_fdw/shippable.c | 4 +++- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c index 1f79344fc98c8..8f50dc2e13dbc 100644 --- a/contrib/postgres_fdw/deparse.c +++ b/contrib/postgres_fdw/deparse.c @@ -382,7 +382,7 @@ foreign_expr_walker(Node *node, * semantics on remote side. */ if (!is_builtin(fe->funcid) && - !is_shippable(fe->funcid, ProcedureRelationId, fpinfo->extensions)) + !is_shippable(fe->funcid, ProcedureRelationId, fpinfo->server, fpinfo->extensions)) return false; /* @@ -431,7 +431,7 @@ foreign_expr_walker(Node *node, * too.) */ if (!is_builtin(oe->opno) && - !is_shippable(oe->opno, OperatorRelationId, fpinfo->extensions)) + !is_shippable(oe->opno, OperatorRelationId, fpinfo->server, fpinfo->extensions)) return false; /* @@ -472,7 +472,7 @@ foreign_expr_walker(Node *node, * Again, only built-in operators can be sent to remote. */ if (!is_builtin(oe->opno) && - !is_shippable(oe->opno, OperatorRelationId, fpinfo->extensions)) + !is_shippable(oe->opno, OperatorRelationId, fpinfo->server, fpinfo->extensions)) return false; /* @@ -624,7 +624,7 @@ foreign_expr_walker(Node *node, */ if (check_type && !is_builtin(exprType(node)) && - !is_shippable(exprType(node), TypeRelationId, fpinfo->extensions)) + !is_shippable(exprType(node), TypeRelationId, fpinfo->server, fpinfo->extensions)) return false; /* @@ -1445,7 +1445,7 @@ deparseConst(Const *node, deparse_expr_cxt *context) * but references to built-in types shouldn't be. */ appendStringInfo(buf, "::%s", - is_shippable(node->consttype, TypeRelationId, fpinfo->extensions) ? + is_shippable(node->consttype, TypeRelationId, fpinfo->server, fpinfo->extensions) ? format_type_be_qualified(node->consttype) : format_type_with_typemod(node->consttype, node->consttypmod)); } diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h index 300bfb359ce16..f30d9d153f95d 100644 --- a/contrib/postgres_fdw/postgres_fdw.h +++ b/contrib/postgres_fdw/postgres_fdw.h @@ -78,7 +78,7 @@ extern List *ExtractExtensionList(char *extensionString, bool populateList); /* in shippable.c */ -extern bool is_shippable(Oid objnumber, Oid classnumber, List *extension_list); +extern bool is_shippable(Oid objnumber, Oid classnumber, ForeignServer *server, List *extension_list); /* in deparse.c */ extern void classifyConditions(PlannerInfo *root, diff --git a/contrib/postgres_fdw/shippable.c b/contrib/postgres_fdw/shippable.c index 4ed37f46ac131..362af52fbf0d0 100644 --- a/contrib/postgres_fdw/shippable.c +++ b/contrib/postgres_fdw/shippable.c @@ -40,6 +40,7 @@ typedef struct /* extension the object appears within, or InvalidOid if none */ Oid objid; Oid classid; + Oid serverid; } ShippableCacheKey; typedef struct @@ -152,7 +153,7 @@ lookup_shippable(Oid objnumber, Oid classnumber, List *extension_list) * part of a declared extension if it is not cached. */ bool -is_shippable(Oid objnumber, Oid classnumber, List *extension_list) +is_shippable(Oid objnumber, Oid classnumber, ForeignServer *server, List *extension_list) { ShippableCacheKey key; ShippableCacheEntry *entry; @@ -170,6 +171,7 @@ is_shippable(Oid objnumber, Oid classnumber, List *extension_list) key.objid = objnumber; key.classid = classnumber; + key.serverid = server->serverid; entry = (ShippableCacheEntry *) hash_search(ShippableCacheHash, From bda377f163df4bedfb4ab10b9327862e57560ac9 Mon Sep 17 00:00:00 2001 From: Paul Ramsey Date: Tue, 6 Oct 2015 10:48:11 -0700 Subject: [PATCH 6/7] Add regression test and small fix turned up by regression --- contrib/postgres_fdw/Makefile | 3 +- contrib/postgres_fdw/deparse.c | 13 +++- contrib/postgres_fdw/expected/shippable.out | 86 +++++++++++++++++++++ contrib/postgres_fdw/shippable.c | 4 +- contrib/postgres_fdw/sql/shippable.sql | 59 +++++++++++++- 5 files changed, 157 insertions(+), 8 deletions(-) diff --git a/contrib/postgres_fdw/Makefile b/contrib/postgres_fdw/Makefile index f78fc64c06919..dc164ee6e5735 100644 --- a/contrib/postgres_fdw/Makefile +++ b/contrib/postgres_fdw/Makefile @@ -12,7 +12,8 @@ DATA = postgres_fdw--1.0.sql # Note: shippable tests depend on postgres_fdw tests setup REGRESS = postgres_fdw shippable -EXTRA_INSTALL = contrib/cube +# Note: shippable tests require cube and seg +EXTRA_INSTALL = contrib/cube contrib/seg ifdef USE_PGXS PG_CONFIG = pg_config diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c index 8f50dc2e13dbc..aa575b39ed078 100644 --- a/contrib/postgres_fdw/deparse.c +++ b/contrib/postgres_fdw/deparse.c @@ -1444,10 +1444,15 @@ deparseConst(Const *node, deparse_expr_cxt *context) * References to extension types need to be fully qualified, * but references to built-in types shouldn't be. */ - appendStringInfo(buf, "::%s", - is_shippable(node->consttype, TypeRelationId, fpinfo->server, fpinfo->extensions) ? - format_type_be_qualified(node->consttype) : - format_type_with_typemod(node->consttype, node->consttypmod)); + if (!is_builtin(node->consttype) && + is_shippable(node->consttype, TypeRelationId, fpinfo->server, fpinfo->extensions)) + { + appendStringInfo(buf, "::%s", format_type_be_qualified(node->consttype)); + } + else + { + appendStringInfo(buf, "::%s", format_type_with_typemod(node->consttype, node->consttypmod)); + } } } diff --git a/contrib/postgres_fdw/expected/shippable.out b/contrib/postgres_fdw/expected/shippable.out index 73afbea0c0e94..abf924efe4ae4 100644 --- a/contrib/postgres_fdw/expected/shippable.out +++ b/contrib/postgres_fdw/expected/shippable.out @@ -129,11 +129,97 @@ SELECT c2 FROM shft1 WHERE cube_dim(c3) = 1 LIMIT 2; 4 (2 rows) +-- =================================================================== +-- add a second server with different extension shipping +-- =================================================================== +DO $d$ + BEGIN + EXECUTE $$CREATE SERVER loopback_two FOREIGN DATA WRAPPER postgres_fdw + OPTIONS (dbname '$$||current_database()||$$', + port '$$||current_setting('port')||$$' + )$$; + END; +$d$; +CREATE USER MAPPING FOR CURRENT_USER SERVER loopback_two; +CREATE EXTENSION seg; +CREATE TABLE seg_local ( + id integer, + s seg, + n text +); +INSERT INTO seg_local (id, s, n) VALUES (1, '1.0 .. 2.0', 'foo'); +INSERT INTO seg_local (id, s, n) VALUES (2, '3.0 .. 4.0', 'bar'); +INSERT INTO seg_local (id, s, n) VALUES (3, '5.0 .. 6.0', 'baz'); +ANALYZE seg_local; +CREATE FOREIGN TABLE seg_remote_two ( + id integer, + s seg, + n text +) SERVER loopback_two +OPTIONS (table_name 'seg_local'); +SELECT id FROM seg_local WHERE s && '5.8 .. 6.2'::seg AND n = 'baz'; + id +---- + 3 +(1 row) + +EXPLAIN VERBOSE SELECT id FROM seg_remote_two WHERE s && '5.8 .. 6.2'::seg AND n = 'baz'; + QUERY PLAN +----------------------------------------------------------------------------- + Foreign Scan on public.seg_remote_two (cost=100.00..157.88 rows=1 width=4) + Output: id + Filter: (seg_remote_two.s && '5.8 .. 6.2'::seg) + Remote SQL: SELECT id, s FROM public.seg_local WHERE ((n = 'baz'::text)) +(4 rows) + +ALTER SERVER loopback_two OPTIONS (ADD extensions 'seg'); +EXPLAIN VERBOSE SELECT id FROM seg_remote_two WHERE s && '5.8 .. 6.2'::seg AND n = 'baz'; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------- + Foreign Scan on public.seg_remote_two (cost=100.00..153.89 rows=1 width=4) + Output: id + Remote SQL: SELECT id FROM public.seg_local WHERE ((s OPERATOR(public.&&) '5.8 .. 6.2'::public.seg)) AND ((n = 'baz'::text)) +(3 rows) + +CREATE FOREIGN TABLE seg_remote_one ( + id integer, + s seg, + n text +) SERVER loopback +OPTIONS (table_name 'seg_local'); +SELECT id FROM seg_remote_one WHERE s && '5.8 .. 6.2'::seg AND n = 'baz'; + id +---- + 3 +(1 row) + +EXPLAIN VERBOSE SELECT id FROM seg_remote_one WHERE s && '5.8 .. 6.2'::seg AND n = 'baz'; + QUERY PLAN +----------------------------------------------------------------------------- + Foreign Scan on public.seg_remote_one (cost=100.00..157.88 rows=1 width=4) + Output: id + Filter: (seg_remote_one.s && '5.8 .. 6.2'::seg) + Remote SQL: SELECT id, s FROM public.seg_local WHERE ((n = 'baz'::text)) +(4 rows) + +EXPLAIN VERBOSE SELECT id FROM seg_remote_two WHERE s && '5.8 .. 6.2'::seg AND n = 'baz'; + QUERY PLAN +-------------------------------------------------------------------------------------------------------------------------------- + Foreign Scan on public.seg_remote_two (cost=100.00..153.89 rows=1 width=4) + Output: id + Remote SQL: SELECT id FROM public.seg_local WHERE ((s OPERATOR(public.&&) '5.8 .. 6.2'::public.seg)) AND ((n = 'baz'::text)) +(3 rows) + -- =================================================================== -- clean up -- =================================================================== +DROP FOREIGN TABLE seg_remote_one, seg_remote_two; +DROP USER MAPPING FOR CURRENT_USER SERVER loopback_two; +DROP SERVER loopback_two; +DROP TABLE seg_local; DROP FOREIGN TABLE shft1; DROP TABLE "SH 1"."TBL 1"; DROP SCHEMA "SH 1"; DROP EXTENSION cube; +DROP EXTENSION seg; ALTER SERVER loopback OPTIONS (DROP extensions); diff --git a/contrib/postgres_fdw/shippable.c b/contrib/postgres_fdw/shippable.c index 362af52fbf0d0..6c218b06639fe 100644 --- a/contrib/postgres_fdw/shippable.c +++ b/contrib/postgres_fdw/shippable.c @@ -38,9 +38,9 @@ static HTAB *ShippableCacheHash = NULL; typedef struct { /* extension the object appears within, or InvalidOid if none */ - Oid objid; - Oid classid; Oid serverid; + Oid classid; + Oid objid; } ShippableCacheKey; typedef struct diff --git a/contrib/postgres_fdw/sql/shippable.sql b/contrib/postgres_fdw/sql/shippable.sql index 83ee38c021bab..d3e03c219bcda 100644 --- a/contrib/postgres_fdw/sql/shippable.sql +++ b/contrib/postgres_fdw/sql/shippable.sql @@ -66,11 +66,68 @@ EXPLAIN VERBOSE SELECT c2 FROM shft1 WHERE cube_dim(c3) = 1 LIMIT 2; SELECT c2 FROM shft1 WHERE cube_dim(c3) = 1 LIMIT 2; -- =================================================================== --- clean up +-- add a second server with different extension shipping -- =================================================================== +DO $d$ + BEGIN + EXECUTE $$CREATE SERVER loopback_two FOREIGN DATA WRAPPER postgres_fdw + OPTIONS (dbname '$$||current_database()||$$', + port '$$||current_setting('port')||$$' + )$$; + END; +$d$; + +CREATE USER MAPPING FOR CURRENT_USER SERVER loopback_two; + +CREATE EXTENSION seg; + +CREATE TABLE seg_local ( + id integer, + s seg, + n text +); + +INSERT INTO seg_local (id, s, n) VALUES (1, '1.0 .. 2.0', 'foo'); +INSERT INTO seg_local (id, s, n) VALUES (2, '3.0 .. 4.0', 'bar'); +INSERT INTO seg_local (id, s, n) VALUES (3, '5.0 .. 6.0', 'baz'); + +ANALYZE seg_local; + +CREATE FOREIGN TABLE seg_remote_two ( + id integer, + s seg, + n text +) SERVER loopback_two +OPTIONS (table_name 'seg_local'); + +SELECT id FROM seg_local WHERE s && '5.8 .. 6.2'::seg AND n = 'baz'; +EXPLAIN VERBOSE SELECT id FROM seg_remote_two WHERE s && '5.8 .. 6.2'::seg AND n = 'baz'; +ALTER SERVER loopback_two OPTIONS (ADD extensions 'seg'); +EXPLAIN VERBOSE SELECT id FROM seg_remote_two WHERE s && '5.8 .. 6.2'::seg AND n = 'baz'; + +CREATE FOREIGN TABLE seg_remote_one ( + id integer, + s seg, + n text +) SERVER loopback +OPTIONS (table_name 'seg_local'); + +SELECT id FROM seg_remote_one WHERE s && '5.8 .. 6.2'::seg AND n = 'baz'; +EXPLAIN VERBOSE SELECT id FROM seg_remote_one WHERE s && '5.8 .. 6.2'::seg AND n = 'baz'; +EXPLAIN VERBOSE SELECT id FROM seg_remote_two WHERE s && '5.8 .. 6.2'::seg AND n = 'baz'; + + +-- =================================================================== +-- clean up +-- =================================================================== +DROP FOREIGN TABLE seg_remote_one, seg_remote_two; +DROP USER MAPPING FOR CURRENT_USER SERVER loopback_two; +DROP SERVER loopback_two; +DROP TABLE seg_local; DROP FOREIGN TABLE shft1; DROP TABLE "SH 1"."TBL 1"; DROP SCHEMA "SH 1"; DROP EXTENSION cube; +DROP EXTENSION seg; ALTER SERVER loopback OPTIONS (DROP extensions); From 91b6622a24c32022b308a0f7729faf9bf3b94995 Mon Sep 17 00:00:00 2001 From: Paul Ramsey Date: Tue, 6 Oct 2015 10:50:20 -0700 Subject: [PATCH 7/7] Use pgsql MemSet? --- contrib/postgres_fdw/shippable.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/postgres_fdw/shippable.c b/contrib/postgres_fdw/shippable.c index 6c218b06639fe..960a7eccc51e0 100644 --- a/contrib/postgres_fdw/shippable.c +++ b/contrib/postgres_fdw/shippable.c @@ -167,7 +167,7 @@ is_shippable(Oid objnumber, Oid classnumber, ForeignServer *server, List *extens InitializeShippableCache(); /* Zero out the key */ - memset(&key, 0, sizeof(key)); + MemSet(&key, 0, sizeof(key)); key.objid = objnumber; key.classid = classnumber; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy