From b54f7e16535582a00addb158832d4097a61a7521 Mon Sep 17 00:00:00 2001 From: Shigeru Hanada Date: Tue, 14 Jun 2011 16:23:21 +0900 Subject: [PATCH] Add force_not_null option support to file_fdw. --- contrib/file_fdw/file_fdw.c | 119 ++++++++++++++++++++++++++++++++++-- doc/src/sgml/file-fdw.sgml | 32 ++++++++-- 2 files changed, 140 insertions(+), 11 deletions(-) diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c index 466c015107..544d1e65cb 100644 --- a/contrib/file_fdw/file_fdw.c +++ b/contrib/file_fdw/file_fdw.c @@ -23,7 +23,9 @@ #include "foreign/fdwapi.h" #include "foreign/foreign.h" #include "miscadmin.h" +#include "nodes/makefuncs.h" #include "optimizer/cost.h" +#include "utils/syscache.h" PG_MODULE_MAGIC; @@ -56,16 +58,12 @@ static struct FileFdwOption valid_options[] = { {"escape", ForeignTableRelationId}, {"null", ForeignTableRelationId}, {"encoding", ForeignTableRelationId}, + {"force_not_null", AttributeRelationId}, /* specified as boolean value */ /* * force_quote is not supported by file_fdw because it's for COPY TO. */ - /* - * force_not_null is not supported by file_fdw. It would need a parser - * for list of columns, not to mention a way to check the column list - * against the table. - */ /* Sentinel */ {NULL, InvalidOid} @@ -111,6 +109,7 @@ static void fileGetOptions(Oid foreigntableid, static void estimate_costs(PlannerInfo *root, RelOptInfo *baserel, const char *filename, Cost *startup_cost, Cost *total_cost); +static List * get_force_not_null(Oid relid); /* @@ -144,6 +143,7 @@ file_fdw_validator(PG_FUNCTION_ARGS) List *options_list = untransformRelOptions(PG_GETARG_DATUM(0)); Oid catalog = PG_GETARG_OID(1); char *filename = NULL; + char *force_not_null = NULL; List *other_options = NIL; ListCell *cell; @@ -197,7 +197,10 @@ file_fdw_validator(PG_FUNCTION_ARGS) buf.data))); } - /* Separate out filename, since ProcessCopyOptions won't allow it */ + /* + * Separate out filename and force_not_null, since ProcessCopyOptions + * won't allow it. + */ if (strcmp(def->defname, "filename") == 0) { if (filename) @@ -206,6 +209,20 @@ file_fdw_validator(PG_FUNCTION_ARGS) errmsg("conflicting or redundant options"))); filename = defGetString(def); } + else if (strcmp(def->defname, "force_not_null") == 0) + { + if (force_not_null) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + + force_not_null = defGetString(def); + if (strcmp(force_not_null, "true") != 0 && + strcmp(force_not_null, "false") != 0) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("force_not_null must be true or false"))); + } else other_options = lappend(other_options, def); } @@ -235,11 +252,96 @@ is_valid_option(const char *option, Oid context) return false; } +/* + * Retrieve per-column generic options from pg_attribute and construct a list + * of column names for "force_not_null". + */ +static List * +get_force_not_null(Oid relid) +{ + Relation rel; + TupleDesc tupleDesc; + AttrNumber natts; + AttrNumber attnum; + List *columns = NIL; + + rel = heap_open(relid, AccessShareLock); + tupleDesc = RelationGetDescr(rel); + natts = tupleDesc->natts; + + /* Retrieve FDW options from every user-defined attributes. */ + for (attnum = 1; attnum < natts; attnum++) + { + HeapTuple tuple; + Form_pg_attribute attr; + Datum datum; + bool isnull; + List *options; + ListCell *cell; + + + tuple = SearchSysCache2(ATTNUM, + RelationGetRelid(rel), + Int16GetDatum(attnum)); + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("cache lookup failed for attribute %d of relation %u", + attnum, RelationGetRelid(rel)))); + attr = (Form_pg_attribute) GETSTRUCT(tuple); + + /* Skip dropped attributes. */ + if (attr->attisdropped) + { + ReleaseSysCache(tuple); + continue; + } + + datum = SysCacheGetAttr(ATTNUM, + tuple, + Anum_pg_attribute_attfdwoptions, + &isnull); + if (isnull) + datum = PointerGetDatum(NULL); + options = untransformRelOptions(datum); + + /* + * Find force_not_null option and append attname to the list if + * the value was true. + */ + foreach (cell, options) + { + DefElem *def = (DefElem *) lfirst(cell); + const char *value = defGetString(def); + + if (strcmp(def->defname, "force_not_null") == 0 && + strcmp(value, "true") == 0) + { + columns = lappend(columns, makeString(NameStr(attr->attname))); + elog(DEBUG1, "%s: force_not_null", NameStr(attr->attname)); + } + + } + + ReleaseSysCache(tuple); + } + + heap_close(rel, AccessShareLock); + + /* Return DefElemn only when any column is set "force_not_null=true". */ + if (columns != NIL) + return list_make1(makeDefElem("force_not_null", (Node *) columns)); + else + return NIL; +} + /* * Fetch the options for a file_fdw foreign table. * * We have to separate out "filename" from the other options because * it must not appear in the options list passed to the core COPY code. + * And we must construct List of DefElem from pg_attribute.attfdwoptions for + * "force_not_null". */ static void fileGetOptions(Oid foreigntableid, @@ -286,6 +388,11 @@ fileGetOptions(Oid foreigntableid, } prev = lc; } + + /* Retrieve force_not_null from pg_attribute and append it to the list. */ + options = list_concat(options, get_force_not_null(foreigntableid)); + + /* The filename is required optiono. */ if (*filename == NULL) ereport(ERROR, (errcode(ERRCODE_FDW_UNABLE_TO_CREATE_REPLY), diff --git a/doc/src/sgml/file-fdw.sgml b/doc/src/sgml/file-fdw.sgml index 8497d9a45f..107476fa4c 100644 --- a/doc/src/sgml/file-fdw.sgml +++ b/doc/src/sgml/file-fdw.sgml @@ -111,14 +111,36 @@ - COPY's OIDS, FORCE_QUOTE, - and FORCE_NOT_NULL options are currently not supported by + A column of a foreign table created using this wrapper can have the + following options: + + + + + + force_not_null + + + + Specifies that the column shouldn't been matched against the null string. + Acceptable values are true and falset + (case sensitive). + the same as COPY's FORCE_NOT_NULL option. + + + + + + + + COPY's OIDS and + FORCE_QUOTE options are currently not supported by file_fdw. - These options can only be specified for a foreign table, not in the - options of the file_fdw foreign-data wrapper, nor in the + These options can only be specified for a foreign table or its columns, not + in the options of the file_fdw foreign-data wrapper, nor in the options of a server or user mapping using the wrapper. @@ -126,7 +148,7 @@ Changing table-level options requires superuser privileges, for security reasons: only a superuser should be able to determine which file is read. In principle non-superusers could be allowed to change the other options, - but that's not supported at present. + but that's only force_not_null supported at present. -- 2.39.5