Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitd894941

Browse files
committed
Allow postgres_fdw to ship extension funcs/operators for remote execution.
The user can whitelist specified extension(s) in the foreign server'soptions, whereupon we will treat immutable functions and operators of thoseextensions as candidates to be sent for remote execution.Whitelisting an extension in this way basically promises that the extensionexists on the remote server and behaves compatibly with the local instance.We have no way to prove that formally, so we have to rely on the user toget it right. But this seems like something that people can usually getright in practice.We might in future allow functions and operators to be whitelistedindividually, but extension granularity is a very convenient special case,so it got done first.The patch as-committed lacks any regression tests, which is unfortunate,but introducing dependencies on other extensions for testing purposeswould break "make installcheck" scenarios, which is worse. I have someideas about klugy ways around that, but it seems like material for aseparate patch. For the moment, leave the problem open.Paul Ramsey, hacked up a bit more by me
1 parentee44cb7 commitd894941

File tree

9 files changed

+427
-91
lines changed

9 files changed

+427
-91
lines changed

‎contrib/postgres_fdw/Makefile

Lines changed: 1 addition & 1 deletion
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.oshippable.o$(WIN32RES)
55
PGFILEDESC = "postgres_fdw - foreign data wrapper for PostgreSQL"
66

77
PG_CPPFLAGS = -I$(libpq_srcdir)

‎contrib/postgres_fdw/deparse.c

Lines changed: 52 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
#include"access/heapam.h"
3939
#include"access/htup_details.h"
4040
#include"access/sysattr.h"
41-
#include"access/transam.h"
4241
#include"catalog/pg_collation.h"
4342
#include"catalog/pg_namespace.h"
4443
#include"catalog/pg_operator.h"
@@ -102,7 +101,7 @@ typedef struct deparse_expr_cxt
102101
staticboolforeign_expr_walker(Node*node,
103102
foreign_glob_cxt*glob_cxt,
104103
foreign_loc_cxt*outer_cxt);
105-
staticboolis_builtin(Oidprocid);
104+
staticchar*deparse_type_name(Oidtype_oid,int32typemod);
106105

