Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Patch status at 20151001
  • Loading branch information
pramsey committed Oct 3, 2015
commit 3f5b45b606a781a603578ac73dc79e67da38b275
6 changes: 4 additions & 2 deletions contrib/postgres_fdw/Makefile
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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
Expand Down
30 changes: 24 additions & 6 deletions contrib/postgres_fdw/deparse.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;

/*
Expand Down Expand Up @@ -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;

/*
Expand Down Expand Up @@ -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;

/*
Expand Down Expand Up @@ -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;

/*
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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));
}
}

/*
Expand Down
139 changes: 139 additions & 0 deletions contrib/postgres_fdw/expected/shippable.out
Original file line number Diff line number Diff line change
@@ -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);
55 changes: 55 additions & 0 deletions contrib/postgres_fdw/option.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"


/*
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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}
};

Expand Down Expand Up @@ -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;
}
37 changes: 4 additions & 33 deletions contrib/postgres_fdw/postgres_fdw.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)
{
Expand All @@ -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)
{
Expand Down
Loading