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