1 /*-------------------------------------------------------------------------
2  *
3  * foreign.c
4  *		  support for foreign-data wrappers, servers and user mappings.
5  *
6  * Portions Copyright (c) 1996-2017, 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 extern 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