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