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-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 *
35 128 : 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 128 : tp = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid));
44 :
45 128 : if (!HeapTupleIsValid(tp))
46 0 : elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwid);
47 :
48 128 : fdwform = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
49 :
50 128 : fdw = (ForeignDataWrapper *) palloc(sizeof(ForeignDataWrapper));
51 128 : fdw->fdwid = fdwid;
52 128 : fdw->owner = fdwform->fdwowner;
53 128 : fdw->fdwname = pstrdup(NameStr(fdwform->fdwname));
54 128 : fdw->fdwhandler = fdwform->fdwhandler;
55 128 : fdw->fdwvalidator = fdwform->fdwvalidator;
56 :
57 : /* Extract the fdwoptions */
58 128 : datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
59 : tp,
60 : Anum_pg_foreign_data_wrapper_fdwoptions,
61 : &isnull);
62 128 : if (isnull)
63 86 : fdw->options = NIL;
64 : else
65 42 : fdw->options = untransformRelOptions(datum);
66 :
67 128 : ReleaseSysCache(tp);
68 :
69 128 : return fdw;
70 : }
71 :
72 :
73 : /*
74 : * GetForeignDataWrapperByName - look up the foreign-data wrapper
75 : * definition by name.
76 : */
77 : ForeignDataWrapper *
78 53 : GetForeignDataWrapperByName(const char *fdwname, bool missing_ok)
79 : {
80 53 : Oid fdwId = get_foreign_data_wrapper_oid(fdwname, missing_ok);
81 :
82 52 : if (!OidIsValid(fdwId))
83 16 : return NULL;
84 :
85 36 : return GetForeignDataWrapper(fdwId);
86 : }
87 :
88 :
89 : /*
90 : * GetForeignServer - look up the foreign server definition.
91 : */
92 : ForeignServer *
93 162 : GetForeignServer(Oid serverid)
94 : {
95 : Form_pg_foreign_server serverform;
96 : ForeignServer *server;
97 : HeapTuple tp;
98 : Datum datum;
99 : bool isnull;
100 :
101 162 : tp = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid));
102 :
103 162 : if (!HeapTupleIsValid(tp))
104 0 : elog(ERROR, "cache lookup failed for foreign server %u", serverid);
105 :
106 162 : serverform = (Form_pg_foreign_server) GETSTRUCT(tp);
107 :
108 162 : server = (ForeignServer *) palloc(sizeof(ForeignServer));
109 162 : server->serverid = serverid;
110 162 : server->servername = pstrdup(NameStr(serverform->srvname));
111 162 : server->owner = serverform->srvowner;
112 162 : server->fdwid = serverform->srvfdw;
113 :
114 : /* Extract server type */
115 162 : datum = SysCacheGetAttr(FOREIGNSERVEROID,
116 : tp,
117 : Anum_pg_foreign_server_srvtype,
118 : &isnull);
119 162 : server->servertype = isnull ? NULL : TextDatumGetCString(datum);
120 :
121 : /* Extract server version */
122 162 : datum = SysCacheGetAttr(FOREIGNSERVEROID,
123 : tp,
124 : Anum_pg_foreign_server_srvversion,
125 : &isnull);
126 162 : server->serverversion = isnull ? NULL : TextDatumGetCString(datum);
127 :
128 : /* Extract the srvoptions */
129 162 : datum = SysCacheGetAttr(FOREIGNSERVEROID,
130 : tp,
131 : Anum_pg_foreign_server_srvoptions,
132 : &isnull);
133 162 : if (isnull)
134 132 : server->options = NIL;
135 : else
136 30 : server->options = untransformRelOptions(datum);
137 :
138 162 : ReleaseSysCache(tp);
139 :
140 162 : return server;
141 : }
142 :
143 :
144 : /*
145 : * GetForeignServerByName - look up the foreign server definition by name.
146 : */
147 : ForeignServer *
148 141 : GetForeignServerByName(const char *srvname, bool missing_ok)
149 : {
150 141 : Oid serverid = get_foreign_server_oid(srvname, missing_ok);
151 :
152 138 : if (!OidIsValid(serverid))
153 38 : return NULL;
154 :
155 100 : 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 *
166 0 : GetUserMapping(Oid userid, Oid serverid)
167 : {
168 : Datum datum;
169 : HeapTuple tp;
170 : bool isnull;
171 : UserMapping *um;
172 :
173 0 : tp = SearchSysCache2(USERMAPPINGUSERSERVER,
174 : ObjectIdGetDatum(userid),
175 : ObjectIdGetDatum(serverid));
176 :
177 0 : if (!HeapTupleIsValid(tp))
178 : {
179 : /* Not found for the specific user -- try PUBLIC */
180 0 : tp = SearchSysCache2(USERMAPPINGUSERSERVER,
181 : ObjectIdGetDatum(InvalidOid),
182 : ObjectIdGetDatum(serverid));
183 : }
184 :
185 0 : if (!HeapTupleIsValid(tp))
186 0 : ereport(ERROR,
187 : (errcode(ERRCODE_UNDEFINED_OBJECT),
188 : errmsg("user mapping not found for \"%s\"",
189 : MappingUserName(userid))));
190 :
191 0 : um = (UserMapping *) palloc(sizeof(UserMapping));
192 0 : um->umid = HeapTupleGetOid(tp);
193 0 : um->userid = userid;
194 0 : um->serverid = serverid;
195 :
196 : /* Extract the umoptions */
197 0 : datum = SysCacheGetAttr(USERMAPPINGUSERSERVER,
198 : tp,
199 : Anum_pg_user_mapping_umoptions,
200 : &isnull);
201 0 : if (isnull)
202 0 : um->options = NIL;
203 : else
204 0 : um->options = untransformRelOptions(datum);
205 :
206 0 : ReleaseSysCache(tp);
207 :
208 0 : return um;
209 : }
210 :
211 :
212 : /*
213 : * GetForeignTable - look up the foreign table definition by relation oid.
214 : */
215 : ForeignTable *
216 0 : GetForeignTable(Oid relid)
217 : {
218 : Form_pg_foreign_table tableform;
219 : ForeignTable *ft;
220 : HeapTuple tp;
221 : Datum datum;
222 : bool isnull;
223 :
224 0 : tp = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(relid));
225 0 : if (!HeapTupleIsValid(tp))
226 0 : elog(ERROR, "cache lookup failed for foreign table %u", relid);
227 0 : tableform = (Form_pg_foreign_table) GETSTRUCT(tp);
228 :
229 0 : ft = (ForeignTable *) palloc(sizeof(ForeignTable));
230 0 : ft->relid = relid;
231 0 : ft->serverid = tableform->ftserver;
232 :
233 : /* Extract the ftoptions */
234 0 : datum = SysCacheGetAttr(FOREIGNTABLEREL,
235 : tp,
236 : Anum_pg_foreign_table_ftoptions,
237 : &isnull);
238 0 : if (isnull)
239 0 : ft->options = NIL;
240 : else
241 0 : ft->options = untransformRelOptions(datum);
242 :
243 0 : ReleaseSysCache(tp);
244 :
245 0 : return ft;
246 : }
247 :
248 :
249 : /*
250 : * GetForeignColumnOptions - Get attfdwoptions of given relation/attnum
251 : * as list of DefElem.
252 : */
253 : List *
254 0 : GetForeignColumnOptions(Oid relid, AttrNumber attnum)
255 : {
256 : List *options;
257 : HeapTuple tp;
258 : Datum datum;
259 : bool isnull;
260 :
261 0 : tp = SearchSysCache2(ATTNUM,
262 : ObjectIdGetDatum(relid),
263 : Int16GetDatum(attnum));
264 0 : if (!HeapTupleIsValid(tp))
265 0 : elog(ERROR, "cache lookup failed for attribute %d of relation %u",
266 : attnum, relid);
267 0 : datum = SysCacheGetAttr(ATTNUM,
268 : tp,
269 : Anum_pg_attribute_attfdwoptions,
270 : &isnull);
271 0 : if (isnull)
272 0 : options = NIL;
273 : else
274 0 : options = untransformRelOptions(datum);
275 :
276 0 : ReleaseSysCache(tp);
277 :
278 0 : 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 *
287 0 : GetFdwRoutine(Oid fdwhandler)
288 : {
289 : Datum datum;
290 : FdwRoutine *routine;
291 :
292 0 : datum = OidFunctionCall0(fdwhandler);
293 0 : routine = (FdwRoutine *) DatumGetPointer(datum);
294 :
295 0 : if (routine == NULL || !IsA(routine, FdwRoutine))
296 0 : elog(ERROR, "foreign-data wrapper handler function %u did not return an FdwRoutine struct",
297 : fdwhandler);
298 :
299 0 : 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
308 4 : GetForeignServerIdByRelId(Oid relid)
309 : {
310 : HeapTuple tp;
311 : Form_pg_foreign_table tableform;
312 : Oid serverid;
313 :
314 4 : tp = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(relid));
315 4 : if (!HeapTupleIsValid(tp))
316 0 : elog(ERROR, "cache lookup failed for foreign table %u", relid);
317 4 : tableform = (Form_pg_foreign_table) GETSTRUCT(tp);
318 4 : serverid = tableform->ftserver;
319 4 : ReleaseSysCache(tp);
320 :
321 4 : 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 *
330 2 : 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 2 : tp = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid));
340 2 : if (!HeapTupleIsValid(tp))
341 0 : elog(ERROR, "cache lookup failed for foreign server %u", serverid);
342 2 : serverform = (Form_pg_foreign_server) GETSTRUCT(tp);
343 2 : fdwid = serverform->srvfdw;
344 2 : ReleaseSysCache(tp);
345 :
346 : /* Get handler function OID for the FDW. */
347 2 : tp = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid));
348 2 : if (!HeapTupleIsValid(tp))
349 0 : elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwid);
350 2 : fdwform = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
351 2 : fdwhandler = fdwform->fdwhandler;
352 :
353 : /* Complain if FDW has been set to NO HANDLER. */
354 2 : if (!OidIsValid(fdwhandler))
355 2 : 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 0 : ReleaseSysCache(tp);
361 :
362 : /* And finally, call the handler function. */
363 0 : 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 *
372 2 : GetFdwRoutineByRelId(Oid relid)
373 : {
374 : Oid serverid;
375 :
376 : /* Get server OID for the foreign table. */
377 2 : serverid = GetForeignServerIdByRelId(relid);
378 :
379 : /* Now retrieve server's FdwRoutine struct. */
380 2 : 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 *
395 2 : GetFdwRoutineForRelation(Relation relation, bool makecopy)
396 : {
397 : FdwRoutine *fdwroutine;
398 : FdwRoutine *cfdwroutine;
399 :
400 2 : if (relation->rd_fdwroutine == NULL)
401 : {
402 : /* Get the info by consulting the catalogs and the FDW code */
403 2 : fdwroutine = GetFdwRoutineByRelId(RelationGetRelid(relation));
404 :
405 : /* Save the data for later reuse in CacheMemoryContext */
406 0 : cfdwroutine = (FdwRoutine *) MemoryContextAlloc(CacheMemoryContext,
407 : sizeof(FdwRoutine));
408 0 : memcpy(cfdwroutine, fdwroutine, sizeof(FdwRoutine));
409 0 : relation->rd_fdwroutine = cfdwroutine;
410 :
411 : /* Give back the locally palloc'd copy regardless of makecopy */
412 0 : return fdwroutine;
413 : }
414 :
415 : /* We have valid cached data --- does the caller want a copy? */
416 0 : if (makecopy)
417 : {
418 0 : fdwroutine = (FdwRoutine *) palloc(sizeof(FdwRoutine));
419 0 : memcpy(fdwroutine, relation->rd_fdwroutine, sizeof(FdwRoutine));
420 0 : return fdwroutine;
421 : }
422 :
423 : /* Only a short-lived reference is needed, so just hand back cached copy */
424 0 : 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
435 0 : IsImportableForeignTable(const char *tablename,
436 : ImportForeignSchemaStmt *stmt)
437 : {
438 : ListCell *lc;
439 :
440 0 : switch (stmt->list_type)
441 : {
442 : case FDW_IMPORT_SCHEMA_ALL:
443 0 : return true;
444 :
445 : case FDW_IMPORT_SCHEMA_LIMIT_TO:
446 0 : foreach(lc, stmt->table_list)
447 : {
448 0 : RangeVar *rv = (RangeVar *) lfirst(lc);
449 :
450 0 : if (strcmp(tablename, rv->relname) == 0)
451 0 : return true;
452 : }
453 0 : return false;
454 :
455 : case FDW_IMPORT_SCHEMA_EXCEPT:
456 0 : foreach(lc, stmt->table_list)
457 : {
458 0 : RangeVar *rv = (RangeVar *) lfirst(lc);
459 :
460 0 : if (strcmp(tablename, rv->relname) == 0)
461 0 : return false;
462 : }
463 0 : return true;
464 : }
465 0 : 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
474 112 : 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 112 : if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
486 0 : ereport(ERROR,
487 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
488 : errmsg("set-valued function called in context that cannot accept a set")));
489 224 : if (!(rsinfo->allowedModes & SFRM_Materialize) ||
490 112 : rsinfo->expectedDesc == NULL)
491 0 : ereport(ERROR,
492 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
493 : errmsg("materialize mode required, but it is not allowed in this context")));
494 :
495 112 : per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
496 112 : oldcontext = MemoryContextSwitchTo(per_query_ctx);
497 :
498 : /*
499 : * Now prepare the result set.
500 : */
501 112 : tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
502 112 : tupstore = tuplestore_begin_heap(true, false, work_mem);
503 112 : rsinfo->returnMode = SFRM_Materialize;
504 112 : rsinfo->setResult = tupstore;
505 112 : rsinfo->setDesc = tupdesc;
506 :
507 337 : foreach(cell, options)
508 : {
509 225 : DefElem *def = lfirst(cell);
510 :
511 225 : values[0] = CStringGetTextDatum(def->defname);
512 225 : nulls[0] = false;
513 225 : if (def->arg)
514 : {
515 225 : values[1] = CStringGetTextDatum(((Value *) (def->arg))->val.str);
516 225 : nulls[1] = false;
517 : }
518 : else
519 : {
520 0 : values[1] = (Datum) 0;
521 0 : nulls[1] = true;
522 : }
523 225 : tuplestore_putvalues(tupstore, tupdesc, values, nulls);
524 : }
525 :
526 : /* clean up and return the tuplestore */
527 : tuplestore_donestoring(tupstore);
528 :
529 112 : MemoryContextSwitchTo(oldcontext);
530 112 : }
531 :
532 :
533 : /*
534 : * Convert options array to name/value table. Useful for information
535 : * schema and pg_dump.
536 : */
537 : Datum
538 112 : pg_options_to_table(PG_FUNCTION_ARGS)
539 : {
540 112 : Datum array = PG_GETARG_DATUM(0);
541 :
542 112 : deflist_to_tuplestore((ReturnSetInfo *) fcinfo->resultinfo,
543 : untransformRelOptions(array));
544 :
545 112 : 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
588 17 : is_conninfo_option(const char *option, Oid context)
589 : {
590 : struct ConnectionOption *opt;
591 :
592 121 : for (opt = libpq_conninfo_options; opt->optname; opt++)
593 117 : if (context == opt->optcontext && strcmp(opt->optname, option) == 0)
594 13 : return true;
595 4 : 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 19 : postgresql_fdw_validator(PG_FUNCTION_ARGS)
613 : {
614 19 : List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
615 19 : Oid catalog = PG_GETARG_OID(1);
616 :
617 : ListCell *cell;
618 :
619 32 : foreach(cell, options_list)
620 : {
621 17 : DefElem *def = lfirst(cell);
622 :
623 17 : 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 4 : initStringInfo(&buf);
633 60 : for (opt = libpq_conninfo_options; opt->optname; opt++)
634 56 : if (catalog == opt->optcontext)
635 28 : appendStringInfo(&buf, "%s%s", (buf.len > 0) ? ", " : "",
636 : opt->optname);
637 :
638 4 : 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 15 : 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
659 99 : get_foreign_data_wrapper_oid(const char *fdwname, bool missing_ok)
660 : {
661 : Oid oid;
662 :
663 99 : oid = GetSysCacheOid1(FOREIGNDATAWRAPPERNAME, CStringGetDatum(fdwname));
664 99 : if (!OidIsValid(oid) && !missing_ok)
665 4 : ereport(ERROR,
666 : (errcode(ERRCODE_UNDEFINED_OBJECT),
667 : errmsg("foreign-data wrapper \"%s\" does not exist",
668 : fdwname)));
669 95 : 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
680 181 : get_foreign_server_oid(const char *servername, bool missing_ok)
681 : {
682 : Oid oid;
683 :
684 181 : oid = GetSysCacheOid1(FOREIGNSERVERNAME, CStringGetDatum(servername));
685 181 : if (!OidIsValid(oid) && !missing_ok)
686 6 : ereport(ERROR,
687 : (errcode(ERRCODE_UNDEFINED_OBJECT),
688 : errmsg("server \"%s\" does not exist", servername)));
689 175 : 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 *
716 0 : GetExistingLocalJoinPath(RelOptInfo *joinrel)
717 : {
718 : ListCell *lc;
719 :
720 0 : Assert(IS_JOIN_REL(joinrel));
721 :
722 0 : foreach(lc, joinrel->pathlist)
723 : {
724 0 : Path *path = (Path *) lfirst(lc);
725 0 : JoinPath *joinpath = NULL;
726 :
727 : /* Skip parameterized paths. */
728 0 : if (path->param_info != NULL)
729 0 : continue;
730 :
731 0 : switch (path->pathtype)
732 : {
733 : case T_HashJoin:
734 : {
735 0 : HashPath *hash_path = makeNode(HashPath);
736 :
737 0 : memcpy(hash_path, path, sizeof(HashPath));
738 0 : joinpath = (JoinPath *) hash_path;
739 : }
740 0 : break;
741 :
742 : case T_NestLoop:
743 : {
744 0 : NestPath *nest_path = makeNode(NestPath);
745 :
746 0 : memcpy(nest_path, path, sizeof(NestPath));
747 0 : joinpath = (JoinPath *) nest_path;
748 : }
749 0 : break;
750 :
751 : case T_MergeJoin:
752 : {
753 0 : MergePath *merge_path = makeNode(MergePath);
754 :
755 0 : memcpy(merge_path, path, sizeof(MergePath));
756 0 : joinpath = (JoinPath *) merge_path;
757 : }
758 0 : 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 0 : break;
768 : }
769 :
770 : /* This path isn't good for us, check next. */
771 0 : if (!joinpath)
772 0 : 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 0 : if (IsA(joinpath->outerjoinpath, ForeignPath))
781 : {
782 : ForeignPath *foreign_path;
783 :
784 0 : foreign_path = (ForeignPath *) joinpath->outerjoinpath;
785 0 : if (IS_JOIN_REL(foreign_path->path.parent))
786 0 : joinpath->outerjoinpath = foreign_path->fdw_outerpath;
787 : }
788 :
789 0 : if (IsA(joinpath->innerjoinpath, ForeignPath))
790 : {
791 : ForeignPath *foreign_path;
792 :
793 0 : foreign_path = (ForeignPath *) joinpath->innerjoinpath;
794 0 : if (IS_JOIN_REL(foreign_path->path.parent))
795 0 : joinpath->innerjoinpath = foreign_path->fdw_outerpath;
796 : }
797 :
798 0 : return (Path *) joinpath;
799 : }
800 0 : return NULL;
801 : }
|