diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c index 1cf3b3c..4a7ffa6 100644 *** a/contrib/file_fdw/file_fdw.c --- b/contrib/file_fdw/file_fdw.c *************** *** 26,32 **** #include "nodes/makefuncs.h" #include "optimizer/cost.h" #include "utils/rel.h" ! #include "utils/syscache.h" PG_MODULE_MAGIC; --- 26,32 ---- #include "nodes/makefuncs.h" #include "optimizer/cost.h" #include "utils/rel.h" ! #include "utils/lsyscache.h" PG_MODULE_MAGIC; *************** get_file_fdw_attribute_options(Oid relid *** 345,398 **** /* Retrieve FDW options for all user-defined attributes. */ for (attnum = 1; attnum <= natts; attnum++) { ! HeapTuple tuple; ! Form_pg_attribute attr; ! Datum datum; ! bool isnull; /* Skip dropped attributes. */ if (tupleDesc->attrs[attnum - 1]->attisdropped) continue; ! /* ! * We need the whole pg_attribute tuple not just what is in the ! * tupleDesc, so must do a catalog lookup. ! */ ! tuple = SearchSysCache2(ATTNUM, ! RelationGetRelid(rel), ! Int16GetDatum(attnum)); ! if (!HeapTupleIsValid(tuple)) ! elog(ERROR, "cache lookup failed for attribute %d of relation %u", ! attnum, RelationGetRelid(rel)); ! attr = (Form_pg_attribute) GETSTRUCT(tuple); ! ! datum = SysCacheGetAttr(ATTNUM, ! tuple, ! Anum_pg_attribute_attfdwoptions, ! &isnull); ! if (!isnull) { ! List *options = untransformRelOptions(datum); ! ListCell *lc; ! foreach(lc, options) { ! DefElem *def = (DefElem *) lfirst(lc); ! ! if (strcmp(def->defname, "force_not_null") == 0) { ! if (defGetBoolean(def)) ! { ! char *attname = pstrdup(NameStr(attr->attname)); ! fnncolumns = lappend(fnncolumns, makeString(attname)); ! } } - /* maybe in future handle other options here */ } } - - ReleaseSysCache(tuple); } heap_close(rel, AccessShareLock); --- 345,373 ---- /* Retrieve FDW options for all user-defined attributes. */ for (attnum = 1; attnum <= natts; attnum++) { ! List *options; ! ListCell *lc; /* Skip dropped attributes. */ if (tupleDesc->attrs[attnum - 1]->attisdropped) continue; ! options = GetForeignColumnOptions(relid, attnum); ! foreach(lc, options) { ! DefElem *def = (DefElem *) lfirst(lc); ! if (strcmp(def->defname, "force_not_null") == 0) { ! if (defGetBoolean(def)) { ! char *attname = pstrdup(get_attname(relid, attnum)); ! fnncolumns = lappend(fnncolumns, makeString(attname)); } } + /* maybe in future handle other options here */ } } heap_close(rel, AccessShareLock); diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml index 76ff243..d809cac 100644 *** a/doc/src/sgml/fdwhandler.sgml --- b/doc/src/sgml/fdwhandler.sgml *************** EndForeignScan (ForeignScanState *node); *** 235,238 **** --- 235,392 ---- + + Foreign Data Wrapper Helper Functions + + + Several helper functions are exported from core so that authors of FDW + can get easy access to attributes of FDW-related objects such as FDW + options. + + + + + ForeignDataWrapper * + GetForeignDataWrapper(Oid fdwid); + + + This function returns a ForeignDataWrapper object + for a foreign-data wrapper with given oid. A + ForeignDataWrapper object contains oid of the + wrapper itself, oid of the owner of the wrapper, name of the wrapper, oid + of fdwhandler function, oid of fdwvalidator function, and FDW options in + the form of list of DefElem. + + + + + ForeignServer * + GetForeignServer(Oid serverid); + + + This function returns a ForeignServer object for + a foreign server with given oid. A ForeignServer + object contains oid of the server, oid of the wrapper for the server, oid + of the owner of the server, name of the server, type of the server, + version of the server, and FDW options in the form of list of + DefElem. + + + + + UserMapping * + GetUserMapping(Oid userid, Oid serverid); + + + This function returns a UserMapping object for a + user mapping with given oid pair. A UserMapping + object contains oid of the user, oid of the server, and FDW options in the + form of list of DefElem. + + + + + ForeignTable * + GetForeignTable(Oid relid); + + + This function returns a ForeignTable object for a + foreign table with given oid. A ForeignTable + object contains oid of the foreign table, oid of the server for the table, + and FDW options in the form of list of DefElem. + + + + + List * + GetForeignTableColumnOptions(Oid relid, AttrNumber attnum); + + + This function returns per-column FDW options for a column with given + relation oid and attribute number in the form of list of + DefElem. + + + + Some object types have name-based functions. + + + + + ForeignDataWrapper * + GetForeignDataWrapperByName(const char *name, bool missing_ok); + + + This function returns a ForeignDataWrapper object + for a foreign-data wrapper with given name. If the wrapper is not found, + return NULL if missing_ok was true, otherwise raise an error. + + + + + ForeignServer * + GetForeignServerByName(const char *name, bool missing_ok); + + + This function returns a ForeignServer object for + a foreign server with given name. If the server is not found, return NULL + if missing_ok was true, otherwise raise an error. + + + + + char * + GetFdwOptionValue(Oid fdwid, Oid serverid, Oid relid, AttrNumber attnum, const char *optname); + + + This function returns a copied string (created in current memory context) + of the value of a FDW option with given name which is set on a object with + given oid and attribute number. This function ignores catalogs if invalid + identifir is given for it. + + + + + If attnum is InvalidAttrNumber or relid is + Invalidoid, pg_attribute is + ignored. + + + + + If relid is InvalidOid, + pg_foreign_table is ignored. + + + + + If both serverid and relid are InvalidOid, + pg_foreign_server is ignored. + + + + + If all of fdwid, serverid and relid are InvalidOid, + pg_foreign_data_wrapper is ignored. + + + + + + + If the option with given name is set in multiple object level, the one in + the finest-grained object is used; e.g. priority is given to user mappings + over than foreign servers. + This function would be useful when you know which option is needed but you + don't know where it is set. If you already know the source object, it + would be more efficient to use object retrieval functions. + + + + To use any of these functions, you need to include + foreign/foreign.h in your source file. + + + + diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c index a7d30a1..b984a5e 100644 *** a/src/backend/foreign/foreign.c --- b/src/backend/foreign/foreign.c *************** GetForeignTable(Oid relid) *** 248,253 **** --- 248,378 ---- /* + * If an option entry which matches the given name was found in the given + * list, returns a copy of arg string, otherwise returns NULL. + */ + static char * + get_options_value(List *options, const char *optname) + { + ListCell *lc; + + /* Find target option from the list. */ + foreach (lc, options) + { + DefElem *def = lfirst(lc); + + if (strcmp(def->defname, optname) == 0) + return pstrdup(strVal(def->arg)); + } + + return NULL; + } + + /* + * Returns a copy of the value of specified option with searching the option + * from appropriate catalog. If an option was stored in multiple object + * levels, one in the finest-grained object level is used; lookup order is: + * 1) pg_attribute (only when both attnum and relid are valid) + * 2) pg_foreign_table (only when relid is valid) + * 3) pg_user_mapping (only when serverid or relid is valid) + * 4) pg_foreign_server (only when serverid or relid is valid) + * 5) pg_foreign_data_wrapper (only when fdwid or serverid or relid is valid) + * This priority rule would be useful in most cases using FDW options. + * + * If attnum was InvalidAttrNumber, we don't retrieve FDW optiosn from + * pg_attribute.attfdwoptions. + */ + char * + GetFdwOptionValue(Oid fdwid, Oid serverid, Oid relid, AttrNumber attnum, + const char *optname) + { + ForeignTable *table = NULL; + UserMapping *user = NULL; + ForeignServer *server = NULL; + ForeignDataWrapper *wrapper = NULL; + char *value; + + /* Do we need to search pg_attribute? */ + if (attnum != InvalidAttrNumber && relid != InvalidOid) + { + value = get_options_value(GetForeignColumnOptions(relid, attnum), + optname); + if (value != NULL) + return value; + } + + /* Do we need to search pg_foreign_table? */ + if (relid != InvalidOid) + { + table = GetForeignTable(relid); + value = get_options_value(table->options, optname); + if (value != NULL) + return value; + + serverid = table->serverid; + } + + /* Do we need to search pg_user_mapping and pg_foreign_server? */ + if (serverid != InvalidOid) + { + user = GetUserMapping(GetOuterUserId(), serverid); + value = get_options_value(user->options, optname); + if (value != NULL) + return value; + + server = GetForeignServer(serverid); + value = get_options_value(server->options, optname); + if (value != NULL) + return value; + + fdwid = server->fdwid; + } + + /* Do we need to search pg_foreign_data_wrapper? */ + if (fdwid != InvalidOid) + { + wrapper = GetForeignDataWrapper(fdwid); + value = get_options_value(wrapper->options, optname); + if (value != NULL) + return value; + } + + return NULL; + } + + + /* + * GetForeignColumnOptions - Get attfdwoptions of given relation/attnum as + * list of DefElem. + */ + List * + GetForeignColumnOptions(Oid relid, AttrNumber attnum) + { + List *options; + HeapTuple tp; + Datum datum; + bool isnull; + + tp = SearchSysCache2(ATTNUM, + ObjectIdGetDatum(relid), + Int16GetDatum(attnum)); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "cache lookup failed for attribute %d of relation %u", attnum, relid); + datum = SysCacheGetAttr(ATTNUM, + tp, + Anum_pg_attribute_attfdwoptions, + &isnull); + if (isnull) + options = NIL; + else + options = untransformRelOptions(datum); + + ReleaseSysCache(tp); + + return options; + } + + /* * GetFdwRoutine - call the specified foreign-data wrapper handler routine * to get its FdwRoutine struct. */ diff --git a/src/include/foreign/foreign.h b/src/include/foreign/foreign.h index 2c436ae..6498162 100644 *** a/src/include/foreign/foreign.h --- b/src/include/foreign/foreign.h *************** extern ForeignDataWrapper *GetForeignDat *** 75,80 **** --- 75,83 ---- extern ForeignDataWrapper *GetForeignDataWrapperByName(const char *name, bool missing_ok); extern ForeignTable *GetForeignTable(Oid relid); + extern char *GetFdwOptionValue(Oid fdwid, Oid serverid, Oid relid, + AttrNumber attnum, const char *optname); + extern List *GetForeignColumnOptions(Oid relid, AttrNumber attnum); extern Oid get_foreign_data_wrapper_oid(const char *fdwname, bool missing_ok); extern Oid get_foreign_server_oid(const char *servername, bool missing_ok);