Skip to content

Commit 3f5b45b

Browse files
committed
Patch status at 20151001
1 parent bf68679 commit 3f5b45b

File tree

9 files changed

+574
-41
lines changed

9 files changed

+574
-41
lines changed

contrib/postgres_fdw/Makefile

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# contrib/postgres_fdw/Makefile
22

33
MODULE_big = postgres_fdw
4-
OBJS = postgres_fdw.o option.o deparse.o connection.o $(WIN32RES)
4+
OBJS = postgres_fdw.o option.o deparse.o connection.o shippable.o $(WIN32RES)
55
PGFILEDESC = "postgres_fdw - foreign data wrapper for PostgreSQL"
66

77
PG_CPPFLAGS = -I$(libpq_srcdir)
@@ -10,7 +10,9 @@ SHLIB_LINK = $(libpq)
1010
EXTENSION = postgres_fdw
1111
DATA = postgres_fdw--1.0.sql
1212

13-
REGRESS = postgres_fdw
13+
# Note: shippable tests depend on postgres_fdw tests setup
14+
REGRESS = postgres_fdw shippable
15+
EXTRA_INSTALL = contrib/cube
1416

1517
ifdef USE_PGXS
1618
PG_CONFIG = pg_config

contrib/postgres_fdw/deparse.c

+24-6
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,9 @@ foreign_expr_walker(Node *node,
233233
Oid collation;
234234
FDWCollateState state;
235235

236+
/* Access extension metadata from fpinfo on baserel */
237+
PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *)(glob_cxt->foreignrel->fdw_private);
238+
236239
/* Need do nothing for empty subexpressions */
237240
if (node == NULL)
238241
return true;
@@ -378,7 +381,8 @@ foreign_expr_walker(Node *node,
378381
* can't be sent to remote because it might have incompatible
379382
* semantics on remote side.
380383
*/
381-
if (!is_builtin(fe->funcid))
384+
if (!is_builtin(fe->funcid) &&
385+
!is_shippable(fe->funcid, fpinfo->extensions))
382386
return false;
383387

384388
/*
@@ -426,7 +430,8 @@ foreign_expr_walker(Node *node,
426430
* (If the operator is, surely its underlying function is
427431
* too.)
428432
*/
429-
if (!is_builtin(oe->opno))
433+
if (!is_builtin(oe->opno) &&
434+
!is_shippable(oe->opno, fpinfo->extensions))
430435
return false;
431436

432437
/*
@@ -466,7 +471,8 @@ foreign_expr_walker(Node *node,
466471
/*
467472
* Again, only built-in operators can be sent to remote.
468473
*/
469-
if (!is_builtin(oe->opno))
474+
if (!is_builtin(oe->opno) &&
475+
!is_shippable(oe->opno, fpinfo->extensions))
470476
return false;
471477

472478
/*
@@ -616,7 +622,9 @@ foreign_expr_walker(Node *node,
616622
* If result type of given expression is not built-in, it can't be sent to
617623
* remote because it might have incompatible semantics on remote side.
618624
*/
619-
if (check_type && !is_builtin(exprType(node)))
625+
if (check_type &&
626+
!is_builtin(exprType(node)) &&
627+
!is_shippable(exprType(node), fpinfo->extensions))
620628
return false;
621629

622630
/*
@@ -1351,6 +1359,9 @@ deparseConst(Const *node, deparse_expr_cxt *context)
13511359
bool isfloat = false;
13521360
bool needlabel;
13531361

1362+
/* Access extension metadata from fpinfo on baserel */
1363+
PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *)(context->foreignrel->fdw_private);
1364+
13541365
if (node->constisnull)
13551366
{
13561367
appendStringInfoString(buf, "NULL");
@@ -1428,9 +1439,16 @@ deparseConst(Const *node, deparse_expr_cxt *context)
14281439
break;
14291440
}
14301441
if (needlabel)
1442+
{
1443+
/*
1444+
* References to extension types need to be fully qualified,
1445+
* but references to built-in types shouldn't be.
1446+
*/
14311447
appendStringInfo(buf, "::%s",
1432-
format_type_with_typemod(node->consttype,
1433-
node->consttypmod));
1448+
is_shippable(node->consttype, fpinfo->extensions) ?
1449+
format_type_be_qualified(node->consttype) :
1450+
format_type_with_typemod(node->consttype, node->consttypmod));
1451+
}
14341452
}
14351453