107106
/*
108107
* Functions to construct string representation of a node tree.
@@ -220,18 +219,20 @@ is_foreign_expr(PlannerInfo *root,
220219
* In addition, *outer_cxt is updated with collation information.
221220
*
222221
* We must check that the expression contains only node types we can deparse,
223-
* that all types/functions/operators are safe to send (which we approximate
224-
* as being built-in), and that all collations used in the expression derive
225-
* from Vars of the foreign table. Because of the latter, the logic is
226-
* pretty close to assign_collations_walker() in parse_collate.c, though we
227-
* can assume here that the given expression is valid.
222+
* that all types/functions/operators are safe to send (they are "shippable"),
223+
* and that all collations used in the expression derive from Vars of the
224+
* foreign table. Because of the latter, the logic is pretty close to
225+
* assign_collations_walker() in parse_collate.c, though we can assume here
226+
* that the given expression is valid. Note function mutability is not
227+
* currently considered here.
228228
*/
229229
staticbool
230230
foreign_expr_walker(Node*node,
231231
foreign_glob_cxt*glob_cxt,
232232
foreign_loc_cxt*outer_cxt)
233233
{
234234
boolcheck_type= true;
235+
PgFdwRelationInfo*fpinfo;
235236
foreign_loc_cxtinner_cxt;
236237
Oidcollation;
237238
FDWCollateStatestate;
@@ -240,6 +241,9 @@ foreign_expr_walker(Node *node,
240241
if (node==NULL)
241242
return true;
242243

244+
/* May need server info from baserel's fdw_private struct */
245+
fpinfo= (PgFdwRelationInfo*) (glob_cxt->foreignrel->fdw_private);
246+
243247
/* Set up inner_cxt for possible recursion to child nodes */
244248
inner_cxt.collation=InvalidOid;
245249
inner_cxt.state=FDW_COLLATE_NONE;
@@ -377,11 +381,11 @@ foreign_expr_walker(Node *node,
377381
FuncExpr*fe= (FuncExpr*)node;
378382

379383
/*
380-
* If function used by the expression is notbuilt-in, it
384+
* If function used by the expression is notshippable, it
381385
* can't be sent to remote because it might have incompatible
382386
* semantics on remote side.
383387
*/
384-
if (!is_builtin(fe->funcid))
388+
if (!is_shippable(fe->funcid,ProcedureRelationId,fpinfo))
385389
return false;
386390

387391
/*
@@ -425,11 +429,11 @@ foreign_expr_walker(Node *node,
425429
OpExpr*oe= (OpExpr*)node;
426430

427431
/*
428-
* Similarly, onlybuilt-in operators can be sent to remote.
429-
* (If the operator is, surelyits underlying function is
430-
* too.)
432+
* Similarly, onlyshippable operators can be sent to remote.
433+
* (If the operator is shippable, we assumeits underlying
434+
*function istoo.)
431435
*/
432-
if (!is_builtin(oe->opno))
436+
if (!is_shippable(oe->opno,OperatorRelationId,fpinfo))
433437
return false;
434438

435439
/*
@@ -467,9 +471,9 @@ foreign_expr_walker(Node *node,
467471
ScalarArrayOpExpr*oe= (ScalarArrayOpExpr*)node;
468472

469473
/*
470-
* Again, onlybuilt-in operators can be sent to remote.
474+
* Again, onlyshippable operators can be sent to remote.
471475
*/
472-
if (!is_builtin(oe->opno))
476+
if (!is_shippable(oe->opno,OperatorRelationId,fpinfo))
473477
return false;
474478

475479
/*
@@ -616,10 +620,10 @@ foreign_expr_walker(Node *node,
616620
}
617621

618622
/*
619-
* If result type of given expression is notbuilt-in, it can't be sent to
620-
* remote because it might have incompatible semantics on remote side.
623+
* If result type of given expression is notshippable, it can't be sent
624+
*toremote because it might have incompatible semantics on remote side.
621625
*/
622-
if (check_type&& !is_builtin(exprType(node)))
626+
if (check_type&& !is_shippable(exprType(node),TypeRelationId,fpinfo))
623627
return false;
624628

625629
/*
@@ -672,27 +676,23 @@ foreign_expr_walker(Node *node,
672676
}
673677

674678
/*
675-
* Return true if given object is one of PostgreSQL's built-in objects.
676-
*
677-
* We use FirstBootstrapObjectId as the cutoff, so that we only consider
678-
* objects with hand-assigned OIDs to be "built in", not for instance any
679-
* function or type defined in the information_schema.
679+
* Convert type OID + typmod info into a type name we can ship to the remote
680+
* server. Someplace else had better have verified that this type name is
681+
* expected to be known on the remote end.
680682
*
681-
* Our constraints for dealing with types are tighter than they are for
682-
* functions or operators: we want to accept only types that are in pg_catalog,
683-
* else format_type might incorrectly fail to schema-qualify their names.
684-
* (This could be fixed with some changes to format_type, but for now there's
685-
* no need.) Thus we must exclude information_schema types.
686-
*
687-
* XXX there is a problem with this, which is that the set of built-in
688-
* objects expands over time. Something that is built-in to us might not
689-
* be known to the remote server, if it's of an older version. But keeping
690-
* track of that would be a huge exercise.
683+
* This is almost just format_type_with_typemod(), except that if left to its
684+
* own devices, that function will make schema-qualification decisions based
685+
* on the local search_path, which is wrong. We must schema-qualify all
686+
* type names that are not in pg_catalog. We assume here that built-in types
687+
* are all in pg_catalog and need not be qualified; otherwise, qualify.
691688
*/
692-
staticbool
693-
is_builtin(Oidoid)
689+
staticchar*
690+
deparse_type_name(Oidtype_oid,int32typemod)
694691
{
695-
return (oid<FirstBootstrapObjectId);
692+
if (is_builtin(type_oid))
693+
returnformat_type_with_typemod(type_oid,typemod);
694+
else
695+
returnformat_type_with_typemod_qualified(type_oid,typemod);
696696
}
697697

698698

@@ -1358,8 +1358,8 @@ deparseConst(Const *node, deparse_expr_cxt *context)
13581358
{
13591359
appendStringInfoString(buf,"NULL");
13601360
appendStringInfo(buf,"::%s",
1361-
format_type_with_typemod(node->consttype,
1362-
node->consttypmod));
1361+
deparse_type_name(node->consttype,
1362+
node->consttypmod));
13631363
return;
13641364
}
13651365

@@ -1432,8 +1432,8 @@ deparseConst(Const *node, deparse_expr_cxt *context)
14321432
}
14331433
if (needlabel)
14341434
appendStringInfo(buf,"::%s",
1435-
format_type_with_typemod(node->consttype,
1436-
node->consttypmod));
1435+
deparse_type_name(node->consttype,
1436+
node->consttypmod));
14371437
}
14381438

