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