14361454
/*
+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
-- ===================================================================
2+
-- create FDW objects
3+
-- ===================================================================
4+
-- Error, extension isn't installed yet
5+
ALTER SERVER loopback OPTIONS (ADD extensions 'cube');
6+
ERROR: required extension "cube" is not installed
7+
HINT: Extension must be installed locally before it can be used on a remote server.
8+
-- Try again
9+
CREATE EXTENSION cube;
10+
ALTER SERVER loopback OPTIONS (ADD extensions 'cube');
11+
ALTER SERVER loopback OPTIONS (DROP extensions);
12+
-- ===================================================================
13+
-- create objects used through FDW loopback server
14+
-- ===================================================================
15+
CREATE SCHEMA "SH 1";
16+
CREATE TABLE "SH 1"."TBL 1" (
17+
"C 1" int NOT NULL,
18+
c2 int NOT NULL,
19+
c3 cube,
20+
c4 timestamptz
21+
);
22+
INSERT INTO "SH 1"."TBL 1"
23+
SELECT id,
24+
2 * id,
25+
cube(id,2*id),
26+
'1970-01-01'::timestamptz + ((id % 100) || ' days')::interval
27+
FROM generate_series(1, 1000) id;
28+
ANALYZE "SH 1"."TBL 1";
29+
-- ===================================================================
30+
-- create foreign table
31+
-- ===================================================================
32+
CREATE FOREIGN TABLE shft1 (
33+
"C 1" int NOT NULL,
34+
c2 int NOT NULL,
35+
c3 cube,
36+
c4 timestamptz
37+
) SERVER loopback
38+
OPTIONS (schema_name 'SH 1', table_name 'TBL 1');
39+
-- ===================================================================
40+
-- simple queries
41+
-- ===================================================================
42+
-- without operator shipping
43+
EXPLAIN (COSTS false) SELECT * FROM shft1 LIMIT 1;
44+
QUERY PLAN
45+
-----------------------------
46+
Limit
47+
-> Foreign Scan on shft1
48+
(2 rows)
49+
50+
EXPLAIN VERBOSE SELECT c2 FROM shft1 WHERE c3 && cube(1.5,2.5);
51+
QUERY PLAN
52+
---------------------------------------------------------------------
53+
Foreign Scan on public.shft1 (cost=100.00..205.06 rows=15 width=4)
54+
Output: c2
55+
Filter: (shft1.c3 && '(1.5),(2.5)'::cube)
56+
Remote SQL: SELECT c2, c3 FROM "SH 1"."TBL 1"
57+
(4 rows)
58+
59+
SELECT c2 FROM shft1 WHERE c3 && cube(1.5,2.5);
60+
c2
61+
----
62+
2
63+
4
64+
(2 rows)
65+
66+
EXPLAIN VERBOSE SELECT c2 FROM shft1 WHERE c3 && '(1.5),(2.5)'::cube;
67+
QUERY PLAN
68+
---------------------------------------------------------------------
69+
Foreign Scan on public.shft1 (cost=100.00..205.06 rows=15 width=4)
70+
Output: c2
71+
Filter: (shft1.c3 && '(1.5),(2.5)'::cube)
72+
Remote SQL: SELECT c2, c3 FROM "SH 1"."TBL 1"
73+
(4 rows)
74+
75+
-- with operator shipping
76+
ALTER SERVER loopback OPTIONS (ADD extensions 'cube');
77+
EXPLAIN VERBOSE SELECT c2 FROM shft1 WHERE c3 && cube(1.5,2.5);
78+
QUERY PLAN
79+
---------------------------------------------------------------------------------------------------------
80+
Foreign Scan on public.shft1 (cost=100.00..146.86 rows=15 width=4)
81+
Output: c2
82+
Remote SQL: SELECT c2 FROM "SH 1"."TBL 1" WHERE ((c3 OPERATOR(public.&&) '(1.5),(2.5)'::public.cube))
83+
(3 rows)
84+
85+
SELECT c2 FROM shft1 WHERE c3 && cube(1.5,2.5);
86+
c2
87+
----
88+
2
89+
4
90+
(2 rows)
91+
92+
EXPLAIN VERBOSE SELECT c2 FROM shft1 WHERE c3 && '(1.5),(2.5)'::cube;
93+
QUERY PLAN
94+
---------------------------------------------------------------------------------------------------------
95+
Foreign Scan on public.shft1 (cost=100.00..146.86 rows=15 width=4)
96+
Output: c2
97+
Remote SQL: SELECT c2 FROM "SH 1"."TBL 1" WHERE ((c3 OPERATOR(public.&&) '(1.5),(2.5)'::public.cube))
98+
(3 rows)
99+
100+
EXPLAIN VERBOSE SELECT cube_dim(c3) FROM shft1 WHERE c3 && '(1.5),(2.5)'::cube;
101+
QUERY PLAN
102+
---------------------------------------------------------------------------------------------------------
103+
Foreign Scan on public.shft1 (cost=100.00..128.43 rows=7 width=32)
104+
Output: cube_dim(c3)
105+
Remote SQL: SELECT c3 FROM "SH 1"."TBL 1" WHERE ((c3 OPERATOR(public.&&) '(1.5),(2.5)'::public.cube))
106+
(3 rows)
107+
108+
SELECT cube_dim(c3) FROM shft1 WHERE c3 && '(1.5),(2.5)'::cube;
109+
cube_dim
110+
----------
111+
1
112+
1
113+
(2 rows)
114+
115+
EXPLAIN VERBOSE SELECT c2 FROM shft1 WHERE cube_dim(c3) = 1 LIMIT 2;
116+
QUERY PLAN
117+
-------------------------------------------------------------------------------------
118+
Limit (cost=100.00..107.22 rows=2 width=4)
119+
Output: c2
120+
-> Foreign Scan on public.shft1 (cost=100.00..154.18 rows=15 width=4)
121+
Output: c2
122+
Remote SQL: SELECT c2 FROM "SH 1"."TBL 1" WHERE ((public.cube_dim(c3) = 1))
123+
(5 rows)
124+
125+
SELECT c2 FROM shft1 WHERE cube_dim(c3) = 1 LIMIT 2;
126+
c2
127+
----
128+
2
129+
4
130+
(2 rows)
131+
132+
-- ===================================================================
133+
-- clean up
134+
-- ===================================================================
135+
DROP FOREIGN TABLE shft1;
136+
DROP TABLE "SH 1"."TBL 1";
137+
DROP SCHEMA "SH 1";
138+
DROP EXTENSION cube;
139+
ALTER SERVER loopback OPTIONS (DROP extensions);

contrib/postgres_fdw/option.c

+55
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
#include "catalog/pg_foreign_table.h"
2020
#include "catalog/pg_user_mapping.h"
2121
#include "commands/defrem.h"
22+
#include "commands/extension.h"
23+
#include "utils/builtins.h"
2224

2325

2426
/*
@@ -124,6 +126,11 @@ postgres_fdw_validator(PG_FUNCTION_ARGS)
124126
errmsg("%s requires a non-negative numeric value",
125127
def->defname)));
126128
}
129+
else if (strcmp(def->defname, "extensions") == 0)
130+
{
131+
/* this must have already-installed extensions */
132+
(void) ExtractExtensionList(defGetString(def), false);
133+
}
127134
}
128135