14391439
/*
@@ -1558,7 +1558,7 @@ deparseFuncExpr(FuncExpr *node, deparse_expr_cxt *context)
15581558

15591559
deparseExpr((Expr*)linitial(node->args),context);
15601560
appendStringInfo(buf,"::%s",
1561-
format_type_with_typemod(rettype,coercedTypmod));
1561+
deparse_type_name(rettype,coercedTypmod));
15621562
return;
15631563
}
15641564

@@ -1753,8 +1753,8 @@ deparseRelabelType(RelabelType *node, deparse_expr_cxt *context)
17531753
deparseExpr(node->arg,context);
17541754
if (node->relabelformat!=COERCE_IMPLICIT_CAST)
17551755
appendStringInfo(context->buf,"::%s",
1756-
format_type_with_typemod(node->resulttype,
1757-
node->resulttypmod));
1756+
deparse_type_name(node->resulttype,
1757+
node->resulttypmod));
17581758
}
17591759

17601760
/*
@@ -1834,7 +1834,7 @@ deparseArrayExpr(ArrayExpr *node, deparse_expr_cxt *context)
18341834
/* If the array is empty, we need an explicit cast to the array type. */
18351835
if (node->elements==NIL)
18361836
appendStringInfo(buf,"::%s",
1837-
format_type_with_typemod(node->array_typeid,-1));
1837+
deparse_type_name(node->array_typeid,-1));
18381838
}
18391839

18401840
/*
@@ -1850,7 +1850,7 @@ printRemoteParam(int paramindex, Oid paramtype, int32 paramtypmod,
18501850
deparse_expr_cxt*context)
18511851
{
18521852
StringInfobuf=context->buf;
1853-
char*ptypename=format_type_with_typemod(paramtype,paramtypmod);
1853+
char*ptypename=deparse_type_name(paramtype,paramtypmod);
18541854

18551855
appendStringInfo(buf,"$%d::%s",paramindex,ptypename);
18561856
}
@@ -1876,7 +1876,7 @@ printRemotePlaceholder(Oid paramtype, int32 paramtypmod,
18761876
deparse_expr_cxt*context)
18771877
{
18781878
StringInfobuf=context->buf;
1879-
char*ptypename=format_type_with_typemod(paramtype,paramtypmod);
1879+
char*ptypename=deparse_type_name(paramtype,paramtypmod);
18801880

18811881
appendStringInfo(buf,"((SELECT null::%s)::%s)",ptypename,ptypename);
18821882
}
@@ -1890,10 +1890,10 @@ void
18901890
appendOrderByClause(StringInfobuf,PlannerInfo*root,RelOptInfo*baserel,
18911891
List*pathkeys)
18921892
{
1893-
ListCell*lcell;
1894-
deparse_expr_cxtcontext;
1895-
intnestlevel;
1896-
char*delim=" ";
1893+
ListCell*lcell;
1894+
deparse_expr_cxtcontext;
1895+
intnestlevel;
1896+
char*delim=" ";
18971897

18981898
/* Set up context struct for recursion */
18991899
context.root=root;
@@ -1907,8 +1907,8 @@ appendOrderByClause(StringInfo buf, PlannerInfo *root, RelOptInfo *baserel,
19071907
appendStringInfo(buf," ORDER BY");
19081908
foreach(lcell,pathkeys)
19091909
{
1910-
PathKey*pathkey=lfirst(lcell);
1911-
Expr*em_expr;
1910+
PathKey*pathkey=lfirst(lcell);
1911+
Expr*em_expr;
19121912

19131913
em_expr=find_em_expr_for_rel(pathkey->pk_eclass,baserel);
19141914
Assert(em_expr!=NULL);

‎contrib/postgres_fdw/option.c

Lines changed: 54 additions & 0 deletions
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+
elseif (strcmp(def->defname,"extensions")==0)
130+
{
131+
/* check list syntax, warn about uninstalled extensions */
132+
(void)ExtractExtensionList(defGetString(def), true);
133+
}
127134
}
128135

129136
PG_RETURN_VOID();
@@ -150,6 +157,8 @@ InitPgFdwOptions(void)
150157
/* cost factors */
151158
{"fdw_startup_cost",ForeignServerRelationId, false},
152159
{"fdw_tuple_cost",ForeignServerRelationId, false},
160+
/* shippable extensions */
161+
{"extensions",ForeignServerRelationId, false},
153162
/* updatable is available on both server and table */
154163
{"updatable",ForeignServerRelationId, false},
155164
{"updatable",ForeignTableRelationId, false},
@@ -293,3 +302,48 @@ ExtractConnectionOptions(List *defelems, const char **keywords,
293302
}
294303
returni;
295304
}
305+
306+
/*
307+
* Parse a comma-separated string and return a List of the OIDs of the
308+
* extensions named in the string. If any names in the list cannot be
309+
* found, report a warning if warnOnMissing is true, else just silently
310+
* ignore them.
311+
*/
312+
List*
313+
ExtractExtensionList(constchar*extensionsString,boolwarnOnMissing)
314+
{
315+
List*extensionOids=NIL;
316+
List*extlist;
317+
ListCell*lc;
318+
319+
/* SplitIdentifierString scribbles on its input, so pstrdup first */
320+
if (!SplitIdentifierString(pstrdup(extensionsString),',',&extlist))
321+
{
322+
/* syntax error in name list */
323+
ereport(ERROR,
324+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
325+
errmsg("parameter \"%s\" must be a list of extension names",
326+
"extensions")));
327+
}
328+
329+
foreach(lc,extlist)
330+
{
331+
constchar*extension_name= (constchar*)lfirst(lc);
332+
Oidextension_oid=get_extension_oid(extension_name, true);
333+
334+
if (OidIsValid(extension_oid))
335+
{
336+
extensionOids=lappend_oid(extensionOids,extension_oid);
337+
}
338+
elseif (warnOnMissing)
339+
{
340+
ereport(WARNING,
341+
(errcode(ERRCODE_UNDEFINED_OBJECT),
342+
errmsg("extension \"%s\" is not installed",
343+
extension_name)));
344+
}
345+
}
346+
347+
list_free(extlist);
348+
returnextensionOids;
349+
}

