aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_clause.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/parse_clause.c')
-rw-r--r--src/backend/parser/parse_clause.c192
1 files changed, 156 insertions, 36 deletions
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 7a1261d0fd2..8b4c0ae0d3b 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -24,6 +24,7 @@
#include "optimizer/tlist.h"
#include "parser/analyze.h"
#include "parser/parsetree.h"
+#include "parser/parser.h"
#include "parser/parse_clause.h"
#include "parser/parse_coerce.h"
#include "parser/parse_collate.h"
@@ -515,24 +516,18 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r)
static RangeTblEntry *
transformRangeFunction(ParseState *pstate, RangeFunction *r)
{
- Node *funcexpr;
- char *funcname;
+ List *funcexprs = NIL;
+ List *funcnames = NIL;
+ List *coldeflists = NIL;
bool is_lateral;
RangeTblEntry *rte;
-
- /*
- * Get function name for possible use as alias. We use the same
- * transformation rules as for a SELECT output expression. For a FuncCall
- * node, the result will be the function name, but it is possible for the
- * grammar to hand back other node types.
- */
- funcname = FigureColname(r->funccallnode);
+ ListCell *lc;
/*
* We make lateral_only names of this level visible, whether or not the
- * function is explicitly marked LATERAL. This is needed for SQL spec
- * compliance in the case of UNNEST(), and seems useful on convenience
- * grounds for all functions in FROM.
+ * RangeFunction is explicitly marked LATERAL. This is needed for SQL
+ * spec compliance in the case of UNNEST(), and seems useful on
+ * convenience grounds for all functions in FROM.
*
* (LATERAL can't nest within a single pstate level, so we don't need
* save/restore logic here.)
@@ -541,46 +536,171 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
pstate->p_lateral_active = true;
/*
- * Transform the raw expression.
+ * Transform the raw expressions.
+ *
+ * While transforming, also save function names for possible use as alias
+ * and column names. We use the same transformation rules as for a SELECT
+ * output expression. For a FuncCall node, the result will be the
+ * function name, but it is possible for the grammar to hand back other
+ * node types.
+ *
+ * We have to get this info now, because FigureColname only works on raw
+ * parsetrees. Actually deciding what to do with the names is left up to
+ * addRangeTableEntryForFunction.
+ *
+ * Likewise, collect column definition lists if there were any. But
+ * complain if we find one here and the RangeFunction has one too.
*/
- funcexpr = transformExpr(pstate, r->funccallnode, EXPR_KIND_FROM_FUNCTION);
+ foreach(lc, r->functions)
+ {
+ List *pair = (List *) lfirst(lc);
+ Node *fexpr;
+ List *coldeflist;
+
+ /* Disassemble the function-call/column-def-list pairs */
+ Assert(list_length(pair) == 2);
+ fexpr = (Node *) linitial(pair);
+ coldeflist = (List *) lsecond(pair);
+
+ /*
+ * If we find a function call unnest() with more than one argument and
+ * no special decoration, transform it into separate unnest() calls on
+ * each argument. This is a kluge, for sure, but it's less nasty than
+ * other ways of implementing the SQL-standard UNNEST() syntax.
+ *
+ * If there is any decoration (including a coldeflist), we don't
+ * transform, which probably means a no-such-function error later. We
+ * could alternatively throw an error right now, but that doesn't seem
+ * tremendously helpful. If someone is using any such decoration,
+ * then they're not using the SQL-standard syntax, and they're more
+ * likely expecting an un-tweaked function call.
+ *
+ * Note: the transformation changes a non-schema-qualified unnest()
+ * function name into schema-qualified pg_catalog.unnest(). This
+ * choice is also a bit debatable, but it seems reasonable to force
+ * use of built-in unnest() when we make this transformation.
+ */
+ if (IsA(fexpr, FuncCall))
+ {
+ FuncCall *fc = (FuncCall *) fexpr;
+
+ if (list_length(fc->funcname) == 1 &&
+ strcmp(strVal(linitial(fc->funcname)), "unnest") == 0 &&
+ list_length(fc->args) > 1 &&
+ fc->agg_order == NIL &&
+ fc->agg_filter == NULL &&
+ !fc->agg_star &&
+ !fc->agg_distinct &&
+ !fc->func_variadic &&
+ fc->over == NULL &&
+ coldeflist == NIL)
+ {
+ ListCell *lc;
+
+ foreach(lc, fc->args)
+ {
+ Node *arg = (Node *) lfirst(lc);
+ FuncCall *newfc;
+
+ newfc = makeFuncCall(SystemFuncName("unnest"),
+ list_make1(arg),
+ fc->location);
+
+ funcexprs = lappend(funcexprs,
+ transformExpr(pstate, (Node *) newfc,
+ EXPR_KIND_FROM_FUNCTION));
+
+ funcnames = lappend(funcnames,
+ FigureColname((Node *) newfc));
+
+ /* coldeflist is empty, so no error is possible */
+
+ coldeflists = lappend(coldeflists, coldeflist);
+ }
+ continue; /* done with this function item */
+ }
+ }
+
+ /* normal case ... */
+ funcexprs = lappend(funcexprs,
+ transformExpr(pstate, fexpr,
+ EXPR_KIND_FROM_FUNCTION));
+
+ funcnames = lappend(funcnames,
+ FigureColname(fexpr));
+
+ if (coldeflist && r->coldeflist)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("multiple column definition lists are not allowed for the same function"),
+ parser_errposition(pstate,
+ exprLocation((Node *) r->coldeflist))));
+
+ coldeflists = lappend(coldeflists, coldeflist);
+ }
pstate->p_lateral_active = false;
/*
- * We must assign collations now so that we can fill funccolcollations.
+ * We must assign collations now so that the RTE exposes correct collation
+ * info for Vars created from it.
*/
- assign_expr_collations(pstate, funcexpr);
+ assign_list_collations(pstate, funcexprs);
+
+ /*
+ * Install the top-level coldeflist if there was one (we already checked
+ * that there was no conflicting per-function coldeflist).
+ *
+ * We only allow this when there's a single function (even after UNNEST
+ * expansion) and no WITH ORDINALITY. The reason for the latter
+ * restriction is that it's not real clear whether the ordinality column
+ * should be in the coldeflist, and users are too likely to make mistakes
+ * in one direction or the other. Putting the coldeflist inside TABLE()
+ * is much clearer in this case.
+ */
+ if (r->coldeflist)
+ {
+ if (list_length(funcexprs) != 1)
+ {
+ if (r->is_table)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("TABLE() with multiple functions cannot have a column definition list"),
+ errhint("Put a separate column definition list for each function inside TABLE()."),
+ parser_errposition(pstate,
+ exprLocation((Node *) r->coldeflist))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("UNNEST() with multiple arguments cannot have a column definition list"),
+ errhint("Use separate UNNEST() calls inside TABLE(), and attach a column definition list to each one."),
+ parser_errposition(pstate,
+ exprLocation((Node *) r->coldeflist))));
+ }
+ if (r->ordinality)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("WITH ORDINALITY cannot be used with a column definition list"),
+ errhint("Put the column definition list inside TABLE()."),
+ parser_errposition(pstate,
+ exprLocation((Node *) r->coldeflist))));
+
+ coldeflists = list_make1(r->coldeflist);
+ }
/*
* Mark the RTE as LATERAL if the user said LATERAL explicitly, or if
* there are any lateral cross-references in it.
*/
- is_lateral = r->lateral || contain_vars_of_level(funcexpr, 0);
+ is_lateral = r->lateral || contain_vars_of_level((Node *) funcexprs, 0);
/*
* OK, build an RTE for the function.
*/
- rte = addRangeTableEntryForFunction(pstate, funcname, funcexpr,
+ rte = addRangeTableEntryForFunction(pstate,
+ funcnames, funcexprs, coldeflists,
r, is_lateral, true);
- /*
- * If a coldeflist was supplied, ensure it defines a legal set of names
- * (no duplicates) and datatypes (no pseudo-types, for instance).
- * addRangeTableEntryForFunction looked up the type names but didn't check
- * them further than that.
- */
- if (r->coldeflist)
- {
- TupleDesc tupdesc;
-
- tupdesc = BuildDescFromLists(rte->eref->colnames,
- rte->funccoltypes,
- rte->funccoltypmods,
- rte->funccolcollations);
- CheckAttributeNamesTypes(tupdesc, RELKIND_COMPOSITE_TYPE, false);
- }
-
return rte;
}