129136
PG_RETURN_VOID();
@@ -153,6 +160,8 @@ InitPgFdwOptions(void)
153160
/* updatable is available on both server and table */
154161
{"updatable", ForeignServerRelationId, false},
155162
{"updatable", ForeignTableRelationId, false},
163+
/* extensions is available on server */
164+
{"extensions", ForeignServerRelationId, false},
156165
{NULL, InvalidOid, false}
157166
};
158167

@@ -293,3 +302,49 @@ ExtractConnectionOptions(List *defelems, const char **keywords,
293302
}
294303
return i;
295304
}
305+
306+
/*
307+
* Parse a comma-separated string and return a List of the Oids of the
308+
* extensions in the string. If an extension provided cannot be looked
309+
* up in the catalog (it hasn't been installed or doesn't exist) then
310+
* throw up an error.
311+
*/
312+
List *
313+
ExtractExtensionList(char *extensionString, bool populateList)
314+
{
315+
List *extlist;
316+
List *extensionOids = NIL;
317+
ListCell *l;
318+
319+
if (!SplitIdentifierString(extensionString, ',', &extlist))
320+
{
321+
ereport(ERROR,
322+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
323+
errmsg("invalid extension list syntax")));
324+
}
325+
326+
foreach(l, extlist)
327+
{
328+
const char *extension_name = (const char *) lfirst(l);
329+
Oid extension_oid = get_extension_oid(extension_name, true);
330+
331+
if (!OidIsValid(extension_oid))
332+
ereport(ERROR,
333+
(errcode(ERRCODE_UNDEFINED_OBJECT),
334+
errmsg("required extension \"%s\" is not installed",
335+
extension_name),
336+
errhint("Extension must be installed locally before it can be used on a remote server.")));
337+
else if (populateList)
338+
{
339+
/*
340+
* Only add this extension OID to the list if it is not already
341+
* in included.
342+
*/
343+
if (!list_member_oid(extensionOids, extension_oid))
344+
extensionOids = lappend_oid(extensionOids, extension_oid);
345+
}
346+
}
347+
348+
list_free(extlist);
349+
return extensionOids;
350+
}

