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