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 db02d13..43ad6ed 100644 *** a/doc/src/sgml/fdwhandler.sgml --- b/doc/src/sgml/fdwhandler.sgml *************** GetForeignTable(Oid relid); *** 297,302 **** --- 297,332 ---- + + char * + GetFdwOptionValue(Oid relid, AttrNumber attnum, const char *optname); + + + This function returns a copy (created in current memory context) of the + value of the given option for the given object (relation or its column). + If attnum is InvalidAttrNumber, pg_attribute is ignored. + If specified option is set in multiple object level, the one in the + finest-grained object is used; e.g. priority is given to user mapping + over than a foreign server for the mapping or foreign-data wrapper for the + server. + This function would be useful when you know which option is needed but you + don't know which object(s) have it. + If you already know the source object, it would be more efficient to use + object retrieval functions. + + + + + 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. diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c index a7d30a1..df0e373 100644 *** a/src/backend/foreign/foreign.c --- b/src/backend/foreign/foreign.c *************** GetForeignTable(Oid relid) *** 248,253 **** --- 248,361 ---- /* + * 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 attnum != InvalidAttrNumber) + * 2) pg_foreign_table + * 3) pg_user_mapping + * 4) pg_foreign_server + * 5) pg_foreign_data_wrapper + * 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 relid, AttrNumber attnum, const char *optname) + { + ForeignTable *table = NULL; + UserMapping *user = NULL; + ForeignServer *server = NULL; + ForeignDataWrapper *wrapper = NULL; + char *value; + + /* Do we need to use pg_attribute.attfdwoptions too? */ + if (attnum != InvalidAttrNumber) + { + value = get_options_value(GetForeignColumnOptions(relid, attnum), + optname); + if (value != NULL) + return value; + } + + table = GetForeignTable(relid); + value = get_options_value(table->options, optname); + if (value != NULL) + return value; + + user = GetUserMapping(GetOuterUserId(), table->serverid); + value = get_options_value(user->options, optname); + if (value != NULL) + return value; + + server = GetForeignServer(table->serverid); + value = get_options_value(server->options, optname); + if (value != NULL) + return value; + + wrapper = GetForeignDataWrapper(server->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..b6c8d5b 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 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);