00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 #include "postgres.h"
00014
00015 #include "access/htup_details.h"
00016 #include "access/reloptions.h"
00017 #include "catalog/pg_foreign_data_wrapper.h"
00018 #include "catalog/pg_foreign_server.h"
00019 #include "catalog/pg_foreign_table.h"
00020 #include "catalog/pg_user_mapping.h"
00021 #include "foreign/fdwapi.h"
00022 #include "foreign/foreign.h"
00023 #include "lib/stringinfo.h"
00024 #include "miscadmin.h"
00025 #include "utils/builtins.h"
00026 #include "utils/memutils.h"
00027 #include "utils/rel.h"
00028 #include "utils/syscache.h"
00029
00030
00031 extern Datum pg_options_to_table(PG_FUNCTION_ARGS);
00032 extern Datum postgresql_fdw_validator(PG_FUNCTION_ARGS);
00033
00034
00035
00036
00037
00038 ForeignDataWrapper *
00039 GetForeignDataWrapper(Oid fdwid)
00040 {
00041 Form_pg_foreign_data_wrapper fdwform;
00042 ForeignDataWrapper *fdw;
00043 Datum datum;
00044 HeapTuple tp;
00045 bool isnull;
00046
00047 tp = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid));
00048
00049 if (!HeapTupleIsValid(tp))
00050 elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwid);
00051
00052 fdwform = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
00053
00054 fdw = (ForeignDataWrapper *) palloc(sizeof(ForeignDataWrapper));
00055 fdw->fdwid = fdwid;
00056 fdw->owner = fdwform->fdwowner;
00057 fdw->fdwname = pstrdup(NameStr(fdwform->fdwname));
00058 fdw->fdwhandler = fdwform->fdwhandler;
00059 fdw->fdwvalidator = fdwform->fdwvalidator;
00060
00061
00062 datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID,
00063 tp,
00064 Anum_pg_foreign_data_wrapper_fdwoptions,
00065 &isnull);
00066 if (isnull)
00067 fdw->options = NIL;
00068 else
00069 fdw->options = untransformRelOptions(datum);
00070
00071 ReleaseSysCache(tp);
00072
00073 return fdw;
00074 }
00075
00076
00077
00078
00079
00080
00081 ForeignDataWrapper *
00082 GetForeignDataWrapperByName(const char *fdwname, bool missing_ok)
00083 {
00084 Oid fdwId = get_foreign_data_wrapper_oid(fdwname, missing_ok);
00085
00086 if (!OidIsValid(fdwId))
00087 return NULL;
00088
00089 return GetForeignDataWrapper(fdwId);
00090 }
00091
00092
00093
00094
00095
00096 ForeignServer *
00097 GetForeignServer(Oid serverid)
00098 {
00099 Form_pg_foreign_server serverform;
00100 ForeignServer *server;
00101 HeapTuple tp;
00102 Datum datum;
00103 bool isnull;
00104
00105 tp = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid));
00106
00107 if (!HeapTupleIsValid(tp))
00108 elog(ERROR, "cache lookup failed for foreign server %u", serverid);
00109
00110 serverform = (Form_pg_foreign_server) GETSTRUCT(tp);
00111
00112 server = (ForeignServer *) palloc(sizeof(ForeignServer));
00113 server->serverid = serverid;
00114 server->servername = pstrdup(NameStr(serverform->srvname));
00115 server->owner = serverform->srvowner;
00116 server->fdwid = serverform->srvfdw;
00117
00118
00119 datum = SysCacheGetAttr(FOREIGNSERVEROID,
00120 tp,
00121 Anum_pg_foreign_server_srvtype,
00122 &isnull);
00123 server->servertype = isnull ? NULL : pstrdup(TextDatumGetCString(datum));
00124
00125
00126 datum = SysCacheGetAttr(FOREIGNSERVEROID,
00127 tp,
00128 Anum_pg_foreign_server_srvversion,
00129 &isnull);
00130 server->serverversion = isnull ? NULL : pstrdup(TextDatumGetCString(datum));
00131
00132
00133 datum = SysCacheGetAttr(FOREIGNSERVEROID,
00134 tp,
00135 Anum_pg_foreign_server_srvoptions,
00136 &isnull);
00137 if (isnull)
00138 server->options = NIL;
00139 else
00140 server->options = untransformRelOptions(datum);
00141
00142 ReleaseSysCache(tp);
00143
00144 return server;
00145 }
00146
00147
00148
00149
00150
00151 ForeignServer *
00152 GetForeignServerByName(const char *srvname, bool missing_ok)
00153 {
00154 Oid serverid = get_foreign_server_oid(srvname, missing_ok);
00155
00156 if (!OidIsValid(serverid))
00157 return NULL;
00158
00159 return GetForeignServer(serverid);
00160 }
00161
00162
00163
00164
00165
00166
00167
00168
00169 UserMapping *
00170 GetUserMapping(Oid userid, Oid serverid)
00171 {
00172 Datum datum;
00173 HeapTuple tp;
00174 bool isnull;
00175 UserMapping *um;
00176
00177 tp = SearchSysCache2(USERMAPPINGUSERSERVER,
00178 ObjectIdGetDatum(userid),
00179 ObjectIdGetDatum(serverid));
00180
00181 if (!HeapTupleIsValid(tp))
00182 {
00183
00184 tp = SearchSysCache2(USERMAPPINGUSERSERVER,
00185 ObjectIdGetDatum(InvalidOid),
00186 ObjectIdGetDatum(serverid));
00187 }
00188
00189 if (!HeapTupleIsValid(tp))
00190 ereport(ERROR,
00191 (errcode(ERRCODE_UNDEFINED_OBJECT),
00192 errmsg("user mapping not found for \"%s\"",
00193 MappingUserName(userid))));
00194
00195 um = (UserMapping *) palloc(sizeof(UserMapping));
00196 um->userid = userid;
00197 um->serverid = serverid;
00198
00199
00200 datum = SysCacheGetAttr(USERMAPPINGUSERSERVER,
00201 tp,
00202 Anum_pg_user_mapping_umoptions,
00203 &isnull);
00204 if (isnull)
00205 um->options = NIL;
00206 else
00207 um->options = untransformRelOptions(datum);
00208
00209 ReleaseSysCache(tp);
00210
00211 return um;
00212 }
00213
00214
00215
00216
00217
00218 ForeignTable *
00219 GetForeignTable(Oid relid)
00220 {
00221 Form_pg_foreign_table tableform;
00222 ForeignTable *ft;
00223 HeapTuple tp;
00224 Datum datum;
00225 bool isnull;
00226
00227 tp = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(relid));
00228 if (!HeapTupleIsValid(tp))
00229 elog(ERROR, "cache lookup failed for foreign table %u", relid);
00230 tableform = (Form_pg_foreign_table) GETSTRUCT(tp);
00231
00232 ft = (ForeignTable *) palloc(sizeof(ForeignTable));
00233 ft->relid = relid;
00234 ft->serverid = tableform->ftserver;
00235
00236
00237 datum = SysCacheGetAttr(FOREIGNTABLEREL,
00238 tp,
00239 Anum_pg_foreign_table_ftoptions,
00240 &isnull);
00241 if (isnull)
00242 ft->options = NIL;
00243 else
00244 ft->options = untransformRelOptions(datum);
00245
00246 ReleaseSysCache(tp);
00247
00248 return ft;
00249 }
00250
00251
00252
00253
00254
00255
00256 List *
00257 GetForeignColumnOptions(Oid relid, AttrNumber attnum)
00258 {
00259 List *options;
00260 HeapTuple tp;
00261 Datum datum;
00262 bool isnull;
00263
00264 tp = SearchSysCache2(ATTNUM,
00265 ObjectIdGetDatum(relid),
00266 Int16GetDatum(attnum));
00267 if (!HeapTupleIsValid(tp))
00268 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
00269 attnum, relid);
00270 datum = SysCacheGetAttr(ATTNUM,
00271 tp,
00272 Anum_pg_attribute_attfdwoptions,
00273 &isnull);
00274 if (isnull)
00275 options = NIL;
00276 else
00277 options = untransformRelOptions(datum);
00278
00279 ReleaseSysCache(tp);
00280
00281 return options;
00282 }
00283
00284
00285
00286
00287
00288
00289 FdwRoutine *
00290 GetFdwRoutine(Oid fdwhandler)
00291 {
00292 Datum datum;
00293 FdwRoutine *routine;
00294
00295 datum = OidFunctionCall0(fdwhandler);
00296 routine = (FdwRoutine *) DatumGetPointer(datum);
00297
00298 if (routine == NULL || !IsA(routine, FdwRoutine))
00299 elog(ERROR, "foreign-data wrapper handler function %u did not return an FdwRoutine struct",
00300 fdwhandler);
00301
00302 return routine;
00303 }
00304
00305
00306
00307
00308
00309
00310 FdwRoutine *
00311 GetFdwRoutineByRelId(Oid relid)
00312 {
00313 HeapTuple tp;
00314 Form_pg_foreign_data_wrapper fdwform;
00315 Form_pg_foreign_server serverform;
00316 Form_pg_foreign_table tableform;
00317 Oid serverid;
00318 Oid fdwid;
00319 Oid fdwhandler;
00320
00321
00322 tp = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(relid));
00323 if (!HeapTupleIsValid(tp))
00324 elog(ERROR, "cache lookup failed for foreign table %u", relid);
00325 tableform = (Form_pg_foreign_table) GETSTRUCT(tp);
00326 serverid = tableform->ftserver;
00327 ReleaseSysCache(tp);
00328
00329
00330 tp = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid));
00331 if (!HeapTupleIsValid(tp))
00332 elog(ERROR, "cache lookup failed for foreign server %u", serverid);
00333 serverform = (Form_pg_foreign_server) GETSTRUCT(tp);
00334 fdwid = serverform->srvfdw;
00335 ReleaseSysCache(tp);
00336
00337
00338 tp = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid));
00339 if (!HeapTupleIsValid(tp))
00340 elog(ERROR, "cache lookup failed for foreign-data wrapper %u", fdwid);
00341 fdwform = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
00342 fdwhandler = fdwform->fdwhandler;
00343
00344
00345 if (!OidIsValid(fdwhandler))
00346 ereport(ERROR,
00347 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
00348 errmsg("foreign-data wrapper \"%s\" has no handler",
00349 NameStr(fdwform->fdwname))));
00350
00351 ReleaseSysCache(tp);
00352
00353
00354 return GetFdwRoutine(fdwhandler);
00355 }
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368 FdwRoutine *
00369 GetFdwRoutineForRelation(Relation relation, bool makecopy)
00370 {
00371 FdwRoutine *fdwroutine;
00372 FdwRoutine *cfdwroutine;
00373
00374 if (relation->rd_fdwroutine == NULL)
00375 {
00376
00377 fdwroutine = GetFdwRoutineByRelId(RelationGetRelid(relation));
00378
00379
00380 cfdwroutine = (FdwRoutine *) MemoryContextAlloc(CacheMemoryContext,
00381 sizeof(FdwRoutine));
00382 memcpy(cfdwroutine, fdwroutine, sizeof(FdwRoutine));
00383 relation->rd_fdwroutine = cfdwroutine;
00384
00385
00386 return fdwroutine;
00387 }
00388
00389
00390 if (makecopy)
00391 {
00392 fdwroutine = (FdwRoutine *) palloc(sizeof(FdwRoutine));
00393 memcpy(fdwroutine, relation->rd_fdwroutine, sizeof(FdwRoutine));
00394 return fdwroutine;
00395 }
00396
00397
00398 return relation->rd_fdwroutine;
00399 }
00400
00401
00402
00403
00404
00405
00406 static void
00407 deflist_to_tuplestore(ReturnSetInfo *rsinfo, List *options)
00408 {
00409 ListCell *cell;
00410 TupleDesc tupdesc;
00411 Tuplestorestate *tupstore;
00412 Datum values[2];
00413 bool nulls[2];
00414 MemoryContext per_query_ctx;
00415 MemoryContext oldcontext;
00416
00417
00418 if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
00419 ereport(ERROR,
00420 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00421 errmsg("set-valued function called in context that cannot accept a set")));
00422 if (!(rsinfo->allowedModes & SFRM_Materialize) ||
00423 rsinfo->expectedDesc == NULL)
00424 ereport(ERROR,
00425 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00426 errmsg("materialize mode required, but it is not allowed in this context")));
00427
00428 per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
00429 oldcontext = MemoryContextSwitchTo(per_query_ctx);
00430
00431
00432
00433
00434 tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
00435 tupstore = tuplestore_begin_heap(true, false, work_mem);
00436 rsinfo->returnMode = SFRM_Materialize;
00437 rsinfo->setResult = tupstore;
00438 rsinfo->setDesc = tupdesc;
00439
00440 foreach(cell, options)
00441 {
00442 DefElem *def = lfirst(cell);
00443
00444 values[0] = CStringGetTextDatum(def->defname);
00445 nulls[0] = false;
00446 if (def->arg)
00447 {
00448 values[1] = CStringGetTextDatum(((Value *) (def->arg))->val.str);
00449 nulls[1] = false;
00450 }
00451 else
00452 {
00453 values[1] = (Datum) 0;
00454 nulls[1] = true;
00455 }
00456 tuplestore_putvalues(tupstore, tupdesc, values, nulls);
00457 }
00458
00459
00460 tuplestore_donestoring(tupstore);
00461
00462 MemoryContextSwitchTo(oldcontext);
00463 }
00464
00465
00466
00467
00468
00469
00470 Datum
00471 pg_options_to_table(PG_FUNCTION_ARGS)
00472 {
00473 Datum array = PG_GETARG_DATUM(0);
00474
00475 deflist_to_tuplestore((ReturnSetInfo *) fcinfo->resultinfo,
00476 untransformRelOptions(array));
00477
00478 return (Datum) 0;
00479 }
00480
00481
00482
00483
00484
00485 struct ConnectionOption
00486 {
00487 const char *optname;
00488 Oid optcontext;
00489 };
00490
00491
00492
00493
00494
00495
00496 static struct ConnectionOption libpq_conninfo_options[] = {
00497 {"authtype", ForeignServerRelationId},
00498 {"service", ForeignServerRelationId},
00499 {"user", UserMappingRelationId},
00500 {"password", UserMappingRelationId},
00501 {"connect_timeout", ForeignServerRelationId},
00502 {"dbname", ForeignServerRelationId},
00503 {"host", ForeignServerRelationId},
00504 {"hostaddr", ForeignServerRelationId},
00505 {"port", ForeignServerRelationId},
00506 {"tty", ForeignServerRelationId},
00507 {"options", ForeignServerRelationId},
00508 {"requiressl", ForeignServerRelationId},
00509 {"sslmode", ForeignServerRelationId},
00510 {"gsslib", ForeignServerRelationId},
00511 {NULL, InvalidOid}
00512 };
00513
00514
00515
00516
00517
00518
00519
00520 static bool
00521 is_conninfo_option(const char *option, Oid context)
00522 {
00523 struct ConnectionOption *opt;
00524
00525 for (opt = libpq_conninfo_options; opt->optname; opt++)
00526 if (context == opt->optcontext && strcmp(opt->optname, option) == 0)
00527 return true;
00528 return false;
00529 }
00530
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544 Datum
00545 postgresql_fdw_validator(PG_FUNCTION_ARGS)
00546 {
00547 List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
00548 Oid catalog = PG_GETARG_OID(1);
00549
00550 ListCell *cell;
00551
00552 foreach(cell, options_list)
00553 {
00554 DefElem *def = lfirst(cell);
00555
00556 if (!is_conninfo_option(def->defname, catalog))
00557 {
00558 struct ConnectionOption *opt;
00559 StringInfoData buf;
00560
00561
00562
00563
00564
00565 initStringInfo(&buf);
00566 for (opt = libpq_conninfo_options; opt->optname; opt++)
00567 if (catalog == opt->optcontext)
00568 appendStringInfo(&buf, "%s%s", (buf.len > 0) ? ", " : "",
00569 opt->optname);
00570
00571 ereport(ERROR,
00572 (errcode(ERRCODE_SYNTAX_ERROR),
00573 errmsg("invalid option \"%s\"", def->defname),
00574 errhint("Valid options in this context are: %s",
00575 buf.data)));
00576
00577 PG_RETURN_BOOL(false);
00578 }
00579 }
00580
00581 PG_RETURN_BOOL(true);
00582 }
00583
00584
00585
00586
00587
00588
00589
00590
00591 Oid
00592 get_foreign_data_wrapper_oid(const char *fdwname, bool missing_ok)
00593 {
00594 Oid oid;
00595
00596 oid = GetSysCacheOid1(FOREIGNDATAWRAPPERNAME, CStringGetDatum(fdwname));
00597 if (!OidIsValid(oid) && !missing_ok)
00598 ereport(ERROR,
00599 (errcode(ERRCODE_UNDEFINED_OBJECT),
00600 errmsg("foreign-data wrapper \"%s\" does not exist",
00601 fdwname)));
00602 return oid;
00603 }
00604
00605
00606
00607
00608
00609
00610
00611
00612 Oid
00613 get_foreign_server_oid(const char *servername, bool missing_ok)
00614 {
00615 Oid oid;
00616
00617 oid = GetSysCacheOid1(FOREIGNSERVERNAME, CStringGetDatum(servername));
00618 if (!OidIsValid(oid) && !missing_ok)
00619 ereport(ERROR,
00620 (errcode(ERRCODE_UNDEFINED_OBJECT),
00621 errmsg("server \"%s\" does not exist", servername)));
00622 return oid;
00623 }