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