Skip to content

Allow postgres_fdw to ship extension funcs/operators #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion 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 Down
92 changes: 46 additions & 46 deletions contrib/postgres_fdw/deparse.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/sysattr.h"
#include "access/transam.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_operator.h"
Expand Down Expand Up @@ -102,7 +101,7 @@ typedef struct deparse_expr_cxt
static bool foreign_expr_walker(Node *node,
foreign_glob_cxt *glob_cxt,
foreign_loc_cxt *outer_cxt);
static bool is_builtin(Oid procid);
static char *deparse_type_name(Oid type_oid, int32 typemod);

/*
* Functions to construct string representation of a node tree.
Expand Down Expand Up @@ -217,18 +216,20 @@ is_foreign_expr(PlannerInfo *root,
* In addition, *outer_cxt is updated with collation information.
*
* We must check that the expression contains only node types we can deparse,
* that all types/functions/operators are safe to send (which we approximate
* as being built-in), and that all collations used in the expression derive
* from Vars of the foreign table. Because of the latter, the logic is
* pretty close to assign_collations_walker() in parse_collate.c, though we
* can assume here that the given expression is valid.
* that all types/functions/operators are safe to send (they are "shippable"),
* and that all collations used in the expression derive from Vars of the
* foreign table. Because of the latter, the logic is pretty close to
* assign_collations_walker() in parse_collate.c, though we can assume here
* that the given expression is valid. Note function mutability is not
* currently considered here.
*/
static bool
foreign_expr_walker(Node *node,
foreign_glob_cxt *glob_cxt,
foreign_loc_cxt *outer_cxt)
{
bool check_type = true;
PgFdwRelationInfo *fpinfo;
foreign_loc_cxt inner_cxt;
Oid collation;
FDWCollateState state;
Expand All @@ -237,6 +238,9 @@ foreign_expr_walker(Node *node,
if (node == NULL)
return true;

/* May need server info from baserel's fdw_private struct */
fpinfo = (PgFdwRelationInfo *) (glob_cxt->foreignrel->fdw_private);

/* Set up inner_cxt for possible recursion to child nodes */
inner_cxt.collation = InvalidOid;
inner_cxt.state = FDW_COLLATE_NONE;
Expand Down Expand Up @@ -374,11 +378,11 @@ foreign_expr_walker(Node *node,
FuncExpr *fe = (FuncExpr *) node;

/*
* If function used by the expression is not built-in, it
* If function used by the expression is not shippable, it
* can't be sent to remote because it might have incompatible
* semantics on remote side.
*/
if (!is_builtin(fe->funcid))
if (!is_shippable(fe->funcid, ProcedureRelationId, fpinfo))
return false;

/*
Expand Down Expand Up @@ -422,11 +426,11 @@ foreign_expr_walker(Node *node,
OpExpr *oe = (OpExpr *) node;

/*
* Similarly, only built-in operators can be sent to remote.
* (If the operator is, surely its underlying function is
* too.)
* Similarly, only shippable operators can be sent to remote.
* (If the operator is shippable, we assume its underlying
* function is too.)
*/
if (!is_builtin(oe->opno))
if (!is_shippable(oe->opno, OperatorRelationId, fpinfo))
return false;

/*
Expand Down Expand Up @@ -464,9 +468,9 @@ foreign_expr_walker(Node *node,
ScalarArrayOpExpr *oe = (ScalarArrayOpExpr *) node;

/*
* Again, only built-in operators can be sent to remote.
* Again, only shippable operators can be sent to remote.
*/
if (!is_builtin(oe->opno))
if (!is_shippable(oe->opno, OperatorRelationId, fpinfo))
return false;

/*
Expand Down Expand Up @@ -613,10 +617,10 @@ 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 result type of given expression is not shippable, 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_shippable(exprType(node), TypeRelationId, fpinfo))
return false;

/*
Expand Down Expand Up @@ -669,27 +673,23 @@ foreign_expr_walker(Node *node,
}

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


Expand Down Expand Up @@ -1355,8 +1355,8 @@ deparseConst(Const *node, deparse_expr_cxt *context)
{
appendStringInfoString(buf, "NULL");
appendStringInfo(buf, "::%s",
format_type_with_typemod(node->consttype,
node->consttypmod));
deparse_type_name(node->consttype,
node->consttypmod));
return;
}

Expand Down Expand Up @@ -1429,8 +1429,8 @@ deparseConst(Const *node, deparse_expr_cxt *context)
}
if (needlabel)
appendStringInfo(buf, "::%s",
format_type_with_typemod(node->consttype,
node->consttypmod));
deparse_type_name(node->consttype,
node->consttypmod));
}

/*
Expand Down Expand Up @@ -1555,7 +1555,7 @@ deparseFuncExpr(FuncExpr *node, deparse_expr_cxt *context)

deparseExpr((Expr *) linitial(node->args), context);
appendStringInfo(buf, "::%s",
format_type_with_typemod(rettype, coercedTypmod));
deparse_type_name(rettype, coercedTypmod));
return;
}

Expand Down Expand Up @@ -1750,8 +1750,8 @@ deparseRelabelType(RelabelType *node, deparse_expr_cxt *context)
deparseExpr(node->arg, context);
if (node->relabelformat != COERCE_IMPLICIT_CAST)
appendStringInfo(context->buf, "::%s",
format_type_with_typemod(node->resulttype,
node->resulttypmod));
deparse_type_name(node->resulttype,
node->resulttypmod));
}

/*
Expand Down Expand Up @@ -1831,7 +1831,7 @@ deparseArrayExpr(ArrayExpr *node, deparse_expr_cxt *context)
/* If the array is empty, we need an explicit cast to the array type. */
if (node->elements == NIL)
appendStringInfo(buf, "::%s",
format_type_with_typemod(node->array_typeid, -1));
deparse_type_name(node->array_typeid, -1));
}

/*
Expand All @@ -1847,7 +1847,7 @@ printRemoteParam(int paramindex, Oid paramtype, int32 paramtypmod,
deparse_expr_cxt *context)
{
StringInfo buf = context->buf;
char *ptypename = format_type_with_typemod(paramtype, paramtypmod);
char *ptypename = deparse_type_name(paramtype, paramtypmod);

appendStringInfo(buf, "$%d::%s", paramindex, ptypename);
}
Expand All @@ -1873,7 +1873,7 @@ printRemotePlaceholder(Oid paramtype, int32 paramtypmod,
deparse_expr_cxt *context)
{
StringInfo buf = context->buf;
char *ptypename = format_type_with_typemod(paramtype, paramtypmod);
char *ptypename = deparse_type_name(paramtype, paramtypmod);

appendStringInfo(buf, "((SELECT null::%s)::%s)", ptypename, ptypename);
}
54 changes: 54 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)
{
/* check list syntax, warn about uninstalled extensions */
(void) ExtractExtensionList(defGetString(def), true);
}
}

