From 784e762e886e6f72f548da86a27cd2ead87dbd1c Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 21 Nov 2013 19:37:02 -0500 Subject: Support multi-argument UNNEST(), and TABLE() syntax for multiple functions. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds the ability to write TABLE( function1(), function2(), ...) as a single FROM-clause entry. The result is the concatenation of the first row from each function, followed by the second row from each function, etc; with NULLs inserted if any function produces fewer rows than others. This is believed to be a much more useful behavior than what Postgres currently does with multiple SRFs in a SELECT list. This syntax also provides a reasonable way to combine use of column definition lists with WITH ORDINALITY: put the column definition list inside TABLE(), where it's clear that it doesn't control the ordinality column as well. Also implement SQL-compliant multiple-argument UNNEST(), by turning UNNEST(a,b,c) into TABLE(unnest(a), unnest(b), unnest(c)). The SQL standard specifies TABLE() with only a single function, not multiple functions, and it seems to require an implicit UNNEST() which is not what this patch does. There may be something wrong with that reading of the spec, though, because if it's right then the spec's TABLE() is just a pointless alternative spelling of UNNEST(). After further review of that, we might choose to adopt a different syntax for what this patch does, but in any case this functionality seems clearly worthwhile. Andrew Gierth, reviewed by Zoltán Böszörményi and Heikki Linnakangas, and significantly revised by me --- src/backend/access/common/tupdesc.c | 75 ++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 34 deletions(-) (limited to 'src/backend/access/common/tupdesc.c') diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c index bf47640092a..d766ae72873 100644 --- a/src/backend/access/common/tupdesc.c +++ b/src/backend/access/common/tupdesc.c @@ -157,40 +157,6 @@ CreateTupleDescCopy(TupleDesc tupdesc) return desc; } -/* - * CreateTupleDescCopyExtend - * This function creates a new TupleDesc by copying from an existing - * TupleDesc, but adding space for more columns. The new tupdesc is - * not regarded as the same record type as the old one (and therefore - * does not inherit its typeid/typmod, which instead are left as an - * anonymous record type). - * - * The additional column slots are not initialized in any way; - * callers must do their own TupleDescInitEntry on each. - * - * !!! Constraints and defaults are not copied !!! - */ -TupleDesc -CreateTupleDescCopyExtend(TupleDesc tupdesc, int moreatts) -{ - TupleDesc desc; - int i; - int src_natts = tupdesc->natts; - - Assert(moreatts >= 0); - - desc = CreateTemplateTupleDesc(src_natts + moreatts, tupdesc->tdhasoid); - - for (i = 0; i < src_natts; i++) - { - memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_FIXED_PART_SIZE); - desc->attrs[i]->attnotnull = false; - desc->attrs[i]->atthasdef = false; - } - - return desc; -} - /* * CreateTupleDescCopyConstr * This function creates a new TupleDesc by copying from an existing @@ -250,6 +216,47 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc) return desc; } +/* + * TupleDescCopyEntry + * This function copies a single attribute structure from one tuple + * descriptor to another. + * + * !!! Constraints and defaults are not copied !!! + */ +void +TupleDescCopyEntry(TupleDesc dst, AttrNumber dstAttno, + TupleDesc src, AttrNumber srcAttno) +{ + /* + * sanity checks + */ + AssertArg(PointerIsValid(src)); + AssertArg(PointerIsValid(dst)); + AssertArg(srcAttno >= 1); + AssertArg(srcAttno <= src->natts); + AssertArg(dstAttno >= 1); + AssertArg(dstAttno <= dst->natts); + + memcpy(dst->attrs[dstAttno - 1], src->attrs[srcAttno - 1], + ATTRIBUTE_FIXED_PART_SIZE); + + /* + * Aside from updating the attno, we'd better reset attcacheoff. + * + * XXX Actually, to be entirely safe we'd need to reset the attcacheoff of + * all following columns in dst as well. Current usage scenarios don't + * require that though, because all following columns will get initialized + * by other uses of this function or TupleDescInitEntry. So we cheat a + * bit to avoid a useless O(N^2) penalty. + */ + dst->attrs[dstAttno - 1]->attnum = dstAttno; + dst->attrs[dstAttno - 1]->attcacheoff = -1; + + /* since we're not copying constraints or defaults, clear these */ + dst->attrs[dstAttno - 1]->attnotnull = false; + dst->attrs[dstAttno - 1]->atthasdef = false; +} + /* * Free a TupleDesc including all substructure */ -- cgit v1.2.3