1 /*-------------------------------------------------------------------------
4 * support for foreign-data wrappers, servers and user mappings.
6 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
9 * src/backend/foreign/foreign.c
11 *-------------------------------------------------------------------------
15 #include "access/htup_details.h"
16 #include "access/reloptions.h"
17 #include "catalog/pg_foreign_data_wrapper.h"
18 #include "catalog/pg_foreign_server.h"
19 #include "catalog/pg_foreign_table.h"
20 #include "catalog/pg_user_mapping.h"
21 #include "foreign/fdwapi.h"
22 #include "foreign/foreign.h"
23 #include "lib/stringinfo.h"
24 #include "miscadmin.h"
25 #include "utils/builtins.h"
26 #include "utils/memutils.h"
27 #include "utils/rel.h"
28 #include "utils/syscache.h"
32 * GetForeignDataWrapper - look up the foreign-data wrapper by OID.
35 GetForeignDataWrapper(Oid fdwid
)
37 return GetForeignDataWrapperExtended(fdwid
, 0);
42 * GetForeignDataWrapperExtended - look up the foreign-data wrapper
43 * by OID. If flags uses FDW_MISSING_OK, return NULL if the object cannot
44 * be found instead of raising an error.
47 GetForeignDataWrapperExtended(Oid fdwid
, bits16 flags
)
49 Form_pg_foreign_data_wrapper fdwform
;
50 ForeignDataWrapper
*fdw
;
55 tp
= SearchSysCache1(FOREIGNDATAWRAPPEROID
, ObjectIdGetDatum(fdwid
));
57 if (!HeapTupleIsValid(tp
))
59 if ((flags
& FDW_MISSING_OK
) == 0)
60 elog(ERROR
, "cache lookup failed for foreign-data wrapper %u", fdwid
);
64 fdwform
= (Form_pg_foreign_data_wrapper
) GETSTRUCT(tp
);
66 fdw
= (ForeignDataWrapper
*) palloc(sizeof(ForeignDataWrapper
));
68 fdw
->owner
= fdwform
->fdwowner
;
69 fdw
->fdwname
= pstrdup(NameStr(fdwform
->fdwname
));
70 fdw
->fdwhandler
= fdwform
->fdwhandler
;
71 fdw
->fdwvalidator
= fdwform
->fdwvalidator
;
73 /* Extract the fdwoptions */
74 datum
= SysCacheGetAttr(FOREIGNDATAWRAPPEROID
,
76 Anum_pg_foreign_data_wrapper_fdwoptions
,
81 fdw
->options
= untransformRelOptions(datum
);
90 * GetForeignDataWrapperByName - look up the foreign-data wrapper
94 GetForeignDataWrapperByName(const char *fdwname
, bool missing_ok
)
96 Oid fdwId
= get_foreign_data_wrapper_oid(fdwname
, missing_ok
);
98 if (!OidIsValid(fdwId
))
101 return GetForeignDataWrapper(fdwId
);
106 * GetForeignServer - look up the foreign server definition.
109 GetForeignServer(Oid serverid
)
111 return GetForeignServerExtended(serverid
, 0);
116 * GetForeignServerExtended - look up the foreign server definition. If
117 * flags uses FSV_MISSING_OK, return NULL if the object cannot be found
118 * instead of raising an error.
121 GetForeignServerExtended(Oid serverid
, bits16 flags
)
123 Form_pg_foreign_server serverform
;
124 ForeignServer
*server
;
129 tp
= SearchSysCache1(FOREIGNSERVEROID
, ObjectIdGetDatum(serverid
));
131 if (!HeapTupleIsValid(tp
))
133 if ((flags
& FSV_MISSING_OK
) == 0)
134 elog(ERROR
, "cache lookup failed for foreign server %u", serverid
);
138 serverform
= (Form_pg_foreign_server
) GETSTRUCT(tp
);
140 server
= (ForeignServer
*) palloc(sizeof(ForeignServer
));
141 server
->serverid
= serverid
;
142 server
->servername
= pstrdup(NameStr(serverform
->srvname
));
143 server
->owner
= serverform
->srvowner
;
144 server
->fdwid
= serverform
->srvfdw
;
146 /* Extract server type */
147 datum
= SysCacheGetAttr(FOREIGNSERVEROID
,
149 Anum_pg_foreign_server_srvtype
,
151 server
->servertype
= isnull
? NULL
: TextDatumGetCString(datum
);
153 /* Extract server version */
154 datum
= SysCacheGetAttr(FOREIGNSERVEROID
,
156 Anum_pg_foreign_server_srvversion
,
158 server
->serverversion
= isnull
? NULL
: TextDatumGetCString(datum
);
160 /* Extract the srvoptions */
161 datum
= SysCacheGetAttr(FOREIGNSERVEROID
,
163 Anum_pg_foreign_server_srvoptions
,
166 server
->options
= NIL
;
168 server
->options
= untransformRelOptions(datum
);
177 * GetForeignServerByName - look up the foreign server definition by name.
180 GetForeignServerByName(const char *srvname
, bool missing_ok
)
182 Oid serverid
= get_foreign_server_oid(srvname
, missing_ok
);
184 if (!OidIsValid(serverid
))
187 return GetForeignServer(serverid
);
192 * GetUserMapping - look up the user mapping.
194 * If no mapping is found for the supplied user, we also look for
195 * PUBLIC mappings (userid == InvalidOid).
198 GetUserMapping(Oid userid
, Oid serverid
)
205 tp
= SearchSysCache2(USERMAPPINGUSERSERVER
,
206 ObjectIdGetDatum(userid
),
207 ObjectIdGetDatum(serverid
));
209 if (!HeapTupleIsValid(tp
))
211 /* Not found for the specific user -- try PUBLIC */
212 tp
= SearchSysCache2(USERMAPPINGUSERSERVER
,
213 ObjectIdGetDatum(InvalidOid
),
214 ObjectIdGetDatum(serverid
));
217 if (!HeapTupleIsValid(tp
))
219 (errcode(ERRCODE_UNDEFINED_OBJECT
),
220 errmsg("user mapping not found for \"%s\"",
221 MappingUserName(userid
))));
223 um
= (UserMapping
*) palloc(sizeof(UserMapping
));
224 um
->umid
= ((Form_pg_user_mapping
) GETSTRUCT(tp
))->oid
;
226 um
->serverid
= serverid
;
228 /* Extract the umoptions */
229 datum
= SysCacheGetAttr(USERMAPPINGUSERSERVER
,
231 Anum_pg_user_mapping_umoptions
,
236 um
->options
= untransformRelOptions(datum
);
245 * GetForeignTable - look up the foreign table definition by relation oid.
248 GetForeignTable(Oid relid
)
250 Form_pg_foreign_table tableform
;
256 tp
= SearchSysCache1(FOREIGNTABLEREL
, ObjectIdGetDatum(relid
));
257 if (!HeapTupleIsValid(tp
))
258 elog(ERROR
, "cache lookup failed for foreign table %u", relid
);
259 tableform
= (Form_pg_foreign_table
) GETSTRUCT(tp
);
261 ft
= (ForeignTable
*) palloc(sizeof(ForeignTable
));
263 ft
->serverid
= tableform
->ftserver
;
265 /* Extract the ftoptions */
266 datum
= SysCacheGetAttr(FOREIGNTABLEREL
,
268 Anum_pg_foreign_table_ftoptions
,
273 ft
->options
= untransformRelOptions(datum
);
282 * GetForeignColumnOptions - Get attfdwoptions of given relation/attnum
283 * as list of DefElem.
286 GetForeignColumnOptions(Oid relid
, AttrNumber attnum
)
293 tp
= SearchSysCache2(ATTNUM
,
294 ObjectIdGetDatum(relid
),
295 Int16GetDatum(attnum
));
296 if (!HeapTupleIsValid(tp
))
297 elog(ERROR
, "cache lookup failed for attribute %d of relation %u",
299 datum
= SysCacheGetAttr(ATTNUM
,
301 Anum_pg_attribute_attfdwoptions
,
306 options
= untransformRelOptions(datum
);
315 * GetFdwRoutine - call the specified foreign-data wrapper handler routine
316 * to get its FdwRoutine struct.
319 GetFdwRoutine(Oid fdwhandler
)
324 datum
= OidFunctionCall0(fdwhandler
);
325 routine
= (FdwRoutine
*) DatumGetPointer(datum
);
327 if (routine
== NULL
|| !IsA(routine
, FdwRoutine
))
328 elog(ERROR
, "foreign-data wrapper handler function %u did not return an FdwRoutine struct",
336 * GetForeignServerIdByRelId - look up the foreign server
337 * for the given foreign table, and return its OID.
340 GetForeignServerIdByRelId(Oid relid
)
343 Form_pg_foreign_table tableform
;
346 tp
= SearchSysCache1(FOREIGNTABLEREL
, ObjectIdGetDatum(relid
));
347 if (!HeapTupleIsValid(tp
))
348 elog(ERROR
, "cache lookup failed for foreign table %u", relid
);
349 tableform
= (Form_pg_foreign_table
) GETSTRUCT(tp
);
350 serverid
= tableform
->ftserver
;
358 * GetFdwRoutineByServerId - look up the handler of the foreign-data wrapper
359 * for the given foreign server, and retrieve its FdwRoutine struct.
362 GetFdwRoutineByServerId(Oid serverid
)
365 Form_pg_foreign_data_wrapper fdwform
;
366 Form_pg_foreign_server serverform
;
370 /* Get foreign-data wrapper OID for the server. */
371 tp
= SearchSysCache1(FOREIGNSERVEROID
, ObjectIdGetDatum(serverid
));
372 if (!HeapTupleIsValid(tp
))
373 elog(ERROR
, "cache lookup failed for foreign server %u", serverid
);
374 serverform
= (Form_pg_foreign_server
) GETSTRUCT(tp
);
375 fdwid
= serverform
->srvfdw
;
378 /* Get handler function OID for the FDW. */
379 tp
= SearchSysCache1(FOREIGNDATAWRAPPEROID
, ObjectIdGetDatum(fdwid
));
380 if (!HeapTupleIsValid(tp
))
381 elog(ERROR
, "cache lookup failed for foreign-data wrapper %u", fdwid
);
382 fdwform
= (Form_pg_foreign_data_wrapper
) GETSTRUCT(tp
);
383 fdwhandler
= fdwform
->fdwhandler
;
385 /* Complain if FDW has been set to NO HANDLER. */
386 if (!OidIsValid(fdwhandler
))
388 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE
),
389 errmsg("foreign-data wrapper \"%s\" has no handler",
390 NameStr(fdwform
->fdwname
))));
394 /* And finally, call the handler function. */
395 return GetFdwRoutine(fdwhandler
);
400 * GetFdwRoutineByRelId - look up the handler of the foreign-data wrapper
401 * for the given foreign table, and retrieve its FdwRoutine struct.
404 GetFdwRoutineByRelId(Oid relid
)
408 /* Get server OID for the foreign table. */
409 serverid
= GetForeignServerIdByRelId(relid
);
411 /* Now retrieve server's FdwRoutine struct. */
412 return GetFdwRoutineByServerId(serverid
);
416 * GetFdwRoutineForRelation - look up the handler of the foreign-data wrapper
417 * for the given foreign table, and retrieve its FdwRoutine struct.
419 * This function is preferred over GetFdwRoutineByRelId because it caches
420 * the data in the relcache entry, saving a number of catalog lookups.
422 * If makecopy is true then the returned data is freshly palloc'd in the
423 * caller's memory context. Otherwise, it's a pointer to the relcache data,
424 * which will be lost in any relcache reset --- so don't rely on it long.
427 GetFdwRoutineForRelation(Relation relation
, bool makecopy
)
429 FdwRoutine
*fdwroutine
;
430 FdwRoutine
*cfdwroutine
;
432 if (relation
->rd_fdwroutine
== NULL
)
434 /* Get the info by consulting the catalogs and the FDW code */
435 fdwroutine
= GetFdwRoutineByRelId(RelationGetRelid(relation
));
437 /* Save the data for later reuse in CacheMemoryContext */
438 cfdwroutine
= (FdwRoutine
*) MemoryContextAlloc(CacheMemoryContext
,
440 memcpy(cfdwroutine
, fdwroutine
, sizeof(FdwRoutine
));
441 relation
->rd_fdwroutine
= cfdwroutine
;
443 /* Give back the locally palloc'd copy regardless of makecopy */
447 /* We have valid cached data --- does the caller want a copy? */
450 fdwroutine
= (FdwRoutine
*) palloc(sizeof(FdwRoutine
));
451 memcpy(fdwroutine
, relation
->rd_fdwroutine
, sizeof(FdwRoutine
));
455 /* Only a short-lived reference is needed, so just hand back cached copy */
456 return relation
->rd_fdwroutine
;
461 * IsImportableForeignTable - filter table names for IMPORT FOREIGN SCHEMA
463 * Returns true if given table name should be imported according to the
464 * statement's import filter options.
467 IsImportableForeignTable(const char *tablename
,
468 ImportForeignSchemaStmt
*stmt
)
472 switch (stmt
->list_type
)
474 case FDW_IMPORT_SCHEMA_ALL
:
477 case FDW_IMPORT_SCHEMA_LIMIT_TO
:
478 foreach(lc
, stmt
->table_list
)
480 RangeVar
*rv
= (RangeVar
*) lfirst(lc
);
482 if (strcmp(tablename
, rv
->relname
) == 0)
487 case FDW_IMPORT_SCHEMA_EXCEPT
:
488 foreach(lc
, stmt
->table_list
)
490 RangeVar
*rv
= (RangeVar
*) lfirst(lc
);
492 if (strcmp(tablename
, rv
->relname
) == 0)
497 return false; /* shouldn't get here */
502 * deflist_to_tuplestore - Helper function to convert DefElem list to
503 * tuplestore usable in SRF.
506 deflist_to_tuplestore(ReturnSetInfo
*rsinfo
, List
*options
)
510 Tuplestorestate
*tupstore
;
513 MemoryContext per_query_ctx
;
514 MemoryContext oldcontext
;
516 /* check to see if caller supports us returning a tuplestore */
517 if (rsinfo
== NULL
|| !IsA(rsinfo
, ReturnSetInfo
))
519 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
520 errmsg("set-valued function called in context that cannot accept a set")));
521 if (!(rsinfo
->allowedModes
& SFRM_Materialize
) ||
522 rsinfo
->expectedDesc
== NULL
)
524 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
525 errmsg("materialize mode required, but it is not allowed in this context")));
527 per_query_ctx
= rsinfo
->econtext
->ecxt_per_query_memory
;
528 oldcontext
= MemoryContextSwitchTo(per_query_ctx
);
531 * Now prepare the result set.
533 tupdesc
= CreateTupleDescCopy(rsinfo
->expectedDesc
);
534 tupstore
= tuplestore_begin_heap(true, false, work_mem
);
535 rsinfo
->returnMode
= SFRM_Materialize
;
536 rsinfo
->setResult
= tupstore
;
537 rsinfo
->setDesc
= tupdesc
;
539 foreach(cell
, options
)
541 DefElem
*def
= lfirst(cell
);
543 values
[0] = CStringGetTextDatum(def
->defname
);
547 values
[1] = CStringGetTextDatum(strVal(def
->arg
));
552 values
[1] = (Datum
) 0;
555 tuplestore_putvalues(tupstore
, tupdesc
, values
, nulls
);
558 /* clean up and return the tuplestore */
559 tuplestore_donestoring(tupstore
);
561 MemoryContextSwitchTo(oldcontext
);
566 * Convert options array to name/value table. Useful for information
567 * schema and pg_dump.
570 pg_options_to_table(PG_FUNCTION_ARGS
)
572 Datum array
= PG_GETARG_DATUM(0);
574 deflist_to_tuplestore((ReturnSetInfo
*) fcinfo
->resultinfo
,
575 untransformRelOptions(array
));
582 * Describes the valid options for postgresql FDW, server, and user mapping.
584 struct ConnectionOption
587 Oid optcontext
; /* Oid of catalog in which option may appear */
591 * Copied from fe-connect.c PQconninfoOptions.
593 * The list is small - don't bother with bsearch if it stays so.
595 static const struct ConnectionOption libpq_conninfo_options
[] = {
596 {"authtype", ForeignServerRelationId
},
597 {"service", ForeignServerRelationId
},
598 {"user", UserMappingRelationId
},
599 {"password", UserMappingRelationId
},
600 {"connect_timeout", ForeignServerRelationId
},
601 {"dbname", ForeignServerRelationId
},
602 {"host", ForeignServerRelationId
},
603 {"hostaddr", ForeignServerRelationId
},
604 {"port", ForeignServerRelationId
},
605 {"tty", ForeignServerRelationId
},
606 {"options", ForeignServerRelationId
},
607 {"requiressl", ForeignServerRelationId
},
608 {"sslmode", ForeignServerRelationId
},
609 {"gsslib", ForeignServerRelationId
},
615 * Check if the provided option is one of libpq conninfo options.
616 * context is the Oid of the catalog the option came from, or 0 if we
620 is_conninfo_option(const char *option
, Oid context
)
622 const struct ConnectionOption
*opt
;
624 for (opt
= libpq_conninfo_options
; opt
->optname
; opt
++)
625 if (context
== opt
->optcontext
&& strcmp(opt
->optname
, option
) == 0)
632 * Validate the generic option given to SERVER or USER MAPPING.
633 * Raise an ERROR if the option or its value is considered invalid.
635 * Valid server options are all libpq conninfo options except
636 * user and password -- these may only appear in USER MAPPING options.
638 * Caution: this function is deprecated, and is now meant only for testing
639 * purposes, because the list of options it knows about doesn't necessarily
640 * square with those known to whichever libpq instance you might be using.
641 * Inquire of libpq itself, instead.
644 postgresql_fdw_validator(PG_FUNCTION_ARGS
)
646 List
*options_list
= untransformRelOptions(PG_GETARG_DATUM(0));
647 Oid catalog
= PG_GETARG_OID(1);
651 foreach(cell
, options_list
)
653 DefElem
*def
= lfirst(cell
);
655 if (!is_conninfo_option(def
->defname
, catalog
))
657 const struct ConnectionOption
*opt
;
661 * Unknown option specified, complain about it. Provide a hint
662 * with list of valid options for the object.
664 initStringInfo(&buf
);
665 for (opt
= libpq_conninfo_options
; opt
->optname
; opt
++)
666 if (catalog
== opt
->optcontext
)
667 appendStringInfo(&buf
, "%s%s", (buf
.len
> 0) ? ", " : "",
671 (errcode(ERRCODE_SYNTAX_ERROR
),
672 errmsg("invalid option \"%s\"", def
->defname
),
674 ? errhint("Valid options in this context are: %s",
676 : errhint("There are no valid options in this context.")));
678 PG_RETURN_BOOL(false);
682 PG_RETURN_BOOL(true);
687 * get_foreign_data_wrapper_oid - given a FDW name, look up the OID
689 * If missing_ok is false, throw an error if name not found. If true, just
693 get_foreign_data_wrapper_oid(const char *fdwname
, bool missing_ok
)
697 oid
= GetSysCacheOid1(FOREIGNDATAWRAPPERNAME
,
698 Anum_pg_foreign_data_wrapper_oid
,
699 CStringGetDatum(fdwname
));
700 if (!OidIsValid(oid
) && !missing_ok
)
702 (errcode(ERRCODE_UNDEFINED_OBJECT
),
703 errmsg("foreign-data wrapper \"%s\" does not exist",
710 * get_foreign_server_oid - given a server name, look up the OID
712 * If missing_ok is false, throw an error if name not found. If true, just
716 get_foreign_server_oid(const char *servername
, bool missing_ok
)
720 oid
= GetSysCacheOid1(FOREIGNSERVERNAME
, Anum_pg_foreign_server_oid
,
721 CStringGetDatum(servername
));
722 if (!OidIsValid(oid
) && !missing_ok
)
724 (errcode(ERRCODE_UNDEFINED_OBJECT
),
725 errmsg("server \"%s\" does not exist", servername
)));
730 * Get a copy of an existing local path for a given join relation.
732 * This function is usually helpful to obtain an alternate local path for EPQ
735 * Right now, this function only supports unparameterized foreign joins, so we
736 * only search for unparameterized path in the given list of paths. Since we
737 * are searching for a path which can be used to construct an alternative local
738 * plan for a foreign join, we look for only MergeJoin, HashJoin or NestLoop
741 * If the inner or outer subpath of the chosen path is a ForeignScan, we
742 * replace it with its outer subpath. For this reason, and also because the
743 * planner might free the original path later, the path returned by this
744 * function is a shallow copy of the original. There's no need to copy
745 * the substructure, so we don't.
747 * Since the plan created using this path will presumably only be used to
748 * execute EPQ checks, efficiency of the path is not a concern. But since the
749 * path list in RelOptInfo is anyway sorted by total cost we are likely to
750 * choose the most efficient path, which is all for the best.
753 GetExistingLocalJoinPath(RelOptInfo
*joinrel
)
757 Assert(IS_JOIN_REL(joinrel
));
759 foreach(lc
, joinrel
->pathlist
)
761 Path
*path
= (Path
*) lfirst(lc
);
762 JoinPath
*joinpath
= NULL
;
764 /* Skip parameterized paths. */
765 if (path
->param_info
!= NULL
)
768 switch (path
->pathtype
)
772 HashPath
*hash_path
= makeNode(HashPath
);
774 memcpy(hash_path
, path
, sizeof(HashPath
));
775 joinpath
= (JoinPath
*) hash_path
;
781 NestPath
*nest_path
= makeNode(NestPath
);
783 memcpy(nest_path
, path
, sizeof(NestPath
));
784 joinpath
= (JoinPath
*) nest_path
;
790 MergePath
*merge_path
= makeNode(MergePath
);
792 memcpy(merge_path
, path
, sizeof(MergePath
));
793 joinpath
= (JoinPath
*) merge_path
;
800 * Just skip anything else. We don't know if corresponding
801 * plan would build the output row from whole-row references
802 * of base relations and execute the EPQ checks.
807 /* This path isn't good for us, check next. */
812 * If either inner or outer path is a ForeignPath corresponding to a
813 * pushed down join, replace it with the fdw_outerpath, so that we
814 * maintain path for EPQ checks built entirely of local join
817 if (IsA(joinpath
->outerjoinpath
, ForeignPath
))
819 ForeignPath
*foreign_path
;
821 foreign_path
= (ForeignPath
*) joinpath
->outerjoinpath
;
822 if (IS_JOIN_REL(foreign_path
->path
.parent
))
823 joinpath
->outerjoinpath
= foreign_path
->fdw_outerpath
;
826 if (IsA(joinpath
->innerjoinpath
, ForeignPath
))
828 ForeignPath
*foreign_path
;
830 foreign_path
= (ForeignPath
*) joinpath
->innerjoinpath
;
831 if (IS_JOIN_REL(foreign_path
->path
.parent
))
832 joinpath
->innerjoinpath
= foreign_path
->fdw_outerpath
;
835 return (Path
*) joinpath
;