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