contrib/postgres_fdw/postgres_fdw.c

+4-33
Original file line numberDiff line numberDiff line change
@@ -47,39 +47,6 @@ PG_MODULE_MAGIC;
4747
/* Default CPU cost to process 1 row (above and beyond cpu_tuple_cost). */
4848
#define DEFAULT_FDW_TUPLE_COST 0.01
4949

50-
/*
51-
* FDW-specific planner information kept in RelOptInfo.fdw_private for a
52-
* foreign table. This information is collected by postgresGetForeignRelSize.
53-
*/
54-
typedef struct PgFdwRelationInfo
55-
{
56-
/* baserestrictinfo clauses, broken down into safe and unsafe subsets. */
57-
List *remote_conds;
58-
List *local_conds;
59-
60-
/* Bitmap of attr numbers we need to fetch from the remote server. */
61-
Bitmapset *attrs_used;
62-
63-
/* Cost and selectivity of local_conds. */
64-
QualCost local_conds_cost;
65-
Selectivity local_conds_sel;
66-
67-
/* Estimated size and cost for a scan with baserestrictinfo quals. */
68-
double rows;
69-
int width;
70-
Cost startup_cost;
71-
Cost total_cost;
72-
73-
/* Options extracted from catalogs. */
74-
bool use_remote_estimate;
75-
Cost fdw_startup_cost;
76-
Cost fdw_tuple_cost;
77-
78-
/* Cached catalog information. */
79-
ForeignTable *table;
80-
ForeignServer *server;
81-
UserMapping *user; /* only set in use_remote_estimate mode */
82-
} PgFdwRelationInfo;
8350

8451
/*
8552
* Indexes of FDW-private information stored in fdw_private lists.
@@ -405,6 +372,7 @@ postgresGetForeignRelSize(PlannerInfo *root,
405372
fpinfo->use_remote_estimate = false;
406373
fpinfo->fdw_startup_cost = DEFAULT_FDW_STARTUP_COST;
407374
fpinfo->fdw_tuple_cost = DEFAULT_FDW_TUPLE_COST;
375+
fpinfo->extensions = NIL;
408376

409377
foreach(lc, fpinfo->server->options)
410378
{
@@ -416,6 +384,9 @@ postgresGetForeignRelSize(PlannerInfo *root,
416384
fpinfo->fdw_startup_cost = strtod(defGetString(def), NULL);
417385
else if (strcmp(def->defname, "fdw_tuple_cost") == 0)
418386
fpinfo->fdw_tuple_cost = strtod(defGetString(def), NULL);
387+
else if (strcmp(def->defname, "extensions") == 0)
388+
fpinfo->extensions =
389+
ExtractExtensionList(defGetString(def), true);
419390
}
420391
foreach(lc, fpinfo->table->options)
421392
{

0 commit comments

Comments
 (0)