diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c index 81cb2b4..bbe3c9d 100644 --- a/contrib/postgres_fdw/deparse.c +++ b/contrib/postgres_fdw/deparse.c @@ -34,11 +34,15 @@ #include "postgres_fdw.h" +#include "access/genam.h" #include "access/heapam.h" #include "access/htup_details.h" #include "access/sysattr.h" #include "access/transam.h" +#include "catalog/dependency.h" +#include "catalog/indexing.h" #include "catalog/pg_collation.h" +#include "catalog/pg_depend.h" #include "catalog/pg_namespace.h" #include "catalog/pg_operator.h" #include "catalog/pg_proc.h" @@ -49,8 +53,10 @@ #include "optimizer/var.h" #include "parser/parsetree.h" #include "utils/builtins.h" +#include "utils/fmgroids.h" #include "utils/lsyscache.h" #include "utils/rel.h" +#include "utils/snapmgr.h" #include "utils/syscache.h" @@ -136,6 +142,7 @@ static void printRemoteParam(int paramindex, Oid paramtype, int32 paramtypmod, deparse_expr_cxt *context); static void printRemotePlaceholder(Oid paramtype, int32 paramtypmod, deparse_expr_cxt *context); +static bool is_in_extension(Oid procid, PgFdwRelationInfo *fpinfo); /* @@ -167,6 +174,7 @@ classifyConditions(PlannerInfo *root, } } + /* * Returns true if given expr is safe to evaluate on the foreign server. */ @@ -177,7 +185,7 @@ is_foreign_expr(PlannerInfo *root, { foreign_glob_cxt glob_cxt; foreign_loc_cxt loc_cxt; - + /* * Check that the expression consists of nodes that are safe to execute * remotely. @@ -207,6 +215,8 @@ is_foreign_expr(PlannerInfo *root, return true; } + + /* * Check if expression is safe to execute remotely, and return true if so. * @@ -229,6 +239,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; @@ -361,7 +374,7 @@ 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_in_extension(fe->funcid, fpinfo))) return false; /* @@ -407,7 +420,7 @@ 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_in_extension(oe->opno, fpinfo)) ) return false; /* @@ -445,7 +458,7 @@ 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_in_extension(oe->opno, fpinfo))) return false; /* @@ -591,7 +604,7 @@ 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_in_extension(exprType(node), fpinfo)) ) return false; /* @@ -643,6 +656,8 @@ foreign_expr_walker(Node *node, return true; } + + /* * Return true if given object is one of PostgreSQL's built-in objects. * @@ -669,6 +684,67 @@ is_builtin(Oid oid) /* + * Returns true if given operator/function is part of an extension declared in the + * server options. + */ +static bool +is_in_extension(Oid procnumber, PgFdwRelationInfo *fpinfo) +{ + static int nkeys = 1; + ScanKeyData key[nkeys]; + HeapTuple tup; + Relation depRel; + SysScanDesc scan; + int nresults = 0; + + /* Always return false if we don't have any declared extensions */ + if ( ! fpinfo->extensions ) + return false; + + /* We need this relation to scan */ + depRel = heap_open(DependRelationId, RowExclusiveLock); + + /* Scan the system dependency table for a all entries this operator */ + /* depends on, then iterate through and see if one of them */ + /* is a registered extension */ + ScanKeyInit(&key[0], + Anum_pg_depend_objid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(procnumber)); + + 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 *extlist = fpinfo->extensions; + ListCell *ext; + + foreach(ext, extlist) + { + Oid extension_oid = (Oid) lfirst(ext); + if ( foundDep->refobjid == extension_oid ) + { + nresults++; + } + } + } + if ( nresults > 0 ) break; + } + + systable_endscan(scan); + relation_close(depRel, RowExclusiveLock); + + return nresults > 0; +} + + + +/* * Construct a simple SELECT statement that retrieves desired columns * of the specified foreign table, and append it to "buf". The output * contains just "SELECT ... FROM tablename". @@ -1404,8 +1480,7 @@ deparseConst(Const *node, deparse_expr_cxt *context) } if (needlabel) appendStringInfo(buf, "::%s", - format_type_with_typemod(node->consttype, - node->consttypmod)); + format_type_be_qualified(node->consttype)); } /* diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c index 7547ec2..29a3731 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" /* @@ -105,7 +107,7 @@ postgres_fdw_validator(PG_FUNCTION_ARGS) * Validate option value, when we can do so without any context. */ if (strcmp(def->defname, "use_remote_estimate") == 0 || - strcmp(def->defname, "updatable") == 0) + strcmp(def->defname, "updatable") == 0 ) { /* these accept only boolean values */ (void) defGetBoolean(def); @@ -124,6 +126,10 @@ postgres_fdw_validator(PG_FUNCTION_ARGS) errmsg("%s requires a non-negative numeric value", def->defname))); } + else if (strcmp(def->defname, "extensions") == 0) + { + extractExtensionList(defGetString(def), NULL); + } } PG_RETURN_VOID(); @@ -153,6 +159,8 @@ InitPgFdwOptions(void) /* updatable is available on both server and table */ {"updatable", ForeignServerRelationId, false}, {"updatable", ForeignTableRelationId, false}, + /* use_remote_estimate is available on both server and table */ + {"extensions", ForeignServerRelationId, false}, {NULL, InvalidOid, false} }; @@ -293,3 +301,46 @@ ExtractConnectionOptions(List *defelems, const char **keywords, } return i; } + + +bool +extractExtensionList(char *extensionString, List **extensionOids) +{ + List *extlist; + ListCell *l; + + if ( ! SplitIdentifierString(extensionString, ',', &extlist) ) + { + list_free(extlist); + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("unable to parse extension list '%s'", + extensionString))); + } + + if ( extensionOids ) + *extensionOids = NIL; + + foreach(l, extlist) + { + char *extension_name = (char *) lfirst(l); + Oid extension_oid = get_extension_oid(extension_name, true); + if ( extension_oid == InvalidOid ) + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("the '%s' extension must be installed locally before it can be used on a remote server", + extension_name))); + } + else + { + if ( extensionOids ) + { + *extensionOids = lappend_oid(*extensionOids, extension_oid); + } + } + } + + list_free(extlist); + return true; +} diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index e4d799c..2b1c240 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -17,6 +17,7 @@ #include "access/htup_details.h" #include "access/sysattr.h" #include "commands/defrem.h" +#include "commands/extension.h" #include "commands/explain.h" #include "commands/vacuum.h" #include "foreign/fdwapi.h" @@ -47,39 +48,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. @@ -416,16 +384,15 @@ 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) + extractExtensionList(defGetString(def), &(fpinfo->extensions)); } foreach(lc, fpinfo->table->options) { DefElem *def = (DefElem *) lfirst(lc); if (strcmp(def->defname, "use_remote_estimate") == 0) - { fpinfo->use_remote_estimate = defGetBoolean(def); - break; /* only need the one value */ - } } /* @@ -2994,3 +2961,5 @@ conversion_error_callback(void *arg) NameStr(tupdesc->attrs[errpos->cur_attno - 1]->attname), RelationGetRelationName(errpos->rel)); } + + diff --git a/contrib/postgres_fdw/postgres_fdw.h b/contrib/postgres_fdw/postgres_fdw.h index 3835ddb..d27ed37 100644 --- a/contrib/postgres_fdw/postgres_fdw.h +++ b/contrib/postgres_fdw/postgres_fdw.h @@ -20,6 +20,46 @@ #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; + + /* PostGIS metadata */ + List *extensions; + bool use_postgis; + Oid postgis_oid; + + /* 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 +77,8 @@ extern void pgfdw_report_error(int elevel, PGresult *res, PGconn *conn, extern int ExtractConnectionOptions(List *defelems, const char **keywords, const char **values); +extern bool extractExtensionList(char *extensionString, + List **extensionOids); /* in deparse.c */ extern void classifyConditions(PlannerInfo *root, diff --git a/doc/src/sgml/postgres-fdw.sgml b/doc/src/sgml/postgres-fdw.sgml index 14b12e3..7b3d8c7 100644 --- a/doc/src/sgml/postgres-fdw.sgml +++ b/doc/src/sgml/postgres-fdw.sgml @@ -373,6 +373,37 @@ 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 allows you to declare what extensions you expect are + installed on the foreign server, using a comma-separated list of + extension names. The extensions are also expected to be installed + on the local server too. + + +CREATE SERVER foreign_server + FOREIGN DATA WRAPPER postgres_fdw + OPTIONS (host '127.0.0.1', port '5432', dbname 'my_db', extensions 'cube, seg'); + + + + + + +