Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * foreign.c
4 : * support for foreign-data wrappers, servers and user mappings.
5 : *
6 : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 : *
8 : * IDENTIFICATION
9 : * src/backend/foreign/foreign.c
10 : *
11 : *-------------------------------------------------------------------------
12 : */
13 : #include "postgres.h"
14 :
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 "funcapi.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"
29 : #include "utils/varlena.h"
30 :
31 :
32 : /*
33 : * GetForeignDataWrapper - look up the foreign-data wrapper by OID.
34 : */
35 : ForeignDataWrapper *
36 1466 : GetForeignDataWrapper(Oid fdwid)
37 : {
38 1466 : return GetForeignDataWrapperExtended(fdwid, 0);
39 : }
40 :
41 :
42 : /*
43 : * GetForeignDataWrapperExtended - look up the foreign-data wrapper
44 : * by OID. If flags uses FDW_MISSING_OK, return NULL if the object cannot
45 : * be found instead of raising an error.
46 : */
47 : ForeignDataWrapper *
48 1598 : GetForeignDataWrapperExtended(Oid fdwid, bits16 flags)
49 : {
50 : Form_pg_foreign_data_wrapper fdwform;
51 : ForeignDataWrapper *fdw;
52 : Datum datum;
53 : HeapTuple tp;
54 : bool isnull;
55 :
56 1598 : tp = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid));
57 :
58 1598 : if (!HeapTupleIsValid(tp))
59 : {
60 18 : if ((flags & FDW_MISSING_OK) == 0)
61 0 : elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwid);
62 18 : return NULL;
63 : }
64 :
65 1580 : fdwform = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
66 :
67 1580 : fdw = (ForeignDataWrapper *) palloc(sizeof(ForeignDataWrapper));
68 1580 : fdw->fdwid = fdwid;
69 1580 : fdw->owner = fdwform->fdwowner;
70 1580 : fdw->fdwname = pstrdup(NameStr(fdwform->fdwname));
71 1580 : fdw->fdwhandler = fdwform->fdwhandler;
72 1580 : fdw->fdwvalidator = fdwform->fdwvalidator;
73 :
74 : /* Extract the fdwoptions */
75 1580 : datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
76 : tp,
77 : Anum_pg_foreign_data_wrapper_fdwoptions,
78 : &isnull);
79 1580 : if (isnull)
80 1328 : fdw->options = NIL;
81 : else
82 252 : fdw->options = untransformRelOptions(datum);
83 :
84 1580 : ReleaseSysCache(tp);
85 :
86 1580 : return fdw;
87 : }
88 :
89 :
90 : /*
91 : * GetForeignDataWrapperByName - look up the foreign-data wrapper
92 : * definition by name.
93 : */
94 : ForeignDataWrapper *
95 418 : GetForeignDataWrapperByName(const char *fdwname, bool missing_ok)
96 : {
97 418 : Oid fdwId = get_foreign_data_wrapper_oid(fdwname, missing_ok);
98 :
99 412 : if (!OidIsValid(fdwId))
100 160 : return NULL;
101 :
102 252 : return GetForeignDataWrapper(fdwId);
103 : }
104 :
105 :
106 : /*
107 : * GetForeignServer - look up the foreign server definition.
108 : */
109 : ForeignServer *
110 4850 : GetForeignServer(Oid serverid)
111 : {
112 4850 : return GetForeignServerExtended(serverid, 0);
113 : }
114 :
115 :
116 : /*
117 : * GetForeignServerExtended - look up the foreign server definition. If
118 : * flags uses FSV_MISSING_OK, return NULL if the object cannot be found
119 : * instead of raising an error.
120 : */
121 : ForeignServer *
122 5066 : GetForeignServerExtended(Oid serverid, bits16 flags)
123 : {
124 : Form_pg_foreign_server serverform;
125 : ForeignServer *server;
126 : HeapTuple tp;
127 : Datum datum;
128 : bool isnull;
129 :
130 5066 : tp = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid));
131 :
132 5066 : if (!HeapTupleIsValid(tp))
133 : {
134 20 : if ((flags & FSV_MISSING_OK) == 0)
135 0 : elog(ERROR, "cache lookup failed for foreign server %u", serverid);
136 20 : return NULL;
137 : }
138 :
139 5046 : serverform = (Form_pg_foreign_server) GETSTRUCT(tp);
140 :
141 5046 : server = (ForeignServer *) palloc(sizeof(ForeignServer));
142 5046 : server->serverid = serverid;
143 5046 : server->servername = pstrdup(NameStr(serverform->srvname));
144 5046 : server->owner = serverform->srvowner;
145 5046 : server->fdwid = serverform->srvfdw;
146 :
147 : /* Extract server type */
148 5046 : datum = SysCacheGetAttr(FOREIGNSERVEROID,
149 : tp,
150 : Anum_pg_foreign_server_srvtype,
151 : &isnull);
152 5046 : server->servertype = isnull ? NULL : TextDatumGetCString(datum);
153 :
154 : /* Extract server version */
155 5046 : datum = SysCacheGetAttr(FOREIGNSERVEROID,
156 : tp,
157 : Anum_pg_foreign_server_srvversion,
158 : &isnull);
159 5046 : server->serverversion = isnull ? NULL : TextDatumGetCString(datum);
160 :
161 : /* Extract the srvoptions */
162 5046 : datum = SysCacheGetAttr(FOREIGNSERVEROID,
163 : tp,
164 : Anum_pg_foreign_server_srvoptions,
165 : &isnull);
166 5046 : if (isnull)
167 964 : server->options = NIL;
168 : else
169 4082 : server->options = untransformRelOptions(datum);
170 :
171 5046 : ReleaseSysCache(tp);
172 :
173 5046 : return server;
174 : }
175 :
176 :
177 : /*
178 : * GetForeignServerByName - look up the foreign server definition by name.
179 : */
180 : ForeignServer *
181 950 : GetForeignServerByName(const char *srvname, bool missing_ok)
182 : {
183 950 : Oid serverid = get_foreign_server_oid(srvname, missing_ok);
184 :
185 928 : if (!OidIsValid(serverid))
186 52 : return NULL;
187 :
188 876 : return GetForeignServer(serverid);
189 : }
190 :
191 :
192 : /*
193 : * GetUserMapping - look up the user mapping.
194 : *
195 : * If no mapping is found for the supplied user, we also look for
196 : * PUBLIC mappings (userid == InvalidOid).
197 : */
198 : UserMapping *
199 2306 : GetUserMapping(Oid userid, Oid serverid)
200 : {
201 : Datum datum;
202 : HeapTuple tp;
203 : bool isnull;
204 : UserMapping *um;
205 :
206 2306 : tp = SearchSysCache2(USERMAPPINGUSERSERVER,
207 : ObjectIdGetDatum(userid),
208 : ObjectIdGetDatum(serverid));
209 :
210 2306 : if (!HeapTupleIsValid(tp))
211 : {
212 : /* Not found for the specific user -- try PUBLIC */
213 14 : tp = SearchSysCache2(USERMAPPINGUSERSERVER,
214 : ObjectIdGetDatum(InvalidOid),
215 : ObjectIdGetDatum(serverid));
216 : }
217 :
218 2306 : if (!HeapTupleIsValid(tp))
219 : {
220 4 : ForeignServer *server = GetForeignServer(serverid);
221 :
222 4 : ereport(ERROR,
223 : (errcode(ERRCODE_UNDEFINED_OBJECT),
224 : errmsg("user mapping not found for user \"%s\", server \"%s\"",
225 : MappingUserName(userid), server->servername)));
226 : }
227 :
228 2302 : um = (UserMapping *) palloc(sizeof(UserMapping));
229 2302 : um->umid = ((Form_pg_user_mapping) GETSTRUCT(tp))->oid;
230 2302 : um->userid = userid;
231 2302 : um->serverid = serverid;
232 :
233 : /* Extract the umoptions */
234 2302 : datum = SysCacheGetAttr(USERMAPPINGUSERSERVER,
235 : tp,
236 : Anum_pg_user_mapping_umoptions,
237 : &isnull);
238 2302 : if (isnull)
239 2284 : um->options = NIL;
240 : else
241 18 : um->options = untransformRelOptions(datum);
242 :
243 2302 : ReleaseSysCache(tp);
244 :
245 2302 : return um;
246 : }
247 :
248 :
249 : /*
250 : * GetForeignTable - look up the foreign table definition by relation oid.
251 : */
252 : ForeignTable *
253 12212 : GetForeignTable(Oid relid)
254 : {
255 : Form_pg_foreign_table tableform;
256 : ForeignTable *ft;
257 : HeapTuple tp;
258 : Datum datum;
259 : bool isnull;
260 :
261 12212 : tp = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(relid));
262 12212 : if (!HeapTupleIsValid(tp))
263 0 : elog(ERROR, "cache lookup failed for foreign table %u", relid);
264 12212 : tableform = (Form_pg_foreign_table) GETSTRUCT(tp);
265 :
266 12212 : ft = (ForeignTable *) palloc(sizeof(ForeignTable));
267 12212 : ft->relid = relid;
268 12212 : ft->serverid = tableform->ftserver;
269 :
270 : /* Extract the ftoptions */
271 12212 : datum = SysCacheGetAttr(FOREIGNTABLEREL,
272 : tp,
273 : Anum_pg_foreign_table_ftoptions,
274 : &isnull);
275 12212 : if (isnull)
276 0 : ft->options = NIL;
277 : else
278 12212 : ft->options = untransformRelOptions(datum);
279 :
280 12212 : ReleaseSysCache(tp);
281 :
282 12212 : return ft;
283 : }
284 :
285 :
286 : /*
287 : * GetForeignColumnOptions - Get attfdwoptions of given relation/attnum
288 : * as list of DefElem.
289 : */
290 : List *
291 28990 : GetForeignColumnOptions(Oid relid, AttrNumber attnum)
292 : {
293 : List *options;
294 : HeapTuple tp;
295 : Datum datum;
296 : bool isnull;
297 :
298 28990 : tp = SearchSysCache2(ATTNUM,
299 : ObjectIdGetDatum(relid),
300 : Int16GetDatum(attnum));
301 28990 : if (!HeapTupleIsValid(tp))
302 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
303 : attnum, relid);
304 28990 : datum = SysCacheGetAttr(ATTNUM,
305 : tp,
306 : Anum_pg_attribute_attfdwoptions,
307 : &isnull);
308 28990 : if (isnull)
309 22390 : options = NIL;
310 : else
311 6600 : options = untransformRelOptions(datum);
312 :
313 28990 : ReleaseSysCache(tp);
314 :
315 28990 : return options;
316 : }
317 :
318 :
319 : /*
320 : * GetFdwRoutine - call the specified foreign-data wrapper handler routine
321 : * to get its FdwRoutine struct.
322 : */
323 : FdwRoutine *
324 1310 : GetFdwRoutine(Oid fdwhandler)
325 : {
326 : Datum datum;
327 : FdwRoutine *routine;
328 :
329 1310 : datum = OidFunctionCall0(fdwhandler);
330 1310 : routine = (FdwRoutine *) DatumGetPointer(datum);
331 :
332 1310 : if (routine == NULL || !IsA(routine, FdwRoutine))
333 0 : elog(ERROR, "foreign-data wrapper handler function %u did not return an FdwRoutine struct",
334 : fdwhandler);
335 :
336 1310 : return routine;
337 : }
338 :
339 :
340 : /*
341 : * GetForeignServerIdByRelId - look up the foreign server
342 : * for the given foreign table, and return its OID.
343 : */
344 : Oid
345 3156 : GetForeignServerIdByRelId(Oid relid)
346 : {
347 : HeapTuple tp;
348 : Form_pg_foreign_table tableform;
349 : Oid serverid;
350 :
351 3156 : tp = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(relid));
352 3156 : if (!HeapTupleIsValid(tp))
353 0 : elog(ERROR, "cache lookup failed for foreign table %u", relid);
354 3156 : tableform = (Form_pg_foreign_table) GETSTRUCT(tp);
355 3156 : serverid = tableform->ftserver;
356 3156 : ReleaseSysCache(tp);
357 :
358 3156 : return serverid;
359 : }
360 :
361 :
362 : /*
363 : * GetFdwRoutineByServerId - look up the handler of the foreign-data wrapper
364 : * for the given foreign server, and retrieve its FdwRoutine struct.
365 : */
366 : FdwRoutine *
367 1308 : GetFdwRoutineByServerId(Oid serverid)
368 : {
369 : HeapTuple tp;
370 : Form_pg_foreign_data_wrapper fdwform;
371 : Form_pg_foreign_server serverform;
372 : Oid fdwid;
373 : Oid fdwhandler;
374 :
375 : /* Get foreign-data wrapper OID for the server. */
376 1308 : tp = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid));
377 1308 : if (!HeapTupleIsValid(tp))
378 0 : elog(ERROR, "cache lookup failed for foreign server %u", serverid);
379 1308 : serverform = (Form_pg_foreign_server) GETSTRUCT(tp);
380 1308 : fdwid = serverform->srvfdw;
381 1308 : ReleaseSysCache(tp);
382 :
383 : /* Get handler function OID for the FDW. */
384 1308 : tp = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid));
385 1308 : if (!HeapTupleIsValid(tp))
386 0 : elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwid);
387 1308 : fdwform = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
388 1308 : fdwhandler = fdwform->fdwhandler;
389 :
390 : /* Complain if FDW has been set to NO HANDLER. */
391 1308 : if (!OidIsValid(fdwhandler))
392 14 : ereport(ERROR,
393 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
394 : errmsg("foreign-data wrapper \"%s\" has no handler",
395 : NameStr(fdwform->fdwname))));
396 :
397 1294 : ReleaseSysCache(tp);
398 :
399 : /* And finally, call the handler function. */
400 1294 : return GetFdwRoutine(fdwhandler);
401 : }
402 :
403 :
404 : /*
405 : * GetFdwRoutineByRelId - look up the handler of the foreign-data wrapper
406 : * for the given foreign table, and retrieve its FdwRoutine struct.
407 : */
408 : FdwRoutine *
409 690 : GetFdwRoutineByRelId(Oid relid)
410 : {
411 : Oid serverid;
412 :
413 : /* Get server OID for the foreign table. */
414 690 : serverid = GetForeignServerIdByRelId(relid);
415 :
416 : /* Now retrieve server's FdwRoutine struct. */
417 690 : return GetFdwRoutineByServerId(serverid);
418 : }
419 :
420 : /*
421 : * GetFdwRoutineForRelation - look up the handler of the foreign-data wrapper
422 : * for the given foreign table, and retrieve its FdwRoutine struct.
423 : *
424 : * This function is preferred over GetFdwRoutineByRelId because it caches
425 : * the data in the relcache entry, saving a number of catalog lookups.
426 : *
427 : * If makecopy is true then the returned data is freshly palloc'd in the
428 : * caller's memory context. Otherwise, it's a pointer to the relcache data,
429 : * which will be lost in any relcache reset --- so don't rely on it long.
430 : */
431 : FdwRoutine *
432 4896 : GetFdwRoutineForRelation(Relation relation, bool makecopy)
433 : {
434 : FdwRoutine *fdwroutine;
435 : FdwRoutine *cfdwroutine;
436 :
437 4896 : if (relation->rd_fdwroutine == NULL)
438 : {
439 : /* Get the info by consulting the catalogs and the FDW code */
440 322 : fdwroutine = GetFdwRoutineByRelId(RelationGetRelid(relation));
441 :
442 : /* Save the data for later reuse in CacheMemoryContext */
443 308 : cfdwroutine = (FdwRoutine *) MemoryContextAlloc(CacheMemoryContext,
444 : sizeof(FdwRoutine));
445 308 : memcpy(cfdwroutine, fdwroutine, sizeof(FdwRoutine));
446 308 : relation->rd_fdwroutine = cfdwroutine;
447 :
448 : /* Give back the locally palloc'd copy regardless of makecopy */
449 308 : return fdwroutine;
450 : }
451 :
452 : /* We have valid cached data --- does the caller want a copy? */
453 4574 : if (makecopy)
454 : {
455 4214 : fdwroutine = (FdwRoutine *) palloc(sizeof(FdwRoutine));
456 4214 : memcpy(fdwroutine, relation->rd_fdwroutine, sizeof(FdwRoutine));
457 4214 : return fdwroutine;
458 : }
459 :
460 : /* Only a short-lived reference is needed, so just hand back cached copy */
461 360 : return relation->rd_fdwroutine;
462 : }
463 :
464 :
465 : /*
466 : * IsImportableForeignTable - filter table names for IMPORT FOREIGN SCHEMA
467 : *
468 : * Returns true if given table name should be imported according to the
469 : * statement's import filter options.
470 : */
471 : bool
472 60 : IsImportableForeignTable(const char *tablename,
473 : ImportForeignSchemaStmt *stmt)
474 : {
475 : ListCell *lc;
476 :
477 60 : switch (stmt->list_type)
478 : {
479 44 : case FDW_IMPORT_SCHEMA_ALL:
480 44 : return true;
481 :
482 6 : case FDW_IMPORT_SCHEMA_LIMIT_TO:
483 10 : foreach(lc, stmt->table_list)
484 : {
485 10 : RangeVar *rv = (RangeVar *) lfirst(lc);
486 :
487 10 : if (strcmp(tablename, rv->relname) == 0)
488 6 : return true;
489 : }
490 0 : return false;
491 :
492 10 : case FDW_IMPORT_SCHEMA_EXCEPT:
493 50 : foreach(lc, stmt->table_list)
494 : {
495 40 : RangeVar *rv = (RangeVar *) lfirst(lc);
496 :
497 40 : if (strcmp(tablename, rv->relname) == 0)
498 0 : return false;
499 : }
500 10 : return true;
501 : }
502 0 : return false; /* shouldn't get here */
503 : }
504 :
505 :
506 : /*
507 : * pg_options_to_table - Convert options array to name/value table
508 : *
509 : * This is useful to provide details for information_schema and pg_dump.
510 : */
511 : Datum
512 990 : pg_options_to_table(PG_FUNCTION_ARGS)
513 : {
514 990 : Datum array = PG_GETARG_DATUM(0);
515 : ListCell *cell;
516 : List *options;
517 990 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
518 :
519 990 : options = untransformRelOptions(array);
520 990 : rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
521 :
522 : /* prepare the result set */
523 990 : InitMaterializedSRF(fcinfo, MAT_SRF_USE_EXPECTED_DESC);
524 :
525 2748 : foreach(cell, options)
526 : {
527 1758 : DefElem *def = lfirst(cell);
528 : Datum values[2];
529 : bool nulls[2];
530 :
531 1758 : values[0] = CStringGetTextDatum(def->defname);
532 1758 : nulls[0] = false;
533 1758 : if (def->arg)
534 : {
535 1758 : values[1] = CStringGetTextDatum(strVal(def->arg));
536 1758 : nulls[1] = false;
537 : }
538 : else
539 : {
540 0 : values[1] = (Datum) 0;
541 0 : nulls[1] = true;
542 : }
543 1758 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc,
544 : values, nulls);
545 : }
546 :
547 990 : return (Datum) 0;
548 : }
549 :
550 :
551 : /*
552 : * Describes the valid options for postgresql FDW, server, and user mapping.
553 : */
554 : struct ConnectionOption
555 : {
556 : const char *optname;
557 : Oid optcontext; /* Oid of catalog in which option may appear */
558 : };
559 :
560 : /*
561 : * Copied from fe-connect.c PQconninfoOptions.
562 : *
563 : * The list is small - don't bother with bsearch if it stays so.
564 : */
565 : static const struct ConnectionOption libpq_conninfo_options[] = {
566 : {"authtype", ForeignServerRelationId},
567 : {"service", ForeignServerRelationId},
568 : {"user", UserMappingRelationId},
569 : {"password", UserMappingRelationId},
570 : {"connect_timeout", ForeignServerRelationId},
571 : {"dbname", ForeignServerRelationId},
572 : {"host", ForeignServerRelationId},
573 : {"hostaddr", ForeignServerRelationId},
574 : {"port", ForeignServerRelationId},
575 : {"tty", ForeignServerRelationId},
576 : {"options", ForeignServerRelationId},
577 : {"requiressl", ForeignServerRelationId},
578 : {"sslmode", ForeignServerRelationId},
579 : {"gsslib", ForeignServerRelationId},
580 : {"gssdelegation", ForeignServerRelationId},
581 : {NULL, InvalidOid}
582 : };
583 :
584 :
585 : /*
586 : * Check if the provided option is one of libpq conninfo options.
587 : * context is the Oid of the catalog the option came from, or 0 if we
588 : * don't care.
589 : */
590 : static bool
591 108 : is_conninfo_option(const char *option, Oid context)
592 : {
593 : const struct ConnectionOption *opt;
594 :
595 846 : for (opt = libpq_conninfo_options; opt->optname; opt++)
596 816 : if (context == opt->optcontext && strcmp(opt->optname, option) == 0)
597 78 : return true;
598 30 : return false;
599 : }
600 :
601 :
602 : /*
603 : * Validate the generic option given to SERVER or USER MAPPING.
604 : * Raise an ERROR if the option or its value is considered invalid.
605 : *
606 : * Valid server options are all libpq conninfo options except
607 : * user and password -- these may only appear in USER MAPPING options.
608 : *
609 : * Caution: this function is deprecated, and is now meant only for testing
610 : * purposes, because the list of options it knows about doesn't necessarily
611 : * square with those known to whichever libpq instance you might be using.
612 : * Inquire of libpq itself, instead.
613 : */
614 : Datum
615 138 : postgresql_fdw_validator(PG_FUNCTION_ARGS)
616 : {
617 138 : List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
618 138 : Oid catalog = PG_GETARG_OID(1);
619 :
620 : ListCell *cell;
621 :
622 216 : foreach(cell, options_list)
623 : {
624 108 : DefElem *def = lfirst(cell);
625 :
626 108 : if (!is_conninfo_option(def->defname, catalog))
627 : {
628 : const struct ConnectionOption *opt;
629 : const char *closest_match;
630 : ClosestMatchState match_state;
631 30 : bool has_valid_options = false;
632 :
633 : /*
634 : * Unknown option specified, complain about it. Provide a hint
635 : * with a valid option that looks similar, if there is one.
636 : */
637 30 : initClosestMatch(&match_state, def->defname, 4);
638 480 : for (opt = libpq_conninfo_options; opt->optname; opt++)
639 : {
640 450 : if (catalog == opt->optcontext)
641 : {
642 180 : has_valid_options = true;
643 180 : updateClosestMatch(&match_state, opt->optname);
644 : }
645 : }
646 :
647 30 : closest_match = getClosestMatch(&match_state);
648 30 : ereport(ERROR,
649 : (errcode(ERRCODE_SYNTAX_ERROR),
650 : errmsg("invalid option \"%s\"", def->defname),
651 : has_valid_options ? closest_match ?
652 : errhint("Perhaps you meant the option \"%s\".",
653 : closest_match) : 0 :
654 : errhint("There are no valid options in this context.")));
655 :
656 : PG_RETURN_BOOL(false);
657 : }
658 : }
659 :
660 108 : PG_RETURN_BOOL(true);
661 : }
662 :
663 :
664 : /*
665 : * get_foreign_data_wrapper_oid - given a FDW name, look up the OID
666 : *
667 : * If missing_ok is false, throw an error if name not found. If true, just
668 : * return InvalidOid.
669 : */
670 : Oid
671 726 : get_foreign_data_wrapper_oid(const char *fdwname, bool missing_ok)
672 : {
673 : Oid oid;
674 :
675 726 : oid = GetSysCacheOid1(FOREIGNDATAWRAPPERNAME,
676 : Anum_pg_foreign_data_wrapper_oid,
677 : CStringGetDatum(fdwname));
678 726 : if (!OidIsValid(oid) && !missing_ok)
679 24 : ereport(ERROR,
680 : (errcode(ERRCODE_UNDEFINED_OBJECT),
681 : errmsg("foreign-data wrapper \"%s\" does not exist",
682 : fdwname)));
683 702 : return oid;
684 : }
685 :
686 :
687 : /*
688 : * get_foreign_server_oid - given a server name, look up the OID
689 : *
690 : * If missing_ok is false, throw an error if name not found. If true, just
691 : * return InvalidOid.
692 : */
693 : Oid
694 1494 : get_foreign_server_oid(const char *servername, bool missing_ok)
695 : {
696 : Oid oid;
697 :
698 1494 : oid = GetSysCacheOid1(FOREIGNSERVERNAME, Anum_pg_foreign_server_oid,
699 : CStringGetDatum(servername));
700 1494 : if (!OidIsValid(oid) && !missing_ok)
701 40 : ereport(ERROR,
702 : (errcode(ERRCODE_UNDEFINED_OBJECT),
703 : errmsg("server \"%s\" does not exist", servername)));
704 1454 : return oid;
705 : }
706 :
707 : /*
708 : * Get a copy of an existing local path for a given join relation.
709 : *
710 : * This function is usually helpful to obtain an alternate local path for EPQ
711 : * checks.
712 : *
713 : * Right now, this function only supports unparameterized foreign joins, so we
714 : * only search for unparameterized path in the given list of paths. Since we
715 : * are searching for a path which can be used to construct an alternative local
716 : * plan for a foreign join, we look for only MergeJoin, HashJoin or NestLoop
717 : * paths.
718 : *
719 : * If the inner or outer subpath of the chosen path is a ForeignScan, we
720 : * replace it with its outer subpath. For this reason, and also because the
721 : * planner might free the original path later, the path returned by this
722 : * function is a shallow copy of the original. There's no need to copy
723 : * the substructure, so we don't.
724 : *
725 : * Since the plan created using this path will presumably only be used to
726 : * execute EPQ checks, efficiency of the path is not a concern. But since the
727 : * path list in RelOptInfo is anyway sorted by total cost we are likely to
728 : * choose the most efficient path, which is all for the best.
729 : */
730 : Path *
731 120 : GetExistingLocalJoinPath(RelOptInfo *joinrel)
732 : {
733 : ListCell *lc;
734 :
735 : Assert(IS_JOIN_REL(joinrel));
736 :
737 120 : foreach(lc, joinrel->pathlist)
738 : {
739 120 : Path *path = (Path *) lfirst(lc);
740 120 : JoinPath *joinpath = NULL;
741 :
742 : /* Skip parameterized paths. */
743 120 : if (path->param_info != NULL)
744 0 : continue;
745 :
746 120 : switch (path->pathtype)
747 : {
748 38 : case T_HashJoin:
749 : {
750 38 : HashPath *hash_path = makeNode(HashPath);
751 :
752 38 : memcpy(hash_path, path, sizeof(HashPath));
753 38 : joinpath = (JoinPath *) hash_path;
754 : }
755 38 : break;
756 :
757 32 : case T_NestLoop:
758 : {
759 32 : NestPath *nest_path = makeNode(NestPath);
760 :
761 32 : memcpy(nest_path, path, sizeof(NestPath));
762 32 : joinpath = (JoinPath *) nest_path;
763 : }
764 32 : break;
765 :
766 50 : case T_MergeJoin:
767 : {
768 50 : MergePath *merge_path = makeNode(MergePath);
769 :
770 50 : memcpy(merge_path, path, sizeof(MergePath));
771 50 : joinpath = (JoinPath *) merge_path;
772 : }
773 50 : break;
774 :
775 0 : default:
776 :
777 : /*
778 : * Just skip anything else. We don't know if corresponding
779 : * plan would build the output row from whole-row references
780 : * of base relations and execute the EPQ checks.
781 : */
782 0 : break;
783 : }
784 :
785 : /* This path isn't good for us, check next. */
786 120 : if (!joinpath)
787 0 : continue;
788 :
789 : /*
790 : * If either inner or outer path is a ForeignPath corresponding to a
791 : * pushed down join, replace it with the fdw_outerpath, so that we
792 : * maintain path for EPQ checks built entirely of local join
793 : * strategies.
794 : */
795 120 : if (IsA(joinpath->outerjoinpath, ForeignPath))
796 : {
797 : ForeignPath *foreign_path;
798 :
799 120 : foreign_path = (ForeignPath *) joinpath->outerjoinpath;
800 120 : if (IS_JOIN_REL(foreign_path->path.parent))
801 32 : joinpath->outerjoinpath = foreign_path->fdw_outerpath;
802 : }
803 :
804 120 : if (IsA(joinpath->innerjoinpath, ForeignPath))
805 : {
806 : ForeignPath *foreign_path;
807 :
808 108 : foreign_path = (ForeignPath *) joinpath->innerjoinpath;
809 108 : if (IS_JOIN_REL(foreign_path->path.parent))
810 0 : joinpath->innerjoinpath = foreign_path->fdw_outerpath;
811 : }
812 :
813 120 : return (Path *) joinpath;
814 : }
815 0 : return NULL;
816 : }
|