Content-Length: 49177 | pFad | http://github.com/postgres/postgres/pull/7.patch
thub.com
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;
--- a PPN by Garber Painting Akron. With Image Size Reduction included!Fetched URL: http://github.com/postgres/postgres/pull/7.patch
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy