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