PG_RETURN_VOID();
Expand All @@ -150,6 +157,8 @@ InitPgFdwOptions(void)
/* cost factors */
{"fdw_startup_cost", ForeignServerRelationId, false},
{"fdw_tuple_cost", ForeignServerRelationId, false},
/* shippable extensions */
{"extensions", ForeignServerRelationId, false},
/* updatable is available on both server and table */
{"updatable", ForeignServerRelationId, false},
{"updatable", ForeignTableRelationId, false},
Expand Down Expand Up @@ -293,3 +302,48 @@ ExtractConnectionOptions(List *defelems, const char **keywords,
}
return i;
}

/*
* Parse a comma-separated string and return a List of the OIDs of the
* extensions named in the string. If any names in the list cannot be
* found, report a warning if warnOnMissing is true, else just silently
* ignore them.
*/
List *
ExtractExtensionList(const char *extensionsString, bool warnOnMissing)
{
List *extensionOids = NIL;
List *extlist;
ListCell *lc;

/* SplitIdentifierString scribbles on its input, so pstrdup first */
if (!SplitIdentifierString(pstrdup(extensionsString), ',', &extlist))
{
/* syntax error in name list */
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("parameter \"%s\" must be a list of extension names",
"extensions")));
}

foreach(lc, extlist)
{
const char *extension_name = (const char *) lfirst(lc);
Oid extension_oid = get_extension_oid(extension_name, true);

if (OidIsValid(extension_oid))
{
extensionOids = lappend_oid(extensionOids, extension_oid);
}
else if (warnOnMissing)
{
ereport(WARNING,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("extension \"%s\" is not installed",
extension_name)));
}
}

list_free(extlist);
return extensionOids;
}
38 changes: 4 additions & 34 deletions contrib/postgres_fdw/postgres_fdw.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,40 +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 @@ -406,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->shippable_extensions = NIL;

foreach(lc, fpinfo->server->options)
{
Expand All @@ -417,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->shippable_extensions =
ExtractExtensionList(defGetString(def), false);
}
foreach(lc, fpinfo->table->options)
{
Expand Down
Loading