‎contrib/postgres_fdw/postgres_fdw.c

Lines changed: 4 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -50,40 +50,6 @@ PG_MODULE_MAGIC;
5050
/* If no remote estimates, assume a sort costs 20% extra */
5151
#defineDEFAULT_FDW_SORT_MULTIPLIER 1.2
5252

53-
/*
54-
* FDW-specific planner information kept in RelOptInfo.fdw_private for a
55-
* foreign table. This information is collected by postgresGetForeignRelSize.
56-
*/
57-
typedefstructPgFdwRelationInfo
58-
{
59-
/* baserestrictinfo clauses, broken down into safe and unsafe subsets. */
60-
List*remote_conds;
61-
List*local_conds;
62-
63-
/* Bitmap of attr numbers we need to fetch from the remote server. */
64-
Bitmapset*attrs_used;
65-
66-
/* Cost and selectivity of local_conds. */
67-
QualCostlocal_conds_cost;
68-
Selectivitylocal_conds_sel;
69-
70-
/* Estimated size and cost for a scan with baserestrictinfo quals. */
71-
doublerows;
72-
intwidth;
73-
Coststartup_cost;
74-
Costtotal_cost;
75-
76-
/* Options extracted from catalogs. */
77-
booluse_remote_estimate;
78-
Costfdw_startup_cost;
79-
Costfdw_tuple_cost;
80-
81-
/* Cached catalog information. */
82-
ForeignTable*table;
83-
ForeignServer*server;
84-
UserMapping*user;/* only set in use_remote_estimate mode */
85-
}PgFdwRelationInfo;
86-
8753
/*
8854
* Indexes of FDW-private information stored in fdw_private lists.
8955
*
@@ -409,6 +375,7 @@ postgresGetForeignRelSize(PlannerInfo *root,
409375
fpinfo->use_remote_estimate= false;
410376
fpinfo->fdw_startup_cost=DEFAULT_FDW_STARTUP_COST;
411377
fpinfo->fdw_tuple_cost=DEFAULT_FDW_TUPLE_COST;
378+
fpinfo->shippable_extensions=NIL;
412379

413380
foreach(lc,fpinfo->server->options)
414381
{
@@ -420,6 +387,9 @@ postgresGetForeignRelSize(PlannerInfo *root,
420387
fpinfo->fdw_startup_cost=strtod(defGetString(def),NULL);
421388
elseif (strcmp(def->defname,"fdw_tuple_cost")==0)
422389
fpinfo->fdw_tuple_cost=strtod(defGetString(def),NULL);
390+
elseif (strcmp(def->defname,"extensions")==0)
391+
fpinfo->shippable_extensions=
392+
ExtractExtensionList(defGetString(def), false);
423393
}
424394
foreach(lc,fpinfo->table->options)
425395
{

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp