diff options
Diffstat (limited to 'src/backend/parser')
23 files changed, 5881 insertions, 2633 deletions
diff --git a/src/backend/parser/Makefile b/src/backend/parser/Makefile index 00e8e88e9c..0395bd5934 100644 --- a/src/backend/parser/Makefile +++ b/src/backend/parser/Makefile @@ -17,8 +17,6 @@ OBJS= analyze.o gram.o keywords.o kwlookup.o parser.o \ parse_expr.o parse_func.o parse_node.o parse_oper.o parse_param.o \ parse_relation.o parse_target.o parse_type.o parse_utilcmd.o scansup.o -FLEXFLAGS = -CF -b -p -p - include $(top_srcdir)/src/backend/common.mk @@ -40,21 +38,12 @@ endif gram.h: gram.c ; -gram.c: gram.y -ifdef BISON - $(BISON) -d $(BISONFLAGS) -o $@ $< -else - @$(missing) bison $< $@ -endif +gram.c: BISONFLAGS += -d +gram.c: BISON_CHECK_CMD = $(PERL) $(srcdir)/check_keywords.pl $< $(top_srcdir)/src/include/parser/kwlist.h -scan.c: scan.l -ifdef FLEX - $(FLEX) $(FLEXFLAGS) -o'$@' $< - @if [ `wc -l <lex.backup` -eq 1 ]; then rm lex.backup; else echo "Scanner requires backup, see lex.backup."; exit 1; fi -else - @$(missing) flex $< $@ -endif +scan.c: FLEXFLAGS = -CF -p -p +scan.c: FLEX_NO_BACKUP=yes # Force these dependencies to be known even without dependency info built: @@ -65,7 +54,3 @@ gram.o keywords.o parser.o: gram.h # are not cleaned here. clean distclean maintainer-clean: rm -f lex.backup - - -maintainer-check: - $(PERL) $(top_srcdir)/src/tools/check_keywords.pl $(top_srcdir) diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index c7befc1ba4..2dbf7f6619 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -19,7 +19,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Portions Copyright (c) 2012-2014, TransLattice, Inc. - * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * src/backend/parser/analyze.c @@ -43,6 +43,7 @@ #include "utils/tqual.h" #endif #include "catalog/pg_type.h" +#include "miscadmin.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "optimizer/var.h" @@ -112,6 +113,7 @@ static void transformLockingClause(ParseState *pstate, Query *qry, #ifdef XCP static void ParseAnalyze_rtable_walk(List *rtable); +static void ParseAnalyze_substitute_func(FuncExpr *funcexpr); #endif /* @@ -121,7 +123,7 @@ static void ParseAnalyze_rtable_walk(List *rtable); * Optionally, information about $n parameter types can be supplied. * References to $n indexes not defined by paramTypes[] are disallowed. * - * The result is a Query node. Optimizable statements require considerable + * The result is a Query node. Optimizable statements require considerable * transformation, while utility-type statements are simply hung off * a dummy CMD_UTILITY Query node. */ @@ -232,6 +234,7 @@ transformTopLevelStmt(ParseState *pstate, Node *parseTree) ctas->query = parseTree; ctas->into = stmt->intoClause; + ctas->relkind = OBJECT_TABLE; ctas->is_select_into = true; /* @@ -401,6 +404,7 @@ static Query * transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) { Query *qry = makeNode(Query); + ParseNamespaceItem *nsitem; Node *qual; qry->commandType = CMD_DELETE; @@ -419,8 +423,16 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) true, ACL_DELETE); + /* grab the namespace item made by setTargetTable */ + nsitem = (ParseNamespaceItem *) llast(pstate->p_namespace); + + /* there's no DISTINCT in DELETE */ qry->distinctClause = NIL; + /* subqueries in USING cannot access the result relation */ + nsitem->p_lateral_only = true; + nsitem->p_lateral_ok = false; + /* * The USING clause is non-standard SQL syntax, and is equivalent in * functionality to the FROM list that can be specified for UPDATE. The @@ -429,7 +441,12 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) */ transformFromClause(pstate, stmt->usingClause); - qual = transformWhereClause(pstate, stmt->whereClause, "WHERE"); + /* remaining clauses can reference the result relation normally */ + nsitem->p_lateral_only = false; + nsitem->p_lateral_ok = true; + + qual = transformWhereClause(pstate, stmt->whereClause, + EXPR_KIND_WHERE, "WHERE"); qry->returningList = transformReturningList(pstate, stmt->returningList); @@ -439,8 +456,6 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; - if (pstate->p_hasWindowFuncs) - parseCheckWindowFuncs(pstate, qry); qry->hasAggs = pstate->p_hasAggs; if (pstate->p_hasAggs) parseCheckAggregates(pstate, qry); @@ -462,8 +477,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) List *exprList = NIL; bool isGeneralSelect; List *sub_rtable; - List *sub_relnamespace; - List *sub_varnamespace; + List *sub_namespace; List *icolumns; List *attrnos; RangeTblEntry *rte; @@ -505,7 +519,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* * If a non-nil rangetable/namespace was passed in, and we are doing * INSERT/SELECT, arrange to pass the rangetable/namespace down to the - * SELECT. This can only happen if we are inside a CREATE RULE, and in + * SELECT. This can only happen if we are inside a CREATE RULE, and in * that case we want the rule's OLD and NEW rtable entries to appear as * part of the SELECT's rtable, not as outer references for it. (Kluge!) * The SELECT's joinlist is not affected however. We must do this before @@ -515,16 +529,13 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) { sub_rtable = pstate->p_rtable; pstate->p_rtable = NIL; - sub_relnamespace = pstate->p_relnamespace; - pstate->p_relnamespace = NIL; - sub_varnamespace = pstate->p_varnamespace; - pstate->p_varnamespace = NIL; + sub_namespace = pstate->p_namespace; + pstate->p_namespace = NIL; } else { sub_rtable = NIL; /* not used, but keep compiler quiet */ - sub_relnamespace = NIL; - sub_varnamespace = NIL; + sub_namespace = NIL; } /* @@ -579,8 +590,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) */ sub_pstate->p_rtable = sub_rtable; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ - sub_pstate->p_relnamespace = sub_relnamespace; - sub_pstate->p_varnamespace = sub_varnamespace; + sub_pstate->p_namespace = sub_namespace; selectQuery = transformStmt(sub_pstate, stmt->selectStmt); @@ -599,6 +609,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) rte = addRangeTableEntryForSubquery(pstate, selectQuery, makeAlias("*SELECT*", NIL), + false, false); #ifdef PGXC #ifndef XCP @@ -676,6 +687,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) List *exprsLists = NIL; List *collations = NIL; int sublist_length = -1; + bool lateral = false; int i; Assert(selectStmt->intoClause == NULL); @@ -685,7 +697,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) List *sublist = (List *) lfirst(lc); /* Do basic expression transformation (same as a ROW() expr) */ - sublist = transformExpressionList(pstate, sublist); + sublist = transformExpressionList(pstate, sublist, EXPR_KIND_VALUES); /* * All the sublists must be the same length, *after* @@ -715,7 +727,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * We must assign collations now because assign_query_collations * doesn't process rangetable entries. We just assign all the * collations independently in each row, and don't worry about - * whether they are consistent vertically. The outer INSERT query + * whether they are consistent vertically. The outer INSERT query * isn't going to care about the collations of the VALUES columns, * so it's not worth the effort to identify a common collation for * each one here. (But note this does have one user-visible @@ -736,37 +748,20 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) collations = lappend_oid(collations, InvalidOid); /* - * There mustn't have been any table references in the expressions, - * else strange things would happen, like Cartesian products of those - * tables with the VALUES list ... - */ - if (pstate->p_joinlist != NIL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("VALUES must not contain table references"), - parser_errposition(pstate, - locate_var_of_level((Node *) exprsLists, 0)))); - - /* - * Another thing we can't currently support is NEW/OLD references in - * rules --- seems we'd need something like SQL99's LATERAL construct - * to ensure that the values would be available while evaluating the - * VALUES RTE. This is a shame. FIXME + * Ordinarily there can't be any current-level Vars in the expression + * lists, because the namespace was empty ... but if we're inside + * CREATE RULE, then NEW/OLD references might appear. In that case we + * have to mark the VALUES RTE as LATERAL. */ if (list_length(pstate->p_rtable) != 1 && contain_vars_of_level((Node *) exprsLists, 0)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("VALUES must not contain OLD or NEW references"), - errhint("Use SELECT ... UNION ALL ... instead."), - parser_errposition(pstate, - locate_var_of_level((Node *) exprsLists, 0)))); + lateral = true; /* * Generate the VALUES RTE */ rte = addRangeTableEntryForValues(pstate, exprsLists, collations, - NULL, true); + NULL, lateral, true); rtr = makeNode(RangeTblRef); /* assume new rte is at end */ rtr->rtindex = list_length(pstate->p_rtable); @@ -780,16 +775,11 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) } else { - /*---------- - * Process INSERT ... VALUES with a single VALUES sublist. - * We treat this separately for efficiency and for historical - * compatibility --- specifically, allowing table references, - * such as - * INSERT INTO foo VALUES(bar.*) - * - * The sublist is just computed directly as the Query's targetlist, - * with no VALUES RTE. So it works just like SELECT without FROM. - *---------- + /* + * Process INSERT ... VALUES with a single VALUES sublist. We treat + * this case separately for efficiency. The sublist is just computed + * directly as the Query's targetlist, with no VALUES RTE. So it + * works just like a SELECT without any FROM. */ List *valuesLists = selectStmt->valuesLists; @@ -798,7 +788,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) /* Do basic expression transformation (same as a ROW() expr) */ exprList = transformExpressionList(pstate, - (List *) linitial(valuesLists)); + (List *) linitial(valuesLists), + EXPR_KIND_VALUES); /* Prepare row for assignment to target table */ exprList = transformInsertRow(pstate, exprList, @@ -846,8 +837,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) */ if (stmt->returningList) { - pstate->p_relnamespace = NIL; - pstate->p_varnamespace = NIL; + pstate->p_namespace = NIL; addRTEtoQuery(pstate, pstate->p_target_rangetblentry, false, true, true); qry->returningList = transformReturningList(pstate, @@ -859,19 +849,6 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; - /* aggregates not allowed (but subselects are okay) */ - if (pstate->p_hasAggs) - ereport(ERROR, - (errcode(ERRCODE_GROUPING_ERROR), - errmsg("cannot use aggregate function in VALUES"), - parser_errposition(pstate, - locate_agg_of_level((Node *) qry, 0)))); - if (pstate->p_hasWindowFuncs) - ereport(ERROR, - (errcode(ERRCODE_WINDOWING_ERROR), - errmsg("cannot use window function in VALUES"), - parser_errposition(pstate, - locate_windowfunc((Node *) qry)))); assign_query_collations(pstate, qry); @@ -897,7 +874,7 @@ transformInsertRow(ParseState *pstate, List *exprlist, * Check length of expr list. It must not have more expressions than * there are target columns. We allow fewer, but only if no explicit * columns list was given (the remaining columns are implicitly - * defaulted). Note we must check this *after* transformation because + * defaulted). Note we must check this *after* transformation because * that could expand '*' into multiple items. */ if (list_length(exprlist) > list_length(icolumns)) @@ -946,6 +923,7 @@ transformInsertRow(ParseState *pstate, List *exprlist, Assert(IsA(col, ResTarget)); expr = transformAssignedExpr(pstate, expr, + EXPR_KIND_INSERT_TARGET, col->name, lfirst_int(attnos), col->indirection, @@ -966,7 +944,7 @@ transformInsertRow(ParseState *pstate, List *exprlist, * return -1 if expression isn't a RowExpr or a Var referencing one. * * This is currently used only for hint purposes, so we aren't terribly - * tense about recognizing all possible cases. The Var case is interesting + * tense about recognizing all possible cases. The Var case is interesting * because that's what we'll get in the INSERT ... SELECT (...) case. */ static int @@ -1046,19 +1024,19 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) transformFromClause(pstate, stmt->fromClause); /* transform targetlist */ - qry->targetList = transformTargetList(pstate, stmt->targetList); + qry->targetList = transformTargetList(pstate, stmt->targetList, + EXPR_KIND_SELECT_TARGET); /* mark column origins */ markTargetListOrigins(pstate, qry->targetList); /* transform WHERE */ - qual = transformWhereClause(pstate, stmt->whereClause, "WHERE"); + qual = transformWhereClause(pstate, stmt->whereClause, + EXPR_KIND_WHERE, "WHERE"); - /* - * Initial processing of HAVING clause is just like WHERE clause. - */ + /* initial processing of HAVING clause is much like WHERE clause */ qry->havingQual = transformWhereClause(pstate, stmt->havingClause, - "HAVING"); + EXPR_KIND_HAVING, "HAVING"); /* * Transform sorting/grouping stuff. Do ORDER BY first because both @@ -1069,6 +1047,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) qry->sortClause = transformSortClause(pstate, stmt->sortClause, &qry->targetList, + EXPR_KIND_ORDER_BY, true /* fix unknowns */ , false /* allow SQL92 rules */ ); @@ -1076,6 +1055,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) stmt->groupClause, &qry->targetList, qry->sortClause, + EXPR_KIND_GROUP_BY, false /* allow SQL92 rules */ ); if (stmt->distinctClause == NIL) @@ -1104,9 +1084,9 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) /* transform LIMIT */ qry->limitOffset = transformLimitClause(pstate, stmt->limitOffset, - "OFFSET"); + EXPR_KIND_OFFSET, "OFFSET"); qry->limitCount = transformLimitClause(pstate, stmt->limitCount, - "LIMIT"); + EXPR_KIND_LIMIT, "LIMIT"); /* transform window clauses after we have seen all window functions */ qry->windowClause = transformWindowDefinitions(pstate, @@ -1118,8 +1098,6 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; - if (pstate->p_hasWindowFuncs) - parseCheckWindowFuncs(pstate, qry); qry->hasAggs = pstate->p_hasAggs; if (pstate->p_hasAggs || qry->groupClause || qry->havingQual) parseCheckAggregates(pstate, qry); @@ -1150,8 +1128,9 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) List *collations; List **colexprs = NULL; int sublist_length = -1; + bool lateral = false; RangeTblEntry *rte; - RangeTblRef *rtr; + int rtindex; ListCell *lc; ListCell *lc2; int i; @@ -1191,7 +1170,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) List *sublist = (List *) lfirst(lc); /* Do basic expression transformation (same as a ROW() expr) */ - sublist = transformExpressionList(pstate, sublist); + sublist = transformExpressionList(pstate, sublist, EXPR_KIND_VALUES); /* * All the sublists must be the same length, *after* transformation @@ -1295,23 +1274,31 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) } /* + * Ordinarily there can't be any current-level Vars in the expression + * lists, because the namespace was empty ... but if we're inside CREATE + * RULE, then NEW/OLD references might appear. In that case we have to + * mark the VALUES RTE as LATERAL. + */ + if (pstate->p_rtable != NIL && + contain_vars_of_level((Node *) exprsLists, 0)) + lateral = true; + + /* * Generate the VALUES RTE */ rte = addRangeTableEntryForValues(pstate, exprsLists, collations, - NULL, true); - rtr = makeNode(RangeTblRef); + NULL, lateral, true); + addRTEtoQuery(pstate, rte, true, true, true); + /* assume new rte is at end */ - rtr->rtindex = list_length(pstate->p_rtable); - Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable)); - pstate->p_joinlist = lappend(pstate->p_joinlist, rtr); - pstate->p_relnamespace = lappend(pstate->p_relnamespace, rte); - pstate->p_varnamespace = lappend(pstate->p_varnamespace, rte); + rtindex = list_length(pstate->p_rtable); + Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); /* * Generate a targetlist as though expanding "*" */ Assert(pstate->p_next_resno == 1); - qry->targetList = expandRelAttrs(pstate, rte, rtr->rtindex, 0, -1); + qry->targetList = expandRelAttrs(pstate, rte, rtindex, 0, -1); /* * The grammar allows attaching ORDER BY, LIMIT, and FOR UPDATE to a @@ -1320,64 +1307,28 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt) qry->sortClause = transformSortClause(pstate, stmt->sortClause, &qry->targetList, + EXPR_KIND_ORDER_BY, true /* fix unknowns */ , false /* allow SQL92 rules */ ); qry->limitOffset = transformLimitClause(pstate, stmt->limitOffset, - "OFFSET"); + EXPR_KIND_OFFSET, "OFFSET"); qry->limitCount = transformLimitClause(pstate, stmt->limitCount, - "LIMIT"); + EXPR_KIND_LIMIT, "LIMIT"); if (stmt->lockingClause) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE cannot be applied to VALUES"))); - - /* - * There mustn't have been any table references in the expressions, else - * strange things would happen, like Cartesian products of those tables - * with the VALUES list. We have to check this after parsing ORDER BY et - * al since those could insert more junk. - */ - if (list_length(pstate->p_joinlist) != 1) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("VALUES must not contain table references"), - parser_errposition(pstate, - locate_var_of_level((Node *) exprsLists, 0)))); - - /* - * Another thing we can't currently support is NEW/OLD references in rules - * --- seems we'd need something like SQL99's LATERAL construct to ensure - * that the values would be available while evaluating the VALUES RTE. - * This is a shame. FIXME - */ - if (list_length(pstate->p_rtable) != 1 && - contain_vars_of_level((Node *) exprsLists, 0)) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("VALUES must not contain OLD or NEW references"), - errhint("Use SELECT ... UNION ALL ... instead."), - parser_errposition(pstate, - locate_var_of_level((Node *) exprsLists, 0)))); + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s cannot be applied to VALUES", + LCS_asString(((LockingClause *) + linitial(stmt->lockingClause))->strength)))); qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; - /* aggregates not allowed (but subselects are okay) */ - if (pstate->p_hasAggs) - ereport(ERROR, - (errcode(ERRCODE_GROUPING_ERROR), - errmsg("cannot use aggregate function in VALUES"), - parser_errposition(pstate, - locate_agg_of_level((Node *) exprsLists, 0)))); - if (pstate->p_hasWindowFuncs) - ereport(ERROR, - (errcode(ERRCODE_WINDOWING_ERROR), - errmsg("cannot use window function in VALUES"), - parser_errposition(pstate, - locate_windowfunc((Node *) exprsLists)))); assign_query_collations(pstate, qry); @@ -1406,6 +1357,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) Node *limitOffset; Node *limitCount; List *lockingClause; + WithClause *withClause; Node *node; ListCell *left_tlist, *lct, @@ -1414,22 +1366,13 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) *l; List *targetvars, *targetnames, - *sv_relnamespace, - *sv_varnamespace; + *sv_namespace; int sv_rtable_length; RangeTblEntry *jrte; int tllen; qry->commandType = CMD_SELECT; - /* process the WITH clause independently of all else */ - if (stmt->withClause) - { - qry->hasRecursive = stmt->withClause->recursive; - qry->cteList = transformWithClause(pstate, stmt->withClause); - qry->hasModifyingCTE = pstate->p_hasModifyingCTE; - } - /* * Find leftmost leaf SelectStmt. We currently only need to do this in * order to deliver a suitable error message if there's an INTO clause @@ -1459,17 +1402,31 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) limitOffset = stmt->limitOffset; limitCount = stmt->limitCount; lockingClause = stmt->lockingClause; + withClause = stmt->withClause; stmt->sortClause = NIL; stmt->limitOffset = NULL; stmt->limitCount = NULL; stmt->lockingClause = NIL; + stmt->withClause = NULL; /* We don't support FOR UPDATE/SHARE with set ops at the moment. */ if (lockingClause) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT"))); + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s is not allowed with UNION/INTERSECT/EXCEPT", + LCS_asString(((LockingClause *) + linitial(lockingClause))->strength)))); + + /* Process the WITH clause independently of all else */ + if (withClause) + { + qry->hasRecursive = withClause->recursive; + qry->cteList = transformWithClause(pstate, withClause); + qry->hasModifyingCTE = pstate->p_hasModifyingCTE; + } /* * Recursively transform the components of the tree. @@ -1540,8 +1497,8 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) /* * As a first step towards supporting sort clauses that are expressions - * using the output columns, generate a varnamespace entry that makes the - * output columns visible. A Join RTE node is handy for this, since we + * using the output columns, generate a namespace entry that makes the + * output columns visible. A Join RTE node is handy for this, since we * can easily control the Vars generated upon matches. * * Note: we don't yet do anything useful with such cases, but at least @@ -1557,11 +1514,11 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) NULL, false); - sv_relnamespace = pstate->p_relnamespace; - pstate->p_relnamespace = NIL; /* no qualified names allowed */ + sv_namespace = pstate->p_namespace; + pstate->p_namespace = NIL; - sv_varnamespace = pstate->p_varnamespace; - pstate->p_varnamespace = list_make1(jrte); + /* add jrte to column namespace only */ + addRTEtoQuery(pstate, jrte, false, false, true); /* * For now, we don't support resjunk sort clauses on the output of a @@ -1574,12 +1531,13 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) qry->sortClause = transformSortClause(pstate, sortClause, &qry->targetList, + EXPR_KIND_ORDER_BY, false /* no unknowns expected */ , false /* allow SQL92 rules */ ); + /* restore namespace, remove jrte from rtable */ + pstate->p_namespace = sv_namespace; pstate->p_rtable = list_truncate(pstate->p_rtable, sv_rtable_length); - pstate->p_relnamespace = sv_relnamespace; - pstate->p_varnamespace = sv_varnamespace; if (tllen != list_length(qry->targetList)) ereport(ERROR, @@ -1591,17 +1549,15 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) exprLocation(list_nth(qry->targetList, tllen))))); qry->limitOffset = transformLimitClause(pstate, limitOffset, - "OFFSET"); + EXPR_KIND_OFFSET, "OFFSET"); qry->limitCount = transformLimitClause(pstate, limitCount, - "LIMIT"); + EXPR_KIND_LIMIT, "LIMIT"); qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; - if (pstate->p_hasWindowFuncs) - parseCheckWindowFuncs(pstate, qry); qry->hasAggs = pstate->p_hasAggs; if (pstate->p_hasAggs || qry->groupClause || qry->havingQual) parseCheckAggregates(pstate, qry); @@ -1622,7 +1578,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) * Recursively transform leaves and internal nodes of a set-op tree * * In addition to returning the transformed node, if targetlist isn't NULL - * then we return a list of its non-resjunk TargetEntry nodes. For a leaf + * then we return a list of its non-resjunk TargetEntry nodes. For a leaf * set-op node these are the actual targetlist entries; otherwise they are * dummy entries created to carry the type, typmod, collation, and location * (for error messages) of each output column of the set-op node. This info @@ -1639,6 +1595,9 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, Assert(stmt && IsA(stmt, SelectStmt)); + /* Guard against stack overflow due to overly complex set-expressions */ + check_stack_depth(); + /* * Validity-check both leaf and internal SELECTs for disallowed ops. */ @@ -1653,13 +1612,17 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, if (stmt->lockingClause) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT"))); + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s is not allowed with UNION/INTERSECT/EXCEPT", + LCS_asString(((LockingClause *) + linitial(stmt->lockingClause))->strength)))); /* - * If an internal node of a set-op tree has ORDER BY, LIMIT, or FOR UPDATE - * clauses attached, we need to treat it like a leaf node to generate an - * independent sub-Query tree. Otherwise, it can be represented by a - * SetOperationStmt node underneath the parent Query. + * If an internal node of a set-op tree has ORDER BY, LIMIT, FOR UPDATE, + * or WITH clauses attached, we need to treat it like a leaf node to + * generate an independent sub-Query tree. Otherwise, it can be + * represented by a SetOperationStmt node underneath the parent Query. */ if (stmt->op == SETOP_NONE) { @@ -1670,7 +1633,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, { Assert(stmt->larg != NULL && stmt->rarg != NULL); if (stmt->sortClause || stmt->limitOffset || stmt->limitCount || - stmt->lockingClause) + stmt->lockingClause || stmt->withClause) isLeaf = true; else isLeaf = false; @@ -1700,7 +1663,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, * because the namespace will be empty, but it could happen if we are * inside a rule. */ - if (pstate->p_relnamespace || pstate->p_varnamespace) + if (pstate->p_namespace) { if (contain_vars_of_level((Node *) selectQuery, 1)) ereport(ERROR, @@ -1733,6 +1696,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, rte = addRangeTableEntryForSubquery(pstate, selectQuery, makeAlias(selectName, NIL), + false, false); /* @@ -1833,7 +1797,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, rescoltypmod = -1; /* - * Verify the coercions are actually possible. If not, we'd fail + * Verify the coercions are actually possible. If not, we'd fail * later anyway, but we want to fail now while we have sufficient * context to produce an error cursor position. * @@ -1842,7 +1806,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, * child query's semantics. * * If a child expression is an UNKNOWN-type Const or Param, we - * want to replace it with the coerced expression. This can only + * want to replace it with the coerced expression. This can only * happen when the child is a leaf set-op node. It's safe to * replace the expression because if the child query's semantics * depended on the type of this output column, it'd have already @@ -2023,6 +1987,7 @@ static Query * transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) { Query *qry = makeNode(Query); + ParseNamespaceItem *nsitem; RangeTblEntry *target_rte; Node *qual; ListCell *origTargetList; @@ -2044,15 +2009,28 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) true, ACL_UPDATE); + /* grab the namespace item made by setTargetTable */ + nsitem = (ParseNamespaceItem *) llast(pstate->p_namespace); + + /* subqueries in FROM cannot access the result relation */ + nsitem->p_lateral_only = true; + nsitem->p_lateral_ok = false; + /* * the FROM clause is non-standard SQL syntax. We used to be able to do * this with REPLACE in POSTQUEL so we keep the feature. */ transformFromClause(pstate, stmt->fromClause); - qry->targetList = transformTargetList(pstate, stmt->targetList); + /* remaining clauses can reference the result relation normally */ + nsitem->p_lateral_only = false; + nsitem->p_lateral_ok = true; + + qry->targetList = transformTargetList(pstate, stmt->targetList, + EXPR_KIND_UPDATE_SOURCE); - qual = transformWhereClause(pstate, stmt->whereClause, "WHERE"); + qual = transformWhereClause(pstate, stmt->whereClause, + EXPR_KIND_WHERE, "WHERE"); qry->returningList = transformReturningList(pstate, stmt->returningList); @@ -2062,24 +2040,6 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) qry->hasSubLinks = pstate->p_hasSubLinks; /* - * Top-level aggregates are simply disallowed in UPDATE, per spec. (From - * an implementation point of view, this is forced because the implicit - * ctid reference would otherwise be an ungrouped variable.) - */ - if (pstate->p_hasAggs) - ereport(ERROR, - (errcode(ERRCODE_GROUPING_ERROR), - errmsg("cannot use aggregate function in UPDATE"), - parser_errposition(pstate, - locate_agg_of_level((Node *) qry, 0)))); - if (pstate->p_hasWindowFuncs) - ereport(ERROR, - (errcode(ERRCODE_WINDOWING_ERROR), - errmsg("cannot use window function in UPDATE"), - parser_errposition(pstate, - locate_windowfunc((Node *) qry)))); - - /* * Now we are done with SELECT-like processing, and can get on with * transforming the target list to match the UPDATE target columns. */ @@ -2153,9 +2113,6 @@ transformReturningList(ParseState *pstate, List *returningList) { List *rlist; int save_next_resno; - bool save_hasAggs; - bool save_hasWindowFuncs; - int length_rtable; if (returningList == NIL) return NIL; /* nothing to do */ @@ -2168,58 +2125,27 @@ transformReturningList(ParseState *pstate, List *returningList) save_next_resno = pstate->p_next_resno; pstate->p_next_resno = 1; - /* save other state so that we can detect disallowed stuff */ - save_hasAggs = pstate->p_hasAggs; - pstate->p_hasAggs = false; - save_hasWindowFuncs = pstate->p_hasWindowFuncs; - pstate->p_hasWindowFuncs = false; - length_rtable = list_length(pstate->p_rtable); - /* transform RETURNING identically to a SELECT targetlist */ - rlist = transformTargetList(pstate, returningList); + rlist = transformTargetList(pstate, returningList, EXPR_KIND_RETURNING); - /* check for disallowed stuff */ - - /* aggregates not allowed (but subselects are okay) */ - if (pstate->p_hasAggs) - ereport(ERROR, - (errcode(ERRCODE_GROUPING_ERROR), - errmsg("cannot use aggregate function in RETURNING"), - parser_errposition(pstate, - locate_agg_of_level((Node *) rlist, 0)))); - if (pstate->p_hasWindowFuncs) + /* + * Complain if the nonempty tlist expanded to nothing (which is possible + * if it contains only a star-expansion of a zero-column table). If we + * allow this, the parsed Query will look like it didn't have RETURNING, + * with results that would probably surprise the user. + */ + if (rlist == NIL) ereport(ERROR, - (errcode(ERRCODE_WINDOWING_ERROR), - errmsg("cannot use window function in RETURNING"), + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("RETURNING must have at least one column"), parser_errposition(pstate, - locate_windowfunc((Node *) rlist)))); - - /* no new relation references please */ - if (list_length(pstate->p_rtable) != length_rtable) - { - int vlocation = -1; - int relid; - - /* try to locate such a reference to point to */ - for (relid = length_rtable + 1; relid <= list_length(pstate->p_rtable); relid++) - { - vlocation = locate_var_of_relation((Node *) rlist, relid, 0); - if (vlocation >= 0) - break; - } - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("RETURNING cannot contain references to other relations"), - parser_errposition(pstate, vlocation))); - } + exprLocation(linitial(returningList))))); /* mark column origins */ markTargetListOrigins(pstate, rlist); /* restore state */ pstate->p_next_resno = save_next_resno; - pstate->p_hasAggs = save_hasAggs; - pstate->p_hasWindowFuncs = save_hasWindowFuncs; return rlist; } @@ -2272,21 +2198,33 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt) if (result->rowMarks != NIL && (stmt->options & CURSOR_OPT_HOLD)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("DECLARE CURSOR WITH HOLD ... FOR UPDATE/SHARE is not supported"), + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("DECLARE CURSOR WITH HOLD ... %s is not supported", + LCS_asString(((RowMarkClause *) + linitial(result->rowMarks))->strength)), errdetail("Holdable cursors must be READ ONLY."))); /* FOR UPDATE and SCROLL are not compatible */ if (result->rowMarks != NIL && (stmt->options & CURSOR_OPT_SCROLL)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("DECLARE SCROLL CURSOR ... FOR UPDATE/SHARE is not supported"), + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("DECLARE SCROLL CURSOR ... %s is not supported", + LCS_asString(((RowMarkClause *) + linitial(result->rowMarks))->strength)), errdetail("Scrollable cursors must be READ ONLY."))); /* FOR UPDATE and INSENSITIVE are not compatible */ if (result->rowMarks != NIL && (stmt->options & CURSOR_OPT_INSENSITIVE)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("DECLARE INSENSITIVE CURSOR ... FOR UPDATE/SHARE is not supported"), + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("DECLARE INSENSITIVE CURSOR ... %s is not supported", + LCS_asString(((RowMarkClause *) + linitial(result->rowMarks))->strength)), errdetail("Insensitive cursors must be READ ONLY."))); /* We won't need the raw querytree any more */ @@ -2327,7 +2265,8 @@ transformExplainStmt(ParseState *pstate, ExplainStmt *stmt) /* * transformCreateTableAsStmt - - * transform a CREATE TABLE AS (or SELECT ... INTO) Statement + * transform a CREATE TABLE AS, SELECT ... INTO, or CREATE MATERIALIZED VIEW + * Statement * * As with EXPLAIN, transform the contained statement now. */ @@ -2335,9 +2274,65 @@ static Query * transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt) { Query *result; + Query *query; /* transform contained query */ - stmt->query = (Node *) transformStmt(pstate, stmt->query); + query = transformStmt(pstate, stmt->query); + stmt->query = (Node *) query; + + /* additional work needed for CREATE MATERIALIZED VIEW */ + if (stmt->relkind == OBJECT_MATVIEW) + { + /* + * Prohibit a data-modifying CTE in the query used to create a + * materialized view. It's not sufficiently clear what the user would + * want to happen if the MV is refreshed or incrementally maintained. + */ + if (query->hasModifyingCTE) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("materialized views must not use data-modifying statements in WITH"))); + + /* + * Check whether any temporary database objects are used in the + * creation query. It would be hard to refresh data or incrementally + * maintain it if a source disappeared. + */ + if (isQueryUsingTempRelation(query)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("materialized views must not use temporary tables or views"))); + + /* + * A materialized view would either need to save parameters for use in + * maintaining/loading the data or prohibit them entirely. The latter + * seems safer and more sane. + */ + if (query_contains_extern_params(query)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("materialized views may not be defined using bound parameters"))); + + /* + * For now, we disallow unlogged materialized views, because it seems + * like a bad idea for them to just go to empty after a crash. (If we + * could mark them as unpopulated, that would be better, but that + * requires catalog changes which crash recovery can't presently + * handle.) + */ + if (stmt->into->rel->relpersistence == RELPERSISTENCE_UNLOGGED) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("materialized views cannot be UNLOGGED"))); + + /* + * At runtime, we'll need a copy of the parsed-but-not-rewritten Query + * for purposes of creating the view's ON SELECT rule. We stash that + * in the IntoClause because that's where intorel_startup() can + * conveniently get it from. + */ + stmt->into->viewQuery = copyObject(query); + } /* represent the command as a utility Query */ result = makeNode(Query); @@ -2671,46 +2666,84 @@ is_rel_child_of_rel(RangeTblEntry *child_rte, RangeTblEntry *parent_rte) #endif #endif +char * +LCS_asString(LockClauseStrength strength) +{ + switch (strength) + { + case LCS_FORKEYSHARE: + return "FOR KEY SHARE"; + case LCS_FORSHARE: + return "FOR SHARE"; + case LCS_FORNOKEYUPDATE: + return "FOR NO KEY UPDATE"; + case LCS_FORUPDATE: + return "FOR UPDATE"; + } + return "FOR some"; /* shouldn't happen */ +} + /* - * Check for features that are not supported together with FOR UPDATE/SHARE. + * Check for features that are not supported with FOR [KEY] UPDATE/SHARE. * * exported so planner can check again after rewriting, query pullup, etc */ void -CheckSelectLocking(Query *qry) +CheckSelectLocking(Query *qry, LockClauseStrength strength) { if (qry->setOperations) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT"))); + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s is not allowed with UNION/INTERSECT/EXCEPT", + LCS_asString(strength)))); if (qry->distinctClause != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE is not allowed with DISTINCT clause"))); + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s is not allowed with DISTINCT clause", + LCS_asString(strength)))); if (qry->groupClause != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE is not allowed with GROUP BY clause"))); + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s is not allowed with GROUP BY clause", + LCS_asString(strength)))); if (qry->havingQual != NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE is not allowed with HAVING clause"))); + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s is not allowed with HAVING clause", + LCS_asString(strength)))); if (qry->hasAggs) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE is not allowed with aggregate functions"))); + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s is not allowed with aggregate functions", + LCS_asString(strength)))); if (qry->hasWindowFuncs) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE is not allowed with window functions"))); + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s is not allowed with window functions", + LCS_asString(strength)))); if (expression_returns_set((Node *) qry->targetList)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE is not allowed with set-returning functions in the target list"))); + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s is not allowed with set-returning functions in the target list", + LCS_asString(strength)))); } /* - * Transform a FOR UPDATE/SHARE clause + * Transform a FOR [KEY] UPDATE/SHARE clause * * This basically involves replacing names by integer relids. * @@ -2727,12 +2760,12 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, Index i; LockingClause *allrels; - CheckSelectLocking(qry); + CheckSelectLocking(qry, lc->strength); /* make a clause we can pass down to subqueries to select all rels */ allrels = makeNode(LockingClause); allrels->lockedRels = NIL; /* indicates all rels */ - allrels->forUpdate = lc->forUpdate; + allrels->strength = lc->strength; allrels->noWait = lc->noWait; if (lockedRels == NIL) @@ -2747,16 +2780,13 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - /* ignore foreign tables */ - if (rte->relkind == RELKIND_FOREIGN_TABLE) - break; applyLockingClause(qry, i, - lc->forUpdate, lc->noWait, pushedDown); + lc->strength, lc->noWait, pushedDown); rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; break; case RTE_SUBQUERY: applyLockingClause(qry, i, - lc->forUpdate, lc->noWait, pushedDown); + lc->strength, lc->noWait, pushedDown); /* * FOR UPDATE/SHARE of subquery is propagated to all of @@ -2785,7 +2815,10 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, if (thisrel->catalogname || thisrel->schemaname) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("SELECT FOR UPDATE/SHARE must specify unqualified relation names"), + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s must specify unqualified relation names", + LCS_asString(lc->strength)), parser_errposition(pstate, thisrel->location))); i = 0; @@ -2799,20 +2832,14 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, switch (rte->rtekind) { case RTE_RELATION: - if (rte->relkind == RELKIND_FOREIGN_TABLE) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE cannot be used with foreign table \"%s\"", - rte->eref->aliasname), - parser_errposition(pstate, thisrel->location))); applyLockingClause(qry, i, - lc->forUpdate, lc->noWait, + lc->strength, lc->noWait, pushedDown); rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; break; case RTE_SUBQUERY: applyLockingClause(qry, i, - lc->forUpdate, lc->noWait, + lc->strength, lc->noWait, pushedDown); /* see comment above */ transformLockingClause(pstate, rte->subquery, @@ -2821,25 +2848,37 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, case RTE_JOIN: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE cannot be applied to a join"), + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s cannot be applied to a join", + LCS_asString(lc->strength)), parser_errposition(pstate, thisrel->location))); break; case RTE_FUNCTION: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE cannot be applied to a function"), + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s cannot be applied to a function", + LCS_asString(lc->strength)), parser_errposition(pstate, thisrel->location))); break; case RTE_VALUES: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE cannot be applied to VALUES"), + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s cannot be applied to VALUES", + LCS_asString(lc->strength)), parser_errposition(pstate, thisrel->location))); break; case RTE_CTE: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("SELECT FOR UPDATE/SHARE cannot be applied to a WITH query"), + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("%s cannot be applied to a WITH query", + LCS_asString(lc->strength)), parser_errposition(pstate, thisrel->location))); break; default: @@ -2853,8 +2892,11 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, if (rt == NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE), - errmsg("relation \"%s\" in FOR UPDATE/SHARE clause not found in FROM clause", - thisrel->relname), + /*------ + translator: %s is a SQL row locking clause such as FOR UPDATE */ + errmsg("relation \"%s\" in %s clause not found in FROM clause", + thisrel->relname, + LCS_asString(lc->strength)), parser_errposition(pstate, thisrel->location))); } } @@ -2865,7 +2907,7 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, */ void applyLockingClause(Query *qry, Index rtindex, - bool forUpdate, bool noWait, bool pushedDown) + LockClauseStrength strength, bool noWait, bool pushedDown) { RowMarkClause *rc; @@ -2877,10 +2919,10 @@ applyLockingClause(Query *qry, Index rtindex, if ((rc = get_parse_rowmark(qry, rtindex)) != NULL) { /* - * If the same RTE is specified both FOR UPDATE and FOR SHARE, treat - * it as FOR UPDATE. (Reasonable, since you can't take both a shared - * and exclusive lock at the same time; it'll end up being exclusive - * anyway.) + * If the same RTE is specified for more than one locking strength, + * treat is as the strongest. (Reasonable, since you can't take both + * a shared and exclusive lock at the same time; it'll end up being + * exclusive anyway.) * * We also consider that NOWAIT wins if it's specified both ways. This * is a bit more debatable but raising an error doesn't seem helpful. @@ -2889,7 +2931,7 @@ applyLockingClause(Query *qry, Index rtindex, * * And of course pushedDown becomes false if any clause is explicit. */ - rc->forUpdate |= forUpdate; + rc->strength = Max(rc->strength, strength); rc->noWait |= noWait; rc->pushedDown &= pushedDown; return; @@ -2898,7 +2940,7 @@ applyLockingClause(Query *qry, Index rtindex, /* Make a new RowMarkClause */ rc = makeNode(RowMarkClause); rc->rti = rtindex; - rc->forUpdate = forUpdate; + rc->strength = strength; rc->noWait = noWait; rc->pushedDown = pushedDown; qry->rowMarks = lappend(qry->rowMarks, rc); @@ -2924,59 +2966,69 @@ static void ParseAnalyze_rtable_walk(List *rtable) { ListCell *item; - StringInfoData buf; if (!IsUnderPostmaster || superuser()) return; - initStringInfo(&buf); foreach(item, rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(item); - resetStringInfo(&buf); - if (rte->rtekind == RTE_FUNCTION && - get_func_namespace(((FuncExpr *) rte->funcexpr)->funcid) == - PG_CATALOG_NAMESPACE) + if (rte->rtekind == RTE_FUNCTION) { - Oid funcid = InvalidOid; + ListCell *lc; + foreach(lc, rte->functions) + { + RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc); + ParseAnalyze_substitute_func((FuncExpr *) rtfunc->funcexpr); + } + } + else if (rte->rtekind == RTE_SUBQUERY) /* recurse for subqueries */ + ParseAnalyze_rtable_walk(rte->subquery->rtable); + } +} - FuncExpr *funcexpr = (FuncExpr *) rte->funcexpr; - const char *funcname = get_func_name(funcexpr->funcid); +static void +ParseAnalyze_substitute_func(FuncExpr *funcexpr) +{ + StringInfoData buf; + initStringInfo(&buf); - /* Check if the funcname is in storm_catalog_remap_string */ - appendStringInfoString(&buf, funcname); - appendStringInfoChar(&buf, ','); + if (get_func_namespace(funcexpr->funcid) == PG_CATALOG_NAMESPACE) + { + Oid funcid = InvalidOid; + const char *funcname = get_func_name(funcexpr->funcid); - elog(DEBUG2, "the constructed name is %s", buf.data); + /* Check if the funcname is in storm_catalog_remap_string */ + appendStringInfoString(&buf, funcname); + appendStringInfoChar(&buf, ','); - /* - * The unqualified function name should be satisfied from the - * storm_catalog appropriately. Just provide a warning for now if - * it is not.. - */ - if (strstr(storm_catalog_remap_string, buf.data)) - { - Oid *argtypes = NULL; - int nargs; + elog(DEBUG2, "the constructed name is %s", buf.data); - get_func_signature(funcexpr->funcid, &argtypes, &nargs); - funcid = get_funcid(funcname, buildoidvector(argtypes, nargs), - STORM_CATALOG_NAMESPACE); - } - else - continue; + /* + * The unqualified function name should be satisfied from the + * storm_catalog appropriately. Just provide a warning for now if + * it is not.. + */ + if (strstr(storm_catalog_remap_string, buf.data)) + { + Oid *argtypes = NULL; + int nargs; - if (get_func_namespace(funcid) != STORM_CATALOG_NAMESPACE) - ereport(WARNING, + get_func_signature(funcexpr->funcid, &argtypes, &nargs); + funcid = get_funcid(funcname, buildoidvector(argtypes, nargs), + STORM_CATALOG_NAMESPACE); + } + else + return; + + if (get_func_namespace(funcid) != STORM_CATALOG_NAMESPACE) + ereport(WARNING, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("Entry (%s) present in storm_catalog_remap_string " - "but object not picked from STORM_CATALOG", funcname))); - else /* change the funcid to the storm_catalog one */ - funcexpr->funcid = funcid; - } - else if (rte->rtekind == RTE_SUBQUERY) /* recurse for subqueries */ - ParseAnalyze_rtable_walk(rte->subquery->rtable); + "but object not picked from STORM_CATALOG", funcname))); + else /* change the funcid to the storm_catalog one */ + funcexpr->funcid = funcid; } } #endif diff --git a/src/backend/parser/check_keywords.pl b/src/backend/parser/check_keywords.pl new file mode 100644 index 0000000000..aa9e0d001d --- /dev/null +++ b/src/backend/parser/check_keywords.pl @@ -0,0 +1,235 @@ +#!/usr/bin/perl + +# Check that the keyword lists in gram.y and kwlist.h are sane. +# Usage: check_keywords.pl gram.y kwlist.h + +# src/backend/parser/check_keywords.pl +# Copyright (c) 2009-2014, PostgreSQL Global Development Group + +use warnings; +use strict; + +my $gram_filename = $ARGV[0]; +my $kwlist_filename = $ARGV[1]; + +my $errors = 0; + +sub error(@) +{ + print STDERR @_; + $errors = 1; +} + +$, = ' '; # set output field separator +$\ = "\n"; # set output record separator + +my %keyword_categories; +$keyword_categories{'unreserved_keyword'} = 'UNRESERVED_KEYWORD'; +$keyword_categories{'col_name_keyword'} = 'COL_NAME_KEYWORD'; +$keyword_categories{'type_func_name_keyword'} = 'TYPE_FUNC_NAME_KEYWORD'; +$keyword_categories{'reserved_keyword'} = 'RESERVED_KEYWORD'; + +open(GRAM, $gram_filename) || die("Could not open : $gram_filename"); + +my ($S, $s, $k, $n, $kcat); +my $comment; +my @arr; +my %keywords; + +line: while (<GRAM>) +{ + chomp; # strip record separator + + $S = $_; + + # Make sure any braces are split + $s = '{', $S =~ s/$s/ { /g; + $s = '}', $S =~ s/$s/ } /g; + + # Any comments are split + $s = '[/][*]', $S =~ s#$s# /* #g; + $s = '[*][/]', $S =~ s#$s# */ #g; + + if (!($kcat)) + { + + # Is this the beginning of a keyword list? + foreach $k (keys %keyword_categories) + { + if ($S =~ m/^($k):/) + { + $kcat = $k; + next line; + } + } + next line; + } + + # Now split the line into individual fields + $n = (@arr = split(' ', $S)); + + # Ok, we're in a keyword list. Go through each field in turn + for (my $fieldIndexer = 0; $fieldIndexer < $n; $fieldIndexer++) + { + if ($arr[$fieldIndexer] eq '*/' && $comment) + { + $comment = 0; + next; + } + elsif ($comment) + { + next; + } + elsif ($arr[$fieldIndexer] eq '/*') + { + + # start of a multiline comment + $comment = 1; + next; + } + elsif ($arr[$fieldIndexer] eq '//') + { + next line; + } + + if ($arr[$fieldIndexer] eq ';') + { + + # end of keyword list + $kcat = ''; + next; + } + + if ($arr[$fieldIndexer] eq '|') + { + next; + } + + # Put this keyword into the right list + push @{ $keywords{$kcat} }, $arr[$fieldIndexer]; + } +} +close GRAM; + +# Check that each keyword list is in alphabetical order (just for neatnik-ism) +my ($prevkword, $kword, $bare_kword); +foreach $kcat (keys %keyword_categories) +{ + $prevkword = ''; + + foreach $kword (@{ $keywords{$kcat} }) + { + + # Some keyword have a _P suffix. Remove it for the comparison. + $bare_kword = $kword; + $bare_kword =~ s/_P$//; + if ($bare_kword le $prevkword) + { + error + "'$bare_kword' after '$prevkword' in $kcat list is misplaced"; + } + $prevkword = $bare_kword; + } +} + +# Transform the keyword lists into hashes. +# kwhashes is a hash of hashes, keyed by keyword category id, +# e.g. UNRESERVED_KEYWORD. +# Each inner hash is keyed by keyword id, e.g. ABORT_P, with a dummy value. +my %kwhashes; +while (my ($kcat, $kcat_id) = each(%keyword_categories)) +{ + @arr = @{ $keywords{$kcat} }; + + my $hash; + foreach my $item (@arr) { $hash->{$item} = 1; } + + $kwhashes{$kcat_id} = $hash; +} + +# Now read in kwlist.h + +open(KWLIST, $kwlist_filename) || die("Could not open : $kwlist_filename"); + +my $prevkwstring = ''; +my $bare_kwname; +my %kwhash; +kwlist_line: while (<KWLIST>) +{ + my ($line) = $_; + + if ($line =~ /^PG_KEYWORD\(\"(.*)\", (.*), (.*)\)/) + { + my ($kwstring) = $1; + my ($kwname) = $2; + my ($kwcat_id) = $3; + + # Check that the list is in alphabetical order (critical!) + if ($kwstring le $prevkwstring) + { + error + "'$kwstring' after '$prevkwstring' in kwlist.h is misplaced"; + } + $prevkwstring = $kwstring; + + # Check that the keyword string is valid: all lower-case ASCII chars + if ($kwstring !~ /^[a-z_]+$/) + { + error +"'$kwstring' is not a valid keyword string, must be all lower-case ASCII chars"; + } + + # Check that the keyword name is valid: all upper-case ASCII chars + if ($kwname !~ /^[A-Z_]+$/) + { + error +"'$kwname' is not a valid keyword name, must be all upper-case ASCII chars"; + } + + # Check that the keyword string matches keyword name + $bare_kwname = $kwname; + $bare_kwname =~ s/_P$//; + if ($bare_kwname ne uc($kwstring)) + { + error +"keyword name '$kwname' doesn't match keyword string '$kwstring'"; + } + + # Check that the keyword is present in the grammar + %kwhash = %{ $kwhashes{$kwcat_id} }; + + if (!(%kwhash)) + { + error "Unknown keyword category: $kwcat_id"; + } + else + { + if (!($kwhash{$kwname})) + { + error "'$kwname' not present in $kwcat_id section of gram.y"; + } + else + { + + # Remove it from the hash, so that we can + # complain at the end if there's keywords left + # that were not found in kwlist.h + delete $kwhashes{$kwcat_id}->{$kwname}; + } + } + } +} +close KWLIST; + +# Check that we've paired up all keywords from gram.y with lines in kwlist.h +while (my ($kwcat, $kwcat_id) = each(%keyword_categories)) +{ + %kwhash = %{ $kwhashes{$kwcat_id} }; + + for my $kw (keys %kwhash) + { + error "'$kw' found in gram.y $kwcat category, but not in kwlist.h"; + } +} + +exit $errors; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index d8d64c4d21..5931e2847e 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -6,7 +6,7 @@ * gram.y * POSTGRESQL BISON rules/actions * - * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 2010-2012 Postgres-XC Development Group * @@ -22,8 +22,6 @@ * NOTES * CAPITALS are used to represent terminal symbols. * non-capitals are used to represent non-terminals. - * SQL92-specific syntax is separated from plain SQL/Postgres syntax - * to help isolate the non-extensible portions of the parser. * * In general, nothing in this file should initiate database accesses * nor depend on changeable state (such as SET variables). If you do @@ -57,6 +55,7 @@ #include "catalog/pg_trigger.h" #include "commands/defrem.h" #include "miscadmin.h" +#include "commands/trigger.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "parser/gramparse.h" @@ -70,16 +69,34 @@ #include "utils/xml.h" -/* Location tracking support --- simpler than bison's default */ +/* + * Location tracking support --- simpler than bison's default, since we only + * want to track the start position not the end position of each nonterminal. + */ #define YYLLOC_DEFAULT(Current, Rhs, N) \ do { \ - if (N) \ + if ((N) > 0) \ (Current) = (Rhs)[1]; \ else \ - (Current) = (Rhs)[0]; \ + (Current) = (-1); \ } while (0) /* + * The above macro assigns -1 (unknown) as the parse location of any + * nonterminal that was reduced from an empty rule. This is problematic + * for nonterminals defined like + * OptFooList: / * EMPTY * / { ... } | OptFooList Foo { ... } ; + * because we'll set -1 as the location during the first reduction and then + * copy it during each subsequent reduction, leaving us with -1 for the + * location even when the list is not empty. To fix that, do this in the + * action for the nonempty rule(s): + * if (@$ < 0) @$ = @2; + * (Although we have many nonterminals that follow this pattern, we only + * bother with fixing @$ like this when the nonterminal's parse location + * is actually referenced in some rule.) + */ + +/* * Bison doesn't allocate anything that needs to live across parser calls, * so we can easily have it use palloc instead of malloc. This prevents * memory leaks if we error out during parsing. Note this only works with @@ -104,6 +121,7 @@ typedef struct PrivTarget #define CAS_INITIALLY_IMMEDIATE 0x04 #define CAS_INITIALLY_DEFERRED 0x08 #define CAS_NOT_VALID 0x10 +#define CAS_NO_INHERIT 0x20 #define parser_yyerror(msg) scanner_yyerror(msg, yyscanner) @@ -122,12 +140,13 @@ static Node *makeBitStringConst(char *str, int location); static Node *makeNullAConst(int location); static Node *makeAConst(Value *v, int location); static Node *makeBoolAConst(bool state, int location); -static FuncCall *makeOverlaps(List *largs, List *rargs, - int location, core_yyscan_t yyscanner); static void check_qualified_name(List *names, core_yyscan_t yyscanner); static List *check_func_name(List *names, core_yyscan_t yyscanner); static List *check_indirection(List *indirection, core_yyscan_t yyscanner); static List *extractArgTypes(List *parameters); +static List *extractAggrArgTypes(List *aggrargs); +static List *makeOrderedSetArgs(List *directargs, List *orderedargs, + core_yyscan_t yyscanner); static void insertSelectOptions(SelectStmt *stmt, List *sortClause, List *lockingClause, Node *limitOffset, Node *limitCount, @@ -147,7 +166,8 @@ static void SplitColQualList(List *qualList, core_yyscan_t yyscanner); static void processCASbits(int cas_bits, int location, const char *constrType, bool *deferrable, bool *initdeferred, bool *not_valid, - core_yyscan_t yyscanner); + bool *no_inherit, core_yyscan_t yyscanner); +static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %} @@ -202,10 +222,11 @@ static void processCASbits(int cas_bits, int location, const char *constrType, } %type <node> stmt schema_stmt + AlterEventTrigStmt AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt AlterFdwStmt AlterForeignServerStmt AlterGroupStmt - AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt - AlterExtensionStmt AlterExtensionContentsStmt AlterForeignTableStmt + AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterSystemStmt AlterTableStmt + AlterTblSpcStmt AlterExtensionStmt AlterExtensionContentsStmt AlterForeignTableStmt AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt AlterDefaultPrivilegesStmt DefACLAction @@ -215,7 +236,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType, CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt - CreateAssertStmt CreateTrigStmt + CreateAssertStmt CreateTrigStmt CreateEventTrigStmt CreateUserStmt CreateUserMappingStmt CreateRoleStmt CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt @@ -236,6 +257,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType, AlterTSConfigurationStmt AlterTSDictionaryStmt BarrierStmt PauseStmt AlterNodeStmt CreateNodeStmt DropNodeStmt CreateNodeGroupStmt DropNodeGroupStmt + CreateMatViewStmt RefreshMatViewStmt %type <node> select_no_parens select_with_parens select_clause simple_select values_clause @@ -244,6 +266,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType, %type <ival> add_drop opt_asc_desc opt_nulls_order %type <node> alter_table_cmd alter_type_cmd opt_collate_clause + replica_identity %type <list> alter_table_cmds alter_type_cmds %type <dbehavior> opt_drop_behavior @@ -279,6 +302,10 @@ static void processCASbits(int cas_bits, int location, const char *constrType, %type <value> TriggerFuncArg %type <node> TriggerWhen +%type <list> event_trigger_when_list event_trigger_value_list +%type <defelt> event_trigger_when_item +%type <chr> enable_trigger + %type <str> copy_file_name database_name access_method_clause access_method attr_name name cursor_name file_name @@ -308,19 +335,19 @@ static void processCASbits(int cas_bits, int location, const char *constrType, %type <list> stmtblock stmtmulti OptTableElementList TableElementList OptInherit definition OptTypedTableElementList TypedTableElementList - OptForeignTableElementList ForeignTableElementList reloptions opt_reloptions OptWith opt_distinct opt_definition func_args func_args_list func_args_with_defaults func_args_with_defaults_list + aggr_args aggr_args_list func_as createfunc_opt_list alterfunc_opt_list - aggr_args old_aggr_definition old_aggr_list + old_aggr_definition old_aggr_list oper_argtypes RuleActionList RuleActionMulti opt_column_list columnList opt_name_list sort_clause opt_sort_clause sortby_list index_params - name_list from_clause from_list opt_array_bounds + name_list role_list from_clause from_list opt_array_bounds qualified_name_list any_name any_name_list any_operator expr_list attrs - target_list insert_column_list set_target_list + target_list opt_target_list insert_column_list set_target_list set_clause_list set_clause multiple_set_clause ctext_expr_list ctext_row def_list indirection opt_indirection reloption_list group_clause TriggerFuncArgs select_limit @@ -337,17 +364,19 @@ static void processCASbits(int cas_bits, int location, const char *constrType, %type <defelt> fdw_option %type <range> OptTempTableName -%type <into> into_clause create_as_target +%type <into> into_clause create_as_target create_mv_target %type <defelt> createfunc_opt_item common_func_opt_item dostmt_opt_item -%type <fun_param> func_arg func_arg_with_default table_func_column +%type <fun_param> func_arg func_arg_with_default table_func_column aggr_arg %type <fun_param_mode> arg_class %type <typnam> func_return func_type %type <boolean> opt_trusted opt_restart_seqs %type <ival> OptTemp +%type <ival> OptNoLog %type <oncommit> OnCommitOption +%type <ival> for_locking_strength %type <node> for_locking_item %type <list> for_locking_clause opt_for_locking_clause for_locking_items %type <list> locked_rels_list @@ -369,7 +398,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType, %type <str> DirectStmt CleanConnDbName CleanConnUserName %type <boolean> OptCluster /* PGXC_END */ -%type <boolean> copy_from +%type <boolean> copy_from opt_program %type <ival> opt_column event cursor_options opt_hold opt_set_data %type <objtype> reindex_type drop_type comment_type security_label_type @@ -384,16 +413,17 @@ static void processCASbits(int cas_bits, int location, const char *constrType, %type <istmt> insert_rest -%type <vsetstmt> set_rest set_rest_more SetResetClause FunctionSetResetClause +%type <vsetstmt> generic_set set_rest set_rest_more SetResetClause FunctionSetResetClause %type <node> TableElement TypedTableElement ConstraintElem TableFuncElement - ForeignTableElement %type <node> columnDef columnOptions %type <defelt> def_elem reloption_elem old_aggr_elem %type <node> def_arg columnElem where_clause where_or_current_clause - a_expr b_expr c_expr func_expr AexprConst indirection_el + a_expr b_expr c_expr AexprConst indirection_el columnref in_expr having_clause func_table array_expr ExclusionWhereClause +%type <list> rowsfrom_item rowsfrom_list opt_col_def_list +%type <boolean> opt_ordinality %type <list> ExclusionConstraintList ExclusionConstraintElem %type <list> func_arg_list %type <node> func_arg_expr @@ -404,7 +434,8 @@ static void processCASbits(int cas_bits, int location, const char *constrType, %type <node> ctext_expr %type <value> NumericOnly %type <list> NumericOnly_list -%type <alias> alias_clause +%type <alias> alias_clause opt_alias_clause +%type <list> func_alias_clause %type <sortby> sortby %type <ielem> index_elem %type <node> table_ref @@ -439,9 +470,10 @@ static void processCASbits(int cas_bits, int location, const char *constrType, %type <ival> Iconst SignedIconst %type <str> Sconst comment_text notify_payload -%type <str> RoleId opt_granted_by opt_boolean_or_string ColId_or_Sconst +%type <str> RoleId opt_granted_by opt_boolean_or_string %type <list> var_list %type <str> ColId ColLabel var_name type_function_name param_name +%type <str> NonReservedWord NonReservedWord_or_Sconst %type <node> var_value zone_value %type <keyword> unreserved_keyword type_func_name_keyword @@ -458,7 +490,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType, %type <list> constraints_set_list %type <boolean> constraints_set_mode %type <str> OptTableSpace OptConsTableSpace OptTableSpaceOwner -%type <list> opt_check_option +%type <ival> opt_check_option %type <str> opt_provider security_label @@ -469,10 +501,14 @@ static void processCASbits(int cas_bits, int location, const char *constrType, %type <ival> document_or_content %type <boolean> xml_whitespace_option +%type <node> func_application func_expr_common_subexpr +%type <node> func_expr func_expr_windowless %type <node> common_table_expr %type <with> with_clause opt_with_clause %type <list> cte_list +%type <list> within_group_clause +%type <node> filter_clause %type <list> window_clause window_definition_list opt_partition_clause %type <windef> window_definition over_clause window_specification opt_frame_clause frame_extent frame_bound @@ -482,7 +518,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType, %type <distby> OptDistributeBy OptDistributeByInternal %type <subclus> OptSubCluster OptSubClusterInternal /* PGXC_END */ - +%type <boolean> opt_if_not_exists /* * Non-keyword token types. These are hard-wired into the "flex" lexer. @@ -529,12 +565,12 @@ static void processCASbits(int cas_bits, int location, const char *constrType, /* PGXC_END */ DROP - EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EXCEPT + EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTENSION EXTERNAL EXTRACT - FALSE_P FAMILY FETCH FIRST_P FLOAT_P FOLLOWING FOR FORCE FOREIGN FORWARD - FREEZE FROM FULL FUNCTION FUNCTIONS + FALSE_P FAMILY FETCH FILTER FIRST_P FLOAT_P FOLLOWING FOR + FORCE FOREIGN FORWARD FREEZE FROM FULL FUNCTION FUNCTIONS GLOBAL GRANT GRANTED GREATEST GROUP_P @@ -549,25 +585,28 @@ static void processCASbits(int cas_bits, int location, const char *constrType, KEY - LABEL LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING LEAKPROOF - LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP - LOCATION LOCK_P - MAPPING MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE + LABEL LANGUAGE LARGE_P LAST_P LATERAL_P LC_COLLATE_P LC_CTYPE_P + LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL + LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P + + MAPPING MATCH MATERIALIZED MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE + NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NODE NONE - NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF NULLS_P NUMERIC + NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF + NULLS_P NUMERIC OBJECT_P OF OFF OFFSET OIDS ON ONLY OPERATOR OPTION OPTIONS OR - ORDER OUT_P OUTER_P OVER OVERLAPS OVERLAY OWNED OWNER + ORDER ORDINALITY OUT_P OUTER_P OVER OVERLAPS OVERLAY OWNED OWNER PARSER PARTIAL PARTITION PASSING PASSWORD PAUSE PLACING PLANS POSITION /* PGXC_BEGIN */ PRECEDING PRECISION PREFERRED PRESERVE PREPARE PREPARED PRIMARY /* PGXC_END */ - PRIOR PRIVILEGES PROCEDURAL PROCEDURE + PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROGRAM QUOTE - RANGE READ REAL REASSIGN RECHECK RECURSIVE REF REFERENCES REINDEX + RANGE READ REAL REASSIGN RECHECK RECURSIVE REF REFERENCES REFRESH REINDEX RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA RESET RESTART RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROW ROWS RULE @@ -586,9 +625,9 @@ static void processCASbits(int cas_bits, int location, const char *constrType, UNPAUSE UNTIL UPDATE USER USING VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING - VERBOSE VERSION_P VIEW VOLATILE + VERBOSE VERSION_P VIEW VIEWS VOLATILE - WHEN WHERE WHITESPACE_P WINDOW WITH WITHOUT WORK WRAPPER WRITE + WHEN WHERE WHITESPACE_P WINDOW WITH WITHIN WITHOUT WORK WRAPPER WRITE XML_P XMLATTRIBUTES XMLCONCAT XMLELEMENT XMLEXISTS XMLFOREST XMLPARSE XMLPI XMLROOT XMLSERIALIZE @@ -602,7 +641,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType, * list and so can never be entered directly. The filter in parser.c * creates these tokens when required. */ -%token NULLS_FIRST NULLS_LAST WITH_TIME +%token NULLS_FIRST NULLS_LAST WITH_ORDINALITY WITH_TIME /* Precedence: lowest to highest */ @@ -697,7 +736,8 @@ stmtmulti: stmtmulti ';' stmt ; stmt : - AlterDatabaseStmt + AlterEventTrigStmt + | AlterDatabaseStmt | AlterDatabaseSetStmt | AlterDefaultPrivilegesStmt | AlterDomainStmt @@ -713,7 +753,9 @@ stmt : | AlterObjectSchemaStmt | AlterOwnerStmt | AlterSeqStmt + | AlterSystemStmt | AlterTableStmt + | AlterTblSpcStmt | AlterCompositeTypeStmt | AlterRoleSetStmt | AlterRoleStmt @@ -742,6 +784,7 @@ stmt : | CreateForeignTableStmt | CreateFunctionStmt | CreateGroupStmt + | CreateMatViewStmt | CreateNodeGroupStmt | CreateNodeStmt | CreateOpClassStmt @@ -753,6 +796,7 @@ stmt : | CreateStmt | CreateTableSpaceStmt | CreateTrigStmt + | CreateEventTrigStmt | CreateRoleStmt | CreateUserStmt | CreateUserMappingStmt @@ -791,6 +835,7 @@ stmt : | IndexStmt | InsertStmt | ListenStmt + | RefreshMatViewStmt | LoadStmt | LockStmt | NotifyStmt @@ -890,7 +935,7 @@ AlterOptRoleElem: $$ = makeDefElem("validUntil", (Node *)makeString($3)); } /* Supported but not documented for roles, for use by ALTER GROUP. */ - | USER name_list + | USER role_list { $$ = makeDefElem("rolemembers", (Node *)$2); } @@ -954,19 +999,19 @@ CreateOptRoleElem: { $$ = makeDefElem("sysid", (Node *)makeInteger($2)); } - | ADMIN name_list + | ADMIN role_list { $$ = makeDefElem("adminmembers", (Node *)$2); } - | ROLE name_list + | ROLE role_list { $$ = makeDefElem("rolemembers", (Node *)$2); } - | IN_P ROLE name_list + | IN_P ROLE role_list { $$ = makeDefElem("addroleto", (Node *)$3); } - | IN_P GROUP_P name_list + | IN_P GROUP_P role_list { $$ = makeDefElem("addroleto", (Node *)$3); } @@ -1022,6 +1067,14 @@ AlterRoleSetStmt: n->setstmt = $5; $$ = (Node *)n; } + | ALTER ROLE ALL opt_in_database SetResetClause + { + AlterRoleSetStmt *n = makeNode(AlterRoleSetStmt); + n->role = NULL; + n->database = $4; + n->setstmt = $5; + $$ = (Node *)n; + } ; @@ -1065,14 +1118,14 @@ AlterUserSetStmt: *****************************************************************************/ DropRoleStmt: - DROP ROLE name_list + DROP ROLE role_list { DropRoleStmt *n = makeNode(DropRoleStmt); n->missing_ok = FALSE; n->roles = $3; $$ = (Node *)n; } - | DROP ROLE IF_P EXISTS name_list + | DROP ROLE IF_P EXISTS role_list { DropRoleStmt *n = makeNode(DropRoleStmt); n->missing_ok = TRUE; @@ -1091,14 +1144,14 @@ DropRoleStmt: *****************************************************************************/ DropUserStmt: - DROP USER name_list + DROP USER role_list { DropRoleStmt *n = makeNode(DropRoleStmt); n->missing_ok = FALSE; n->roles = $3; $$ = (Node *)n; } - | DROP USER IF_P EXISTS name_list + | DROP USER IF_P EXISTS role_list { DropRoleStmt *n = makeNode(DropRoleStmt); n->roles = $5; @@ -1133,7 +1186,7 @@ CreateGroupStmt: *****************************************************************************/ AlterGroupStmt: - ALTER GROUP_P RoleId add_drop USER name_list + ALTER GROUP_P RoleId add_drop USER role_list { AlterRoleStmt *n = makeNode(AlterRoleStmt); n->role = $3; @@ -1157,14 +1210,14 @@ add_drop: ADD_P { $$ = +1; } *****************************************************************************/ DropGroupStmt: - DROP GROUP_P name_list + DROP GROUP_P role_list { DropRoleStmt *n = makeNode(DropRoleStmt); n->missing_ok = FALSE; n->roles = $3; $$ = (Node *)n; } - | DROP GROUP_P IF_P EXISTS name_list + | DROP GROUP_P IF_P EXISTS role_list { DropRoleStmt *n = makeNode(DropRoleStmt); n->missing_ok = TRUE; @@ -1191,6 +1244,7 @@ CreateSchemaStmt: n->schemaname = $5; n->authid = $5; n->schemaElts = $6; + n->if_not_exists = false; $$ = (Node *)n; } | CREATE SCHEMA ColId OptSchemaEltList @@ -1200,6 +1254,40 @@ CreateSchemaStmt: n->schemaname = $3; n->authid = NULL; n->schemaElts = $4; + n->if_not_exists = false; + $$ = (Node *)n; + } + | CREATE SCHEMA IF_P NOT EXISTS OptSchemaName AUTHORIZATION RoleId OptSchemaEltList + { + CreateSchemaStmt *n = makeNode(CreateSchemaStmt); + /* One can omit the schema name or the authorization id. */ + if ($6 != NULL) + n->schemaname = $6; + else + n->schemaname = $8; + n->authid = $8; + if ($9 != NIL) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("CREATE SCHEMA IF NOT EXISTS cannot include schema elements"), + parser_errposition(@9))); + n->schemaElts = $9; + n->if_not_exists = true; + $$ = (Node *)n; + } + | CREATE SCHEMA IF_P NOT EXISTS ColId OptSchemaEltList + { + CreateSchemaStmt *n = makeNode(CreateSchemaStmt); + /* ...but not both */ + n->schemaname = $6; + n->authid = NULL; + if ($7 != NIL) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("CREATE SCHEMA IF NOT EXISTS cannot include schema elements"), + parser_errposition(@7))); + n->schemaElts = $7; + n->if_not_exists = true; $$ = (Node *)n; } ; @@ -1210,8 +1298,14 @@ OptSchemaName: ; OptSchemaEltList: - OptSchemaEltList schema_stmt { $$ = lappend($1, $2); } - | /* EMPTY */ { $$ = NIL; } + OptSchemaEltList schema_stmt + { + if (@$ < 0) /* see comments for YYLLOC_DEFAULT */ + @$ = @2; + $$ = lappend($1, $2); + } + | /* EMPTY */ + { $$ = NIL; } ; /* @@ -1232,7 +1326,7 @@ schema_stmt: * * Set PG internal variable * SET name TO 'var_value' - * Include SQL92 syntax (thomas 1997-10-22): + * Include SQL syntax (thomas 1997-10-22): * SET TIME ZONE 'var_value' * *****************************************************************************/ @@ -1278,7 +1372,7 @@ set_rest: | set_rest_more ; -set_rest_more: /* Generic SET syntaxes: */ +generic_set: var_name TO var_list { VariableSetStmt *n = makeNode(VariableSetStmt); @@ -1309,6 +1403,9 @@ set_rest_more: /* Generic SET syntaxes: */ n->name = $1; $$ = n; } + +set_rest_more: /* Generic SET syntaxes: */ + generic_set {$$ = $1;} | var_name FROM CURRENT_P { VariableSetStmt *n = makeNode(VariableSetStmt); @@ -1355,7 +1452,7 @@ set_rest_more: /* Generic SET syntaxes: */ n->kind = VAR_SET_DEFAULT; $$ = n; } - | ROLE ColId_or_Sconst + | ROLE NonReservedWord_or_Sconst { VariableSetStmt *n = makeNode(VariableSetStmt); n->kind = VAR_SET_VALUE; @@ -1363,7 +1460,7 @@ set_rest_more: /* Generic SET syntaxes: */ n->args = list_make1(makeStringConst($2, @2)); $$ = n; } - | SESSION AUTHORIZATION ColId_or_Sconst + | SESSION AUTHORIZATION NonReservedWord_or_Sconst { VariableSetStmt *n = makeNode(VariableSetStmt); n->kind = VAR_SET_VALUE; @@ -1399,10 +1496,7 @@ set_rest_more: /* Generic SET syntaxes: */ var_name: ColId { $$ = $1; } | var_name '.' ColId - { - $$ = palloc(strlen($1) + strlen($3) + 2); - sprintf($$, "%s.%s", $1, $3); - } + { $$ = psprintf("%s.%s", $1, $3); } ; var_list: var_value { $$ = list_make1($1); } @@ -1426,11 +1520,11 @@ opt_boolean_or_string: | FALSE_P { $$ = "false"; } | ON { $$ = "on"; } /* - * OFF is also accepted as a boolean value, but is handled - * by the ColId rule below. The action for booleans and strings + * OFF is also accepted as a boolean value, but is handled by + * the NonReservedWord rule. The action for booleans and strings * is the same, so we don't need to distinguish them here. */ - | ColId_or_Sconst { $$ = $1; } + | NonReservedWord_or_Sconst { $$ = $1; } ; /* Timezone values can be: @@ -1499,8 +1593,8 @@ opt_encoding: | /*EMPTY*/ { $$ = NULL; } ; -ColId_or_Sconst: - ColId { $$ = $1; } +NonReservedWord_or_Sconst: + NonReservedWord { $$ = $1; } | Sconst { $$ = $1; } ; @@ -1623,7 +1717,7 @@ CheckPointStmt: /***************************************************************************** * - * DISCARD { ALL | TEMP | PLANS } + * DISCARD { ALL | TEMP | PLANS | SEQUENCES } * *****************************************************************************/ @@ -1652,14 +1746,21 @@ DiscardStmt: n->target = DISCARD_PLANS; $$ = (Node *) n; } + | DISCARD SEQUENCES + { + DiscardStmt *n = makeNode(DiscardStmt); + n->target = DISCARD_SEQUENCES; + $$ = (Node *) n; + } + ; /***************************************************************************** * - * ALTER [ TABLE | INDEX | SEQUENCE | VIEW ] variations + * ALTER [ TABLE | INDEX | SEQUENCE | VIEW | MATERIALIZED VIEW ] variations * - * Note: we accept all subcommands for each of the four variants, and sort + * Note: we accept all subcommands for each of the five variants, and sort * out what's really legal at execution time. *****************************************************************************/ @@ -1736,6 +1837,24 @@ AlterTableStmt: n->missing_ok = true; $$ = (Node *)n; } + | ALTER MATERIALIZED VIEW qualified_name alter_table_cmds + { + AlterTableStmt *n = makeNode(AlterTableStmt); + n->relation = $4; + n->cmds = $5; + n->relkind = OBJECT_MATVIEW; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER MATERIALIZED VIEW IF_P EXISTS qualified_name alter_table_cmds + { + AlterTableStmt *n = makeNode(AlterTableStmt); + n->relation = $6; + n->cmds = $7; + n->relkind = OBJECT_MATVIEW; + n->missing_ok = true; + $$ = (Node *)n; + } ; alter_table_cmds: @@ -1852,10 +1971,11 @@ alter_table_cmd: n->subtype = AT_AlterColumnType; n->name = $3; n->def = (Node *) def; - /* We only use these three fields of the ColumnDef node */ + /* We only use these fields of the ColumnDef node */ def->typeName = $6; def->collClause = (CollateClause *) $7; def->raw_default = $8; + def->location = @3; $$ = (Node *)n; } /* ALTER FOREIGN TABLE <name> ALTER [COLUMN] <colname> OPTIONS */ @@ -1875,6 +1995,21 @@ alter_table_cmd: n->def = $2; $$ = (Node *)n; } + /* ALTER TABLE <name> ALTER CONSTRAINT ... */ + | ALTER CONSTRAINT name ConstraintAttributeSpec + { + AlterTableCmd *n = makeNode(AlterTableCmd); + Constraint *c = makeNode(Constraint); + n->subtype = AT_AlterConstraint; + n->def = (Node *) c; + c->contype = CONSTR_FOREIGN; /* others not supported, yet */ + c->conname = $3; + processCASbits($4, @4, "ALTER CONSTRAINT statement", + &c->deferrable, + &c->initdeferred, + NULL, NULL, yyscanner); + $$ = (Node *)n; + } /* ALTER TABLE <name> VALIDATE CONSTRAINT ... */ | VALIDATE CONSTRAINT name { @@ -2090,6 +2225,14 @@ alter_table_cmd: n->def = (Node *)$2; $$ = (Node *)n; } + /* ALTER TABLE <name> REPLICA IDENTITY */ + | REPLICA IDENTITY_P replica_identity + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_ReplicaIdentity; + n->def = $3; + $$ = (Node *)n; + } | alter_generic_options { AlterTableCmd *n = makeNode(AlterTableCmd); @@ -2161,6 +2304,37 @@ alter_using: | /* EMPTY */ { $$ = NULL; } ; +replica_identity: + NOTHING + { + ReplicaIdentityStmt *n = makeNode(ReplicaIdentityStmt); + n->identity_type = REPLICA_IDENTITY_NOTHING; + n->name = NULL; + $$ = (Node *) n; + } + | FULL + { + ReplicaIdentityStmt *n = makeNode(ReplicaIdentityStmt); + n->identity_type = REPLICA_IDENTITY_FULL; + n->name = NULL; + $$ = (Node *) n; + } + | DEFAULT + { + ReplicaIdentityStmt *n = makeNode(ReplicaIdentityStmt); + n->identity_type = REPLICA_IDENTITY_DEFAULT; + n->name = NULL; + $$ = (Node *) n; + } + | USING INDEX name + { + ReplicaIdentityStmt *n = makeNode(ReplicaIdentityStmt); + n->identity_type = REPLICA_IDENTITY_INDEX; + n->name = $3; + $$ = (Node *) n; + } +; + reloptions: '(' reloption_list ')' { $$ = $2; } ; @@ -2260,10 +2434,11 @@ alter_type_cmd: n->name = $3; n->def = (Node *) def; n->behavior = $8; - /* We only use these three fields of the ColumnDef node */ + /* We only use these fields of the ColumnDef node */ def->typeName = $6; def->collClause = (CollateClause *) $7; def->raw_default = NULL; + def->location = @3; $$ = (Node *)n; } ; @@ -2296,7 +2471,10 @@ ClosePortalStmt: * * QUERY : * COPY relname [(columnList)] FROM/TO file [WITH] [(options)] - * COPY ( SELECT ... ) TO file [WITH] [(options)] + * COPY ( SELECT ... ) TO file [WITH] [(options)] + * + * where 'file' can be one of: + * { PROGRAM 'command' | STDIN | STDOUT | 'filename' } * * In the preferred syntax the options are comma-separated * and use generic identifiers instead of keywords. The pre-9.0 @@ -2311,14 +2489,21 @@ ClosePortalStmt: *****************************************************************************/ CopyStmt: COPY opt_binary qualified_name opt_column_list opt_oids - copy_from copy_file_name copy_delimiter opt_with copy_options + copy_from opt_program copy_file_name copy_delimiter opt_with copy_options { CopyStmt *n = makeNode(CopyStmt); n->relation = $3; n->query = NULL; n->attlist = $4; n->is_from = $6; - n->filename = $7; + n->is_program = $7; + n->filename = $8; + + if (n->is_program && n->filename == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("STDIN/STDOUT not allowed with PROGRAM"), + parser_errposition(@8))); n->options = NIL; /* Concatenate user-supplied flags */ @@ -2326,21 +2511,29 @@ CopyStmt: COPY opt_binary qualified_name opt_column_list opt_oids n->options = lappend(n->options, $2); if ($5) n->options = lappend(n->options, $5); - if ($8) - n->options = lappend(n->options, $8); - if ($10) - n->options = list_concat(n->options, $10); + if ($9) + n->options = lappend(n->options, $9); + if ($11) + n->options = list_concat(n->options, $11); $$ = (Node *)n; } - | COPY select_with_parens TO copy_file_name opt_with copy_options + | COPY select_with_parens TO opt_program copy_file_name opt_with copy_options { CopyStmt *n = makeNode(CopyStmt); n->relation = NULL; n->query = $2; n->attlist = NIL; n->is_from = false; - n->filename = $4; - n->options = $6; + n->is_program = $4; + n->filename = $5; + n->options = $7; + + if (n->is_program && n->filename == NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("STDIN/STDOUT not allowed with PROGRAM"), + parser_errposition(@5))); + $$ = (Node *)n; } ; @@ -2350,6 +2543,11 @@ copy_from: | TO { $$ = FALSE; } ; +opt_program: + PROGRAM { $$ = TRUE; } + | /* EMPTY */ { $$ = FALSE; } + ; + /* * copy_file_name NULL indicates stdio is used. Whether stdin or stdout is * used depends on the direction. (It really doesn't make sense to copy from @@ -2380,6 +2578,10 @@ copy_opt_item: { $$ = makeDefElem("oids", (Node *)makeInteger(TRUE)); } + | FREEZE + { + $$ = makeDefElem("freeze", (Node *)makeInteger(TRUE)); + } | DELIMITER opt_as Sconst { $$ = makeDefElem("delimiter", (Node *)makeString($3)); @@ -2416,6 +2618,10 @@ copy_opt_item: { $$ = makeDefElem("force_not_null", (Node *)$4); } + | FORCE NULL_P columnList + { + $$ = makeDefElem("force_null", (Node *)$3); + } | ENCODING Sconst { $$ = makeDefElem("encoding", (Node *)makeString($2)); @@ -2711,6 +2917,7 @@ columnDef: ColId Typename create_generic_options ColQualList n->fdwoptions = $3; SplitColQualList($4, &n->constraints, &n->collClause, yyscanner); + n->location = @1; $$ = (Node *)n; } ; @@ -2730,6 +2937,7 @@ columnOptions: ColId WITH OPTIONS ColQualList n->collOid = InvalidOid; SplitColQualList($4, &n->constraints, &n->collClause, yyscanner); + n->location = @1; $$ = (Node *)n; } ; @@ -2770,7 +2978,7 @@ ColConstraint: * to make it explicit. * - thomas 1998-09-13 * - * WITH NULL and NULL are not SQL92-standard syntax elements, + * WITH NULL and NULL are not SQL-standard syntax elements, * so leave them out. Use DEFAULT NULL to explicitly indicate * that a column may have that value. WITH NULL leads to * shift/reduce conflicts with WITH TIME ZONE anyway. @@ -2817,13 +3025,13 @@ ColConstraintElem: n->indexspace = $4; $$ = (Node *)n; } - | CHECK opt_no_inherit '(' a_expr ')' + | CHECK '(' a_expr ')' opt_no_inherit { Constraint *n = makeNode(Constraint); n->contype = CONSTR_CHECK; n->location = @1; - n->is_no_inherit = $2; - n->raw_expr = $4; + n->is_no_inherit = $5; + n->raw_expr = $3; n->cooked_expr = NULL; $$ = (Node *)n; } @@ -2863,10 +3071,10 @@ ColConstraintElem: * combinations. * * See also ConstraintAttributeSpec, which can be used in places where - * there is no parsing conflict. (Note: currently, NOT VALID is an allowed - * clause in ConstraintAttributeSpec, but not here. Someday we might need - * to allow it here too, but for the moment it doesn't seem useful in the - * statements that use ConstraintAttr.) + * there is no parsing conflict. (Note: currently, NOT VALID and NO INHERIT + * are allowed clauses in ConstraintAttributeSpec, but not here. Someday we + * might need to allow them here too, but for the moment it doesn't seem + * useful in the statements that use ConstraintAttr.) */ ConstraintAttr: DEFERRABLE @@ -2943,17 +3151,16 @@ TableConstraint: ; ConstraintElem: - CHECK opt_no_inherit '(' a_expr ')' ConstraintAttributeSpec + CHECK '(' a_expr ')' ConstraintAttributeSpec { Constraint *n = makeNode(Constraint); n->contype = CONSTR_CHECK; n->location = @1; - n->is_no_inherit = $2; - n->raw_expr = $4; + n->raw_expr = $3; n->cooked_expr = NULL; - processCASbits($6, @6, "CHECK", + processCASbits($5, @5, "CHECK", NULL, NULL, &n->skip_validation, - yyscanner); + &n->is_no_inherit, yyscanner); n->initially_valid = !n->skip_validation; $$ = (Node *)n; } @@ -2969,7 +3176,7 @@ ConstraintElem: n->indexspace = $6; processCASbits($7, @7, "UNIQUE", &n->deferrable, &n->initdeferred, NULL, - yyscanner); + NULL, yyscanner); $$ = (Node *)n; } | UNIQUE ExistingIndex ConstraintAttributeSpec @@ -2983,7 +3190,7 @@ ConstraintElem: n->indexspace = NULL; processCASbits($3, @3, "UNIQUE", &n->deferrable, &n->initdeferred, NULL, - yyscanner); + NULL, yyscanner); $$ = (Node *)n; } | PRIMARY KEY '(' columnList ')' opt_definition OptConsTableSpace @@ -2998,7 +3205,7 @@ ConstraintElem: n->indexspace = $7; processCASbits($8, @8, "PRIMARY KEY", &n->deferrable, &n->initdeferred, NULL, - yyscanner); + NULL, yyscanner); $$ = (Node *)n; } | PRIMARY KEY ExistingIndex ConstraintAttributeSpec @@ -3012,7 +3219,7 @@ ConstraintElem: n->indexspace = NULL; processCASbits($4, @4, "PRIMARY KEY", &n->deferrable, &n->initdeferred, NULL, - yyscanner); + NULL, yyscanner); $$ = (Node *)n; } | EXCLUDE access_method_clause '(' ExclusionConstraintList ')' @@ -3030,7 +3237,7 @@ ConstraintElem: n->where_clause = $8; processCASbits($9, @9, "EXCLUDE", &n->deferrable, &n->initdeferred, NULL, - yyscanner); + NULL, yyscanner); $$ = (Node *)n; } | FOREIGN KEY '(' columnList ')' REFERENCES qualified_name @@ -3047,7 +3254,7 @@ ConstraintElem: n->fk_del_action = (char) ($10 & 0xFF); processCASbits($11, @11, "FOREIGN KEY", &n->deferrable, &n->initdeferred, - &n->skip_validation, + &n->skip_validation, NULL, yyscanner); n->initially_valid = !n->skip_validation; $$ = (Node *)n; @@ -3088,11 +3295,11 @@ key_match: MATCH FULL } | MATCH SIMPLE { - $$ = FKCONSTR_MATCH_UNSPECIFIED; + $$ = FKCONSTR_MATCH_SIMPLE; } | /*EMPTY*/ { - $$ = FKCONSTR_MATCH_UNSPECIFIED; + $$ = FKCONSTR_MATCH_SIMPLE; } ; @@ -3262,6 +3469,7 @@ CreateAsStmt: CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt); ctas->query = $6; ctas->into = $4; + ctas->relkind = OBJECT_TABLE; ctas->is_select_into = false; /* cram additional flags into the IntoClause */ $4->rel->relpersistence = $2; @@ -3282,6 +3490,7 @@ create_as_target: $$->options = $3; $$->onCommit = $4; $$->tableSpaceName = $5; + $$->viewQuery = NULL; $$->skipData = false; /* might get changed later */ /* PGXC_BEGIN */ $$->distributeby = $6; @@ -3300,6 +3509,66 @@ opt_with_data: /***************************************************************************** * * QUERY : + * CREATE MATERIALIZED VIEW relname AS SelectStmt + * + *****************************************************************************/ + +CreateMatViewStmt: + CREATE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt opt_with_data + { + CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt); + ctas->query = $7; + ctas->into = $5; + ctas->relkind = OBJECT_MATVIEW; + ctas->is_select_into = false; + /* cram additional flags into the IntoClause */ + $5->rel->relpersistence = $2; + $5->skipData = !($8); + $$ = (Node *) ctas; + } + ; + +create_mv_target: + qualified_name opt_column_list opt_reloptions OptTableSpace + { + $$ = makeNode(IntoClause); + $$->rel = $1; + $$->colNames = $2; + $$->options = $3; + $$->onCommit = ONCOMMIT_NOOP; + $$->tableSpaceName = $4; + $$->viewQuery = NULL; /* filled at analysis time */ + $$->skipData = false; /* might get changed later */ + } + ; + +OptNoLog: UNLOGGED { $$ = RELPERSISTENCE_UNLOGGED; } + | /*EMPTY*/ { $$ = RELPERSISTENCE_PERMANENT; } + ; + + +/***************************************************************************** + * + * QUERY : + * REFRESH MATERIALIZED VIEW qualified_name + * + *****************************************************************************/ + +RefreshMatViewStmt: + REFRESH MATERIALIZED VIEW opt_concurrently qualified_name opt_with_data + { + RefreshMatViewStmt *n = makeNode(RefreshMatViewStmt); + n->concurrent = $4; + n->relation = $5; + n->skipData = !($6); + $$ = (Node *) n; + } + ; + + +/***************************************************************************** + * + * QUERY : * CREATE SEQUENCE seqname * ALTER SEQUENCE seqname * @@ -3428,7 +3697,7 @@ NumericOnly_list: NumericOnly { $$ = list_make1($1); } *****************************************************************************/ CreatePLangStmt: - CREATE opt_or_replace opt_trusted opt_procedural LANGUAGE ColId_or_Sconst + CREATE opt_or_replace opt_trusted opt_procedural LANGUAGE NonReservedWord_or_Sconst { CreatePLangStmt *n = makeNode(CreatePLangStmt); n->replace = $2; @@ -3440,7 +3709,7 @@ CreatePLangStmt: n->pltrusted = false; $$ = (Node *)n; } - | CREATE opt_or_replace opt_trusted opt_procedural LANGUAGE ColId_or_Sconst + | CREATE opt_or_replace opt_trusted opt_procedural LANGUAGE NonReservedWord_or_Sconst HANDLER handler_name opt_inline_handler opt_validator { CreatePLangStmt *n = makeNode(CreatePLangStmt); @@ -3484,7 +3753,7 @@ opt_validator: ; DropPLangStmt: - DROP opt_procedural LANGUAGE ColId_or_Sconst opt_drop_behavior + DROP opt_procedural LANGUAGE NonReservedWord_or_Sconst opt_drop_behavior { DropStmt *n = makeNode(DropStmt); n->removeType = OBJECT_LANGUAGE; @@ -3495,7 +3764,7 @@ DropPLangStmt: n->concurrent = false; $$ = (Node *)n; } - | DROP opt_procedural LANGUAGE IF_P EXISTS ColId_or_Sconst opt_drop_behavior + | DROP opt_procedural LANGUAGE IF_P EXISTS NonReservedWord_or_Sconst opt_drop_behavior { DropStmt *n = makeNode(DropStmt); n->removeType = OBJECT_LANGUAGE; @@ -3519,12 +3788,13 @@ opt_procedural: * *****************************************************************************/ -CreateTableSpaceStmt: CREATE TABLESPACE name OptTableSpaceOwner LOCATION Sconst +CreateTableSpaceStmt: CREATE TABLESPACE name OptTableSpaceOwner LOCATION Sconst opt_reloptions { CreateTableSpaceStmt *n = makeNode(CreateTableSpaceStmt); n->tablespacename = $3; n->owner = $4; n->location = $6; + n->options = $7; $$ = (Node *) n; } ; @@ -3597,11 +3867,11 @@ create_extension_opt_item: { $$ = makeDefElem("schema", (Node *)makeString($2)); } - | VERSION_P ColId_or_Sconst + | VERSION_P NonReservedWord_or_Sconst { $$ = makeDefElem("new_version", (Node *)makeString($2)); } - | FROM ColId_or_Sconst + | FROM NonReservedWord_or_Sconst { $$ = makeDefElem("old_version", (Node *)makeString($2)); } @@ -3630,7 +3900,7 @@ alter_extension_opt_list: ; alter_extension_opt_item: - TO ColId_or_Sconst + TO NonReservedWord_or_Sconst { $$ = makeDefElem("new_version", (Node *)makeString($2)); } @@ -3650,7 +3920,7 @@ AlterExtensionContentsStmt: n->action = $4; n->objtype = OBJECT_AGGREGATE; n->objname = $6; - n->objargs = $7; + n->objargs = extractAggrArgTypes($7); $$ = (Node *)n; } | ALTER EXTENSION name add_drop CAST '(' Typename AS Typename ')' @@ -3748,6 +4018,15 @@ AlterExtensionContentsStmt: n->objname = list_make1(makeString($6)); $$ = (Node *)n; } + | ALTER EXTENSION name add_drop EVENT TRIGGER name + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_EVENT_TRIGGER; + n->objname = list_make1(makeString($7)); + $$ = (Node *)n; + } | ALTER EXTENSION name add_drop TABLE any_name { AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); @@ -3811,6 +4090,15 @@ AlterExtensionContentsStmt: n->objname = $6; $$ = (Node *)n; } + | ALTER EXTENSION name add_drop MATERIALIZED VIEW any_name + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_MATVIEW; + n->objname = $7; + $$ = (Node *)n; + } | ALTER EXTENSION name add_drop FOREIGN TABLE any_name { AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); @@ -3902,7 +4190,7 @@ DropFdwStmt: DROP FOREIGN DATA_P WRAPPER name opt_drop_behavior $$ = (Node *) n; } | DROP FOREIGN DATA_P WRAPPER IF_P EXISTS name opt_drop_behavior - { + { DropStmt *n = makeNode(DropStmt); n->removeType = OBJECT_FDW; n->objects = list_make1(list_make1(makeString($7))); @@ -4064,7 +4352,7 @@ DropForeignServerStmt: DROP SERVER name opt_drop_behavior $$ = (Node *) n; } | DROP SERVER IF_P EXISTS name opt_drop_behavior - { + { DropStmt *n = makeNode(DropStmt); n->removeType = OBJECT_FOREIGN_SERVER; n->objects = list_make1(list_make1(makeString($5))); @@ -4118,57 +4406,37 @@ AlterForeignServerStmt: ALTER SERVER name foreign_server_version alter_generic_o CreateForeignTableStmt: CREATE FOREIGN TABLE qualified_name - OptForeignTableElementList + '(' OptTableElementList ')' SERVER name create_generic_options { CreateForeignTableStmt *n = makeNode(CreateForeignTableStmt); $4->relpersistence = RELPERSISTENCE_PERMANENT; n->base.relation = $4; - n->base.tableElts = $5; + n->base.tableElts = $6; n->base.inhRelations = NIL; n->base.if_not_exists = false; /* FDW-specific data */ - n->servername = $7; - n->options = $8; + n->servername = $9; + n->options = $10; $$ = (Node *) n; } | CREATE FOREIGN TABLE IF_P NOT EXISTS qualified_name - OptForeignTableElementList + '(' OptTableElementList ')' SERVER name create_generic_options { CreateForeignTableStmt *n = makeNode(CreateForeignTableStmt); $7->relpersistence = RELPERSISTENCE_PERMANENT; n->base.relation = $7; - n->base.tableElts = $8; + n->base.tableElts = $9; n->base.inhRelations = NIL; n->base.if_not_exists = true; /* FDW-specific data */ - n->servername = $10; - n->options = $11; + n->servername = $12; + n->options = $13; $$ = (Node *) n; } ; -OptForeignTableElementList: - '(' ForeignTableElementList ')' { $$ = $2; } - | '(' ')' { $$ = NIL; } - ; - -ForeignTableElementList: - ForeignTableElement - { - $$ = list_make1($1); - } - | ForeignTableElementList ',' ForeignTableElement - { - $$ = lappend($1, $3); - } - ; - -ForeignTableElement: - columnDef { $$ = $1; } - ; - /***************************************************************************** * * QUERY: @@ -4310,7 +4578,7 @@ CreateTrigStmt: n->isconstraint = TRUE; processCASbits($10, @10, "TRIGGER", &n->deferrable, &n->initdeferred, NULL, - yyscanner); + NULL, yyscanner); n->constrrel = $9; $$ = (Node *)n; } @@ -4447,6 +4715,7 @@ ConstraintAttributeElem: | INITIALLY IMMEDIATE { $$ = CAS_INITIALLY_IMMEDIATE; } | INITIALLY DEFERRED { $$ = CAS_INITIALLY_DEFERRED; } | NOT VALID { $$ = CAS_NOT_VALID; } + | NO INHERIT { $$ = CAS_NO_INHERIT; } ; @@ -4479,6 +4748,74 @@ DropTrigStmt: /***************************************************************************** * * QUERIES : + * CREATE EVENT TRIGGER ... + * ALTER EVENT TRIGGER ... + * + *****************************************************************************/ + +CreateEventTrigStmt: + CREATE EVENT TRIGGER name ON ColLabel + EXECUTE PROCEDURE func_name '(' ')' + { + CreateEventTrigStmt *n = makeNode(CreateEventTrigStmt); + n->trigname = $4; + n->eventname = $6; + n->whenclause = NULL; + n->funcname = $9; + $$ = (Node *)n; + } + | CREATE EVENT TRIGGER name ON ColLabel + WHEN event_trigger_when_list + EXECUTE PROCEDURE func_name '(' ')' + { + CreateEventTrigStmt *n = makeNode(CreateEventTrigStmt); + n->trigname = $4; + n->eventname = $6; + n->whenclause = $8; + n->funcname = $11; + $$ = (Node *)n; + } + ; + +event_trigger_when_list: + event_trigger_when_item + { $$ = list_make1($1); } + | event_trigger_when_list AND event_trigger_when_item + { $$ = lappend($1, $3); } + ; + +event_trigger_when_item: + ColId IN_P '(' event_trigger_value_list ')' + { $$ = makeDefElem($1, (Node *) $4); } + ; + +event_trigger_value_list: + SCONST + { $$ = list_make1(makeString($1)); } + | event_trigger_value_list ',' SCONST + { $$ = lappend($1, makeString($3)); } + ; + +AlterEventTrigStmt: + ALTER EVENT TRIGGER name enable_trigger + { + AlterEventTrigStmt *n = makeNode(AlterEventTrigStmt); + n->trigname = $4; + n->tgenabled = $5; + $$ = (Node *) n; + } + ; + +enable_trigger: + ENABLE_P { $$ = TRIGGER_FIRES_ON_ORIGIN; } + | ENABLE_P REPLICA { $$ = TRIGGER_FIRES_ON_REPLICA; } + | ENABLE_P ALWAYS { $$ = TRIGGER_FIRES_ALWAYS; } + | DISABLE_P { $$ = TRIGGER_DISABLED; } + ; + +/***************************************************************************** + * + * QUERIES : * CREATE ASSERTION ... * DROP ASSERTION ... * @@ -4494,7 +4831,7 @@ CreateAssertStmt: n->isconstraint = TRUE; processCASbits($8, @8, "ASSERTION", &n->deferrable, &n->initdeferred, NULL, - yyscanner); + NULL, yyscanner); ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), @@ -4684,10 +5021,6 @@ def_arg: func_type { $$ = (Node *)$1; } | Sconst { $$ = (Node *)makeString($1); } ; -aggr_args: '(' type_list ')' { $$ = $2; } - | '(' '*' ')' { $$ = NIL; } - ; - old_aggr_definition: '(' old_aggr_list ')' { $$ = $2; } ; @@ -4724,35 +5057,42 @@ enum_val_list: Sconst *****************************************************************************/ AlterEnumStmt: - ALTER TYPE_P any_name ADD_P VALUE_P Sconst + ALTER TYPE_P any_name ADD_P VALUE_P opt_if_not_exists Sconst { AlterEnumStmt *n = makeNode(AlterEnumStmt); n->typeName = $3; - n->newVal = $6; + n->newVal = $7; n->newValNeighbor = NULL; n->newValIsAfter = true; + n->skipIfExists = $6; $$ = (Node *) n; } - | ALTER TYPE_P any_name ADD_P VALUE_P Sconst BEFORE Sconst + | ALTER TYPE_P any_name ADD_P VALUE_P opt_if_not_exists Sconst BEFORE Sconst { AlterEnumStmt *n = makeNode(AlterEnumStmt); n->typeName = $3; - n->newVal = $6; - n->newValNeighbor = $8; + n->newVal = $7; + n->newValNeighbor = $9; n->newValIsAfter = false; + n->skipIfExists = $6; $$ = (Node *) n; } - | ALTER TYPE_P any_name ADD_P VALUE_P Sconst AFTER Sconst + | ALTER TYPE_P any_name ADD_P VALUE_P opt_if_not_exists Sconst AFTER Sconst { AlterEnumStmt *n = makeNode(AlterEnumStmt); n->typeName = $3; - n->newVal = $6; - n->newValNeighbor = $8; + n->newVal = $7; + n->newValNeighbor = $9; n->newValIsAfter = true; + n->skipIfExists = $6; $$ = (Node *) n; } ; +opt_if_not_exists: IF_P NOT EXISTS { $$ = true; } + | /* empty */ { $$ = false; } + ; + /***************************************************************************** * @@ -4982,7 +5322,7 @@ DropOpFamilyStmt: * *****************************************************************************/ DropOwnedStmt: - DROP OWNED BY name_list opt_drop_behavior + DROP OWNED BY role_list opt_drop_behavior { DropOwnedStmt *n = makeNode(DropOwnedStmt); n->roles = $4; @@ -4992,7 +5332,7 @@ DropOwnedStmt: ; ReassignOwnedStmt: - REASSIGN OWNED BY name_list TO name + REASSIGN OWNED BY role_list TO name { ReassignOwnedStmt *n = makeNode(ReassignOwnedStmt); n->roles = $4; @@ -5047,7 +5387,7 @@ DropStmt: DROP drop_type IF_P EXISTS any_name_list opt_drop_behavior { DropStmt *n = makeNode(DropStmt); n->removeType = OBJECT_INDEX; - n->missing_ok = FALSE; + n->missing_ok = TRUE; n->objects = $6; n->arguments = NIL; n->behavior = $7; @@ -5060,8 +5400,10 @@ DropStmt: DROP drop_type IF_P EXISTS any_name_list opt_drop_behavior drop_type: TABLE { $$ = OBJECT_TABLE; } | SEQUENCE { $$ = OBJECT_SEQUENCE; } | VIEW { $$ = OBJECT_VIEW; } + | MATERIALIZED VIEW { $$ = OBJECT_MATVIEW; } | INDEX { $$ = OBJECT_INDEX; } | FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; } + | EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; } | TYPE_P { $$ = OBJECT_TYPE; } | DOMAIN_P { $$ = OBJECT_DOMAIN; } | COLLATION { $$ = OBJECT_COLLATION; } @@ -5119,19 +5461,25 @@ opt_restart_seqs: * The COMMENT ON statement can take different forms based upon the type of * the object associated with the comment. The form of the statement is: * - * COMMENT ON [ [ DATABASE | DOMAIN | INDEX | SEQUENCE | TABLE | TYPE | VIEW | - * COLLATION | CONVERSION | LANGUAGE | OPERATOR CLASS | - * LARGE OBJECT | CAST | COLUMN | SCHEMA | TABLESPACE | - * EXTENSION | ROLE | TEXT SEARCH PARSER | - * TEXT SEARCH DICTIONARY | TEXT SEARCH TEMPLATE | - * TEXT SEARCH CONFIGURATION | FOREIGN TABLE | - * FOREIGN DATA WRAPPER | SERVER ] <objname> | + * COMMENT ON [ [ CONVERSION | COLLATION | DATABASE | DOMAIN | + * EXTENSION | EVENT TRIGGER | FOREIGN DATA WRAPPER | + * FOREIGN TABLE | INDEX | [PROCEDURAL] LANGUAGE | + * MATERIALIZED VIEW | ROLE | SCHEMA | SEQUENCE | + * SERVER | TABLE | TABLESPACE | + * TEXT SEARCH CONFIGURATION | TEXT SEARCH DICTIONARY | + * TEXT SEARCH PARSER | TEXT SEARCH TEMPLATE | TYPE | + * VIEW] <objname> | * AGGREGATE <aggname> (arg1, ...) | + * CAST (<src type> AS <dst type>) | + * COLUMN <relname>.<colname> | + * CONSTRAINT <constraintname> ON <relname> | * FUNCTION <funcname> (arg1, arg2, ...) | + * LARGE OBJECT <oid> | * OPERATOR <op> (leftoperand_typ, rightoperand_typ) | - * TRIGGER <triggername> ON <relname> | - * CONSTRAINT <constraintname> ON <relname> | - * RULE <rulename> ON <relname> ] + * OPERATOR CLASS <name> USING <access-method> | + * OPERATOR FAMILY <name> USING <access-method> | + * RULE <rulename> ON <relname> | + * TRIGGER <triggername> ON <relname> ] * IS 'text' * *****************************************************************************/ @@ -5151,7 +5499,7 @@ CommentStmt: CommentStmt *n = makeNode(CommentStmt); n->objtype = OBJECT_AGGREGATE; n->objname = $4; - n->objargs = $5; + n->objargs = extractAggrArgTypes($5); n->comment = $7; $$ = (Node *) n; } @@ -5255,38 +5603,6 @@ CommentStmt: n->comment = $7; $$ = (Node *) n; } - | COMMENT ON TEXT_P SEARCH PARSER any_name IS comment_text - { - CommentStmt *n = makeNode(CommentStmt); - n->objtype = OBJECT_TSPARSER; - n->objname = $6; - n->comment = $8; - $$ = (Node *) n; - } - | COMMENT ON TEXT_P SEARCH DICTIONARY any_name IS comment_text - { - CommentStmt *n = makeNode(CommentStmt); - n->objtype = OBJECT_TSDICTIONARY; - n->objname = $6; - n->comment = $8; - $$ = (Node *) n; - } - | COMMENT ON TEXT_P SEARCH TEMPLATE any_name IS comment_text - { - CommentStmt *n = makeNode(CommentStmt); - n->objtype = OBJECT_TSTEMPLATE; - n->objname = $6; - n->comment = $8; - $$ = (Node *) n; - } - | COMMENT ON TEXT_P SEARCH CONFIGURATION any_name IS comment_text - { - CommentStmt *n = makeNode(CommentStmt); - n->objtype = OBJECT_TSCONFIGURATION; - n->objname = $6; - n->comment = $8; - $$ = (Node *) n; - } ; comment_type: @@ -5299,6 +5615,7 @@ comment_type: | DOMAIN_P { $$ = OBJECT_DOMAIN; } | TYPE_P { $$ = OBJECT_TYPE; } | VIEW { $$ = OBJECT_VIEW; } + | MATERIALIZED VIEW { $$ = OBJECT_MATVIEW; } | COLLATION { $$ = OBJECT_COLLATION; } | CONVERSION_P { $$ = OBJECT_CONVERSION; } | TABLESPACE { $$ = OBJECT_TABLESPACE; } @@ -5307,6 +5624,11 @@ comment_type: | FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; } | SERVER { $$ = OBJECT_FOREIGN_SERVER; } | FOREIGN DATA_P WRAPPER { $$ = OBJECT_FDW; } + | EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; } + | TEXT_P SEARCH CONFIGURATION { $$ = OBJECT_TSCONFIGURATION; } + | TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; } + | TEXT_P SEARCH PARSER { $$ = OBJECT_TSPARSER; } + | TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; } ; comment_text: @@ -5343,7 +5665,7 @@ SecLabelStmt: n->provider = $3; n->objtype = OBJECT_AGGREGATE; n->objname = $6; - n->objargs = $7; + n->objargs = extractAggrArgTypes($7); n->label = $9; $$ = (Node *) n; } @@ -5382,13 +5704,14 @@ SecLabelStmt: } ; -opt_provider: FOR ColId_or_Sconst { $$ = $2; } - | /* empty */ { $$ = NULL; } +opt_provider: FOR NonReservedWord_or_Sconst { $$ = $2; } + | /* empty */ { $$ = NULL; } ; security_label_type: COLUMN { $$ = OBJECT_COLUMN; } | DATABASE { $$ = OBJECT_DATABASE; } + | EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; } | FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; } | SCHEMA { $$ = OBJECT_SCHEMA; } | SEQUENCE { $$ = OBJECT_SEQUENCE; } @@ -5398,6 +5721,7 @@ security_label_type: | TABLESPACE { $$ = OBJECT_TABLESPACE; } | TYPE_P { $$ = OBJECT_TYPE; } | VIEW { $$ = OBJECT_VIEW; } + | MATERIALIZED VIEW { $$ = OBJECT_MATVIEW; } ; security_label: Sconst { $$ = $1; } @@ -5874,7 +6198,7 @@ function_with_argtypes: *****************************************************************************/ GrantRoleStmt: - GRANT privilege_list TO name_list opt_grant_admin_option opt_granted_by + GRANT privilege_list TO role_list opt_grant_admin_option opt_granted_by { GrantRoleStmt *n = makeNode(GrantRoleStmt); n->is_grant = true; @@ -5887,7 +6211,7 @@ GrantRoleStmt: ; RevokeRoleStmt: - REVOKE privilege_list FROM name_list opt_granted_by opt_drop_behavior + REVOKE privilege_list FROM role_list opt_granted_by opt_drop_behavior { GrantRoleStmt *n = makeNode(GrantRoleStmt); n->is_grant = false; @@ -5897,7 +6221,7 @@ RevokeRoleStmt: n->behavior = $6; $$ = (Node*)n; } - | REVOKE ADMIN OPTION FOR privilege_list FROM name_list opt_granted_by opt_drop_behavior + | REVOKE ADMIN OPTION FOR privilege_list FROM role_list opt_granted_by opt_drop_behavior { GrantRoleStmt *n = makeNode(GrantRoleStmt); n->is_grant = false; @@ -5943,11 +6267,11 @@ DefACLOption: { $$ = makeDefElem("schemas", (Node *)$3); } - | FOR ROLE name_list + | FOR ROLE role_list { $$ = makeDefElem("roles", (Node *)$3); } - | FOR USER name_list + | FOR USER role_list { $$ = makeDefElem("roles", (Node *)$3); } @@ -6031,7 +6355,14 @@ IndexStmt: CREATE opt_unique INDEX opt_concurrently opt_index_name n->options = $12; n->tableSpace = $13; n->whereClause = $14; + n->excludeOpNames = NIL; + n->idxcomment = NULL; n->indexOid = InvalidOid; + n->oldNode = InvalidOid; + n->primary = false; + n->isconstraint = false; + n->deferrable = false; + n->initdeferred = false; $$ = (Node *)n; } ; @@ -6076,7 +6407,7 @@ index_elem: ColId opt_collate opt_class opt_asc_desc opt_nulls_order $$->ordering = $4; $$->nulls_ordering = $5; } - | func_expr opt_collate opt_class opt_asc_desc opt_nulls_order + | func_expr_windowless opt_collate opt_class opt_asc_desc opt_nulls_order { $$ = makeNode(IndexElem); $$->name = NULL; @@ -6321,6 +6652,71 @@ func_arg_with_default: } ; +/* Aggregate args can be most things that function args can be */ +aggr_arg: func_arg + { + if (!($1->mode == FUNC_PARAM_IN || + $1->mode == FUNC_PARAM_VARIADIC)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("aggregates cannot have output arguments"), + parser_errposition(@1))); + $$ = $1; + } + ; + +/* + * The SQL standard offers no guidance on how to declare aggregate argument + * lists, since it doesn't have CREATE AGGREGATE etc. We accept these cases: + * + * (*) - normal agg with no args + * (aggr_arg,...) - normal agg with args + * (ORDER BY aggr_arg,...) - ordered-set agg with no direct args + * (aggr_arg,... ORDER BY aggr_arg,...) - ordered-set agg with direct args + * + * The zero-argument case is spelled with '*' for consistency with COUNT(*). + * + * An additional restriction is that if the direct-args list ends in a + * VARIADIC item, the ordered-args list must contain exactly one item that + * is also VARIADIC with the same type. This allows us to collapse the two + * VARIADIC items into one, which is necessary to represent the aggregate in + * pg_proc. We check this at the grammar stage so that we can return a list + * in which the second VARIADIC item is already discarded, avoiding extra work + * in cases such as DROP AGGREGATE. + * + * The return value of this production is a two-element list, in which the + * first item is a sublist of FunctionParameter nodes (with any duplicate + * VARIADIC item already dropped, as per above) and the second is an integer + * Value node, containing -1 if there was no ORDER BY and otherwise the number + * of argument declarations before the ORDER BY. (If this number is equal + * to the first sublist's length, then we dropped a duplicate VARIADIC item.) + * This representation is passed as-is to CREATE AGGREGATE; for operations + * on existing aggregates, we can just apply extractArgTypes to the first + * sublist. + */ +aggr_args: '(' '*' ')' + { + $$ = list_make2(NIL, makeInteger(-1)); + } + | '(' aggr_args_list ')' + { + $$ = list_make2($2, makeInteger(-1)); + } + | '(' ORDER BY aggr_args_list ')' + { + $$ = list_make2($4, makeInteger(0)); + } + | '(' aggr_args_list ORDER BY aggr_args_list ')' + { + /* this is the only case requiring consistency checking */ + $$ = makeOrderedSetArgs($2, $5, yyscanner); + } + ; + +aggr_args_list: + aggr_arg { $$ = list_make1($1); } + | aggr_args_list ',' aggr_arg { $$ = lappend($1, $3); } + ; createfunc_opt_list: /* Must be at least one to prevent conflict */ @@ -6400,7 +6796,7 @@ createfunc_opt_item: { $$ = makeDefElem("as", (Node *)$2); } - | LANGUAGE ColId_or_Sconst + | LANGUAGE NonReservedWord_or_Sconst { $$ = makeDefElem("language", (Node *)makeString($2)); } @@ -6520,7 +6916,7 @@ RemoveAggrStmt: DropStmt *n = makeNode(DropStmt); n->removeType = OBJECT_AGGREGATE; n->objects = list_make1($3); - n->arguments = list_make1($4); + n->arguments = list_make1(extractAggrArgTypes($4)); n->behavior = $5; n->missing_ok = false; n->concurrent = false; @@ -6531,7 +6927,7 @@ RemoveAggrStmt: DropStmt *n = makeNode(DropStmt); n->removeType = OBJECT_AGGREGATE; n->objects = list_make1($5); - n->arguments = list_make1($6); + n->arguments = list_make1(extractAggrArgTypes($6)); n->behavior = $7; n->missing_ok = true; n->concurrent = false; @@ -6615,7 +7011,7 @@ dostmt_opt_item: { $$ = makeDefElem("as", (Node *)makeString($1)); } - | LANGUAGE ColId_or_Sconst + | LANGUAGE NonReservedWord_or_Sconst { $$ = makeDefElem("language", (Node *)makeString($2)); } @@ -6738,6 +7134,128 @@ opt_force: FORCE { $$ = TRUE; } /***************************************************************************** * + * ALTER TABLESPACE + * + *****************************************************************************/ + +AlterTblSpcStmt: ALTER TABLESPACE name MOVE ALL TO name opt_nowait + { + AlterTableSpaceMoveStmt *n = + makeNode(AlterTableSpaceMoveStmt); + n->orig_tablespacename = $3; + n->objtype = -1; + n->move_all = true; + n->roles = NIL; + n->new_tablespacename = $7; + n->nowait = $8; + $$ = (Node *)n; + } + | ALTER TABLESPACE name MOVE TABLES TO name opt_nowait + { + AlterTableSpaceMoveStmt *n = + makeNode(AlterTableSpaceMoveStmt); + n->orig_tablespacename = $3; + n->objtype = OBJECT_TABLE; + n->move_all = false; + n->roles = NIL; + n->new_tablespacename = $7; + n->nowait = $8; + $$ = (Node *)n; + } + | ALTER TABLESPACE name MOVE INDEXES TO name opt_nowait + { + AlterTableSpaceMoveStmt *n = + makeNode(AlterTableSpaceMoveStmt); + n->orig_tablespacename = $3; + n->objtype = OBJECT_INDEX; + n->move_all = false; + n->roles = NIL; + n->new_tablespacename = $7; + n->nowait = $8; + $$ = (Node *)n; + } + | ALTER TABLESPACE name MOVE MATERIALIZED VIEWS TO name opt_nowait + { + AlterTableSpaceMoveStmt *n = + makeNode(AlterTableSpaceMoveStmt); + n->orig_tablespacename = $3; + n->objtype = OBJECT_MATVIEW; + n->move_all = false; + n->roles = NIL; + n->new_tablespacename = $8; + n->nowait = $9; + $$ = (Node *)n; + } + | ALTER TABLESPACE name MOVE ALL OWNED BY role_list TO name opt_nowait + { + AlterTableSpaceMoveStmt *n = + makeNode(AlterTableSpaceMoveStmt); + n->orig_tablespacename = $3; + n->objtype = -1; + n->move_all = true; + n->roles = $8; + n->new_tablespacename = $10; + n->nowait = $11; + $$ = (Node *)n; + } + | ALTER TABLESPACE name MOVE TABLES OWNED BY role_list TO name opt_nowait + { + AlterTableSpaceMoveStmt *n = + makeNode(AlterTableSpaceMoveStmt); + n->orig_tablespacename = $3; + n->objtype = OBJECT_TABLE; + n->move_all = false; + n->roles = $8; + n->new_tablespacename = $10; + n->nowait = $11; + $$ = (Node *)n; + } + | ALTER TABLESPACE name MOVE INDEXES OWNED BY role_list TO name opt_nowait + { + AlterTableSpaceMoveStmt *n = + makeNode(AlterTableSpaceMoveStmt); + n->orig_tablespacename = $3; + n->objtype = OBJECT_INDEX; + n->move_all = false; + n->roles = $8; + n->new_tablespacename = $10; + n->nowait = $11; + $$ = (Node *)n; + } + | ALTER TABLESPACE name MOVE MATERIALIZED VIEWS OWNED BY role_list TO name opt_nowait + { + AlterTableSpaceMoveStmt *n = + makeNode(AlterTableSpaceMoveStmt); + n->orig_tablespacename = $3; + n->objtype = OBJECT_MATVIEW; + n->move_all = false; + n->roles = $9; + n->new_tablespacename = $11; + n->nowait = $12; + $$ = (Node *)n; + } + | ALTER TABLESPACE name SET reloptions + { + AlterTableSpaceOptionsStmt *n = + makeNode(AlterTableSpaceOptionsStmt); + n->tablespacename = $3; + n->options = $5; + n->isReset = FALSE; + $$ = (Node *)n; + } + | ALTER TABLESPACE name RESET reloptions + { + AlterTableSpaceOptionsStmt *n = + makeNode(AlterTableSpaceOptionsStmt); + n->tablespacename = $3; + n->options = $5; + n->isReset = TRUE; + $$ = (Node *)n; + } + ; + +/***************************************************************************** + * * ALTER THING name RENAME TO newname * *****************************************************************************/ @@ -6747,7 +7265,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name RenameStmt *n = makeNode(RenameStmt); n->renameType = OBJECT_AGGREGATE; n->object = $3; - n->objarg = $4; + n->objarg = extractAggrArgTypes($4); n->newname = $7; n->missing_ok = false; $$ = (Node *)n; @@ -6802,7 +7320,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name { RenameStmt *n = makeNode(RenameStmt); n->renameType = OBJECT_FDW; - n->subname = $5; + n->object = list_make1(makeString($5)); n->newname = $8; n->missing_ok = false; $$ = (Node *)n; @@ -6830,7 +7348,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name { RenameStmt *n = makeNode(RenameStmt); n->renameType = OBJECT_LANGUAGE; - n->subname = $4; + n->object = list_make1(makeString($4)); n->newname = $7; n->missing_ok = false; $$ = (Node *)n; @@ -6840,7 +7358,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name RenameStmt *n = makeNode(RenameStmt); n->renameType = OBJECT_OPCLASS; n->object = $4; - n->subname = $6; + n->objarg = list_make1(makeString($6)); n->newname = $9; n->missing_ok = false; $$ = (Node *)n; @@ -6850,7 +7368,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name RenameStmt *n = makeNode(RenameStmt); n->renameType = OBJECT_OPFAMILY; n->object = $4; - n->subname = $6; + n->objarg = list_make1(makeString($6)); n->newname = $9; n->missing_ok = false; $$ = (Node *)n; @@ -6868,7 +7386,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name { RenameStmt *n = makeNode(RenameStmt); n->renameType = OBJECT_FOREIGN_SERVER; - n->subname = $3; + n->object = list_make1(makeString($3)); n->newname = $6; n->missing_ok = false; $$ = (Node *)n; @@ -6933,6 +7451,26 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name n->missing_ok = true; $$ = (Node *)n; } + | ALTER MATERIALIZED VIEW qualified_name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_MATVIEW; + n->relation = $4; + n->subname = NULL; + n->newname = $7; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER MATERIALIZED VIEW IF_P EXISTS qualified_name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_MATVIEW; + n->relation = $6; + n->subname = NULL; + n->newname = $9; + n->missing_ok = true; + $$ = (Node *)n; + } | ALTER INDEX qualified_name RENAME TO name { RenameStmt *n = makeNode(RenameStmt); @@ -6995,6 +7533,28 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name n->missing_ok = true; $$ = (Node *)n; } + | ALTER MATERIALIZED VIEW qualified_name RENAME opt_column name TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_COLUMN; + n->relationType = OBJECT_MATVIEW; + n->relation = $4; + n->subname = $7; + n->newname = $9; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER MATERIALIZED VIEW IF_P EXISTS qualified_name RENAME opt_column name TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_COLUMN; + n->relationType = OBJECT_MATVIEW; + n->relation = $6; + n->subname = $9; + n->newname = $11; + n->missing_ok = true; + $$ = (Node *)n; + } | ALTER TABLE relation_expr RENAME CONSTRAINT name TO name { RenameStmt *n = makeNode(RenameStmt); @@ -7027,6 +7587,16 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name n->missing_ok = true; $$ = (Node *)n; } + | ALTER RULE name ON qualified_name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_RULE; + n->relation = $5; + n->subname = $3; + n->newname = $8; + n->missing_ok = false; + $$ = (Node *)n; + } | ALTER TRIGGER name ON qualified_name RENAME TO name { RenameStmt *n = makeNode(RenameStmt); @@ -7037,6 +7607,14 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name n->missing_ok = false; $$ = (Node *)n; } + | ALTER EVENT TRIGGER name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_EVENT_TRIGGER; + n->object = list_make1(makeString($4)); + n->newname = $7; + $$ = (Node *)n; + } | ALTER ROLE RoleId RENAME TO RoleId { RenameStmt *n = makeNode(RenameStmt); @@ -7064,24 +7642,6 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name n->missing_ok = false; $$ = (Node *)n; } - | ALTER TABLESPACE name SET reloptions - { - AlterTableSpaceOptionsStmt *n = - makeNode(AlterTableSpaceOptionsStmt); - n->tablespacename = $3; - n->options = $5; - n->isReset = FALSE; - $$ = (Node *)n; - } - | ALTER TABLESPACE name RESET reloptions - { - AlterTableSpaceOptionsStmt *n = - makeNode(AlterTableSpaceOptionsStmt); - n->tablespacename = $3; - n->options = $5; - n->isReset = TRUE; - $$ = (Node *)n; - } | ALTER TEXT_P SEARCH PARSER any_name RENAME TO name { RenameStmt *n = makeNode(RenameStmt); @@ -7161,7 +7721,7 @@ AlterObjectSchemaStmt: AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); n->objectType = OBJECT_AGGREGATE; n->object = $3; - n->objarg = $4; + n->objarg = extractAggrArgTypes($4); n->newschema = $7; n->missing_ok = false; $$ = (Node *)n; @@ -7227,7 +7787,7 @@ AlterObjectSchemaStmt: AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); n->objectType = OBJECT_OPCLASS; n->object = $4; - n->addname = $6; + n->objarg = list_make1(makeString($6)); n->newschema = $9; n->missing_ok = false; $$ = (Node *)n; @@ -7237,7 +7797,7 @@ AlterObjectSchemaStmt: AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); n->objectType = OBJECT_OPFAMILY; n->object = $4; - n->addname = $6; + n->objarg = list_make1(makeString($6)); n->newschema = $9; n->missing_ok = false; $$ = (Node *)n; @@ -7332,6 +7892,24 @@ AlterObjectSchemaStmt: n->missing_ok = true; $$ = (Node *)n; } + | ALTER MATERIALIZED VIEW qualified_name SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_MATVIEW; + n->relation = $4; + n->newschema = $7; + n->missing_ok = false; + $$ = (Node *)n; + } + | ALTER MATERIALIZED VIEW IF_P EXISTS qualified_name SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_MATVIEW; + n->relation = $6; + n->newschema = $9; + n->missing_ok = true; + $$ = (Node *)n; + } | ALTER FOREIGN TABLE relation_expr SET SCHEMA name { AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); @@ -7372,7 +7950,7 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId AlterOwnerStmt *n = makeNode(AlterOwnerStmt); n->objectType = OBJECT_AGGREGATE; n->object = $3; - n->objarg = $4; + n->objarg = extractAggrArgTypes($4); n->newowner = $7; $$ = (Node *)n; } @@ -7447,7 +8025,7 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId AlterOwnerStmt *n = makeNode(AlterOwnerStmt); n->objectType = OBJECT_OPCLASS; n->object = $4; - n->addname = $6; + n->objarg = list_make1(makeString($6)); n->newowner = $9; $$ = (Node *)n; } @@ -7456,7 +8034,7 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId AlterOwnerStmt *n = makeNode(AlterOwnerStmt); n->objectType = OBJECT_OPFAMILY; n->object = $4; - n->addname = $6; + n->objarg = list_make1(makeString($6)); n->newowner = $9; $$ = (Node *)n; } @@ -7516,6 +8094,14 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId n->newowner = $6; $$ = (Node *)n; } + | ALTER EVENT TRIGGER name OWNER TO RoleId + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_EVENT_TRIGGER; + n->object = list_make1(makeString($4)); + n->newowner = $7; + $$ = (Node *)n; + } ; @@ -7834,6 +8420,7 @@ ViewStmt: CREATE OptTemp VIEW qualified_name opt_column_list opt_reloptions n->query = $8; n->replace = false; n->options = $6; + n->withCheckOption = $9; $$ = (Node *) n; } | CREATE OR REPLACE OptTemp VIEW qualified_name opt_column_list opt_reloptions @@ -7846,30 +8433,52 @@ ViewStmt: CREATE OptTemp VIEW qualified_name opt_column_list opt_reloptions n->query = $10; n->replace = true; n->options = $8; + n->withCheckOption = $11; $$ = (Node *) n; } - ; - -opt_check_option: - WITH CHECK OPTION - { - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("WITH CHECK OPTION is not implemented"))); - } - | WITH CASCADED CHECK OPTION + | CREATE OptTemp RECURSIVE VIEW qualified_name '(' columnList ')' opt_reloptions + AS SelectStmt opt_check_option { - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("WITH CHECK OPTION is not implemented"))); + ViewStmt *n = makeNode(ViewStmt); + n->view = $5; + n->view->relpersistence = $2; + n->aliases = $7; + n->query = makeRecursiveViewSelect(n->view->relname, n->aliases, $11); + n->replace = false; + n->options = $9; + n->withCheckOption = $12; + if (n->withCheckOption != NO_CHECK_OPTION) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("WITH CHECK OPTION not supported on recursive views"), + parser_errposition(@12))); + $$ = (Node *) n; } - | WITH LOCAL CHECK OPTION + | CREATE OR REPLACE OptTemp RECURSIVE VIEW qualified_name '(' columnList ')' opt_reloptions + AS SelectStmt opt_check_option { - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("WITH CHECK OPTION is not implemented"))); + ViewStmt *n = makeNode(ViewStmt); + n->view = $7; + n->view->relpersistence = $4; + n->aliases = $9; + n->query = makeRecursiveViewSelect(n->view->relname, n->aliases, $13); + n->replace = true; + n->options = $11; + n->withCheckOption = $14; + if (n->withCheckOption != NO_CHECK_OPTION) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("WITH CHECK OPTION not supported on recursive views"), + parser_errposition(@14))); + $$ = (Node *) n; } - | /* EMPTY */ { $$ = NIL; } + ; + +opt_check_option: + WITH CHECK OPTION { $$ = CASCADED_CHECK_OPTION; } + | WITH CASCADED CHECK OPTION { $$ = CASCADED_CHECK_OPTION; } + | WITH LOCAL CHECK OPTION { $$ = LOCAL_CHECK_OPTION; } + | /* EMPTY */ { $$ = NO_CHECK_OPTION; } ; /***************************************************************************** @@ -8059,6 +8668,23 @@ DropdbStmt: DROP DATABASE database_name /***************************************************************************** * + * ALTER SYSTEM SET + * + * This is used to change configuration parameters persistently. + *****************************************************************************/ + +AlterSystemStmt: + ALTER SYSTEM_P SET generic_set + { + AlterSystemStmt *n = makeNode(AlterSystemStmt); + n->setstmt = $4; + $$ = (Node *)n; + } + ; + + +/***************************************************************************** + * * Manipulate a domain * *****************************************************************************/ @@ -8308,6 +8934,8 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose n->options |= VACOPT_VERBOSE; n->freeze_min_age = $3 ? 0 : -1; n->freeze_table_age = $3 ? 0 : -1; + n->multixact_freeze_min_age = $3 ? 0 : -1; + n->multixact_freeze_table_age = $3 ? 0 : -1; n->relation = NULL; n->va_cols = NIL; $$ = (Node *)n; @@ -8322,6 +8950,8 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose n->options |= VACOPT_VERBOSE; n->freeze_min_age = $3 ? 0 : -1; n->freeze_table_age = $3 ? 0 : -1; + n->multixact_freeze_min_age = $3 ? 0 : -1; + n->multixact_freeze_table_age = $3 ? 0 : -1; n->relation = $5; n->va_cols = NIL; $$ = (Node *)n; @@ -8336,6 +8966,8 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose n->options |= VACOPT_VERBOSE; n->freeze_min_age = $3 ? 0 : -1; n->freeze_table_age = $3 ? 0 : -1; + n->multixact_freeze_min_age = $3 ? 0 : -1; + n->multixact_freeze_table_age = $3 ? 0 : -1; $$ = (Node *)n; } | VACUUM '(' vacuum_option_list ')' @@ -8343,9 +8975,17 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose VacuumStmt *n = makeNode(VacuumStmt); n->options = VACOPT_VACUUM | $3; if (n->options & VACOPT_FREEZE) + { n->freeze_min_age = n->freeze_table_age = 0; + n->multixact_freeze_min_age = 0; + n->multixact_freeze_table_age = 0; + } else + { n->freeze_min_age = n->freeze_table_age = -1; + n->multixact_freeze_min_age = -1; + n->multixact_freeze_table_age = -1; + } n->relation = NULL; n->va_cols = NIL; $$ = (Node *) n; @@ -8355,9 +8995,17 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose VacuumStmt *n = makeNode(VacuumStmt); n->options = VACOPT_VACUUM | $3; if (n->options & VACOPT_FREEZE) + { n->freeze_min_age = n->freeze_table_age = 0; + n->multixact_freeze_min_age = 0; + n->multixact_freeze_table_age = 0; + } else + { n->freeze_min_age = n->freeze_table_age = -1; + n->multixact_freeze_min_age = -1; + n->multixact_freeze_table_age = -1; + } n->relation = $5; n->va_cols = $6; if (n->va_cols != NIL) /* implies analyze */ @@ -8387,6 +9035,8 @@ AnalyzeStmt: n->options |= VACOPT_VERBOSE; n->freeze_min_age = -1; n->freeze_table_age = -1; + n->multixact_freeze_min_age = -1; + n->multixact_freeze_table_age = -1; n->relation = NULL; n->va_cols = NIL; $$ = (Node *)n; @@ -8399,6 +9049,8 @@ AnalyzeStmt: n->options |= VACOPT_VERBOSE; n->freeze_min_age = -1; n->freeze_table_age = -1; + n->multixact_freeze_min_age = -1; + n->multixact_freeze_table_age = -1; n->relation = $3; n->va_cols = $4; $$ = (Node *)n; @@ -8630,6 +9282,8 @@ ExplainableStmt: | DeleteStmt | DeclareCursorStmt | CreateAsStmt + | CreateMatViewStmt + | RefreshMatViewStmt | ExecuteStmt /* by default all are $$=$1 */ ; @@ -8652,9 +9306,8 @@ explain_option_elem: ; explain_option_name: - ColId { $$ = $1; } + NonReservedWord { $$ = $1; } | analyze_keyword { $$ = "analyze"; } - | VERBOSE { $$ = "verbose"; } ; explain_option_arg: @@ -8792,6 +9445,7 @@ ExecuteStmt: EXECUTE name execute_param_clause n->params = $8; ctas->query = (Node *) n; ctas->into = $4; + ctas->relkind = OBJECT_TABLE; ctas->is_select_into = false; /* cram additional flags into the IntoClause */ $4->rel->relpersistence = $2; @@ -9145,9 +9799,10 @@ select_with_parens: * The duplicative productions are annoying, but hard to get rid of without * creating shift/reduce conflicts. * - * FOR UPDATE/SHARE may be before or after LIMIT/OFFSET. + * The locking clause (FOR UPDATE etc) may be before or after LIMIT/OFFSET. * In <=7.2.X, LIMIT/OFFSET had to be after FOR UPDATE - * We now support both orderings, but prefer LIMIT/OFFSET before FOR UPDATE/SHARE + * We now support both orderings, but prefer LIMIT/OFFSET before the locking + * clause. * 2002-08-28 bjm */ select_no_parens: @@ -9223,7 +9878,7 @@ select_clause: * As with select_no_parens, simple_select cannot have outer parentheses, * but can have parenthesized subclauses. * - * Note that sort clauses cannot be included at this level --- SQL92 requires + * Note that sort clauses cannot be included at this level --- SQL requires * SELECT foo UNION SELECT bar ORDER BY baz * to be parsed as * (SELECT foo UNION SELECT bar) ORDER BY baz @@ -9238,7 +9893,7 @@ select_clause: * However, this is not checked by the grammar; parse analysis must check it. */ simple_select: - SELECT opt_distinct target_list + SELECT opt_distinct opt_target_list into_clause from_clause where_clause group_clause having_clause window_clause { @@ -9342,6 +9997,7 @@ into_clause: $$->options = NIL; $$->onCommit = ONCOMMIT_NOOP; $$->tableSpaceName = NULL; + $$->viewQuery = NULL; $$->skipData = false; } | /*EMPTY*/ @@ -9566,24 +10222,23 @@ for_locking_items: ; for_locking_item: - FOR UPDATE locked_rels_list opt_nowait + for_locking_strength locked_rels_list opt_nowait { LockingClause *n = makeNode(LockingClause); - n->lockedRels = $3; - n->forUpdate = TRUE; - n->noWait = $4; - $$ = (Node *) n; - } - | FOR SHARE locked_rels_list opt_nowait - { - LockingClause *n = makeNode(LockingClause); - n->lockedRels = $3; - n->forUpdate = FALSE; - n->noWait = $4; + n->lockedRels = $2; + n->strength = $1; + n->noWait = $3; $$ = (Node *) n; } ; +for_locking_strength: + FOR UPDATE { $$ = LCS_FORUPDATE; } + | FOR NO KEY UPDATE { $$ = LCS_FORNOKEYUPDATE; } + | FOR SHARE { $$ = LCS_FORSHARE; } + | FOR KEY SHARE { $$ = LCS_FORKEYSHARE; } + ; + locked_rels_list: OF qualified_name_list { $$ = $2; } | /* EMPTY */ { $$ = NIL; } @@ -9625,65 +10280,34 @@ from_list: ; /* - * table_ref is where an alias clause can be attached. Note we cannot make - * alias_clause have an empty production because that causes parse conflicts - * between table_ref := '(' joined_table ')' alias_clause - * and joined_table := '(' joined_table ')'. So, we must have the - * redundant-looking productions here instead. + * table_ref is where an alias clause can be attached. */ -table_ref: relation_expr - { - $$ = (Node *) $1; - } - | relation_expr alias_clause +table_ref: relation_expr opt_alias_clause { $1->alias = $2; $$ = (Node *) $1; } - | func_table + | func_table func_alias_clause { - RangeFunction *n = makeNode(RangeFunction); - n->funccallnode = $1; - n->coldeflist = NIL; + RangeFunction *n = (RangeFunction *) $1; + n->alias = linitial($2); + n->coldeflist = lsecond($2); $$ = (Node *) n; } - | func_table alias_clause + | LATERAL_P func_table func_alias_clause { - RangeFunction *n = makeNode(RangeFunction); - n->funccallnode = $1; - n->alias = $2; - n->coldeflist = NIL; + RangeFunction *n = (RangeFunction *) $2; + n->lateral = true; + n->alias = linitial($3); + n->coldeflist = lsecond($3); $$ = (Node *) n; } - | func_table AS '(' TableFuncElementList ')' - { - RangeFunction *n = makeNode(RangeFunction); - n->funccallnode = $1; - n->coldeflist = $4; - $$ = (Node *) n; - } - | func_table AS ColId '(' TableFuncElementList ')' - { - RangeFunction *n = makeNode(RangeFunction); - Alias *a = makeNode(Alias); - n->funccallnode = $1; - a->aliasname = $3; - n->alias = a; - n->coldeflist = $5; - $$ = (Node *) n; - } - | func_table ColId '(' TableFuncElementList ')' - { - RangeFunction *n = makeNode(RangeFunction); - Alias *a = makeNode(Alias); - n->funccallnode = $1; - a->aliasname = $2; - n->alias = a; - n->coldeflist = $4; - $$ = (Node *) n; - } - | select_with_parens + | select_with_parens opt_alias_clause { + RangeSubselect *n = makeNode(RangeSubselect); + n->lateral = false; + n->subquery = $1; + n->alias = $2; /* * The SQL spec does not permit a subselect * (<derived_table>) without an alias clause, @@ -9695,26 +10319,47 @@ table_ref: relation_expr * However, it does seem like a good idea to emit * an error message that's better than "syntax error". */ - if (IsA($1, SelectStmt) && - ((SelectStmt *) $1)->valuesLists) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("VALUES in FROM must have an alias"), - errhint("For example, FROM (VALUES ...) [AS] foo."), - parser_errposition(@1))); - else - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("subquery in FROM must have an alias"), - errhint("For example, FROM (SELECT ...) [AS] foo."), - parser_errposition(@1))); - $$ = NULL; + if ($2 == NULL) + { + if (IsA($1, SelectStmt) && + ((SelectStmt *) $1)->valuesLists) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("VALUES in FROM must have an alias"), + errhint("For example, FROM (VALUES ...) [AS] foo."), + parser_errposition(@1))); + else + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subquery in FROM must have an alias"), + errhint("For example, FROM (SELECT ...) [AS] foo."), + parser_errposition(@1))); + } + $$ = (Node *) n; } - | select_with_parens alias_clause + | LATERAL_P select_with_parens opt_alias_clause { RangeSubselect *n = makeNode(RangeSubselect); - n->subquery = $1; - n->alias = $2; + n->lateral = true; + n->subquery = $2; + n->alias = $3; + /* same coment as above */ + if ($3 == NULL) + { + if (IsA($2, SelectStmt) && + ((SelectStmt *) $2)->valuesLists) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("VALUES in FROM must have an alias"), + errhint("For example, FROM (VALUES ...) [AS] foo."), + parser_errposition(@2))); + else + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("subquery in FROM must have an alias"), + errhint("For example, FROM (SELECT ...) [AS] foo."), + parser_errposition(@2))); + } $$ = (Node *) n; } | joined_table @@ -9731,7 +10376,7 @@ table_ref: relation_expr /* * It may seem silly to separate joined_table from table_ref, but there is - * method in SQL92's madness: if you don't do it this way you get reduce- + * method in SQL's madness: if you don't do it this way you get reduce- * reduce conflicts, because it's not clear to the parser generator whether * to expect alias_clause after ')' or not. For the same reason we must * treat 'JOIN' and 'join_type JOIN' separately, rather than allowing @@ -9840,6 +10485,41 @@ alias_clause: } ; +opt_alias_clause: alias_clause { $$ = $1; } + | /*EMPTY*/ { $$ = NULL; } + ; + +/* + * func_alias_clause can include both an Alias and a coldeflist, so we make it + * return a 2-element list that gets disassembled by calling production. + */ +func_alias_clause: + alias_clause + { + $$ = list_make2($1, NIL); + } + | AS '(' TableFuncElementList ')' + { + $$ = list_make2(NULL, $3); + } + | AS ColId '(' TableFuncElementList ')' + { + Alias *a = makeNode(Alias); + a->aliasname = $2; + $$ = list_make2(a, $4); + } + | ColId '(' TableFuncElementList ')' + { + Alias *a = makeNode(Alias); + a->aliasname = $1; + $$ = list_make2(a, $3); + } + | /*EMPTY*/ + { + $$ = list_make2(NULL, NIL); + } + ; + join_type: FULL join_outer { $$ = JOIN_FULL; } | LEFT join_outer { $$ = JOIN_LEFT; } | RIGHT join_outer { $$ = JOIN_RIGHT; } @@ -9932,8 +10612,55 @@ relation_expr_opt_alias: relation_expr %prec UMINUS } ; +/* + * func_table represents a function invocation in a FROM list. It can be + * a plain function call, like "foo(...)", or a ROWS FROM expression with + * one or more function calls, "ROWS FROM (foo(...), bar(...))", + * optionally with WITH ORDINALITY attached. + * In the ROWS FROM syntax, a column definition list can be given for each + * function, for example: + * ROWS FROM (foo() AS (foo_res_a text, foo_res_b text), + * bar() AS (bar_res_a text, bar_res_b text)) + * It's also possible to attach a column definition list to the RangeFunction + * as a whole, but that's handled by the table_ref production. + */ +func_table: func_expr_windowless opt_ordinality + { + RangeFunction *n = makeNode(RangeFunction); + n->lateral = false; + n->ordinality = $2; + n->is_rowsfrom = false; + n->functions = list_make1(list_make2($1, NIL)); + /* alias and coldeflist are set by table_ref production */ + $$ = (Node *) n; + } + | ROWS FROM '(' rowsfrom_list ')' opt_ordinality + { + RangeFunction *n = makeNode(RangeFunction); + n->lateral = false; + n->ordinality = $6; + n->is_rowsfrom = true; + n->functions = $4; + /* alias and coldeflist are set by table_ref production */ + $$ = (Node *) n; + } + ; + +rowsfrom_item: func_expr_windowless opt_col_def_list + { $$ = list_make2($1, $2); } + ; -func_table: func_expr { $$ = $1; } +rowsfrom_list: + rowsfrom_item { $$ = list_make1($1); } + | rowsfrom_list ',' rowsfrom_item { $$ = lappend($1, $3); } + ; + +opt_col_def_list: AS '(' TableFuncElementList ')' { $$ = $3; } + | /*EMPTY*/ { $$ = NIL; } + ; + +opt_ordinality: WITH_ORDINALITY { $$ = true; } + | /*EMPTY*/ { $$ = false; } ; @@ -9988,6 +10715,7 @@ TableFuncElement: ColId Typename opt_collate_clause n->collClause = (CollateClause *) $3; n->collOid = InvalidOid; n->constraints = NIL; + n->location = @1; $$ = (Node *)n; } ; @@ -9995,7 +10723,7 @@ TableFuncElement: ColId Typename opt_collate_clause /***************************************************************************** * * Type syntax - * SQL92 introduces a large amount of type-specific syntax. + * SQL introduces a large amount of type-specific syntax. * Define individual clauses to handle these cases, and use * the generic case to handle regular type-extensible Postgres syntax. * - thomas 1997-10-10 @@ -10121,7 +10849,7 @@ opt_type_modifiers: '(' expr_list ')' { $$ = $2; } ; /* - * SQL92 numeric data types + * SQL numeric data types */ Numeric: INT_P { @@ -10211,7 +10939,7 @@ opt_float: '(' Iconst ')' ; /* - * SQL92 bit-field data types + * SQL bit-field data types * The following implements BIT() and BIT VARYING(). */ Bit: BitWithLength @@ -10268,7 +10996,7 @@ BitWithoutLength: /* - * SQL92 character data types + * SQL character data types * The following implements CHAR() and VARCHAR(). */ Character: CharacterWithLength @@ -10301,15 +11029,7 @@ ConstCharacter: CharacterWithLength CharacterWithLength: character '(' Iconst ')' opt_charset { if (($5 != NULL) && (strcmp($5, "sql_text") != 0)) - { - char *type; - - type = palloc(strlen($1) + 1 + strlen($5) + 1); - strcpy(type, $1); - strcat(type, "_"); - strcat(type, $5); - $1 = type; - } + $1 = psprintf("%s_%s", $1, $5); $$ = SystemTypeName($1); $$->typmods = list_make1(makeIntConst($3, @3)); @@ -10320,15 +11040,7 @@ CharacterWithLength: character '(' Iconst ')' opt_charset CharacterWithoutLength: character opt_charset { if (($2 != NULL) && (strcmp($2, "sql_text") != 0)) - { - char *type; - - type = palloc(strlen($1) + 1 + strlen($2) + 1); - strcpy(type, $1); - strcat(type, "_"); - strcat(type, $2); - $1 = type; - } + $1 = psprintf("%s_%s", $1, $2); $$ = SystemTypeName($1); @@ -10365,7 +11077,7 @@ opt_charset: ; /* - * SQL92 date/time types + * SQL date/time types */ ConstDatetime: TIMESTAMP '(' Iconst ')' opt_timezone @@ -10525,16 +11237,9 @@ a_expr: c_expr { $$ = $1; } } | a_expr AT TIME ZONE a_expr %prec AT { - FuncCall *n = makeNode(FuncCall); - n->funcname = SystemFuncName("timezone"); - n->args = list_make2($5, $1); - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = NULL; - n->location = @2; - $$ = (Node *) n; + $$ = (Node *) makeFuncCall(SystemFuncName("timezone"), + list_make2($5, $1), + @2); } /* * These operators must be called out explicitly in order to make use @@ -10586,118 +11291,70 @@ a_expr: c_expr { $$ = $1; } { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~~", $1, $3, @2); } | a_expr LIKE a_expr ESCAPE a_expr { - FuncCall *n = makeNode(FuncCall); - n->funcname = SystemFuncName("like_escape"); - n->args = list_make2($3, $5); - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = NULL; - n->location = @2; + FuncCall *n = makeFuncCall(SystemFuncName("like_escape"), + list_make2($3, $5), + @2); $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~~", $1, (Node *) n, @2); } | a_expr NOT LIKE a_expr { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~~", $1, $4, @2); } | a_expr NOT LIKE a_expr ESCAPE a_expr { - FuncCall *n = makeNode(FuncCall); - n->funcname = SystemFuncName("like_escape"); - n->args = list_make2($4, $6); - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = NULL; - n->location = @2; + FuncCall *n = makeFuncCall(SystemFuncName("like_escape"), + list_make2($4, $6), + @2); $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~~", $1, (Node *) n, @2); } | a_expr ILIKE a_expr { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~~*", $1, $3, @2); } | a_expr ILIKE a_expr ESCAPE a_expr { - FuncCall *n = makeNode(FuncCall); - n->funcname = SystemFuncName("like_escape"); - n->args = list_make2($3, $5); - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = NULL; - n->location = @2; + FuncCall *n = makeFuncCall(SystemFuncName("like_escape"), + list_make2($3, $5), + @2); $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~~*", $1, (Node *) n, @2); } | a_expr NOT ILIKE a_expr { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~~*", $1, $4, @2); } | a_expr NOT ILIKE a_expr ESCAPE a_expr { - FuncCall *n = makeNode(FuncCall); - n->funcname = SystemFuncName("like_escape"); - n->args = list_make2($4, $6); - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = NULL; - n->location = @2; + FuncCall *n = makeFuncCall(SystemFuncName("like_escape"), + list_make2($4, $6), + @2); $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~~*", $1, (Node *) n, @2); } | a_expr SIMILAR TO a_expr %prec SIMILAR { - FuncCall *n = makeNode(FuncCall); - n->funcname = SystemFuncName("similar_escape"); - n->args = list_make2($4, makeNullAConst(-1)); - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = NULL; - n->location = @2; + FuncCall *n = makeFuncCall(SystemFuncName("similar_escape"), + list_make2($4, makeNullAConst(-1)), + @2); $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~", $1, (Node *) n, @2); } | a_expr SIMILAR TO a_expr ESCAPE a_expr { - FuncCall *n = makeNode(FuncCall); - n->funcname = SystemFuncName("similar_escape"); - n->args = list_make2($4, $6); - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = NULL; - n->location = @2; + FuncCall *n = makeFuncCall(SystemFuncName("similar_escape"), + list_make2($4, $6), + @2); $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~", $1, (Node *) n, @2); } | a_expr NOT SIMILAR TO a_expr %prec SIMILAR { - FuncCall *n = makeNode(FuncCall); - n->funcname = SystemFuncName("similar_escape"); - n->args = list_make2($5, makeNullAConst(-1)); - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = NULL; - n->location = @2; + FuncCall *n = makeFuncCall(SystemFuncName("similar_escape"), + list_make2($5, makeNullAConst(-1)), + @2); $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~", $1, (Node *) n, @2); } | a_expr NOT SIMILAR TO a_expr ESCAPE a_expr { - FuncCall *n = makeNode(FuncCall); - n->funcname = SystemFuncName("similar_escape"); - n->args = list_make2($5, $7); - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = NULL; - n->location = @2; + FuncCall *n = makeFuncCall(SystemFuncName("similar_escape"), + list_make2($5, $7), + @2); $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~", $1, (Node *) n, @2); } /* NullTest clause - * Define SQL92-style Null test clause. + * Define SQL-style Null test clause. * Allow two forms described in the standard: * a IS NULL * a IS NOT NULL @@ -10735,7 +11392,19 @@ a_expr: c_expr { $$ = $1; } } | row OVERLAPS row { - $$ = (Node *)makeOverlaps($1, $3, @2, yyscanner); + if (list_length($1) != 2) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("wrong number of parameters on left side of OVERLAPS expression"), + parser_errposition(@1))); + if (list_length($3) != 2) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("wrong number of parameters on right side of OVERLAPS expression"), + parser_errposition(@3))); + $$ = (Node *) makeFuncCall(SystemFuncName("overlaps"), + list_concat($1, $3), + @2); } | a_expr IS TRUE_P %prec IS { @@ -11055,6 +11724,29 @@ c_expr: columnref { $$ = $1; } n->location = @1; $$ = (Node *)n; } + | select_with_parens indirection + { + /* + * Because the select_with_parens nonterminal is designed + * to "eat" as many levels of parens as possible, the + * '(' a_expr ')' opt_indirection production above will + * fail to match a sub-SELECT with indirection decoration; + * the sub-SELECT won't be regarded as an a_expr as long + * as there are parens around it. To support applying + * subscripting or field selection to a sub-SELECT result, + * we need this redundant-looking production. + */ + SubLink *n = makeNode(SubLink); + A_Indirection *a = makeNode(A_Indirection); + n->subLinkType = EXPR_SUBLINK; + n->testexpr = NULL; + n->operName = NIL; + n->subselect = $1; + n->location = @1; + a->arg = (Node *)n; + a->indirection = check_indirection($2, yyscanner); + $$ = (Node *)a; + } | EXISTS select_with_parens { SubLink *n = makeNode(SubLink); @@ -11094,144 +11786,134 @@ c_expr: columnref { $$ = $1; } } ; -/* - * func_expr is split out from c_expr just so that we have a classification - * for "everything that is a function call or looks like one". This isn't - * very important, but it saves us having to document which variants are - * legal in the backwards-compatible functional-index syntax for CREATE INDEX. - * (Note that many of the special SQL functions wouldn't actually make any - * sense as functional index entries, but we ignore that consideration here.) - */ -func_expr: func_name '(' ')' over_clause +func_application: func_name '(' ')' { - FuncCall *n = makeNode(FuncCall); - n->funcname = $1; - n->args = NIL; - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = $4; - n->location = @1; - $$ = (Node *)n; + $$ = (Node *) makeFuncCall($1, NIL, @1); } - | func_name '(' func_arg_list ')' over_clause + | func_name '(' func_arg_list opt_sort_clause ')' { - FuncCall *n = makeNode(FuncCall); - n->funcname = $1; - n->args = $3; - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = $5; - n->location = @1; + FuncCall *n = makeFuncCall($1, $3, @1); + n->agg_order = $4; $$ = (Node *)n; } - | func_name '(' VARIADIC func_arg_expr ')' over_clause + | func_name '(' VARIADIC func_arg_expr opt_sort_clause ')' { - FuncCall *n = makeNode(FuncCall); - n->funcname = $1; - n->args = list_make1($4); - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; + FuncCall *n = makeFuncCall($1, list_make1($4), @1); n->func_variadic = TRUE; - n->over = $6; - n->location = @1; + n->agg_order = $5; $$ = (Node *)n; } - | func_name '(' func_arg_list ',' VARIADIC func_arg_expr ')' over_clause + | func_name '(' func_arg_list ',' VARIADIC func_arg_expr opt_sort_clause ')' { - FuncCall *n = makeNode(FuncCall); - n->funcname = $1; - n->args = lappend($3, $6); - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; + FuncCall *n = makeFuncCall($1, lappend($3, $6), @1); n->func_variadic = TRUE; - n->over = $8; - n->location = @1; + n->agg_order = $7; $$ = (Node *)n; } - | func_name '(' func_arg_list sort_clause ')' over_clause + | func_name '(' ALL func_arg_list opt_sort_clause ')' { - FuncCall *n = makeNode(FuncCall); - n->funcname = $1; - n->args = $3; - n->agg_order = $4; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = $6; - n->location = @1; - $$ = (Node *)n; - } - | func_name '(' ALL func_arg_list opt_sort_clause ')' over_clause - { - FuncCall *n = makeNode(FuncCall); - n->funcname = $1; - n->args = $4; + FuncCall *n = makeFuncCall($1, $4, @1); n->agg_order = $5; - n->agg_star = FALSE; - n->agg_distinct = FALSE; /* Ideally we'd mark the FuncCall node to indicate * "must be an aggregate", but there's no provision * for that in FuncCall at the moment. */ - n->func_variadic = FALSE; - n->over = $7; - n->location = @1; $$ = (Node *)n; } - | func_name '(' DISTINCT func_arg_list opt_sort_clause ')' over_clause + | func_name '(' DISTINCT func_arg_list opt_sort_clause ')' { - FuncCall *n = makeNode(FuncCall); - n->funcname = $1; - n->args = $4; + FuncCall *n = makeFuncCall($1, $4, @1); n->agg_order = $5; - n->agg_star = FALSE; n->agg_distinct = TRUE; - n->func_variadic = FALSE; - n->over = $7; - n->location = @1; $$ = (Node *)n; } - | func_name '(' '*' ')' over_clause + | func_name '(' '*' ')' { /* * We consider AGGREGATE(*) to invoke a parameterless * aggregate. This does the right thing for COUNT(*), - * and there are no other aggregates in SQL92 that accept + * and there are no other aggregates in SQL that accept * '*' as parameter. * * The FuncCall node is also marked agg_star = true, * so that later processing can detect what the argument * really was. */ - FuncCall *n = makeNode(FuncCall); - n->funcname = $1; - n->args = NIL; - n->agg_order = NIL; + FuncCall *n = makeFuncCall($1, NIL, @1); n->agg_star = TRUE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = $5; - n->location = @1; $$ = (Node *)n; } - | COLLATION FOR '(' a_expr ')' + ; + + +/* + * func_expr and its cousin func_expr_windowless are split out from c_expr just + * so that we have classifications for "everything that is a function call or + * looks like one". This isn't very important, but it saves us having to + * document which variants are legal in places like "FROM function()" or the + * backwards-compatible functional-index syntax for CREATE INDEX. + * (Note that many of the special SQL functions wouldn't actually make any + * sense as functional index entries, but we ignore that consideration here.) + */ +func_expr: func_application within_group_clause filter_clause over_clause { - FuncCall *n = makeNode(FuncCall); - n->funcname = SystemFuncName("pg_collation_for"); - n->args = list_make1($4); - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = NULL; - n->location = @1; - $$ = (Node *)n; + FuncCall *n = (FuncCall *) $1; + /* + * The order clause for WITHIN GROUP and the one for + * plain-aggregate ORDER BY share a field, so we have to + * check here that at most one is present. We also check + * for DISTINCT and VARIADIC here to give a better error + * location. Other consistency checks are deferred to + * parse analysis. + */ + if ($2 != NIL) + { + if (n->agg_order != NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("cannot use multiple ORDER BY clauses with WITHIN GROUP"), + parser_errposition(@2))); + if (n->agg_distinct) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("cannot use DISTINCT with WITHIN GROUP"), + parser_errposition(@2))); + if (n->func_variadic) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("cannot use VARIADIC with WITHIN GROUP"), + parser_errposition(@2))); + n->agg_order = $2; + n->agg_within_group = TRUE; + } + n->agg_filter = $3; + n->over = $4; + $$ = (Node *) n; + } + | func_expr_common_subexpr + { $$ = $1; } + ; + +/* + * As func_expr but does not accept WINDOW functions directly + * (but they can still be contained in arguments for functions etc). + * Use this when window expressions are not allowed, where needed to + * disambiguate the grammar (e.g. in CREATE INDEX). + */ +func_expr_windowless: + func_application { $$ = $1; } + | func_expr_common_subexpr { $$ = $1; } + ; + +/* + * Special expressions that are considered to be functions. + */ +func_expr_common_subexpr: + COLLATION FOR '(' a_expr ')' + { + $$ = (Node *) makeFuncCall(SystemFuncName("pg_collation_for"), + list_make1($4), + @1); } | CURRENT_DATE { @@ -11249,10 +11931,15 @@ func_expr: func_name '(' ')' over_clause * of type-input conversion functions. (As of PG 7.3 * that is actually possible, but not clear that we want * to rely on it.) + * + * The token location is attached to the run-time + * typecast, not to the Const, for the convenience of + * pg_stat_statements (which doesn't want these constructs + * to appear to be replaceable constants). */ Node *n; - n = makeStringConstCast("now", @1, SystemTypeName("text")); - $$ = makeTypeCast(n, SystemTypeName("date"), -1); + n = makeStringConstCast("now", -1, SystemTypeName("text")); + $$ = makeTypeCast(n, SystemTypeName("date"), @1); } | CURRENT_TIME { @@ -11261,8 +11948,8 @@ func_expr: func_name '(' ')' over_clause * See comments for CURRENT_DATE. */ Node *n; - n = makeStringConstCast("now", @1, SystemTypeName("text")); - $$ = makeTypeCast(n, SystemTypeName("timetz"), -1); + n = makeStringConstCast("now", -1, SystemTypeName("text")); + $$ = makeTypeCast(n, SystemTypeName("timetz"), @1); } | CURRENT_TIME '(' Iconst ')' { @@ -11272,10 +11959,10 @@ func_expr: func_name '(' ')' over_clause */ Node *n; TypeName *d; - n = makeStringConstCast("now", @1, SystemTypeName("text")); + n = makeStringConstCast("now", -1, SystemTypeName("text")); d = SystemTypeName("timetz"); d->typmods = list_make1(makeIntConst($3, @3)); - $$ = makeTypeCast(n, d, -1); + $$ = makeTypeCast(n, d, @1); } | CURRENT_TIMESTAMP { @@ -11283,16 +11970,7 @@ func_expr: func_name '(' ')' over_clause * Translate as "now()", since we have a function that * does exactly what is needed. */ - FuncCall *n = makeNode(FuncCall); - n->funcname = SystemFuncName("now"); - n->args = NIL; - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = NULL; - n->location = @1; - $$ = (Node *)n; + $$ = (Node *) makeFuncCall(SystemFuncName("now"), NIL, @1); } | CURRENT_TIMESTAMP '(' Iconst ')' { @@ -11302,10 +11980,10 @@ func_expr: func_name '(' ')' over_clause */ Node *n; TypeName *d; - n = makeStringConstCast("now", @1, SystemTypeName("text")); + n = makeStringConstCast("now", -1, SystemTypeName("text")); d = SystemTypeName("timestamptz"); d->typmods = list_make1(makeIntConst($3, @3)); - $$ = makeTypeCast(n, d, -1); + $$ = makeTypeCast(n, d, @1); } | LOCALTIME { @@ -11314,8 +11992,8 @@ func_expr: func_name '(' ')' over_clause * See comments for CURRENT_DATE. */ Node *n; - n = makeStringConstCast("now", @1, SystemTypeName("text")); - $$ = makeTypeCast((Node *)n, SystemTypeName("time"), -1); + n = makeStringConstCast("now", -1, SystemTypeName("text")); + $$ = makeTypeCast((Node *)n, SystemTypeName("time"), @1); } | LOCALTIME '(' Iconst ')' { @@ -11325,10 +12003,10 @@ func_expr: func_name '(' ')' over_clause */ Node *n; TypeName *d; - n = makeStringConstCast("now", @1, SystemTypeName("text")); + n = makeStringConstCast("now", -1, SystemTypeName("text")); d = SystemTypeName("time"); d->typmods = list_make1(makeIntConst($3, @3)); - $$ = makeTypeCast((Node *)n, d, -1); + $$ = makeTypeCast((Node *)n, d, @1); } | LOCALTIMESTAMP { @@ -11337,8 +12015,8 @@ func_expr: func_name '(' ')' over_clause * See comments for CURRENT_DATE. */ Node *n; - n = makeStringConstCast("now", @1, SystemTypeName("text")); - $$ = makeTypeCast(n, SystemTypeName("timestamp"), -1); + n = makeStringConstCast("now", -1, SystemTypeName("text")); + $$ = makeTypeCast(n, SystemTypeName("timestamp"), @1); } | LOCALTIMESTAMP '(' Iconst ')' { @@ -11348,103 +12026,40 @@ func_expr: func_name '(' ')' over_clause */ Node *n; TypeName *d; - n = makeStringConstCast("now", @1, SystemTypeName("text")); + n = makeStringConstCast("now", -1, SystemTypeName("text")); d = SystemTypeName("timestamp"); d->typmods = list_make1(makeIntConst($3, @3)); - $$ = makeTypeCast(n, d, -1); + $$ = makeTypeCast(n, d, @1); } | CURRENT_ROLE { - FuncCall *n = makeNode(FuncCall); - n->funcname = SystemFuncName("current_user"); - n->args = NIL; - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = NULL; - n->location = @1; - $$ = (Node *)n; + $$ = (Node *) makeFuncCall(SystemFuncName("current_user"), NIL, @1); } | CURRENT_USER { - FuncCall *n = makeNode(FuncCall); - n->funcname = SystemFuncName("current_user"); - n->args = NIL; - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = NULL; - n->location = @1; - $$ = (Node *)n; + $$ = (Node *) makeFuncCall(SystemFuncName("current_user"), NIL, @1); } | SESSION_USER { - FuncCall *n = makeNode(FuncCall); - n->funcname = SystemFuncName("session_user"); - n->args = NIL; - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = NULL; - n->location = @1; - $$ = (Node *)n; + $$ = (Node *) makeFuncCall(SystemFuncName("session_user"), NIL, @1); } | USER { - FuncCall *n = makeNode(FuncCall); - n->funcname = SystemFuncName("current_user"); - n->args = NIL; - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = NULL; - n->location = @1; - $$ = (Node *)n; + $$ = (Node *) makeFuncCall(SystemFuncName("current_user"), NIL, @1); } | CURRENT_CATALOG { - FuncCall *n = makeNode(FuncCall); - n->funcname = SystemFuncName("current_database"); - n->args = NIL; - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = NULL; - n->location = @1; - $$ = (Node *)n; + $$ = (Node *) makeFuncCall(SystemFuncName("current_database"), NIL, @1); } | CURRENT_SCHEMA { - FuncCall *n = makeNode(FuncCall); - n->funcname = SystemFuncName("current_schema"); - n->args = NIL; - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = NULL; - n->location = @1; - $$ = (Node *)n; + $$ = (Node *) makeFuncCall(SystemFuncName("current_schema"), NIL, @1); } | CAST '(' a_expr AS Typename ')' { $$ = makeTypeCast($3, $5, @1); } | EXTRACT '(' extract_list ')' { - FuncCall *n = makeNode(FuncCall); - n->funcname = SystemFuncName("date_part"); - n->args = $3; - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = NULL; - n->location = @1; - $$ = (Node *)n; + $$ = (Node *) makeFuncCall(SystemFuncName("date_part"), $3, @1); } | OVERLAY '(' overlay_list ')' { @@ -11453,46 +12068,19 @@ func_expr: func_name '(' ')' over_clause * overlay(A PLACING B FROM C) is converted to * overlay(A, B, C) */ - FuncCall *n = makeNode(FuncCall); - n->funcname = SystemFuncName("overlay"); - n->args = $3; - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = NULL; - n->location = @1; - $$ = (Node *)n; + $$ = (Node *) makeFuncCall(SystemFuncName("overlay"), $3, @1); } | POSITION '(' position_list ')' { /* position(A in B) is converted to position(B, A) */ - FuncCall *n = makeNode(FuncCall); - n->funcname = SystemFuncName("position"); - n->args = $3; - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = NULL; - n->location = @1; - $$ = (Node *)n; + $$ = (Node *) makeFuncCall(SystemFuncName("position"), $3, @1); } | SUBSTRING '(' substr_list ')' { /* substring(A from B for C) is converted to * substring(A, B, C) - thomas 2000-11-28 */ - FuncCall *n = makeNode(FuncCall); - n->funcname = SystemFuncName("substring"); - n->args = $3; - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = NULL; - n->location = @1; - $$ = (Node *)n; + $$ = (Node *) makeFuncCall(SystemFuncName("substring"), $3, @1); } | TREAT '(' a_expr AS Typename ')' { @@ -11501,75 +12089,32 @@ func_expr: func_name '(' ')' over_clause * In SQL99, this is intended for use with structured UDTs, * but let's make this a generally useful form allowing stronger * coercions than are handled by implicit casting. - */ - FuncCall *n = makeNode(FuncCall); - /* Convert SystemTypeName() to SystemFuncName() even though + * + * Convert SystemTypeName() to SystemFuncName() even though * at the moment they result in the same thing. */ - n->funcname = SystemFuncName(((Value *)llast($5->names))->val.str); - n->args = list_make1($3); - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = NULL; - n->location = @1; - $$ = (Node *)n; + $$ = (Node *) makeFuncCall(SystemFuncName(((Value *)llast($5->names))->val.str), + list_make1($3), + @1); } | TRIM '(' BOTH trim_list ')' { - /* various trim expressions are defined in SQL92 + /* various trim expressions are defined in SQL * - thomas 1997-07-19 */ - FuncCall *n = makeNode(FuncCall); - n->funcname = SystemFuncName("btrim"); - n->args = $4; - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = NULL; - n->location = @1; - $$ = (Node *)n; + $$ = (Node *) makeFuncCall(SystemFuncName("btrim"), $4, @1); } | TRIM '(' LEADING trim_list ')' { - FuncCall *n = makeNode(FuncCall); - n->funcname = SystemFuncName("ltrim"); - n->args = $4; - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = NULL; - n->location = @1; - $$ = (Node *)n; + $$ = (Node *) makeFuncCall(SystemFuncName("ltrim"), $4, @1); } | TRIM '(' TRAILING trim_list ')' { - FuncCall *n = makeNode(FuncCall); - n->funcname = SystemFuncName("rtrim"); - n->args = $4; - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = NULL; - n->location = @1; - $$ = (Node *)n; + $$ = (Node *) makeFuncCall(SystemFuncName("rtrim"), $4, @1); } | TRIM '(' trim_list ')' { - FuncCall *n = makeNode(FuncCall); - n->funcname = SystemFuncName("btrim"); - n->args = $3; - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = NULL; - n->location = @1; - $$ = (Node *)n; + $$ = (Node *) makeFuncCall(SystemFuncName("btrim"), $3, @1); } | NULLIF '(' a_expr ',' a_expr ')' { @@ -11622,16 +12167,7 @@ func_expr: func_name '(' ')' over_clause { /* xmlexists(A PASSING [BY REF] B [BY REF]) is * converted to xmlexists(A, B)*/ - FuncCall *n = makeNode(FuncCall); - n->funcname = SystemFuncName("xmlexists"); - n->args = list_make2($3, $4); - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = NULL; - n->location = @1; - $$ = (Node *)n; + $$ = (Node *) makeFuncCall(SystemFuncName("xmlexists"), list_make2($3, $4), @1); } | XMLFOREST '(' xml_attribute_list ')' { @@ -11745,6 +12281,20 @@ xmlexists_argument: /* + * Aggregate decoration clauses + */ +within_group_clause: + WITHIN GROUP_P '(' sort_clause ')' { $$ = $4; } + | /*EMPTY*/ { $$ = NIL; } + ; + +filter_clause: + FILTER '(' WHERE a_expr ')' { $$ = $4; } + | /*EMPTY*/ { $$ = NULL; } + ; + + +/* * Window Definitions */ window_clause: @@ -12221,7 +12771,7 @@ in_expr: select_with_parens ; /* - * Define SQL92-style case clause. + * Define SQL-style CASE clause. * - Full specification * CASE WHEN a = b THEN c ... ELSE d END * - Implicit argument @@ -12351,6 +12901,10 @@ ctext_row: '(' ctext_expr_list ')' { $$ = $2; } * *****************************************************************************/ +opt_target_list: target_list { $$ = $1; } + | /* EMPTY */ { $$ = NIL; } + ; + target_list: target_el { $$ = list_make1($1); } | target_list ',' target_el { $$ = lappend($1, $3); } @@ -12527,16 +13081,17 @@ AexprConst: Iconst t->location = @1; $$ = makeStringConstCast($2, @2, t); } - | func_name '(' func_arg_list ')' Sconst + | func_name '(' func_arg_list opt_sort_clause ')' Sconst { /* generic syntax with a type modifier */ TypeName *t = makeTypeNameFromNameList($1); ListCell *lc; /* - * We must use func_arg_list in the production to avoid - * reduce/reduce conflicts, but we don't actually wish - * to allow NamedArgExpr in this context. + * We must use func_arg_list and opt_sort_clause in the + * production to avoid reduce/reduce conflicts, but we + * don't actually wish to allow NamedArgExpr in this + * context, nor ORDER BY. */ foreach(lc, $3) { @@ -12548,9 +13103,15 @@ AexprConst: Iconst errmsg("type modifier cannot have parameter name"), parser_errposition(arg->location))); } + if ($4 != NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("type modifier cannot have ORDER BY"), + parser_errposition(@4))); + t->typmods = $3; t->location = @1; - $$ = makeStringConstCast($5, @5, t); + $$ = makeStringConstCast($6, @6, t); } | ConstTypename Sconst { @@ -12595,7 +13156,13 @@ AexprConst: Iconst Iconst: ICONST { $$ = $1; }; Sconst: SCONST { $$ = $1; }; -RoleId: ColId { $$ = $1; }; +RoleId: NonReservedWord { $$ = $1; }; + +role_list: RoleId + { $$ = list_make1(makeString($1)); } + | role_list ',' RoleId + { $$ = lappend($1, makeString($3)); } + ; SignedIconst: Iconst { $$ = $1; } | '+' Iconst { $$ = + $2; } @@ -12627,6 +13194,14 @@ type_function_name: IDENT { $$ = $1; } | type_func_name_keyword { $$ = pstrdup($1); } ; +/* Any not-fully-reserved word --- these names can be, eg, role names. + */ +NonReservedWord: IDENT { $$ = $1; } + | unreserved_keyword { $$ = pstrdup($1); } + | col_name_keyword { $$ = pstrdup($1); } + | type_func_name_keyword { $$ = pstrdup($1); } + ; + /* Column label --- allowed labels in "AS" clauses. * This presently includes *all* Postgres keywords. */ @@ -12734,6 +13309,7 @@ unreserved_keyword: | ENCRYPTED | ENUM_P | ESCAPE + | EVENT | EXCLUDE | EXCLUDING | EXCLUSIVE @@ -12742,6 +13318,7 @@ unreserved_keyword: | EXTENSION | EXTERNAL | FAMILY + | FILTER | FIRST_P | FOLLOWING | FORCE @@ -12788,6 +13365,7 @@ unreserved_keyword: | LOCK_P | MAPPING | MATCH + | MATERIALIZED | MAXVALUE | MINUTE_P | MINVALUE @@ -12810,6 +13388,8 @@ unreserved_keyword: | OPERATOR | OPTION | OPTIONS + | ORDINALITY + | OVER | OWNED | OWNER | PARSER @@ -12832,6 +13412,7 @@ unreserved_keyword: | PRIVILEGES | PROCEDURAL | PROCEDURE + | PROGRAM | QUOTE | RANGE | READ @@ -12839,6 +13420,7 @@ unreserved_keyword: | RECHECK | RECURSIVE | REF + | REFRESH | REINDEX | RELATIVE_P | RELEASE @@ -12914,8 +13496,10 @@ unreserved_keyword: | VARYING | VERSION_P | VIEW + | VIEWS | VOLATILE | WHITESPACE_P + | WITHIN | WITHOUT | WORK | WRAPPER @@ -13015,7 +13599,6 @@ type_func_name_keyword: | NATURAL | NOTNULL | OUTER_P - | OVER | OVERLAPS | RIGHT | SIMILAR @@ -13072,6 +13655,7 @@ reserved_keyword: | INITIALLY | INTERSECT | INTO + | LATERAL_P | LEADING | LIMIT | LOCALTIME @@ -13288,39 +13872,6 @@ makeBoolAConst(bool state, int location) return makeTypeCast((Node *)n, SystemTypeName("bool"), -1); } -/* makeOverlaps() - * Create and populate a FuncCall node to support the OVERLAPS operator. - */ -static FuncCall * -makeOverlaps(List *largs, List *rargs, int location, core_yyscan_t yyscanner) -{ - FuncCall *n = makeNode(FuncCall); - - n->funcname = SystemFuncName("overlaps"); - if (list_length(largs) == 1) - largs = lappend(largs, largs); - else if (list_length(largs) != 2) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("wrong number of parameters on left side of OVERLAPS expression"), - parser_errposition(location))); - if (list_length(rargs) == 1) - rargs = lappend(rargs, rargs); - else if (list_length(rargs) != 2) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("wrong number of parameters on right side of OVERLAPS expression"), - parser_errposition(location))); - n->args = list_concat(largs, rargs); - n->agg_order = NIL; - n->agg_star = FALSE; - n->agg_distinct = FALSE; - n->func_variadic = FALSE; - n->over = NULL; - n->location = location; - return n; -} - /* check_qualified_name --- check the result of qualified_name production * * It's easiest to let the grammar production for qualified_name allow @@ -13399,6 +13950,56 @@ extractArgTypes(List *parameters) return result; } +/* extractAggrArgTypes() + * As above, but work from the output of the aggr_args production. + */ +static List * +extractAggrArgTypes(List *aggrargs) +{ + Assert(list_length(aggrargs) == 2); + return extractArgTypes((List *) linitial(aggrargs)); +} + +/* makeOrderedSetArgs() + * Build the result of the aggr_args production (which see the comments for). + * This handles only the case where both given lists are nonempty, so that + * we have to deal with multiple VARIADIC arguments. + */ +static List * +makeOrderedSetArgs(List *directargs, List *orderedargs, + core_yyscan_t yyscanner) +{ + FunctionParameter *lastd = (FunctionParameter *) llast(directargs); + int ndirectargs; + + /* No restriction unless last direct arg is VARIADIC */ + if (lastd->mode == FUNC_PARAM_VARIADIC) + { + FunctionParameter *firsto = (FunctionParameter *) linitial(orderedargs); + + /* + * We ignore the names, though the aggr_arg production allows them; + * it doesn't allow default values, so those need not be checked. + */ + if (list_length(orderedargs) != 1 || + firsto->mode != FUNC_PARAM_VARIADIC || + !equal(lastd->argType, firsto->argType)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("an ordered-set aggregate with a VARIADIC direct argument must have one VARIADIC aggregated argument of the same data type"), + parser_errposition(exprLocation((Node *) firsto)))); + + /* OK, drop the duplicate VARIADIC argument from the internal form */ + orderedargs = NIL; + } + + /* don't merge into the next line, as list_concat changes directargs */ + ndirectargs = list_length(directargs); + + return list_make2(list_concat(directargs, orderedargs), + makeInteger(ndirectargs)); +} + /* insertSelectOptions() * Insert ORDER BY, etc into an already-constructed SelectStmt. * @@ -13540,13 +14141,7 @@ doNegateFloat(Value *v) if (*oldval == '-') v->val.str = oldval+1; /* just strip the '-' */ else - { - char *newval = (char *) palloc(strlen(oldval) + 2); - - *newval = '-'; - strcpy(newval+1, oldval); - v->val.str = newval; - } + v->val.str = psprintf("-%s", oldval); } static Node * @@ -13576,6 +14171,7 @@ makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args, x->args = args; /* xmloption, if relevant, must be filled in by caller */ /* type and typmod will be filled in during parse analysis */ + x->type = InvalidOid; /* marks the node as not analyzed */ x->location = location; return (Node *) x; } @@ -13717,7 +14313,7 @@ SplitColQualList(List *qualList, static void processCASbits(int cas_bits, int location, const char *constrType, bool *deferrable, bool *initdeferred, bool *not_valid, - core_yyscan_t yyscanner) + bool *no_inherit, core_yyscan_t yyscanner) { /* defaults */ if (deferrable) @@ -13765,6 +14361,79 @@ processCASbits(int cas_bits, int location, const char *constrType, constrType), parser_errposition(location))); } + + if (cas_bits & CAS_NO_INHERIT) + { + if (no_inherit) + *no_inherit = true; + else + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + /* translator: %s is CHECK, UNIQUE, or similar */ + errmsg("%s constraints cannot be marked NO INHERIT", + constrType), + parser_errposition(location))); + } +} + +/*---------- + * Recursive view transformation + * + * Convert + * + * CREATE RECURSIVE VIEW relname (aliases) AS query + * + * to + * + * CREATE VIEW relname (aliases) AS + * WITH RECURSIVE relname (aliases) AS (query) + * SELECT aliases FROM relname + * + * Actually, just the WITH ... part, which is then inserted into the original + * view definition as the query. + * ---------- + */ +static Node * +makeRecursiveViewSelect(char *relname, List *aliases, Node *query) +{ + SelectStmt *s = makeNode(SelectStmt); + WithClause *w = makeNode(WithClause); + CommonTableExpr *cte = makeNode(CommonTableExpr); + List *tl = NIL; + ListCell *lc; + + /* create common table expression */ + cte->ctename = relname; + cte->aliascolnames = aliases; + cte->ctequery = query; + cte->location = -1; + + /* create WITH clause and attach CTE */ + w->recursive = true; + w->ctes = list_make1(cte); + w->location = -1; + + /* create target list for the new SELECT from the alias list of the + * recursive view specification */ + foreach (lc, aliases) + { + ResTarget *rt = makeNode(ResTarget); + + rt->name = NULL; + rt->indirection = NIL; + rt->val = makeColumnRef(strVal(lfirst(lc)), NIL, -1, 0); + rt->location = -1; + + tl = lappend(tl, rt); + } + + /* create new SELECT combining WITH clause, target list, and fake FROM + * clause */ + s->withClause = w; + s->targetList = tl; + s->fromClause = list_make1(makeRangeVar(NULL, relname, -1)); + + return (Node *) s; } /* parser_init() diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c index 5ff6d44924..df9eaab189 100644 --- a/src/backend/parser/keywords.c +++ b/src/backend/parser/keywords.c @@ -4,7 +4,7 @@ * lexical token lookup for key words in PostgreSQL * * - * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * diff --git a/src/backend/parser/kwlookup.c b/src/backend/parser/kwlookup.c index 028ca6af41..af05aa70ea 100644 --- a/src/backend/parser/kwlookup.c +++ b/src/backend/parser/kwlookup.c @@ -6,7 +6,7 @@ * NB - this file is also used by ECPG and several frontend programs in * src/bin/ including pg_dump and psql * - * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -52,7 +52,7 @@ ScanKeywordLookup(const char *text, return NULL; /* - * Apply an ASCII-only downcasing. We must not use tolower() since it may + * Apply an ASCII-only downcasing. We must not use tolower() since it may * produce the wrong translation in some locales (eg, Turkish). */ for (i = 0; i < len; i++) diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c index 24ce14d56b..9e81051893 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -8,7 +8,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Portions Copyright (c) 2012-2014, TransLattice, Inc. - * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -19,12 +19,16 @@ */ #include "postgres.h" +#include "catalog/pg_aggregate.h" #include "catalog/pg_constraint.h" +#include "catalog/pg_type.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "optimizer/tlist.h" #include "parser/parse_agg.h" #include "parser/parse_clause.h" +#include "parser/parse_coerce.h" +#include "parser/parse_expr.h" #include "parser/parsetree.h" #include "rewrite/rewriteManip.h" #include "utils/builtins.h" @@ -34,18 +38,34 @@ #include "catalog/pg_aggregate.h" #include "utils/syscache.h" #endif +#include "utils/lsyscache.h" typedef struct { ParseState *pstate; + int min_varlevel; + int min_agglevel; + int sublevels_up; +} check_agg_arguments_context; + +typedef struct +{ + ParseState *pstate; Query *qry; List *groupClauses; bool have_non_var_grouping; List **func_grouped_rels; int sublevels_up; + bool in_agg_direct_args; } check_ungrouped_columns_context; +static int check_agg_arguments(ParseState *pstate, + List *directargs, + List *args, + Expr *filter); +static bool check_agg_arguments_walker(Node *node, + check_agg_arguments_context *context); static void check_ungrouped_columns(Node *node, ParseState *pstate, Query *qry, List *groupClauses, bool have_non_var_grouping, List **func_grouped_rels); @@ -58,15 +78,21 @@ static bool check_ungrouped_columns_walker(Node *node, * Finish initial transformation of an aggregate call * * parse_func.c has recognized the function as an aggregate, and has set up - * all the fields of the Aggref except args, aggorder, aggdistinct and - * agglevelsup. The passed-in args list has been through standard expression - * transformation, while the passed-in aggorder list hasn't been transformed - * at all. + * all the fields of the Aggref except aggdirectargs, args, aggorder, + * aggdistinct and agglevelsup. The passed-in args list has been through + * standard expression transformation and type coercion to match the agg's + * declared arg types, while the passed-in aggorder list hasn't been + * transformed at all. * - * Here we convert the args list into a targetlist by inserting TargetEntry - * nodes, and then transform the aggorder and agg_distinct specifications to - * produce lists of SortGroupClause nodes. (That might also result in adding - * resjunk expressions to the targetlist.) + * Here we separate the args list into direct and aggregated args, storing the + * former in agg->aggdirectargs and the latter in agg->args. The regular + * args, but not the direct args, are converted into a targetlist by inserting + * TargetEntry nodes. We then transform the aggorder and agg_distinct + * specifications to produce lists of SortGroupClause nodes for agg->aggorder + * and agg->aggdistinct. (For a regular aggregate, this might result in + * adding resjunk expressions to the targetlist; but for ordered-set + * aggregates the aggorder list will always be one-to-one with the aggregated + * args.) * * We must also determine which query level the aggregate actually belongs to, * set agglevelsup accordingly, and mark p_hasAggs true in the corresponding @@ -76,10 +102,10 @@ void transformAggregateCall(ParseState *pstate, Aggref *agg, List *args, List *aggorder, bool agg_distinct) { - List *tlist; - List *torder; + List *tlist = NIL; + List *torder = NIL; List *tdistinct = NIL; - AttrNumber attno; + AttrNumber attno = 1; int save_next_resno; int min_varlevel; ListCell *lc; @@ -89,66 +115,115 @@ transformAggregateCall(ParseState *pstate, Aggref *agg, Form_pg_aggregate aggform; #endif /* XCP */ #endif /* PGXC */ + const char *err; + bool errkind; - /* - * Transform the plain list of Exprs into a targetlist. We don't bother - * to assign column names to the entries. - */ - tlist = NIL; - attno = 1; - foreach(lc, args) + if (AGGKIND_IS_ORDERED_SET(agg->aggkind)) { - Expr *arg = (Expr *) lfirst(lc); - TargetEntry *tle = makeTargetEntry(arg, attno++, NULL, false); + /* + * For an ordered-set agg, the args list includes direct args and + * aggregated args; we must split them apart. + */ + int numDirectArgs = list_length(args) - list_length(aggorder); + List *aargs; + ListCell *lc2; - tlist = lappend(tlist, tle); - } + Assert(numDirectArgs >= 0); - /* - * If we have an ORDER BY, transform it. This will add columns to the - * tlist if they appear in ORDER BY but weren't already in the arg list. - * They will be marked resjunk = true so we can tell them apart from - * regular aggregate arguments later. - * - * We need to mess with p_next_resno since it will be used to number any - * new targetlist entries. - */ - save_next_resno = pstate->p_next_resno; - pstate->p_next_resno = attno; + aargs = list_copy_tail(args, numDirectArgs); + agg->aggdirectargs = list_truncate(args, numDirectArgs); + + /* + * Build a tlist from the aggregated args, and make a sortlist entry + * for each one. Note that the expressions in the SortBy nodes are + * ignored (they are the raw versions of the transformed args); we are + * just looking at the sort information in the SortBy nodes. + */ + forboth(lc, aargs, lc2, aggorder) + { + Expr *arg = (Expr *) lfirst(lc); + SortBy *sortby = (SortBy *) lfirst(lc2); + TargetEntry *tle; - torder = transformSortClause(pstate, - aggorder, - &tlist, - true /* fix unknowns */ , - true /* force SQL99 rules */ ); + /* We don't bother to assign column names to the entries */ + tle = makeTargetEntry(arg, attno++, NULL, false); + tlist = lappend(tlist, tle); - /* - * If we have DISTINCT, transform that to produce a distinctList. - */ - if (agg_distinct) + torder = addTargetToSortList(pstate, tle, + torder, tlist, sortby, + true /* fix unknowns */ ); + } + + /* Never any DISTINCT in an ordered-set agg */ + Assert(!agg_distinct); + } + else { - tdistinct = transformDistinctClause(pstate, &tlist, torder, true); + /* Regular aggregate, so it has no direct args */ + agg->aggdirectargs = NIL; + + /* + * Transform the plain list of Exprs into a targetlist. + */ + foreach(lc, args) + { + Expr *arg = (Expr *) lfirst(lc); + TargetEntry *tle; + + /* We don't bother to assign column names to the entries */ + tle = makeTargetEntry(arg, attno++, NULL, false); + tlist = lappend(tlist, tle); + } /* - * Remove this check if executor support for hashed distinct for - * aggregates is ever added. + * If we have an ORDER BY, transform it. This will add columns to the + * tlist if they appear in ORDER BY but weren't already in the arg + * list. They will be marked resjunk = true so we can tell them apart + * from regular aggregate arguments later. + * + * We need to mess with p_next_resno since it will be used to number + * any new targetlist entries. + */ + save_next_resno = pstate->p_next_resno; + pstate->p_next_resno = attno; + + torder = transformSortClause(pstate, + aggorder, + &tlist, + EXPR_KIND_ORDER_BY, + true /* fix unknowns */ , + true /* force SQL99 rules */ ); + + /* + * If we have DISTINCT, transform that to produce a distinctList. */ - foreach(lc, tdistinct) + if (agg_distinct) { - SortGroupClause *sortcl = (SortGroupClause *) lfirst(lc); + tdistinct = transformDistinctClause(pstate, &tlist, torder, true); - if (!OidIsValid(sortcl->sortop)) + /* + * Remove this check if executor support for hashed distinct for + * aggregates is ever added. + */ + foreach(lc, tdistinct) { - Node *expr = get_sortgroupclause_expr(sortcl, tlist); - - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_FUNCTION), - errmsg("could not identify an ordering operator for type %s", - format_type_be(exprType(expr))), - errdetail("Aggregates with DISTINCT must be able to sort their inputs."), - parser_errposition(pstate, exprLocation(expr)))); + SortGroupClause *sortcl = (SortGroupClause *) lfirst(lc); + + if (!OidIsValid(sortcl->sortop)) + { + Node *expr = get_sortgroupclause_expr(sortcl, tlist); + + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("could not identify an ordering operator for type %s", + format_type_be(exprType(expr))), + errdetail("Aggregates with DISTINCT must be able to sort their inputs."), + parser_errposition(pstate, exprLocation(expr)))); + } } } + + pstate->p_next_resno = save_next_resno; } /* Update the Aggref with the transformation results */ @@ -156,45 +231,17 @@ transformAggregateCall(ParseState *pstate, Aggref *agg, agg->aggorder = torder; agg->aggdistinct = tdistinct; - pstate->p_next_resno = save_next_resno; - - /* - * The aggregate's level is the same as the level of the lowest-level - * variable or aggregate in its arguments; or if it contains no variables - * at all, we presume it to be local. - */ - min_varlevel = find_minimum_var_level((Node *) agg->args); - /* - * An aggregate can't directly contain another aggregate call of the same - * level (though outer aggs are okay). We can skip this check if we - * didn't find any local vars or aggs. + * Check the arguments to compute the aggregate's level and detect + * improper nesting. */ - if (min_varlevel == 0) - { - if (pstate->p_hasAggs && - checkExprHasAggs((Node *) agg->args)) - ereport(ERROR, - (errcode(ERRCODE_GROUPING_ERROR), - errmsg("aggregate function calls cannot be nested"), - parser_errposition(pstate, - locate_agg_of_level((Node *) agg->args, 0)))); - } - - /* It can't contain window functions either */ - if (pstate->p_hasWindowFuncs && - checkExprHasWindowFuncs((Node *) agg->args)) - ereport(ERROR, - (errcode(ERRCODE_GROUPING_ERROR), - errmsg("aggregate function calls cannot contain window function calls"), - parser_errposition(pstate, - locate_windowfunc((Node *) agg->args)))); - - if (min_varlevel < 0) - min_varlevel = 0; + min_varlevel = check_agg_arguments(pstate, + agg->aggdirectargs, + agg->args, + agg->aggfilter); agg->agglevelsup = min_varlevel; - /* Mark the correct pstate as having aggregates */ + /* Mark the correct pstate level as having aggregates */ while (min_varlevel-- > 0) pstate = pstate->parentParseState; pstate->p_hasAggs = true; @@ -222,6 +269,308 @@ transformAggregateCall(ParseState *pstate, Aggref *agg, ReleaseSysCache(aggTuple); #endif #endif + + /* + * Check to see if the aggregate function is in an invalid place within + * its aggregation query. + * + * For brevity we support two schemes for reporting an error here: set + * "err" to a custom message, or set "errkind" true if the error context + * is sufficiently identified by what ParseExprKindName will return, *and* + * what it will return is just a SQL keyword. (Otherwise, use a custom + * message to avoid creating translation problems.) + */ + err = NULL; + errkind = false; + switch (pstate->p_expr_kind) + { + case EXPR_KIND_NONE: + Assert(false); /* can't happen */ + break; + case EXPR_KIND_OTHER: + /* Accept aggregate here; caller must throw error if wanted */ + break; + case EXPR_KIND_JOIN_ON: + case EXPR_KIND_JOIN_USING: + err = _("aggregate functions are not allowed in JOIN conditions"); + break; + case EXPR_KIND_FROM_SUBSELECT: + /* Should only be possible in a LATERAL subquery */ + Assert(pstate->p_lateral_active); + /* Aggregate scope rules make it worth being explicit here */ + err = _("aggregate functions are not allowed in FROM clause of their own query level"); + break; + case EXPR_KIND_FROM_FUNCTION: + err = _("aggregate functions are not allowed in functions in FROM"); + break; + case EXPR_KIND_WHERE: + errkind = true; + break; + case EXPR_KIND_HAVING: + /* okay */ + break; + case EXPR_KIND_FILTER: + errkind = true; + break; + case EXPR_KIND_WINDOW_PARTITION: + /* okay */ + break; + case EXPR_KIND_WINDOW_ORDER: + /* okay */ + break; + case EXPR_KIND_WINDOW_FRAME_RANGE: + err = _("aggregate functions are not allowed in window RANGE"); + break; + case EXPR_KIND_WINDOW_FRAME_ROWS: + err = _("aggregate functions are not allowed in window ROWS"); + break; + case EXPR_KIND_SELECT_TARGET: + /* okay */ + break; + case EXPR_KIND_INSERT_TARGET: + case EXPR_KIND_UPDATE_SOURCE: + case EXPR_KIND_UPDATE_TARGET: + errkind = true; + break; + case EXPR_KIND_GROUP_BY: + errkind = true; + break; + case EXPR_KIND_ORDER_BY: + /* okay */ + break; + case EXPR_KIND_DISTINCT_ON: + /* okay */ + break; + case EXPR_KIND_LIMIT: + case EXPR_KIND_OFFSET: + errkind = true; + break; + case EXPR_KIND_RETURNING: + errkind = true; + break; + case EXPR_KIND_VALUES: + errkind = true; + break; + case EXPR_KIND_CHECK_CONSTRAINT: + case EXPR_KIND_DOMAIN_CHECK: + err = _("aggregate functions are not allowed in check constraints"); + break; + case EXPR_KIND_COLUMN_DEFAULT: + case EXPR_KIND_FUNCTION_DEFAULT: + err = _("aggregate functions are not allowed in DEFAULT expressions"); + break; + case EXPR_KIND_INDEX_EXPRESSION: + err = _("aggregate functions are not allowed in index expressions"); + break; + case EXPR_KIND_INDEX_PREDICATE: + err = _("aggregate functions are not allowed in index predicates"); + break; + case EXPR_KIND_ALTER_COL_TRANSFORM: + err = _("aggregate functions are not allowed in transform expressions"); + break; + case EXPR_KIND_EXECUTE_PARAMETER: + err = _("aggregate functions are not allowed in EXECUTE parameters"); + break; + case EXPR_KIND_TRIGGER_WHEN: + err = _("aggregate functions are not allowed in trigger WHEN conditions"); + break; + + /* + * There is intentionally no default: case here, so that the + * compiler will warn if we add a new ParseExprKind without + * extending this switch. If we do see an unrecognized value at + * runtime, the behavior will be the same as for EXPR_KIND_OTHER, + * which is sane anyway. + */ + } + if (err) + ereport(ERROR, + (errcode(ERRCODE_GROUPING_ERROR), + errmsg_internal("%s", err), + parser_errposition(pstate, agg->location))); + if (errkind) + ereport(ERROR, + (errcode(ERRCODE_GROUPING_ERROR), + /* translator: %s is name of a SQL construct, eg GROUP BY */ + errmsg("aggregate functions are not allowed in %s", + ParseExprKindName(pstate->p_expr_kind)), + parser_errposition(pstate, agg->location))); +} + +/* + * check_agg_arguments + * Scan the arguments of an aggregate function to determine the + * aggregate's semantic level (zero is the current select's level, + * one is its parent, etc). + * + * The aggregate's level is the same as the level of the lowest-level variable + * or aggregate in its aggregated arguments (including any ORDER BY columns) + * or filter expression; or if it contains no variables at all, we presume it + * to be local. + * + * Vars/Aggs in direct arguments are *not* counted towards determining the + * agg's level, as those arguments aren't evaluated per-row but only + * per-group, and so in some sense aren't really agg arguments. However, + * this can mean that we decide an agg is upper-level even when its direct + * args contain lower-level Vars/Aggs, and that case has to be disallowed. + * (This is a little strange, but the SQL standard seems pretty definite that + * direct args are not to be considered when setting the agg's level.) + * + * We also take this opportunity to detect any aggregates or window functions + * nested within the arguments. We can throw error immediately if we find + * a window function. Aggregates are a bit trickier because it's only an + * error if the inner aggregate is of the same semantic level as the outer, + * which we can't know until we finish scanning the arguments. + */ +static int +check_agg_arguments(ParseState *pstate, + List *directargs, + List *args, + Expr *filter) +{ + int agglevel; + check_agg_arguments_context context; + + context.pstate = pstate; + context.min_varlevel = -1; /* signifies nothing found yet */ + context.min_agglevel = -1; + context.sublevels_up = 0; + + (void) expression_tree_walker((Node *) args, + check_agg_arguments_walker, + (void *) &context); + + (void) expression_tree_walker((Node *) filter, + check_agg_arguments_walker, + (void *) &context); + + /* + * If we found no vars nor aggs at all, it's a level-zero aggregate; + * otherwise, its level is the minimum of vars or aggs. + */ + if (context.min_varlevel < 0) + { + if (context.min_agglevel < 0) + agglevel = 0; + else + agglevel = context.min_agglevel; + } + else if (context.min_agglevel < 0) + agglevel = context.min_varlevel; + else + agglevel = Min(context.min_varlevel, context.min_agglevel); + + /* + * If there's a nested aggregate of the same semantic level, complain. + */ + if (agglevel == context.min_agglevel) + { + int aggloc; + + aggloc = locate_agg_of_level((Node *) args, agglevel); + if (aggloc < 0) + aggloc = locate_agg_of_level((Node *) filter, agglevel); + ereport(ERROR, + (errcode(ERRCODE_GROUPING_ERROR), + errmsg("aggregate function calls cannot be nested"), + parser_errposition(pstate, aggloc))); + } + + /* + * Now check for vars/aggs in the direct arguments, and throw error if + * needed. Note that we allow a Var of the agg's semantic level, but not + * an Agg of that level. In principle such Aggs could probably be + * supported, but it would create an ordering dependency among the + * aggregates at execution time. Since the case appears neither to be + * required by spec nor particularly useful, we just treat it as a + * nested-aggregate situation. + */ + if (directargs) + { + context.min_varlevel = -1; + context.min_agglevel = -1; + (void) expression_tree_walker((Node *) directargs, + check_agg_arguments_walker, + (void *) &context); + if (context.min_varlevel >= 0 && context.min_varlevel < agglevel) + ereport(ERROR, + (errcode(ERRCODE_GROUPING_ERROR), + errmsg("outer-level aggregate cannot contain a lower-level variable in its direct arguments"), + parser_errposition(pstate, + locate_var_of_level((Node *) directargs, + context.min_varlevel)))); + if (context.min_agglevel >= 0 && context.min_agglevel <= agglevel) + ereport(ERROR, + (errcode(ERRCODE_GROUPING_ERROR), + errmsg("aggregate function calls cannot be nested"), + parser_errposition(pstate, + locate_agg_of_level((Node *) directargs, + context.min_agglevel)))); + } + + return agglevel; +} + +static bool +check_agg_arguments_walker(Node *node, + check_agg_arguments_context *context) +{ + if (node == NULL) + return false; + if (IsA(node, Var)) + { + int varlevelsup = ((Var *) node)->varlevelsup; + + /* convert levelsup to frame of reference of original query */ + varlevelsup -= context->sublevels_up; + /* ignore local vars of subqueries */ + if (varlevelsup >= 0) + { + if (context->min_varlevel < 0 || + context->min_varlevel > varlevelsup) + context->min_varlevel = varlevelsup; + } + return false; + } + if (IsA(node, Aggref)) + { + int agglevelsup = ((Aggref *) node)->agglevelsup; + + /* convert levelsup to frame of reference of original query */ + agglevelsup -= context->sublevels_up; + /* ignore local aggs of subqueries */ + if (agglevelsup >= 0) + { + if (context->min_agglevel < 0 || + context->min_agglevel > agglevelsup) + context->min_agglevel = agglevelsup; + } + /* no need to examine args of the inner aggregate */ + return false; + } + /* We can throw error on sight for a window function */ + if (IsA(node, WindowFunc)) + ereport(ERROR, + (errcode(ERRCODE_GROUPING_ERROR), + errmsg("aggregate function calls cannot contain window function calls"), + parser_errposition(context->pstate, + ((WindowFunc *) node)->location))); + if (IsA(node, Query)) + { + /* Recurse into subselects */ + bool result; + + context->sublevels_up++; + result = query_tree_walker((Query *) node, + check_agg_arguments_walker, + (void *) context, + 0); + context->sublevels_up--; + return result; + } + return expression_tree_walker(node, + check_agg_arguments_walker, + (void *) context); } /* @@ -239,12 +588,19 @@ void transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc, WindowDef *windef) { + const char *err; + bool errkind; + /* * A window function call can't contain another one (but aggs are OK). XXX * is this required by spec, or just an unimplemented feature? + * + * Note: we don't need to check the filter expression here, because the + * context checks done below and in transformAggregateCall would have + * already rejected any window funcs or aggs within the filter. */ if (pstate->p_hasWindowFuncs && - checkExprHasWindowFuncs((Node *) wfunc->args)) + contain_windowfuncs((Node *) wfunc->args)) ereport(ERROR, (errcode(ERRCODE_WINDOWING_ERROR), errmsg("window function calls cannot be nested"), @@ -252,6 +608,124 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc, locate_windowfunc((Node *) wfunc->args)))); /* + * Check to see if the window function is in an invalid place within the + * query. + * + * For brevity we support two schemes for reporting an error here: set + * "err" to a custom message, or set "errkind" true if the error context + * is sufficiently identified by what ParseExprKindName will return, *and* + * what it will return is just a SQL keyword. (Otherwise, use a custom + * message to avoid creating translation problems.) + */ + err = NULL; + errkind = false; + switch (pstate->p_expr_kind) + { + case EXPR_KIND_NONE: + Assert(false); /* can't happen */ + break; + case EXPR_KIND_OTHER: + /* Accept window func here; caller must throw error if wanted */ + break; + case EXPR_KIND_JOIN_ON: + case EXPR_KIND_JOIN_USING: + err = _("window functions are not allowed in JOIN conditions"); + break; + case EXPR_KIND_FROM_SUBSELECT: + /* can't get here, but just in case, throw an error */ + errkind = true; + break; + case EXPR_KIND_FROM_FUNCTION: + err = _("window functions are not allowed in functions in FROM"); + break; + case EXPR_KIND_WHERE: + errkind = true; + break; + case EXPR_KIND_HAVING: + errkind = true; + break; + case EXPR_KIND_FILTER: + errkind = true; + break; + case EXPR_KIND_WINDOW_PARTITION: + case EXPR_KIND_WINDOW_ORDER: + case EXPR_KIND_WINDOW_FRAME_RANGE: + case EXPR_KIND_WINDOW_FRAME_ROWS: + err = _("window functions are not allowed in window definitions"); + break; + case EXPR_KIND_SELECT_TARGET: + /* okay */ + break; + case EXPR_KIND_INSERT_TARGET: + case EXPR_KIND_UPDATE_SOURCE: + case EXPR_KIND_UPDATE_TARGET: + errkind = true; + break; + case EXPR_KIND_GROUP_BY: + errkind = true; + break; + case EXPR_KIND_ORDER_BY: + /* okay */ + break; + case EXPR_KIND_DISTINCT_ON: + /* okay */ + break; + case EXPR_KIND_LIMIT: + case EXPR_KIND_OFFSET: + errkind = true; + break; + case EXPR_KIND_RETURNING: + errkind = true; + break; + case EXPR_KIND_VALUES: + errkind = true; + break; + case EXPR_KIND_CHECK_CONSTRAINT: + case EXPR_KIND_DOMAIN_CHECK: + err = _("window functions are not allowed in check constraints"); + break; + case EXPR_KIND_COLUMN_DEFAULT: + case EXPR_KIND_FUNCTION_DEFAULT: + err = _("window functions are not allowed in DEFAULT expressions"); + break; + case EXPR_KIND_INDEX_EXPRESSION: + err = _("window functions are not allowed in index expressions"); + break; + case EXPR_KIND_INDEX_PREDICATE: + err = _("window functions are not allowed in index predicates"); + break; + case EXPR_KIND_ALTER_COL_TRANSFORM: + err = _("window functions are not allowed in transform expressions"); + break; + case EXPR_KIND_EXECUTE_PARAMETER: + err = _("window functions are not allowed in EXECUTE parameters"); + break; + case EXPR_KIND_TRIGGER_WHEN: + err = _("window functions are not allowed in trigger WHEN conditions"); + break; + + /* + * There is intentionally no default: case here, so that the + * compiler will warn if we add a new ParseExprKind without + * extending this switch. If we do see an unrecognized value at + * runtime, the behavior will be the same as for EXPR_KIND_OTHER, + * which is sane anyway. + */ + } + if (err) + ereport(ERROR, + (errcode(ERRCODE_WINDOWING_ERROR), + errmsg_internal("%s", err), + parser_errposition(pstate, wfunc->location))); + if (errkind) + ereport(ERROR, + (errcode(ERRCODE_WINDOWING_ERROR), + /* translator: %s is name of a SQL construct, eg GROUP BY */ + errmsg("window functions are not allowed in %s", + ParseExprKindName(pstate->p_expr_kind)), + parser_errposition(pstate, wfunc->location))); + + /* * If the OVER clause just specifies a window name, find that WINDOW * clause (which had better be present). Otherwise, try to match all the * properties of the OVER clause, and make a new entry in the p_windowdefs @@ -325,11 +799,14 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc, /* * parseCheckAggregates * Check for aggregates where they shouldn't be and improper grouping. + * This function should be called after the target list and qualifications + * are finalized. * - * Ideally this should be done earlier, but it's difficult to distinguish - * aggregates from plain functions at the grammar level. So instead we - * check here. This function should be called after the target list and - * qualifications are finalized. + * Misplaced aggregates are now mostly detected in transformAggregateCall, + * but it seems more robust to check for aggregates in recursive queries + * only after everything is finalized. In any case it's hard to detect + * improper grouping on-the-fly, so we have to make another pass over the + * query for that. */ void parseCheckAggregates(ParseState *pstate, Query *qry) @@ -362,31 +839,8 @@ parseCheckAggregates(ParseState *pstate, Query *qry) } /* - * Aggregates must never appear in WHERE or JOIN/ON clauses. - * - * (Note this check should appear first to deliver an appropriate error - * message; otherwise we are likely to complain about some innocent - * variable in the target list, which is outright misleading if the - * problem is in WHERE.) - */ - if (checkExprHasAggs(qry->jointree->quals)) - ereport(ERROR, - (errcode(ERRCODE_GROUPING_ERROR), - errmsg("aggregates not allowed in WHERE clause"), - parser_errposition(pstate, - locate_agg_of_level(qry->jointree->quals, 0)))); - if (checkExprHasAggs((Node *) qry->jointree->fromlist)) - ereport(ERROR, - (errcode(ERRCODE_GROUPING_ERROR), - errmsg("aggregates not allowed in JOIN conditions"), - parser_errposition(pstate, - locate_agg_of_level((Node *) qry->jointree->fromlist, 0)))); - - /* - * No aggregates allowed in GROUP BY clauses, either. - * - * While we are at it, build a list of the acceptable GROUP BY expressions - * for use by check_ungrouped_columns(). + * Build a list of the acceptable GROUP BY expressions for use by + * check_ungrouped_columns(). */ foreach(l, qry->groupClause) { @@ -396,19 +850,13 @@ parseCheckAggregates(ParseState *pstate, Query *qry) expr = get_sortgroupclause_expr(grpcl, qry->targetList); if (expr == NULL) continue; /* probably cannot happen */ - if (checkExprHasAggs(expr)) - ereport(ERROR, - (errcode(ERRCODE_GROUPING_ERROR), - errmsg("aggregates not allowed in GROUP BY clause"), - parser_errposition(pstate, - locate_agg_of_level(expr, 0)))); groupClauses = lcons(expr, groupClauses); } /* * If there are join alias vars involved, we have to flatten them to the * underlying vars, so that aliased and unaliased vars will be correctly - * taken as equal. We can skip the expense of doing this if no rangetable + * taken as equal. We can skip the expense of doing this if no rangetable * entries are RTE_JOIN kind. We use the planner's flatten_join_alias_vars * routine to do the flattening; it wants a PlannerInfo root node, which * fortunately can be mostly dummy. @@ -447,7 +895,7 @@ parseCheckAggregates(ParseState *pstate, Query *qry) * * Note: because we check resjunk tlist elements as well as regular ones, * this will also find ungrouped variables that came from ORDER BY and - * WINDOW clauses. For that matter, it's also going to examine the + * WINDOW clauses. For that matter, it's also going to examine the * grouping expressions themselves --- but they'll all pass the test ... */ clause = (Node *) qry->targetList; @@ -470,97 +918,12 @@ parseCheckAggregates(ParseState *pstate, Query *qry) if (pstate->p_hasAggs && hasSelfRefRTEs) ereport(ERROR, (errcode(ERRCODE_INVALID_RECURSION), - errmsg("aggregate functions not allowed in a recursive query's recursive term"), + errmsg("aggregate functions are not allowed in a recursive query's recursive term"), parser_errposition(pstate, locate_agg_of_level((Node *) qry, 0)))); } /* - * parseCheckWindowFuncs - * Check for window functions where they shouldn't be. - * - * We have to forbid window functions in WHERE, JOIN/ON, HAVING, GROUP BY, - * and window specifications. (Other clauses, such as RETURNING and LIMIT, - * have already been checked.) Transformation of all these clauses must - * be completed already. - */ -void -parseCheckWindowFuncs(ParseState *pstate, Query *qry) -{ - ListCell *l; - - /* This should only be called if we found window functions */ - Assert(pstate->p_hasWindowFuncs); - - if (checkExprHasWindowFuncs(qry->jointree->quals)) - ereport(ERROR, - (errcode(ERRCODE_WINDOWING_ERROR), - errmsg("window functions not allowed in WHERE clause"), - parser_errposition(pstate, - locate_windowfunc(qry->jointree->quals)))); - if (checkExprHasWindowFuncs((Node *) qry->jointree->fromlist)) - ereport(ERROR, - (errcode(ERRCODE_WINDOWING_ERROR), - errmsg("window functions not allowed in JOIN conditions"), - parser_errposition(pstate, - locate_windowfunc((Node *) qry->jointree->fromlist)))); - if (checkExprHasWindowFuncs(qry->havingQual)) - ereport(ERROR, - (errcode(ERRCODE_WINDOWING_ERROR), - errmsg("window functions not allowed in HAVING clause"), - parser_errposition(pstate, - locate_windowfunc(qry->havingQual)))); - - foreach(l, qry->groupClause) - { - SortGroupClause *grpcl = (SortGroupClause *) lfirst(l); - Node *expr; - - expr = get_sortgroupclause_expr(grpcl, qry->targetList); - if (checkExprHasWindowFuncs(expr)) - ereport(ERROR, - (errcode(ERRCODE_WINDOWING_ERROR), - errmsg("window functions not allowed in GROUP BY clause"), - parser_errposition(pstate, - locate_windowfunc(expr)))); - } - - foreach(l, qry->windowClause) - { - WindowClause *wc = (WindowClause *) lfirst(l); - ListCell *l2; - - foreach(l2, wc->partitionClause) - { - SortGroupClause *grpcl = (SortGroupClause *) lfirst(l2); - Node *expr; - - expr = get_sortgroupclause_expr(grpcl, qry->targetList); - if (checkExprHasWindowFuncs(expr)) - ereport(ERROR, - (errcode(ERRCODE_WINDOWING_ERROR), - errmsg("window functions not allowed in window definition"), - parser_errposition(pstate, - locate_windowfunc(expr)))); - } - foreach(l2, wc->orderClause) - { - SortGroupClause *grpcl = (SortGroupClause *) lfirst(l2); - Node *expr; - - expr = get_sortgroupclause_expr(grpcl, qry->targetList); - if (checkExprHasWindowFuncs(expr)) - ereport(ERROR, - (errcode(ERRCODE_WINDOWING_ERROR), - errmsg("window functions not allowed in window definition"), - parser_errposition(pstate, - locate_windowfunc(expr)))); - } - /* startOffset and limitOffset were checked in transformFrameOffset */ - } -} - -/* * check_ungrouped_columns - * Scan the given expression tree for ungrouped variables (variables * that are not listed in the groupClauses list and are not within @@ -594,6 +957,7 @@ check_ungrouped_columns(Node *node, ParseState *pstate, Query *qry, context.have_non_var_grouping = have_non_var_grouping; context.func_grouped_rels = func_grouped_rels; context.sublevels_up = 0; + context.in_agg_direct_args = false; check_ungrouped_columns_walker(node, &context); } @@ -609,17 +973,39 @@ check_ungrouped_columns_walker(Node *node, IsA(node, Param)) return false; /* constants are always acceptable */ - /* - * If we find an aggregate call of the original level, do not recurse into - * its arguments; ungrouped vars in the arguments are not an error. We can - * also skip looking at the arguments of aggregates of higher levels, - * since they could not possibly contain Vars that are of concern to us - * (see transformAggregateCall). We do need to look into the arguments of - * aggregates of lower levels, however. - */ - if (IsA(node, Aggref) && - (int) ((Aggref *) node)->agglevelsup >= context->sublevels_up) - return false; + if (IsA(node, Aggref)) + { + Aggref *agg = (Aggref *) node; + + if ((int) agg->agglevelsup == context->sublevels_up) + { + /* + * If we find an aggregate call of the original level, do not + * recurse into its normal arguments, ORDER BY arguments, or + * filter; ungrouped vars there are not an error. But we should + * check direct arguments as though they weren't in an aggregate. + * We set a special flag in the context to help produce a useful + * error message for ungrouped vars in direct arguments. + */ + bool result; + + Assert(!context->in_agg_direct_args); + context->in_agg_direct_args = true; + result = check_ungrouped_columns_walker((Node *) agg->aggdirectargs, + context); + context->in_agg_direct_args = false; + return result; + } + + /* + * We can skip recursing into aggregates of higher levels altogether, + * since they could not possibly contain Vars of concern to us (see + * transformAggregateCall). We do need to look at aggregates of lower + * levels, however. + */ + if ((int) agg->agglevelsup > context->sublevels_up) + return false; + } /* * If we have any GROUP BY items that are not simple Vars, check to see if @@ -640,7 +1026,7 @@ check_ungrouped_columns_walker(Node *node, /* * If we have an ungrouped Var of the original query level, we have a * failure. Vars below the original query level are not a problem, and - * neither are Vars from above it. (If such Vars are ungrouped as far as + * neither are Vars from above it. (If such Vars are ungrouped as far as * their own query level is concerned, that's someone else's problem...) */ if (IsA(node, Var)) @@ -671,7 +1057,7 @@ check_ungrouped_columns_walker(Node *node, /* * Check whether the Var is known functionally dependent on the GROUP - * BY columns. If so, we can allow the Var to be used, because the + * BY columns. If so, we can allow the Var to be used, because the * grouping is really a no-op for this table. However, this deduction * depends on one or more constraints of the table, so we have to add * those constraints to the query's constraintDeps list, because it's @@ -712,6 +1098,8 @@ check_ungrouped_columns_walker(Node *node, (errcode(ERRCODE_GROUPING_ERROR), errmsg("column \"%s.%s\" must appear in the GROUP BY clause or be used in an aggregate function", rte->eref->aliasname, attname), + context->in_agg_direct_args ? + errdetail("Direct arguments of an ordered-set aggregate must use only grouped columns.") : 0, parser_errposition(context->pstate, var->location))); else ereport(ERROR, @@ -739,6 +1127,93 @@ check_ungrouped_columns_walker(Node *node, } /* + * get_aggregate_argtypes + * Identify the specific datatypes passed to an aggregate call. + * + * Given an Aggref, extract the actual datatypes of the input arguments. + * The input datatypes are reported in a way that matches up with the + * aggregate's declaration, ie, any ORDER BY columns attached to a plain + * aggregate are ignored, but we report both direct and aggregated args of + * an ordered-set aggregate. + * + * Datatypes are returned into inputTypes[], which must reference an array + * of length FUNC_MAX_ARGS. + * + * The function result is the number of actual arguments. + */ +int +get_aggregate_argtypes(Aggref *aggref, Oid *inputTypes) +{ + int numArguments = 0; + ListCell *lc; + + /* Any direct arguments of an ordered-set aggregate come first */ + foreach(lc, aggref->aggdirectargs) + { + Node *expr = (Node *) lfirst(lc); + + inputTypes[numArguments] = exprType(expr); + numArguments++; + } + + /* Now get the regular (aggregated) arguments */ + foreach(lc, aggref->args) + { + TargetEntry *tle = (TargetEntry *) lfirst(lc); + + /* Ignore ordering columns of a plain aggregate */ + if (tle->resjunk) + continue; + + inputTypes[numArguments] = exprType((Node *) tle->expr); + numArguments++; + } + + return numArguments; +} + +/* + * resolve_aggregate_transtype + * Identify the transition state value's datatype for an aggregate call. + * + * This function resolves a polymorphic aggregate's state datatype. + * It must be passed the aggtranstype from the aggregate's catalog entry, + * as well as the actual argument types extracted by get_aggregate_argtypes. + * (We could fetch these values internally, but for all existing callers that + * would just duplicate work the caller has to do too, so we pass them in.) + */ +Oid +resolve_aggregate_transtype(Oid aggfuncid, + Oid aggtranstype, + Oid *inputTypes, + int numArguments) +{ + /* resolve actual type of transition state, if polymorphic */ + if (IsPolymorphicType(aggtranstype)) + { + /* have to fetch the agg's declared input types... */ + Oid *declaredArgTypes; + int agg_nargs; + + (void) get_func_signature(aggfuncid, &declaredArgTypes, &agg_nargs); + + /* + * VARIADIC ANY aggs could have more actual than declared args, but + * such extra args can't affect polymorphic type resolution. + */ + Assert(agg_nargs <= numArguments); + + aggtranstype = enforce_generic_type_consistency(inputTypes, + declaredArgTypes, + agg_nargs, + aggtranstype, + false); + pfree(declaredArgTypes); + } + return aggtranstype; +} + +/* * Create expression trees for the transition and final functions * of an aggregate. These are needed so that polymorphic functions * can be used within an aggregate --- without the expression trees, @@ -751,15 +1226,23 @@ check_ungrouped_columns_walker(Node *node, * resolved to actual types (ie, none should ever be ANYELEMENT etc). * agg_input_collation is the aggregate function's input collation. * - * transfn_oid and finalfn_oid identify the funcs to be called; the latter - * may be InvalidOid. + * For an ordered-set aggregate, remember that agg_input_types describes + * the direct arguments followed by the aggregated arguments. + * + * transfn_oid, invtransfn_oid and finalfn_oid identify the funcs to be + * called; the latter two may be InvalidOid. * - * Pointers to the constructed trees are returned into *transfnexpr and - * *finalfnexpr. The latter is set to NULL if there's no finalfn. + * Pointers to the constructed trees are returned into *transfnexpr, + * *invtransfnexpr and *finalfnexpr. If there is no invtransfn or finalfn, + * the respective pointers are set to NULL. Since use of the invtransfn is + * optional, NULL may be passed for invtransfnexpr. */ void build_aggregate_fnexprs(Oid *agg_input_types, int agg_num_inputs, + int agg_num_direct_inputs, + int num_finalfn_inputs, + bool agg_variadic, Oid agg_state_type, #ifdef XCP Oid agg_collect_type, @@ -770,15 +1253,18 @@ build_aggregate_fnexprs(Oid *agg_input_types, #ifdef XCP Oid collectfn_oid, #endif + Oid invtransfn_oid, Oid finalfn_oid, Expr **transfnexpr, #ifdef XCP Expr **collectfnexpr, #endif + Expr **invtransfnexpr, Expr **finalfnexpr) { Param *argp; List *args; + FuncExpr *fexpr; int i; /* @@ -797,7 +1283,7 @@ build_aggregate_fnexprs(Oid *agg_input_types, args = list_make1(argp); - for (i = 0; i < agg_num_inputs; i++) + for (i = agg_num_direct_inputs; i < agg_num_inputs; i++) { argp = makeNode(Param); argp->paramkind = PARAM_EXEC; @@ -809,12 +1295,34 @@ build_aggregate_fnexprs(Oid *agg_input_types, args = lappend(args, argp); } - *transfnexpr = (Expr *) makeFuncExpr(transfn_oid, - agg_state_type, - args, - InvalidOid, - agg_input_collation, - COERCE_DONTCARE); + fexpr = makeFuncExpr(transfn_oid, + agg_state_type, + args, + InvalidOid, + agg_input_collation, + COERCE_EXPLICIT_CALL); + fexpr->funcvariadic = agg_variadic; + *transfnexpr = (Expr *) fexpr; + + /* + * Build invtransfn expression if requested, with same args as transfn + */ + if (invtransfnexpr != NULL) + { + if (OidIsValid(invtransfn_oid)) + { + fexpr = makeFuncExpr(invtransfn_oid, + agg_state_type, + args, + InvalidOid, + agg_input_collation, + COERCE_EXPLICIT_CALL); + fexpr->funcvariadic = agg_variadic; + *invtransfnexpr = (Expr *) fexpr; + } + else + *invtransfnexpr = NULL; + } #ifdef XCP /* see if we have a collect function */ @@ -844,7 +1352,7 @@ build_aggregate_fnexprs(Oid *agg_input_types, args, InvalidOid, agg_input_collation, - COERCE_DONTCARE); + COERCE_EXPLICIT_CALL); } else *collectfnexpr = NULL; @@ -878,10 +1386,24 @@ build_aggregate_fnexprs(Oid *agg_input_types, argp->location = -1; args = list_make1(argp); + /* finalfn may take additional args, which match agg's input types */ + for (i = 0; i < num_finalfn_inputs - 1; i++) + { + argp = makeNode(Param); + argp->paramkind = PARAM_EXEC; + argp->paramid = -1; + argp->paramtype = agg_input_types[i]; + argp->paramtypmod = -1; + argp->paramcollid = agg_input_collation; + argp->location = -1; + args = lappend(args, argp); + } + *finalfnexpr = (Expr *) makeFuncExpr(finalfn_oid, agg_result_type, args, InvalidOid, agg_input_collation, - COERCE_DONTCARE); + COERCE_EXPLICIT_CALL); + /* finalfn is currently never treated as variadic */ } diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 97ab9d5581..fcee1379c0 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -3,7 +3,7 @@ * parse_clause.c * handle clauses in parser * - * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -21,10 +21,10 @@ #include "commands/defrem.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" -#include "optimizer/clauses.h" #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" @@ -38,16 +38,8 @@ #include "utils/rel.h" -/* clause types for findTargetlistEntrySQL92 */ -#define ORDER_CLAUSE 0 -#define GROUP_CLAUSE 1 -#define DISTINCT_ON_CLAUSE 2 - -static const char *const clauseText[] = { - "ORDER BY", - "GROUP BY", - "DISTINCT ON" -}; +/* Convenience macro for the most common makeNamespaceItem() case */ +#define makeDefaultNSItem(rte) makeNamespaceItem(rte, true, true, false, true) static void extractRemainingColumns(List *common_colnames, List *src_colnames, List *src_colvars, @@ -56,10 +48,7 @@ static Node *transformJoinUsingClause(ParseState *pstate, RangeTblEntry *leftRTE, RangeTblEntry *rightRTE, List *leftVars, List *rightVars); static Node *transformJoinOnClause(ParseState *pstate, JoinExpr *j, - RangeTblEntry *l_rte, - RangeTblEntry *r_rte, - List *relnamespace, - Relids containedRels); + List *namespace); static RangeTblEntry *transformTableEntry(ParseState *pstate, RangeVar *r); static RangeTblEntry *transformCTEReference(ParseState *pstate, RangeVar *r, CommonTableExpr *cte, Index levelsup); @@ -69,21 +58,23 @@ static RangeTblEntry *transformRangeFunction(ParseState *pstate, RangeFunction *r); static Node *transformFromClauseItem(ParseState *pstate, Node *n, RangeTblEntry **top_rte, int *top_rti, - List **relnamespace, - Relids *containedRels); + List **namespace); static Node *buildMergedJoinVar(ParseState *pstate, JoinType jointype, Var *l_colvar, Var *r_colvar); +static ParseNamespaceItem *makeNamespaceItem(RangeTblEntry *rte, + bool rel_visible, bool cols_visible, + bool lateral_only, bool lateral_ok); +static void setNamespaceColumnVisibility(List *namespace, bool cols_visible); +static void setNamespaceLateralState(List *namespace, + bool lateral_only, bool lateral_ok); static void checkExprIsVarFree(ParseState *pstate, Node *n, const char *constructName); static TargetEntry *findTargetlistEntrySQL92(ParseState *pstate, Node *node, - List **tlist, int clause); + List **tlist, ParseExprKind exprKind); static TargetEntry *findTargetlistEntrySQL99(ParseState *pstate, Node *node, - List **tlist); + List **tlist, ParseExprKind exprKind); static int get_matching_location(int sortgroupref, List *sortgrouprefs, List *exprs); -static List *addTargetToSortList(ParseState *pstate, TargetEntry *tle, - List *sortlist, List *targetlist, SortBy *sortby, - bool resolveUnknown); static List *addTargetToGroupList(ParseState *pstate, TargetEntry *tle, List *grouplist, List *targetlist, int location, bool resolveUnknown); @@ -95,17 +86,12 @@ static Node *transformFrameOffset(ParseState *pstate, int frameOptions, /* * transformFromClause - * Process the FROM clause and add items to the query's range table, - * joinlist, and namespaces. + * joinlist, and namespace. * - * Note: we assume that pstate's p_rtable, p_joinlist, p_relnamespace, and - * p_varnamespace lists were initialized to NIL when the pstate was created. + * Note: we assume that the pstate's p_rtable, p_joinlist, and p_namespace + * lists were initialized to NIL when the pstate was created. * We will add onto any entries already present --- this is needed for rule * processing, as well as for UPDATE and DELETE. - * - * The range table may grow still further when we transform the expressions - * in the query's quals and target list. (This is possible because in - * POSTQUEL, we allowed references to relations not specified in the - * from-clause. PostgreSQL keeps this extension to standard SQL.) */ void transformFromClause(ParseState *pstate, List *frmList) @@ -116,28 +102,39 @@ transformFromClause(ParseState *pstate, List *frmList) * The grammar will have produced a list of RangeVars, RangeSubselects, * RangeFunctions, and/or JoinExprs. Transform each one (possibly adding * entries to the rtable), check for duplicate refnames, and then add it - * to the joinlist and namespaces. + * to the joinlist and namespace. + * + * Note we must process the items left-to-right for proper handling of + * LATERAL references. */ foreach(fl, frmList) { Node *n = lfirst(fl); RangeTblEntry *rte; int rtindex; - List *relnamespace; - Relids containedRels; + List *namespace; n = transformFromClauseItem(pstate, n, &rte, &rtindex, - &relnamespace, - &containedRels); - checkNameSpaceConflicts(pstate, pstate->p_relnamespace, relnamespace); + &namespace); + + checkNameSpaceConflicts(pstate, pstate->p_namespace, namespace); + + /* Mark the new namespace items as visible only to LATERAL */ + setNamespaceLateralState(namespace, true, true); + pstate->p_joinlist = lappend(pstate->p_joinlist, n); - pstate->p_relnamespace = list_concat(pstate->p_relnamespace, - relnamespace); - pstate->p_varnamespace = lappend(pstate->p_varnamespace, rte); - bms_free(containedRels); + pstate->p_namespace = list_concat(pstate->p_namespace, namespace); } + + /* + * We're done parsing the FROM list, so make all namespace items + * unconditionally visible. Note that this will also reset lateral_only + * for any namespace items that were already present when we were called; + * but those should have been that way already. + */ + setNamespaceLateralState(pstate->p_namespace, false, true); } /* @@ -152,7 +149,7 @@ transformFromClause(ParseState *pstate, List *frmList) * * If alsoSource is true, add the target to the query's joinlist and * namespace. For INSERT, we don't want the target to be joined to; - * it's a destination of tuples, not a source. For UPDATE/DELETE, + * it's a destination of tuples, not a source. For UPDATE/DELETE, * we do need to scan or join the target. (NOTE: we do not bother * to check for namespace conflict; we assume that the namespace was * initially empty in these cases.) @@ -206,7 +203,11 @@ setTargetTable(ParseState *pstate, RangeVar *relation, rte->requiredPerms = requiredPerms; /* - * If UPDATE/DELETE, add table to joinlist and namespaces. + * If UPDATE/DELETE, add table to joinlist and namespace. + * + * Note: some callers know that they can find the new ParseNamespaceItem + * at the end of the pstate->p_namespace list. This is a bit ugly but not + * worth complicating this function's signature for. */ if (alsoSource) addRTEtoQuery(pstate, rte, true, true, true); @@ -218,7 +219,7 @@ setTargetTable(ParseState *pstate, RangeVar *relation, * Simplify InhOption (yes/no/default) into boolean yes/no. * * The reason we do things this way is that we don't want to examine the - * SQL_inheritance option flag until parse_analyze() is run. Otherwise, + * SQL_inheritance option flag until parse_analyze() is run. Otherwise, * we'd do the wrong thing with query strings that intermix SET commands * with queries. */ @@ -243,9 +244,13 @@ interpretInhOption(InhOption inhOpt) * table/result set should be created with OIDs. This needs to be done after * parsing the query string because the return value can depend upon the * default_with_oids GUC var. + * + * In some situations, we want to reject an OIDS option even if it's present. + * That's (rather messily) handled here rather than reloptions.c, because that + * code explicitly punts checking for oids to here. */ bool -interpretOidsOption(List *defList) +interpretOidsOption(List *defList, bool allowOids) { ListCell *cell; @@ -256,9 +261,20 @@ interpretOidsOption(List *defList) if (def->defnamespace == NULL && pg_strcasecmp(def->defname, "oids") == 0) + { + if (!allowOids) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("unrecognized parameter \"%s\"", + def->defname))); return defGetBoolean(def); + } } + /* Force no-OIDS result if caller disallows OIDS. */ + if (!allowOids) + return false; + /* OIDS option was not specified, so use default. */ return default_with_oids; } @@ -360,7 +376,7 @@ transformJoinUsingClause(ParseState *pstate, * transformJoinOnClause() does. Just invoke transformExpr() to fix up * the operators, and we're done. */ - result = transformExpr(pstate, result); + result = transformExpr(pstate, result, EXPR_KIND_JOIN_USING); result = coerce_to_boolean(pstate, result, "JOIN/USING"); @@ -372,57 +388,28 @@ transformJoinUsingClause(ParseState *pstate, * Result is a transformed qualification expression. */ static Node * -transformJoinOnClause(ParseState *pstate, JoinExpr *j, - RangeTblEntry *l_rte, - RangeTblEntry *r_rte, - List *relnamespace, - Relids containedRels) +transformJoinOnClause(ParseState *pstate, JoinExpr *j, List *namespace) { Node *result; - List *save_relnamespace; - List *save_varnamespace; - Relids clause_varnos; - int varno; + List *save_namespace; /* - * This is a tad tricky, for two reasons. First, the namespace that the - * join expression should see is just the two subtrees of the JOIN plus - * any outer references from upper pstate levels. So, temporarily set - * this pstate's namespace accordingly. (We need not check for refname - * conflicts, because transformFromClauseItem() already did.) NOTE: this - * code is OK only because the ON clause can't legally alter the namespace - * by causing implicit relation refs to be added. + * The namespace that the join expression should see is just the two + * subtrees of the JOIN plus any outer references from upper pstate + * levels. Temporarily set this pstate's namespace accordingly. (We need + * not check for refname conflicts, because transformFromClauseItem() + * already did.) All namespace items are marked visible regardless of + * LATERAL state. */ - save_relnamespace = pstate->p_relnamespace; - save_varnamespace = pstate->p_varnamespace; - - pstate->p_relnamespace = relnamespace; - pstate->p_varnamespace = list_make2(l_rte, r_rte); + setNamespaceLateralState(namespace, false, true); - result = transformWhereClause(pstate, j->quals, "JOIN/ON"); + save_namespace = pstate->p_namespace; + pstate->p_namespace = namespace; - pstate->p_relnamespace = save_relnamespace; - pstate->p_varnamespace = save_varnamespace; + result = transformWhereClause(pstate, j->quals, + EXPR_KIND_JOIN_ON, "JOIN/ON"); - /* - * Second, we need to check that the ON condition doesn't refer to any - * rels outside the input subtrees of the JOIN. It could do that despite - * our hack on the namespace if it uses fully-qualified names. So, grovel - * through the transformed clause and make sure there are no bogus - * references. (Outer references are OK, and are ignored here.) - */ - clause_varnos = pull_varnos(result); - clause_varnos = bms_del_members(clause_varnos, containedRels); - if ((varno = bms_first_member(clause_varnos)) >= 0) - { - ereport(ERROR, - (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("JOIN/ON clause refers to \"%s\", which is not part of JOIN", - rt_fetch(varno, pstate->p_rtable)->eref->aliasname), - parser_errposition(pstate, - locate_var_of_relation(result, varno, 0)))); - } - bms_free(clause_varnos); + pstate->p_namespace = save_namespace; return result; } @@ -435,13 +422,7 @@ transformTableEntry(ParseState *pstate, RangeVar *r) { RangeTblEntry *rte; - /* - * mark this entry to indicate it comes from the FROM clause. In SQL, the - * target list can only refer to range variables specified in the from - * clause but we follow the more powerful POSTQUEL semantics and - * automatically generate the range variable if not specified. However - * there are times we need to know whether the entries are legitimate. - */ + /* We need only build a range table entry */ rte = addRangeTableEntry(pstate, r, r->alias, interpretInhOption(r->inhOpt), true); @@ -476,19 +457,40 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r) * We require user to supply an alias for a subselect, per SQL92. To relax * this, we'd have to be prepared to gin up a unique alias for an * unlabeled subselect. (This is just elog, not ereport, because the - * grammar should have enforced it already.) + * grammar should have enforced it already. It'd probably be better to + * report the error here, but we don't have a good error location here.) */ if (r->alias == NULL) elog(ERROR, "subquery in FROM must have an alias"); /* + * Set p_expr_kind to show this parse level is recursing to a subselect. + * We can't be nested within any expression, so don't need save-restore + * logic here. + */ + Assert(pstate->p_expr_kind == EXPR_KIND_NONE); + pstate->p_expr_kind = EXPR_KIND_FROM_SUBSELECT; + + /* + * If the subselect is LATERAL, make lateral_only names of this level + * visible to it. (LATERAL can't nest within a single pstate level, so we + * don't need save/restore logic here.) + */ + Assert(!pstate->p_lateral_active); + pstate->p_lateral_active = r->lateral; + + /* * Analyze and transform the subquery. */ query = parse_sub_analyze(r->subquery, pstate, NULL, isLockedRefname(pstate, r->alias->aliasname)); + /* Restore state */ + pstate->p_lateral_active = false; + pstate->p_expr_kind = EXPR_KIND_NONE; + /* - * Check that we got something reasonable. Many of these conditions are + * Check that we got something reasonable. Many of these conditions are * impossible given restrictions of the grammar, but check 'em anyway. */ if (!IsA(query, Query) || @@ -497,32 +499,13 @@ transformRangeSubselect(ParseState *pstate, RangeSubselect *r) elog(ERROR, "unexpected non-SELECT command in subquery in FROM"); /* - * The subquery cannot make use of any variables from FROM items created - * earlier in the current query. Per SQL92, the scope of a FROM item does - * not include other FROM items. Formerly we hacked the namespace so that - * the other variables weren't even visible, but it seems more useful to - * leave them visible and give a specific error message. - * - * XXX this will need further work to support SQL99's LATERAL() feature, - * wherein such references would indeed be legal. - * - * We can skip groveling through the subquery if there's not anything - * visible in the current query. Also note that outer references are OK. - */ - if (pstate->p_relnamespace || pstate->p_varnamespace) - { - if (contain_vars_of_level((Node *) query, 1)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("subquery in FROM cannot refer to other relations of same query level"), - parser_errposition(pstate, - locate_var_of_level((Node *) query, 1)))); - } - - /* * OK, build an RTE for the subquery. */ - rte = addRangeTableEntryForSubquery(pstate, query, r->alias, true); + rte = addRangeTableEntryForSubquery(pstate, + query, + r->alias, + r->lateral, + true); return rte; } @@ -534,88 +517,190 @@ 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; + ListCell *lc; /* - * 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. + * We make lateral_only names of this level visible, whether or not the + * 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.) */ - funcname = FigureColname(r->funccallnode); + Assert(!pstate->p_lateral_active); + 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); + 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); /* - * The function parameters cannot make use of any variables from other - * FROM items. (Compare to transformRangeSubselect(); the coding is - * different though because we didn't parse as a sub-select with its own - * level of namespace.) + * Install the top-level coldeflist if there was one (we already checked + * that there was no conflicting per-function coldeflist). * - * XXX this will need further work to support SQL99's LATERAL() feature, - * wherein such references would indeed be legal. + * 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 ROWS + * FROM() is much clearer in this case. */ - if (pstate->p_relnamespace || pstate->p_varnamespace) + if (r->coldeflist) { - if (contain_vars_of_level(funcexpr, 0)) + if (list_length(funcexprs) != 1) + { + if (r->is_rowsfrom) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("ROWS FROM() with multiple functions cannot have a column definition list"), + errhint("Put a separate column definition list for each function inside ROWS FROM()."), + 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 ROWS FROM(), and attach a column definition list to each one."), + parser_errposition(pstate, + exprLocation((Node *) r->coldeflist)))); + } + if (r->ordinality) ereport(ERROR, - (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("function expression in FROM cannot refer to other relations of same query level"), + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("WITH ORDINALITY cannot be used with a column definition list"), + errhint("Put the column definition list inside ROWS FROM()."), parser_errposition(pstate, - locate_var_of_level(funcexpr, 0)))); + exprLocation((Node *) r->coldeflist)))); + + coldeflists = list_make1(r->coldeflist); } /* - * Disallow aggregate functions in the expression. (No reason to postpone - * this check until parseCheckAggregates.) + * Mark the RTE as LATERAL if the user said LATERAL explicitly, or if + * there are any lateral cross-references in it. */ - if (pstate->p_hasAggs && - checkExprHasAggs(funcexpr)) - ereport(ERROR, - (errcode(ERRCODE_GROUPING_ERROR), - errmsg("cannot use aggregate function in function expression in FROM"), - parser_errposition(pstate, - locate_agg_of_level(funcexpr, 0)))); - if (pstate->p_hasWindowFuncs && - checkExprHasWindowFuncs(funcexpr)) - ereport(ERROR, - (errcode(ERRCODE_WINDOWING_ERROR), - errmsg("cannot use window function in function expression in FROM"), - parser_errposition(pstate, - locate_windowfunc(funcexpr)))); + is_lateral = r->lateral || contain_vars_of_level((Node *) funcexprs, 0); /* * OK, build an RTE for the function. */ - rte = addRangeTableEntryForFunction(pstate, funcname, funcexpr, - r, 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); - } + rte = addRangeTableEntryForFunction(pstate, + funcnames, funcexprs, coldeflists, + r, is_lateral, true); return rte; } @@ -625,7 +710,8 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r) * transformFromClauseItem - * Transform a FROM-clause item, adding any required entries to the * range table list being built in the ParseState, and return the - * transformed item ready to include in the joinlist and namespaces. + * transformed item ready to include in the joinlist. Also build a + * ParseNamespaceItem list describing the names exposed by this item. * This routine can recurse to handle SQL92 JOIN expressions. * * The function return value is the node to add to the jointree (a @@ -635,23 +721,16 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r) * (We could extract this from the function return node, but it saves cycles * to pass it back separately.) * - * *top_rti: receives the rangetable index of top_rte. (Ditto.) - * - * *relnamespace: receives a List of the RTEs exposed as relation names - * by this item. + * *top_rti: receives the rangetable index of top_rte. (Ditto.) * - * *containedRels: receives a bitmap set of the rangetable indexes - * of all the base and join relations represented in this jointree item. - * This is needed for checking JOIN/ON conditions in higher levels. - * - * We do not need to pass back an explicit varnamespace value, because - * in all cases the varnamespace contribution is exactly top_rte. + * *namespace: receives a List of ParseNamespaceItems for the RTEs exposed + * as table/column names by this item. (The lateral_only flags in these items + * are indeterminate and should be explicitly set by the caller before use.) */ static Node * transformFromClauseItem(ParseState *pstate, Node *n, RangeTblEntry **top_rte, int *top_rti, - List **relnamespace, - Relids *containedRels) + List **namespace) { if (IsA(n, RangeVar)) { @@ -681,8 +760,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); *top_rte = rte; *top_rti = rtindex; - *relnamespace = list_make1(rte); - *containedRels = bms_make_singleton(rtindex); + *namespace = list_make1(makeDefaultNSItem(rte)); rtr = makeNode(RangeTblRef); rtr->rtindex = rtindex; return (Node *) rtr; @@ -700,8 +778,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); *top_rte = rte; *top_rti = rtindex; - *relnamespace = list_make1(rte); - *containedRels = bms_make_singleton(rtindex); + *namespace = list_make1(makeDefaultNSItem(rte)); rtr = makeNode(RangeTblRef); rtr->rtindex = rtindex; return (Node *) rtr; @@ -719,8 +796,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); *top_rte = rte; *top_rti = rtindex; - *relnamespace = list_make1(rte); - *containedRels = bms_make_singleton(rtindex); + *namespace = list_make1(makeDefaultNSItem(rte)); rtr = makeNode(RangeTblRef); rtr->rtindex = rtindex; return (Node *) rtr; @@ -733,50 +809,71 @@ transformFromClauseItem(ParseState *pstate, Node *n, RangeTblEntry *r_rte; int l_rtindex; int r_rtindex; - Relids l_containedRels, - r_containedRels, - my_containedRels; - List *l_relnamespace, - *r_relnamespace, - *my_relnamespace, + List *l_namespace, + *r_namespace, + *my_namespace, *l_colnames, *r_colnames, *res_colnames, *l_colvars, *r_colvars, *res_colvars; + bool lateral_ok; + int sv_namespace_length; RangeTblEntry *rte; int k; /* - * Recursively process the left and right subtrees + * Recursively process the left subtree, then the right. We must do + * it in this order for correct visibility of LATERAL references. */ j->larg = transformFromClauseItem(pstate, j->larg, &l_rte, &l_rtindex, - &l_relnamespace, - &l_containedRels); + &l_namespace); + + /* + * Make the left-side RTEs available for LATERAL access within the + * right side, by temporarily adding them to the pstate's namespace + * list. Per SQL:2008, if the join type is not INNER or LEFT then the + * left-side names must still be exposed, but it's an error to + * reference them. (Stupid design, but that's what it says.) Hence, + * we always push them into the namespace, but mark them as not + * lateral_ok if the jointype is wrong. + * + * Notice that we don't require the merged namespace list to be + * conflict-free. See the comments for scanNameSpaceForRefname(). + * + * NB: this coding relies on the fact that list_concat is not + * destructive to its second argument. + */ + lateral_ok = (j->jointype == JOIN_INNER || j->jointype == JOIN_LEFT); + setNamespaceLateralState(l_namespace, true, lateral_ok); + + sv_namespace_length = list_length(pstate->p_namespace); + pstate->p_namespace = list_concat(pstate->p_namespace, l_namespace); + + /* And now we can process the RHS */ j->rarg = transformFromClauseItem(pstate, j->rarg, &r_rte, &r_rtindex, - &r_relnamespace, - &r_containedRels); + &r_namespace); + + /* Remove the left-side RTEs from the namespace list again */ + pstate->p_namespace = list_truncate(pstate->p_namespace, + sv_namespace_length); /* * Check for conflicting refnames in left and right subtrees. Must do * this because higher levels will assume I hand back a self- - * consistent namespace subtree. + * consistent namespace list. */ - checkNameSpaceConflicts(pstate, l_relnamespace, r_relnamespace); + checkNameSpaceConflicts(pstate, l_namespace, r_namespace); /* - * Generate combined relation membership info for possible use by - * transformJoinOnClause below. + * Generate combined namespace info for possible use below. */ - my_relnamespace = list_concat(l_relnamespace, r_relnamespace); - my_containedRels = bms_join(l_containedRels, r_containedRels); - - pfree(r_relnamespace); /* free unneeded list header */ + my_namespace = list_concat(l_namespace, r_namespace); /* * Extract column name and var lists from both subtrees @@ -939,10 +1036,7 @@ transformFromClauseItem(ParseState *pstate, Node *n, else if (j->quals) { /* User-written ON-condition; transform it */ - j->quals = transformJoinOnClause(pstate, j, - l_rte, r_rte, - my_relnamespace, - my_containedRels); + j->quals = transformJoinOnClause(pstate, j, my_namespace); } else { @@ -1001,22 +1095,30 @@ transformFromClauseItem(ParseState *pstate, Node *n, /* * Prepare returned namespace list. If the JOIN has an alias then it - * hides the contained RTEs as far as the relnamespace goes; - * otherwise, put the contained RTEs and *not* the JOIN into - * relnamespace. + * hides the contained RTEs completely; otherwise, the contained RTEs + * are still visible as table names, but are not visible for + * unqualified column-name access. + * + * Note: if there are nested alias-less JOINs, the lower-level ones + * will remain in the list although they have neither p_rel_visible + * nor p_cols_visible set. We could delete such list items, but it's + * unclear that it's worth expending cycles to do so. */ - if (j->alias) - { - *relnamespace = list_make1(rte); - list_free(my_relnamespace); - } + if (j->alias != NULL) + my_namespace = NIL; else - *relnamespace = my_relnamespace; + setNamespaceColumnVisibility(my_namespace, false); /* - * Include join RTE in returned containedRels set + * The join RTE itself is always made visible for unqualified column + * names. It's visible as a relation name only if it has an alias. */ - *containedRels = bms_add_member(my_containedRels, j->rtindex); + *namespace = lappend(my_namespace, + makeNamespaceItem(rte, + (j->alias != NULL), + true, + false, + true)); return (Node *) j; } @@ -1144,6 +1246,60 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype, return res_node; } +/* + * makeNamespaceItem - + * Convenience subroutine to construct a ParseNamespaceItem. + */ +static ParseNamespaceItem * +makeNamespaceItem(RangeTblEntry *rte, bool rel_visible, bool cols_visible, + bool lateral_only, bool lateral_ok) +{ + ParseNamespaceItem *nsitem; + + nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); + nsitem->p_rte = rte; + nsitem->p_rel_visible = rel_visible; + nsitem->p_cols_visible = cols_visible; + nsitem->p_lateral_only = lateral_only; + nsitem->p_lateral_ok = lateral_ok; + return nsitem; +} + +/* + * setNamespaceColumnVisibility - + * Convenience subroutine to update cols_visible flags in a namespace list. + */ +static void +setNamespaceColumnVisibility(List *namespace, bool cols_visible) +{ + ListCell *lc; + + foreach(lc, namespace) + { + ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(lc); + + nsitem->p_cols_visible = cols_visible; + } +} + +/* + * setNamespaceLateralState - + * Convenience subroutine to update LATERAL flags in a namespace list. + */ +static void +setNamespaceLateralState(List *namespace, bool lateral_only, bool lateral_ok) +{ + ListCell *lc; + + foreach(lc, namespace) + { + ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(lc); + + nsitem->p_lateral_only = lateral_only; + nsitem->p_lateral_ok = lateral_ok; + } +} + /* * transformWhereClause - @@ -1154,14 +1310,14 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype, */ Node * transformWhereClause(ParseState *pstate, Node *clause, - const char *constructName) + ParseExprKind exprKind, const char *constructName) { Node *qual; if (clause == NULL) return NULL; - qual = transformExpr(pstate, clause); + qual = transformExpr(pstate, clause, exprKind); qual = coerce_to_boolean(pstate, qual, constructName); @@ -1181,18 +1337,18 @@ transformWhereClause(ParseState *pstate, Node *clause, */ Node * transformLimitClause(ParseState *pstate, Node *clause, - const char *constructName) + ParseExprKind exprKind, const char *constructName) { Node *qual; if (clause == NULL) return NULL; - qual = transformExpr(pstate, clause); + qual = transformExpr(pstate, clause, exprKind); qual = coerce_to_specific_type(pstate, qual, INT8OID, constructName); - /* LIMIT can't refer to any vars or aggregates of the current query */ + /* LIMIT can't refer to any variables of the current query */ checkExprIsVarFree(pstate, qual, constructName); return qual; @@ -1201,7 +1357,7 @@ transformLimitClause(ParseState *pstate, Node *clause, /* * checkExprIsVarFree * Check that given expr has no Vars of the current query level - * (and no aggregates or window functions, either). + * (aggregates and window functions should have been rejected already). * * This is used to check expressions that have to have a consistent value * across all rows of the query, such as a LIMIT. Arguably it should reject @@ -1223,31 +1379,57 @@ checkExprIsVarFree(ParseState *pstate, Node *n, const char *constructName) parser_errposition(pstate, locate_var_of_level(n, 0)))); } - if (pstate->p_hasAggs && - checkExprHasAggs(n)) - { - ereport(ERROR, - (errcode(ERRCODE_GROUPING_ERROR), - /* translator: %s is name of a SQL construct, eg LIMIT */ - errmsg("argument of %s must not contain aggregate functions", - constructName), - parser_errposition(pstate, - locate_agg_of_level(n, 0)))); - } - if (pstate->p_hasWindowFuncs && - checkExprHasWindowFuncs(n)) +} + + +/* + * checkTargetlistEntrySQL92 - + * Validate a targetlist entry found by findTargetlistEntrySQL92 + * + * When we select a pre-existing tlist entry as a result of syntax such + * as "GROUP BY 1", we have to make sure it is acceptable for use in the + * indicated clause type; transformExpr() will have treated it as a regular + * targetlist item. + */ +static void +checkTargetlistEntrySQL92(ParseState *pstate, TargetEntry *tle, + ParseExprKind exprKind) +{ + switch (exprKind) { - ereport(ERROR, - (errcode(ERRCODE_WINDOWING_ERROR), - /* translator: %s is name of a SQL construct, eg LIMIT */ - errmsg("argument of %s must not contain window functions", - constructName), - parser_errposition(pstate, - locate_windowfunc(n)))); + case EXPR_KIND_GROUP_BY: + /* reject aggregates and window functions */ + if (pstate->p_hasAggs && + contain_aggs_of_level((Node *) tle->expr, 0)) + ereport(ERROR, + (errcode(ERRCODE_GROUPING_ERROR), + /* translator: %s is name of a SQL construct, eg GROUP BY */ + errmsg("aggregate functions are not allowed in %s", + ParseExprKindName(exprKind)), + parser_errposition(pstate, + locate_agg_of_level((Node *) tle->expr, 0)))); + if (pstate->p_hasWindowFuncs && + contain_windowfuncs((Node *) tle->expr)) + ereport(ERROR, + (errcode(ERRCODE_WINDOWING_ERROR), + /* translator: %s is name of a SQL construct, eg GROUP BY */ + errmsg("window functions are not allowed in %s", + ParseExprKindName(exprKind)), + parser_errposition(pstate, + locate_windowfunc((Node *) tle->expr)))); + break; + case EXPR_KIND_ORDER_BY: + /* no extra checks needed */ + break; + case EXPR_KIND_DISTINCT_ON: + /* no extra checks needed */ + break; + default: + elog(ERROR, "unexpected exprKind in checkTargetlistEntrySQL92"); + break; } } - /* * findTargetlistEntrySQL92 - * Returns the targetlist entry matching the given (untransformed) node. @@ -1256,18 +1438,18 @@ checkExprIsVarFree(ParseState *pstate, Node *n, const char *constructName) * * This function supports the old SQL92 ORDER BY interpretation, where the * expression is an output column name or number. If we fail to find a - * match of that sort, we fall through to the SQL99 rules. For historical + * match of that sort, we fall through to the SQL99 rules. For historical * reasons, Postgres also allows this interpretation for GROUP BY, though - * the standard never did. However, for GROUP BY we prefer a SQL99 match. + * the standard never did. However, for GROUP BY we prefer a SQL99 match. * This function is *not* used for WINDOW definitions. * * node the ORDER BY, GROUP BY, or DISTINCT ON expression to be matched * tlist the target list (passed by reference so we can append to it) - * clause identifies clause type being processed + * exprKind identifies clause type being processed */ static TargetEntry * findTargetlistEntrySQL92(ParseState *pstate, Node *node, List **tlist, - int clause) + ParseExprKind exprKind) { ListCell *tl; @@ -1276,7 +1458,7 @@ findTargetlistEntrySQL92(ParseState *pstate, Node *node, List **tlist, * * 1. Bare ColumnName (no qualifier or subscripts) * For a bare identifier, we search for a matching column name - * in the existing target list. Multiple matches are an error + * in the existing target list. Multiple matches are an error * unless they refer to identical values; for example, * we allow SELECT a, a FROM table ORDER BY a * but not SELECT a AS b, b FROM table ORDER BY b @@ -1285,7 +1467,7 @@ findTargetlistEntrySQL92(ParseState *pstate, Node *node, List **tlist, * For GROUP BY, it is incorrect to match the grouping item against * targetlist entries: according to SQL92, an identifier in GROUP BY * is a reference to a column name exposed by FROM, not to a target - * list column. However, many implementations (including pre-7.0 + * list column. However, many implementations (including pre-7.0 * PostgreSQL) accept this anyway. So for GROUP BY, we look first * to see if the identifier matches any FROM column name, and only * try for a targetlist name if it doesn't. This ensures that we @@ -1316,7 +1498,7 @@ findTargetlistEntrySQL92(ParseState *pstate, Node *node, List **tlist, char *name = strVal(linitial(((ColumnRef *) node)->fields)); int location = ((ColumnRef *) node)->location; - if (clause == GROUP_CLAUSE) + if (exprKind == EXPR_KIND_GROUP_BY) { /* * In GROUP BY, we must prefer a match against a FROM-clause @@ -1358,7 +1540,8 @@ findTargetlistEntrySQL92(ParseState *pstate, Node *node, List **tlist, /*------ translator: first %s is name of a SQL construct, eg ORDER BY */ errmsg("%s \"%s\" is ambiguous", - clauseText[clause], name), + ParseExprKindName(exprKind), + name), parser_errposition(pstate, location))); } else @@ -1367,7 +1550,11 @@ findTargetlistEntrySQL92(ParseState *pstate, Node *node, List **tlist, } } if (target_result != NULL) - return target_result; /* return the first match */ + { + /* return the first match, after suitable validation */ + checkTargetlistEntrySQL92(pstate, target_result, exprKind); + return target_result; + } } } if (IsA(node, A_Const)) @@ -1382,7 +1569,7 @@ findTargetlistEntrySQL92(ParseState *pstate, Node *node, List **tlist, (errcode(ERRCODE_SYNTAX_ERROR), /* translator: %s is name of a SQL construct, eg ORDER BY */ errmsg("non-integer constant in %s", - clauseText[clause]), + ParseExprKindName(exprKind)), parser_errposition(pstate, location))); target_pos = intVal(val); @@ -1393,21 +1580,25 @@ findTargetlistEntrySQL92(ParseState *pstate, Node *node, List **tlist, if (!tle->resjunk) { if (++targetlist_pos == target_pos) - return tle; /* return the unique match */ + { + /* return the unique match, after suitable validation */ + checkTargetlistEntrySQL92(pstate, tle, exprKind); + return tle; + } } } ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), /* translator: %s is name of a SQL construct, eg ORDER BY */ errmsg("%s position %d is not in select list", - clauseText[clause], target_pos), + ParseExprKindName(exprKind), target_pos), parser_errposition(pstate, location))); } /* * Otherwise, we have an expression, so process it per SQL99 rules. */ - return findTargetlistEntrySQL99(pstate, node, tlist); + return findTargetlistEntrySQL99(pstate, node, tlist, exprKind); } /* @@ -1421,9 +1612,11 @@ findTargetlistEntrySQL92(ParseState *pstate, Node *node, List **tlist, * * node the ORDER BY, GROUP BY, etc expression to be matched * tlist the target list (passed by reference so we can append to it) + * exprKind identifies clause type being processed */ static TargetEntry * -findTargetlistEntrySQL99(ParseState *pstate, Node *node, List **tlist) +findTargetlistEntrySQL99(ParseState *pstate, Node *node, List **tlist, + ParseExprKind exprKind) { TargetEntry *target_result; ListCell *tl; @@ -1432,11 +1625,11 @@ findTargetlistEntrySQL99(ParseState *pstate, Node *node, List **tlist) /* * Convert the untransformed node to a transformed expression, and search * for a match in the tlist. NOTE: it doesn't really matter whether there - * is more than one match. Also, we are willing to match an existing + * is more than one match. Also, we are willing to match an existing * resjunk target here, though the SQL92 cases above must ignore resjunk * targets. */ - expr = transformExpr(pstate, node); + expr = transformExpr(pstate, node, exprKind); foreach(tl, *tlist) { @@ -1460,10 +1653,11 @@ findTargetlistEntrySQL99(ParseState *pstate, Node *node, List **tlist) /* * If no matches, construct a new target entry which is appended to the - * end of the target list. This target is given resjunk = TRUE so that it + * end of the target list. This target is given resjunk = TRUE so that it * will not be projected into the final tuple. */ - target_result = transformTargetEntry(pstate, node, expr, NULL, true); + target_result = transformTargetEntry(pstate, node, expr, exprKind, + NULL, true); *tlist = lappend(*tlist, target_result); @@ -1483,7 +1677,7 @@ findTargetlistEntrySQL99(ParseState *pstate, Node *node, List **tlist) List * transformGroupClause(ParseState *pstate, List *grouplist, List **targetlist, List *sortClause, - bool useSQL99) + ParseExprKind exprKind, bool useSQL99) { List *result = NIL; ListCell *gl; @@ -1495,10 +1689,11 @@ transformGroupClause(ParseState *pstate, List *grouplist, bool found = false; if (useSQL99) - tle = findTargetlistEntrySQL99(pstate, gexpr, targetlist); + tle = findTargetlistEntrySQL99(pstate, gexpr, + targetlist, exprKind); else - tle = findTargetlistEntrySQL92(pstate, gexpr, targetlist, - GROUP_CLAUSE); + tle = findTargetlistEntrySQL92(pstate, gexpr, + targetlist, exprKind); /* Eliminate duplicates (GROUP BY x, x) */ if (targetIsInSortList(tle, InvalidOid, result)) @@ -1560,6 +1755,7 @@ List * transformSortClause(ParseState *pstate, List *orderlist, List **targetlist, + ParseExprKind exprKind, bool resolveUnknown, bool useSQL99) { @@ -1572,10 +1768,11 @@ transformSortClause(ParseState *pstate, TargetEntry *tle; if (useSQL99) - tle = findTargetlistEntrySQL99(pstate, sortby->node, targetlist); + tle = findTargetlistEntrySQL99(pstate, sortby->node, + targetlist, exprKind); else - tle = findTargetlistEntrySQL92(pstate, sortby->node, targetlist, - ORDER_CLAUSE); + tle = findTargetlistEntrySQL92(pstate, sortby->node, + targetlist, exprKind); sortlist = addTargetToSortList(pstate, tle, sortlist, *targetlist, sortby, @@ -1640,12 +1837,14 @@ transformWindowDefinitions(ParseState *pstate, orderClause = transformSortClause(pstate, windef->orderClause, targetlist, + EXPR_KIND_WINDOW_ORDER, true /* fix unknowns */ , true /* force SQL99 rules */ ); partitionClause = transformGroupClause(pstate, windef->partitionClause, targetlist, orderClause, + EXPR_KIND_WINDOW_PARTITION, true /* force SQL99 rules */ ); /* @@ -1658,11 +1857,16 @@ transformWindowDefinitions(ParseState *pstate, /* * Per spec, a windowdef that references a previous one copies the * previous partition clause (and mustn't specify its own). It can - * specify its own ordering clause. but only if the previous one had + * specify its own ordering clause, but only if the previous one had * none. It always specifies its own frame clause, and the previous - * one must not have a frame clause. (Yeah, it's bizarre that each of + * one must not have a frame clause. Yeah, it's bizarre that each of * these cases works differently, but SQL:2008 says so; see 7.11 - * <window clause> syntax rule 10 and general rule 1.) + * <window clause> syntax rule 10 and general rule 1. The frame + * clause rule is especially bizarre because it makes "OVER foo" + * different from "OVER (foo)", and requires the latter to throw an + * error if foo has a nondefault frame clause. Well, ours not to + * reason why, but we do go out of our way to throw a useful error + * message for such cases. */ if (refwc) { @@ -1701,11 +1905,27 @@ transformWindowDefinitions(ParseState *pstate, wc->copiedOrder = false; } if (refwc && refwc->frameOptions != FRAMEOPTION_DEFAULTS) + { + /* + * Use this message if this is a WINDOW clause, or if it's an OVER + * clause that includes ORDER BY or framing clauses. (We already + * rejected PARTITION BY above, so no need to check that.) + */ + if (windef->name || + orderClause || windef->frameOptions != FRAMEOPTION_DEFAULTS) + ereport(ERROR, + (errcode(ERRCODE_WINDOWING_ERROR), + errmsg("cannot copy window \"%s\" because it has a frame clause", + windef->refname), + parser_errposition(pstate, windef->location))); + /* Else this clause is just OVER (foo), so say this: */ ereport(ERROR, (errcode(ERRCODE_WINDOWING_ERROR), - errmsg("cannot override frame clause of window \"%s\"", - windef->refname), + errmsg("cannot copy window \"%s\" because it has a frame clause", + windef->refname), + errhint("Omit the parentheses in this OVER clause."), parser_errposition(pstate, windef->location))); + } wc->frameOptions = windef->frameOptions; /* Process frame offset expressions */ wc->startOffset = transformFrameOffset(pstate, wc->frameOptions, @@ -1747,7 +1967,7 @@ transformDistinctClause(ParseState *pstate, /* * The distinctClause should consist of all ORDER BY items followed by all - * other non-resjunk targetlist items. There must not be any resjunk + * other non-resjunk targetlist items. There must not be any resjunk * ORDER BY items --- that would imply that we are sorting by a value that * isn't necessarily unique within a DISTINCT group, so the results * wouldn't be well-defined. This construction ensures we follow the rule @@ -1792,6 +2012,20 @@ transformDistinctClause(ParseState *pstate, true); } + /* + * Complain if we found nothing to make DISTINCT. Returning an empty list + * would cause the parsed Query to look like it didn't have DISTINCT, with + * results that would probably surprise the user. Note: this case is + * presently impossible for aggregates because of grammar restrictions, + * but we check anyway. + */ + if (result == NIL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + is_agg ? + errmsg("an aggregate with DISTINCT must have at least one argument") : + errmsg("SELECT DISTINCT must have at least one column"))); + return result; } @@ -1833,7 +2067,7 @@ transformDistinctOnClause(ParseState *pstate, List *distinctlist, TargetEntry *tle; tle = findTargetlistEntrySQL92(pstate, dexpr, targetlist, - DISTINCT_ON_CLAUSE); + EXPR_KIND_DISTINCT_ON); sortgroupref = assignSortGroupRef(tle, *targetlist); sortgrouprefs = lappend_int(sortgrouprefs, sortgroupref); } @@ -1870,7 +2104,7 @@ transformDistinctOnClause(ParseState *pstate, List *distinctlist, /* * Now add any remaining DISTINCT ON items, using default sort/group - * semantics for their data types. (Note: this is pretty questionable; if + * semantics for their data types. (Note: this is pretty questionable; if * the ORDER BY list doesn't include all the DISTINCT ON items and more * besides, you certainly aren't using DISTINCT ON in the intended way, * and you probably aren't going to get consistent results. It might be @@ -1896,6 +2130,12 @@ transformDistinctOnClause(ParseState *pstate, List *distinctlist, true); } + /* + * An empty result list is impossible here because of grammar + * restrictions. + */ + Assert(result != NIL); + return result; } @@ -1939,7 +2179,7 @@ get_matching_location(int sortgroupref, List *sortgrouprefs, List *exprs) * * Returns the updated SortGroupClause list. */ -static List * +List * addTargetToSortList(ParseState *pstate, TargetEntry *tle, List *sortlist, List *targetlist, SortBy *sortby, bool resolveUnknown) @@ -2242,11 +2482,11 @@ transformFrameOffset(ParseState *pstate, int frameOptions, Node *clause) if (clause == NULL) return NULL; - /* Transform the raw expression tree */ - node = transformExpr(pstate, clause); - if (frameOptions & FRAMEOPTION_ROWS) { + /* Transform the raw expression tree */ + node = transformExpr(pstate, clause, EXPR_KIND_WINDOW_FRAME_ROWS); + /* * Like LIMIT clause, simply coerce to int8 */ @@ -2255,6 +2495,9 @@ transformFrameOffset(ParseState *pstate, int frameOptions, Node *clause) } else if (frameOptions & FRAMEOPTION_RANGE) { + /* Transform the raw expression tree */ + node = transformExpr(pstate, clause, EXPR_KIND_WINDOW_FRAME_RANGE); + /* * this needs a lot of thought to decide how to support in the context * of Postgres' extensible datatype framework @@ -2264,9 +2507,12 @@ transformFrameOffset(ParseState *pstate, int frameOptions, Node *clause) elog(ERROR, "window frame with value offset is not implemented"); } else + { Assert(false); + node = NULL; + } - /* Disallow variables and aggregates in frame offsets */ + /* Disallow variables in frame offsets */ checkExprIsVarFree(pstate, node, constructName); return node; diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 3cfa5bce55..96c70065a1 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -3,7 +3,7 @@ * parse_coerce.c * handle type coercions/conversions for parser * - * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -14,6 +14,7 @@ */ #include "postgres.h" +#include "access/htup_details.h" #include "catalog/pg_cast.h" #include "catalog/pg_class.h" #include "catalog/pg_inherits_fn.h" @@ -58,12 +59,12 @@ static bool typeIsOfTypedTable(Oid reltypeId, Oid reloftypeId); * Convert an expression to a target type and typmod. * * This is the general-purpose entry point for arbitrary type coercion - * operations. Direct use of the component operations can_coerce_type, + * operations. Direct use of the component operations can_coerce_type, * coerce_type, and coerce_type_typmod should be restricted to special * cases (eg, when the conversion is expected to succeed). * * Returns the possibly-transformed expression tree, or NULL if the type - * conversion is not possible. (We do this, rather than ereport'ing directly, + * conversion is not possible. (We do this, rather than ereport'ing directly, * so that callers can generate custom error messages indicating context.) * * pstate - parse state (can be NULL, see coerce_type) @@ -151,7 +152,7 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype, * already be properly coerced to the specified typmod. * * pstate is only used in the case that we are able to resolve the type of - * a previously UNKNOWN Param. It is okay to pass pstate = NULL if the + * a previously UNKNOWN Param. It is okay to pass pstate = NULL if the * caller does not want type information updated for Params. * * Note: this function must not modify the given expression tree, only add @@ -181,7 +182,7 @@ coerce_type(ParseState *pstate, Node *node, * * Note: by returning the unmodified node here, we are saying that * it's OK to treat an UNKNOWN constant as a valid input for a - * function accepting ANY, ANYELEMENT, or ANYNONARRAY. This should be + * function accepting ANY, ANYELEMENT, or ANYNONARRAY. This should be * all right, since an UNKNOWN value is still a perfectly valid Datum. * * NB: we do NOT want a RelabelType here: the exposed type of the @@ -256,7 +257,7 @@ coerce_type(ParseState *pstate, Node *node, /* * If the target type is a domain, we want to call its base type's - * input routine, not domain_in(). This is to avoid premature failure + * input routine, not domain_in(). This is to avoid premature failure * when the domain applies a typmod: existing input routines follow * implicit-coercion semantics for length checks, which is not always * what we want here. The needed check will be applied properly @@ -269,7 +270,7 @@ coerce_type(ParseState *pstate, Node *node, * For most types we pass typmod -1 to the input routine, because * existing input routines follow implicit-coercion semantics for * length checks, which is not always what we want here. Any length - * constraint will be applied later by our caller. An exception + * constraint will be applied later by our caller. An exception * however is the INTERVAL type, for which we *must* pass the typmod * or it won't be able to obey the bizarre SQL-spec input rules. (Ugly * as sin, but so is this part of the spec...) @@ -349,7 +350,7 @@ coerce_type(ParseState *pstate, Node *node, { /* * If we have a COLLATE clause, we have to push the coercion - * underneath the COLLATE. This is really ugly, but there is little + * underneath the COLLATE. This is really ugly, but there is little * choice because the above hacks on Consts and Params wouldn't happen * otherwise. This kluge has consequences in coerce_to_target_type. */ @@ -372,7 +373,7 @@ coerce_type(ParseState *pstate, Node *node, { /* * Generate an expression tree representing run-time application - * of the conversion function. If we are dealing with a domain + * of the conversion function. If we are dealing with a domain * target type, the conversion function will yield the base type, * and we need to extract the correct typmod to use from the * domain's typtypmod. @@ -408,7 +409,7 @@ coerce_type(ParseState *pstate, Node *node, * to have the intended type when inspected by higher-level code. * * Also, domains may have value restrictions beyond the base type - * that must be accounted for. If the destination is a domain + * that must be accounted for. If the destination is a domain * then we won't need a RelabelType node. */ result = coerce_to_domain(node, InvalidOid, -1, targetTypeId, @@ -655,7 +656,7 @@ coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod, Oid typeId, } /* - * Now build the domain coercion node. This represents run-time checking + * Now build the domain coercion node. This represents run-time checking * of any constraints currently attached to the domain. This also ensures * that the expression is properly labeled as to result type. */ @@ -728,7 +729,7 @@ coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod, * Mark a coercion node as IMPLICIT so it will never be displayed by * ruleutils.c. We use this when we generate a nest of coercion nodes * to implement what is logically one conversion; the inner nodes are - * forced to IMPLICIT_CAST format. This does not change their semantics, + * forced to IMPLICIT_CAST format. This does not change their semantics, * only display behavior. * * It is caller error to call this on something that doesn't have a @@ -1187,7 +1188,7 @@ select_common_type(ParseState *pstate, List *exprs, const char *context, } /* - * Nope, so set up for the full algorithm. Note that at this point, lc + * Nope, so set up for the full algorithm. Note that at this point, lc * points to the first list item with type different from pexpr's; we need * not re-examine any items the previous loop advanced over. */ @@ -1482,7 +1483,7 @@ check_generic_type_consistency(Oid *actual_arg_types, * * If any polymorphic pseudotype is used in a function's arguments or * return type, we make sure the actual data types are consistent with - * each other. The argument consistency rules are shown above for + * each other. The argument consistency rules are shown above for * check_generic_type_consistency(). * * If we have UNKNOWN input (ie, an untyped literal) for any polymorphic @@ -1504,7 +1505,7 @@ check_generic_type_consistency(Oid *actual_arg_types, * impossible to determine the range type from the subtype alone.) * 4) If return type is ANYARRAY, but no argument is ANYARRAY or ANYELEMENT, * generate an error. Similarly, if return type is ANYRANGE, but no - * argument is ANYRANGE, generate an error. (These conditions are + * argument is ANYRANGE, generate an error. (These conditions are * prevented by CREATE FUNCTION and therefore are not expected here.) * 5) If return type is ANYELEMENT, and any argument is ANYELEMENT, use the * argument's actual type as the function's return type. @@ -1514,7 +1515,7 @@ check_generic_type_consistency(Oid *actual_arg_types, * type or the range type's corresponding subtype (or both, in which case * they must match). * 7) If return type is ANYELEMENT, no argument is ANYELEMENT, ANYARRAY, or - * ANYRANGE, generate an error. (This condition is prevented by CREATE + * ANYRANGE, generate an error. (This condition is prevented by CREATE * FUNCTION and therefore is not expected here.) * 8) ANYENUM is treated the same as ANYELEMENT except that if it is used * (alone or in combination with plain ANYELEMENT), we add the extra @@ -1531,14 +1532,14 @@ check_generic_type_consistency(Oid *actual_arg_types, * * When allow_poly is false, we are not expecting any of the actual_arg_types * to be polymorphic, and we should not return a polymorphic result type - * either. When allow_poly is true, it is okay to have polymorphic "actual" + * either. When allow_poly is true, it is okay to have polymorphic "actual" * arg types, and we can return ANYARRAY, ANYRANGE, or ANYELEMENT as the - * result. (This case is currently used only to check compatibility of an + * result. (This case is currently used only to check compatibility of an * aggregate's declaration with the underlying transfn.) * * A special case is that we could see ANYARRAY as an actual_arg_type even * when allow_poly is false (this is possible only because pg_statistic has - * columns shown as anyarray in the catalogs). We allow this to match a + * columns shown as anyarray in the catalogs). We allow this to match a * declared ANYARRAY argument, but only if there is no ANYELEMENT argument * or result (since we can't determine a specific element type to match to * ANYELEMENT). Note this means that functions taking ANYARRAY had better @@ -1644,7 +1645,7 @@ enforce_generic_type_consistency(Oid *actual_arg_types, /* * Fast Track: if none of the arguments are polymorphic, return the - * unmodified rettype. We assume it can't be polymorphic either. + * unmodified rettype. We assume it can't be polymorphic either. */ if (!have_generics) return rettype; @@ -1700,7 +1701,7 @@ enforce_generic_type_consistency(Oid *actual_arg_types, if (!OidIsValid(range_typelem)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("argument declared \"anyrange\" is not a range but type %s", + errmsg("argument declared \"anyrange\" is not a range type but type %s", format_type_be(range_typeid)))); } @@ -1921,7 +1922,7 @@ resolve_generic_type(Oid declared_type, if (!OidIsValid(range_typelem)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("argument declared \"anyrange\" is not a range but type %s", + errmsg("argument declared \"anyrange\" is not a range type but type %s", format_type_be(context_base_type)))); return range_typelem; } @@ -1987,8 +1988,8 @@ IsPreferredType(TYPCATEGORY category, Oid type) * Check if srctype is binary-coercible to targettype. * * This notion allows us to cheat and directly exchange values without - * going through the trouble of calling a conversion function. Note that - * in general, this should only be an implementation shortcut. Before 7.4, + * going through the trouble of calling a conversion function. Note that + * in general, this should only be an implementation shortcut. Before 7.4, * this was also used as a heuristic for resolving overloaded functions and * operators, but that's basically a bad idea. * @@ -2001,7 +2002,7 @@ IsPreferredType(TYPCATEGORY category, Oid type) * types. * * This function replaces IsBinaryCompatible(), which was an inherently - * symmetric test. Since the pg_cast entries aren't necessarily symmetric, + * symmetric test. Since the pg_cast entries aren't necessarily symmetric, * the order of the operands is now significant. */ bool @@ -2015,6 +2016,10 @@ IsBinaryCoercible(Oid srctype, Oid targettype) if (srctype == targettype) return true; + /* Anything is coercible to ANY or ANYELEMENT */ + if (targettype == ANYOID || targettype == ANYELEMENTOID) + return true; + /* If srctype is a domain, reduce to its base type */ if (OidIsValid(srctype)) srctype = getBaseType(srctype); @@ -2183,7 +2188,7 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, * Hack: disallow coercions to oidvector and int2vector, which * otherwise tend to capture coercions that should go to "real" array * types. We want those types to be considered "real" arrays for many - * purposes, but not this one. (Also, ArrayCoerceExpr isn't + * purposes, but not this one. (Also, ArrayCoerceExpr isn't * guaranteed to produce an output that meets the restrictions of * these datatypes, such as being 1-dimensional.) */ diff --git a/src/backend/parser/parse_collate.c b/src/backend/parser/parse_collate.c index ba49f9fd59..bbd10304cc 100644 --- a/src/backend/parser/parse_collate.c +++ b/src/backend/parser/parse_collate.c @@ -14,22 +14,22 @@ * 1. The output collation of each expression node, or InvalidOid if it * returns a noncollatable data type. This can also be InvalidOid if the * result type is collatable but the collation is indeterminate. - * 2. The collation to be used in executing each function. InvalidOid means + * 2. The collation to be used in executing each function. InvalidOid means * that there are no collatable inputs or their collation is indeterminate. * This value is only stored in node types that might call collation-using * functions. * * You might think we could get away with storing only one collation per - * node, but the two concepts really need to be kept distinct. Otherwise + * node, but the two concepts really need to be kept distinct. Otherwise * it's too confusing when a function produces a collatable output type but * has no collatable inputs or produces noncollatable output from collatable * inputs. * * Cases with indeterminate collation might result in an error being thrown - * at runtime. If we knew exactly which functions require collation + * at runtime. If we knew exactly which functions require collation * information, we could throw those errors at parse time instead. * - * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -40,7 +40,9 @@ */ #include "postgres.h" +#include "catalog/pg_aggregate.h" #include "catalog/pg_collation.h" +#include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "parser/parse_collate.h" #include "utils/lsyscache.h" @@ -73,6 +75,18 @@ typedef struct static bool assign_query_collations_walker(Node *node, ParseState *pstate); static bool assign_collations_walker(Node *node, assign_collations_context *context); +static void merge_collation_state(Oid collation, + CollateStrength strength, + int location, + Oid collation2, + int location2, + assign_collations_context *context); +static void assign_aggregate_collations(Aggref *aggref, + assign_collations_context *loccontext); +static void assign_ordered_set_collations(Aggref *aggref, + assign_collations_context *loccontext); +static void assign_hypothetical_collations(Aggref *aggref, + assign_collations_context *loccontext); /* @@ -231,7 +245,7 @@ select_common_collation(ParseState *pstate, List *exprs, bool none_ok) * Recursive guts of collation processing. * * Nodes with no children (eg, Vars, Consts, Params) must have been marked - * when built. All upper-level nodes are marked here. + * when built. All upper-level nodes are marked here. * * Note: if this is invoked directly on a List, it will attempt to infer a * common collation for all the list members. In particular, it will throw @@ -258,6 +272,9 @@ assign_collations_walker(Node *node, assign_collations_context *context) loccontext.collation = InvalidOid; loccontext.strength = COLLATE_NONE; loccontext.location = -1; + /* Set these fields just to suppress uninitialized-value warnings: */ + loccontext.collation2 = InvalidOid; + loccontext.location2 = -1; /* * Recurse if appropriate, then determine the collation for this node. @@ -319,86 +336,6 @@ assign_collations_walker(Node *node, assign_collations_context *context) } } break; - case T_CaseExpr: - { - /* - * CaseExpr is a special case because we do not want to - * recurse into the test expression (if any). It was already - * marked with collations during transformCaseExpr, and - * furthermore its collation is not relevant to the result of - * the CASE --- only the output expressions are. So we can't - * use expression_tree_walker here. - */ - CaseExpr *expr = (CaseExpr *) node; - Oid typcollation; - ListCell *lc; - - foreach(lc, expr->args) - { - CaseWhen *when = (CaseWhen *) lfirst(lc); - - Assert(IsA(when, CaseWhen)); - - /* - * The condition expressions mustn't affect the CASE's - * result collation either; but since they are known to - * yield boolean, it's safe to recurse directly on them - * --- they won't change loccontext. - */ - (void) assign_collations_walker((Node *) when->expr, - &loccontext); - (void) assign_collations_walker((Node *) when->result, - &loccontext); - } - (void) assign_collations_walker((Node *) expr->defresult, - &loccontext); - - /* - * Now determine the CASE's output collation. This is the - * same as the general case below. - */ - typcollation = get_typcollation(exprType(node)); - if (OidIsValid(typcollation)) - { - /* Node's result is collatable; what about its input? */ - if (loccontext.strength > COLLATE_NONE) - { - /* Collation state bubbles up from children. */ - collation = loccontext.collation; - strength = loccontext.strength; - location = loccontext.location; - } - else - { - /* - * Collatable output produced without any collatable - * input. Use the type's collation (which is usually - * DEFAULT_COLLATION_OID, but might be different for a - * domain). - */ - collation = typcollation; - strength = COLLATE_IMPLICIT; - location = exprLocation(node); - } - } - else - { - /* Node's result type isn't collatable. */ - collation = InvalidOid; - strength = COLLATE_NONE; - location = -1; /* won't be used */ - } - - /* - * Save the state into the expression node. We know it - * doesn't care about input collation. - */ - if (strength == COLLATE_CONFLICT) - exprSetCollation(node, InvalidOid); - else - exprSetCollation(node, collation); - } - break; case T_RowExpr: { /* @@ -511,7 +448,7 @@ assign_collations_walker(Node *node, assign_collations_context *context) /* * TargetEntry can have only one child, and should bubble that - * state up to its parent. We can't use the general-case code + * state up to its parent. We can't use the general-case code * below because exprType and friends don't work on TargetEntry. */ collation = loccontext.collation; @@ -526,7 +463,7 @@ assign_collations_walker(Node *node, assign_collations_context *context) * There are some cases where there might not be a failure, for * example if the planner chooses to use hash aggregation instead * of sorting for grouping; but it seems better to predictably - * throw an error. (Compare transformSetOperationTree, which will + * throw an error. (Compare transformSetOperationTree, which will * throw error for indeterminate collation of set-op columns, even * though the planner might be able to implement the set-op * without sorting.) @@ -564,16 +501,22 @@ assign_collations_walker(Node *node, assign_collations_context *context) * SubLink. Act as though the Query returns its first output * column, which indeed is what it does for EXPR_SUBLINK and * ARRAY_SUBLINK cases. In the cases where the SubLink - * returns boolean, this info will be ignored. + * returns boolean, this info will be ignored. Special case: + * in EXISTS, the Query might return no columns, in which case + * we need do nothing. * * We needn't recurse, since the Query is already processed. */ Query *qtree = (Query *) node; TargetEntry *tent; + if (qtree->targetList == NIL) + return false; tent = (TargetEntry *) linitial(qtree->targetList); Assert(IsA(tent, TargetEntry)); - Assert(!tent->resjunk); + if (tent->resjunk) + return false; + collation = exprCollation((Node *) tent->expr); /* collation doesn't change if it's converted to array */ strength = COLLATE_IMPLICIT; @@ -630,14 +573,116 @@ assign_collations_walker(Node *node, assign_collations_context *context) { /* * General case for most expression nodes with children. First - * recurse, then figure out what to assign here. + * recurse, then figure out what to assign to this node. */ Oid typcollation; - (void) expression_tree_walker(node, - assign_collations_walker, - (void *) &loccontext); + /* + * For most node types, we want to treat all the child + * expressions alike; but there are a few exceptions, hence + * this inner switch. + */ + switch (nodeTag(node)) + { + case T_Aggref: + { + /* + * Aggref is messy enough that we give it its own + * function, in fact three of them. The FILTER + * clause is independent of the rest of the + * aggregate, however, so it can be processed + * separately. + */ + Aggref *aggref = (Aggref *) node; + + switch (aggref->aggkind) + { + case AGGKIND_NORMAL: + assign_aggregate_collations(aggref, + &loccontext); + break; + case AGGKIND_ORDERED_SET: + assign_ordered_set_collations(aggref, + &loccontext); + break; + case AGGKIND_HYPOTHETICAL: + assign_hypothetical_collations(aggref, + &loccontext); + break; + default: + elog(ERROR, "unrecognized aggkind: %d", + (int) aggref->aggkind); + } + + assign_expr_collations(context->pstate, + (Node *) aggref->aggfilter); + } + break; + case T_WindowFunc: + { + /* + * WindowFunc requires special processing only for + * its aggfilter clause, as for aggregates. + */ + WindowFunc *wfunc = (WindowFunc *) node; + + (void) assign_collations_walker((Node *) wfunc->args, + &loccontext); + assign_expr_collations(context->pstate, + (Node *) wfunc->aggfilter); + } + break; + case T_CaseExpr: + { + /* + * CaseExpr is a special case because we do not + * want to recurse into the test expression (if + * any). It was already marked with collations + * during transformCaseExpr, and furthermore its + * collation is not relevant to the result of the + * CASE --- only the output expressions are. + */ + CaseExpr *expr = (CaseExpr *) node; + ListCell *lc; + + foreach(lc, expr->args) + { + CaseWhen *when = (CaseWhen *) lfirst(lc); + + Assert(IsA(when, CaseWhen)); + + /* + * The condition expressions mustn't affect + * the CASE's result collation either; but + * since they are known to yield boolean, it's + * safe to recurse directly on them --- they + * won't change loccontext. + */ + (void) assign_collations_walker((Node *) when->expr, + &loccontext); + (void) assign_collations_walker((Node *) when->result, + &loccontext); + } + (void) assign_collations_walker((Node *) expr->defresult, + &loccontext); + } + break; + default: + + /* + * Normal case: all child expressions contribute + * equally to loccontext. + */ + (void) expression_tree_walker(node, + assign_collations_walker, + (void *) &loccontext); + break; + } + + /* + * Now figure out what collation to assign to this node. + */ typcollation = get_typcollation(exprType(node)); if (OidIsValid(typcollation)) { @@ -693,9 +738,33 @@ assign_collations_walker(Node *node, assign_collations_context *context) } /* - * Now, merge my information into my parent's state. If the collation - * strength for this node is different from what's already in *context, - * then this node either dominates or is dominated by earlier siblings. + * Now, merge my information into my parent's state. + */ + merge_collation_state(collation, + strength, + location, + loccontext.collation2, + loccontext.location2, + context); + + return false; +} + +/* + * Merge collation state of a subexpression into the context for its parent. + */ +static void +merge_collation_state(Oid collation, + CollateStrength strength, + int location, + Oid collation2, + int location2, + assign_collations_context *context) +{ + /* + * If the collation strength for this node is different from what's + * already in *context, then this node either dominates or is dominated by + * earlier siblings. */ if (strength > context->strength) { @@ -706,8 +775,8 @@ assign_collations_walker(Node *node, assign_collations_context *context) /* Bubble up error info if applicable */ if (strength == COLLATE_CONFLICT) { - context->collation2 = loccontext.collation2; - context->location2 = loccontext.location2; + context->collation2 = collation2; + context->location2 = location2; } } else if (strength == context->strength) @@ -768,6 +837,201 @@ assign_collations_walker(Node *node, assign_collations_context *context) break; } } +} - return false; +/* + * Aggref is a special case because expressions used only for ordering + * shouldn't be taken to conflict with each other or with regular args, + * indeed shouldn't affect the aggregate's result collation at all. + * We handle this by applying assign_expr_collations() to them rather than + * passing down our loccontext. + * + * Note that we recurse to each TargetEntry, not directly to its contained + * expression, so that the case above for T_TargetEntry will complain if we + * can't resolve a collation for an ORDER BY item (whether or not it is also + * a normal aggregate arg). + * + * We need not recurse into the aggorder or aggdistinct lists, because those + * contain only SortGroupClause nodes which we need not process. + */ +static void +assign_aggregate_collations(Aggref *aggref, + assign_collations_context *loccontext) +{ + ListCell *lc; + + /* Plain aggregates have no direct args */ + Assert(aggref->aggdirectargs == NIL); + + /* Process aggregated args, holding resjunk ones at arm's length */ + foreach(lc, aggref->args) + { + TargetEntry *tle = (TargetEntry *) lfirst(lc); + + Assert(IsA(tle, TargetEntry)); + if (tle->resjunk) + assign_expr_collations(loccontext->pstate, (Node *) tle); + else + (void) assign_collations_walker((Node *) tle, loccontext); + } +} + +/* + * For ordered-set aggregates, it's somewhat unclear how best to proceed. + * The spec-defined inverse distribution functions have only one sort column + * and don't return collatable types, but this is clearly too restrictive in + * the general case. Our solution is to consider that the aggregate's direct + * arguments contribute normally to determination of the aggregate's own + * collation, while aggregated arguments contribute only when the aggregate + * is designed to have exactly one aggregated argument (i.e., it has a single + * aggregated argument and is non-variadic). If it can have more than one + * aggregated argument, we process the aggregated arguments as independent + * sort columns. This avoids throwing error for something like + * agg(...) within group (order by x collate "foo", y collate "bar") + * while also guaranteeing that variadic aggregates don't change in behavior + * depending on how many sort columns a particular call happens to have. + * + * Otherwise this is much like the plain-aggregate case. + */ +static void +assign_ordered_set_collations(Aggref *aggref, + assign_collations_context *loccontext) +{ + bool merge_sort_collations; + ListCell *lc; + + /* Merge sort collations to parent only if there can be only one */ + merge_sort_collations = (list_length(aggref->args) == 1 && + get_func_variadictype(aggref->aggfnoid) == InvalidOid); + + /* Direct args, if any, are normal children of the Aggref node */ + (void) assign_collations_walker((Node *) aggref->aggdirectargs, + loccontext); + + /* Process aggregated args appropriately */ + foreach(lc, aggref->args) + { + TargetEntry *tle = (TargetEntry *) lfirst(lc); + + Assert(IsA(tle, TargetEntry)); + if (merge_sort_collations) + (void) assign_collations_walker((Node *) tle, loccontext); + else + assign_expr_collations(loccontext->pstate, (Node *) tle); + } +} + +/* + * Hypothetical-set aggregates are even more special: per spec, we need to + * unify the collations of each pair of hypothetical and aggregated args. + * And we need to force the choice of collation down into the sort column + * to ensure that the sort happens with the chosen collation. Other than + * that, the behavior is like regular ordered-set aggregates. Note that + * hypothetical direct arguments contribute to the aggregate collation + * only when their partner aggregated arguments do. + */ +static void +assign_hypothetical_collations(Aggref *aggref, + assign_collations_context *loccontext) +{ + ListCell *h_cell = list_head(aggref->aggdirectargs); + ListCell *s_cell = list_head(aggref->args); + bool merge_sort_collations; + int extra_args; + + /* Merge sort collations to parent only if there can be only one */ + merge_sort_collations = (list_length(aggref->args) == 1 && + get_func_variadictype(aggref->aggfnoid) == InvalidOid); + + /* Process any non-hypothetical direct args */ + extra_args = list_length(aggref->aggdirectargs) - list_length(aggref->args); + Assert(extra_args >= 0); + while (extra_args-- > 0) + { + (void) assign_collations_walker((Node *) lfirst(h_cell), loccontext); + h_cell = lnext(h_cell); + } + + /* Scan hypothetical args and aggregated args in parallel */ + while (h_cell && s_cell) + { + Node *h_arg = (Node *) lfirst(h_cell); + TargetEntry *s_tle = (TargetEntry *) lfirst(s_cell); + assign_collations_context paircontext; + + /* + * Assign collations internally in this pair of expressions, then + * choose a common collation for them. This should match + * select_common_collation(), but we can't use that function as-is + * because we need access to the whole collation state so we can + * bubble it up to the aggregate function's level. + */ + paircontext.pstate = loccontext->pstate; + paircontext.collation = InvalidOid; + paircontext.strength = COLLATE_NONE; + paircontext.location = -1; + /* Set these fields just to suppress uninitialized-value warnings: */ + paircontext.collation2 = InvalidOid; + paircontext.location2 = -1; + + (void) assign_collations_walker(h_arg, &paircontext); + (void) assign_collations_walker((Node *) s_tle->expr, &paircontext); + + /* deal with collation conflict */ + if (paircontext.strength == COLLATE_CONFLICT) + ereport(ERROR, + (errcode(ERRCODE_COLLATION_MISMATCH), + errmsg("collation mismatch between implicit collations \"%s\" and \"%s\"", + get_collation_name(paircontext.collation), + get_collation_name(paircontext.collation2)), + errhint("You can choose the collation by applying the COLLATE clause to one or both expressions."), + parser_errposition(paircontext.pstate, + paircontext.location2))); + + /* + * At this point paircontext.collation can be InvalidOid only if the + * type is not collatable; no need to do anything in that case. If we + * do have to change the sort column's collation, do it by inserting a + * RelabelType node into the sort column TLE. + * + * XXX This is pretty grotty for a couple of reasons: + * assign_collations_walker isn't supposed to be changing the + * expression structure like this, and a parse-time change of + * collation ought to be signaled by a CollateExpr not a RelabelType + * (the use of RelabelType for collation marking is supposed to be a + * planner/executor thing only). But we have no better alternative. + * In particular, injecting a CollateExpr could result in the + * expression being interpreted differently after dump/reload, since + * we might be effectively promoting an implicit collation to + * explicit. This kluge is relying on ruleutils.c not printing a + * COLLATE clause for a RelabelType, and probably on some other + * fragile behaviors. + */ + if (OidIsValid(paircontext.collation) && + paircontext.collation != exprCollation((Node *) s_tle->expr)) + { + s_tle->expr = (Expr *) + makeRelabelType(s_tle->expr, + exprType((Node *) s_tle->expr), + exprTypmod((Node *) s_tle->expr), + paircontext.collation, + COERCE_IMPLICIT_CAST); + } + + /* + * If appropriate, merge this column's collation state up to the + * aggregate function. + */ + if (merge_sort_collations) + merge_collation_state(paircontext.collation, + paircontext.strength, + paircontext.location, + paircontext.collation2, + paircontext.location2, + loccontext); + + h_cell = lnext(h_cell); + s_cell = lnext(s_cell); + } + Assert(h_cell == NULL && s_cell == NULL); } diff --git a/src/backend/parser/parse_cte.c b/src/backend/parser/parse_cte.c index 2a7d4cd072..04b585d1e2 100644 --- a/src/backend/parser/parse_cte.c +++ b/src/backend/parser/parse_cte.c @@ -3,7 +3,7 @@ * parse_cte.c * handle CTEs (common table expressions) in parser * - * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -181,7 +181,7 @@ transformWithClause(ParseState *pstate, WithClause *withClause) checkWellFormedRecursion(&cstate); /* - * Set up the ctenamespace for parse analysis. Per spec, all the WITH + * Set up the ctenamespace for parse analysis. Per spec, all the WITH * items are visible to all others, so stuff them all in before parse * analysis. We build the list in safe processing order so that the * planner can process the queries in sequence. @@ -207,7 +207,7 @@ transformWithClause(ParseState *pstate, WithClause *withClause) { /* * For non-recursive WITH, just analyze each CTE in sequence and then - * add it to the ctenamespace. This corresponds to the spec's + * add it to the ctenamespace. This corresponds to the spec's * definition of the scope of each WITH name. However, to allow error * reports to be aware of the possibility of an erroneous reference, * we maintain a list in p_future_ctes of the not-yet-visible CTEs. @@ -245,7 +245,7 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte) cte->ctequery = (Node *) query; /* - * Check that we got something reasonable. These first two cases should + * Check that we got something reasonable. These first two cases should * be prevented by the grammar. */ if (!IsA(query, Query)) @@ -393,7 +393,7 @@ analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist) /* * If the CTE is recursive, force the exposed column type of any - * "unknown" column to "text". This corresponds to the fact that + * "unknown" column to "text". This corresponds to the fact that * SELECT 'foo' UNION SELECT 'bar' will ultimately produce text. We * might see "unknown" as a result of an untyped literal in the * non-recursive term's select list, and if we don't convert to text @@ -678,6 +678,18 @@ checkWellFormedRecursion(CteState *cstate) if (cstate->selfrefcount != 1) /* shouldn't happen */ elog(ERROR, "missing recursive reference"); + /* WITH mustn't contain self-reference, either */ + if (stmt->withClause) + { + cstate->curitem = i; + cstate->innerwiths = NIL; + cstate->selfrefcount = 0; + cstate->context = RECURSION_SUBLINK; + checkWellFormedRecursionWalker((Node *) stmt->withClause->ctes, + cstate); + Assert(cstate->innerwiths == NIL); + } + /* * Disallow ORDER BY and similar decoration atop the UNION. These * don't make sense because it's impossible to figure out what they @@ -933,7 +945,7 @@ checkWellFormedSelectStmt(SelectStmt *stmt, CteState *cstate) cstate); checkWellFormedRecursionWalker((Node *) stmt->lockingClause, cstate); - break; + /* stmt->withClause is intentionally ignored here */ break; case SETOP_EXCEPT: if (stmt->all) @@ -952,6 +964,7 @@ checkWellFormedSelectStmt(SelectStmt *stmt, CteState *cstate) cstate); checkWellFormedRecursionWalker((Node *) stmt->lockingClause, cstate); + /* stmt->withClause is intentionally ignored here */ break; default: elog(ERROR, "unrecognized set op: %d", diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 53de843cbe..f055042d5e 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -3,7 +3,7 @@ * parse_expr.c * handle expressions in parser * - * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -22,6 +22,7 @@ #include "nodes/nodeFuncs.h" #include "optimizer/var.h" #include "parser/analyze.h" +#include "parser/parse_clause.h" #include "parser/parse_coerce.h" #include "parser/parse_collate.h" #include "parser/parse_expr.h" @@ -37,6 +38,7 @@ bool Transform_null_equals = false; +static Node *transformExprRecurse(ParseState *pstate, Node *expr); static Node *transformParamRef(ParseState *pstate, ParamRef *pref); static Node *transformAExprOp(ParseState *pstate, A_Expr *a); static Node *transformAExprAnd(ParseState *pstate, A_Expr *a); @@ -91,6 +93,8 @@ static Expr *make_distinct_op(ParseState *pstate, List *opname, * function argument to the required type (via coerce_type()) * can apply transformExpr to an already-transformed subexpression. * An example here is "SELECT count(*) + 1.0 FROM table". + * 3. CREATE TABLE t1 (LIKE t2 INCLUDING INDEXES) can pass in + * already-transformed index expressions. * While it might be possible to eliminate these cases, the path of * least resistance so far has been to ensure that transformExpr() does * no damage if applied to an already-transformed tree. This is pretty @@ -100,9 +104,27 @@ static Expr *make_distinct_op(ParseState *pstate, List *opname, * input and output of transformExpr; see SubLink for example. */ Node * -transformExpr(ParseState *pstate, Node *expr) +transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind) { - Node *result = NULL; + Node *result; + ParseExprKind sv_expr_kind; + + /* Save and restore identity of expression type we're parsing */ + Assert(exprKind != EXPR_KIND_NONE); + sv_expr_kind = pstate->p_expr_kind; + pstate->p_expr_kind = exprKind; + + result = transformExprRecurse(pstate, expr); + + pstate->p_expr_kind = sv_expr_kind; + + return result; +} + +static Node * +transformExprRecurse(ParseState *pstate, Node *expr) +{ + Node *result; if (expr == NULL) return NULL; @@ -133,7 +155,7 @@ transformExpr(ParseState *pstate, Node *expr) { A_Indirection *ind = (A_Indirection *) expr; - result = transformExpr(pstate, ind->arg); + result = transformExprRecurse(pstate, ind->arg); result = transformIndirection(pstate, result, ind->indirection); break; @@ -230,6 +252,8 @@ transformExpr(ParseState *pstate, Node *expr) break; default: elog(ERROR, "unrecognized A_Expr kind: %d", a->kind); + result = NULL; /* keep compiler quiet */ + break; } break; } @@ -242,7 +266,7 @@ transformExpr(ParseState *pstate, Node *expr) { NamedArgExpr *na = (NamedArgExpr *) expr; - na->arg = (Expr *) transformExpr(pstate, (Node *) na->arg); + na->arg = (Expr *) transformExprRecurse(pstate, (Node *) na->arg); result = expr; break; } @@ -279,7 +303,7 @@ transformExpr(ParseState *pstate, Node *expr) { NullTest *n = (NullTest *) expr; - n->arg = (Expr *) transformExpr(pstate, (Node *) n->arg); + n->arg = (Expr *) transformExprRecurse(pstate, (Node *) n->arg); /* the argument can be any type, so don't coerce it */ n->argisrow = type_is_rowtype(exprType((Node *) n->arg)); result = expr; @@ -334,6 +358,7 @@ transformExpr(ParseState *pstate, Node *expr) default: /* should not reach here */ elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr)); + result = NULL; /* keep compiler quiet */ break; } @@ -438,8 +463,8 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection) newresult = ParseFuncOrColumn(pstate, list_make1(n), list_make1(result), - NIL, false, false, false, - NULL, true, location); + NULL, + location); if (newresult == NULL) unknown_attribute(pstate, result, strVal(n), location); result = newresult; @@ -481,7 +506,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) } crerr = CRERR_NO_COLUMN; /* - * Give the PreParseColumnRefHook, if any, first shot. If it returns + * Give the PreParseColumnRefHook, if any, first shot. If it returns * non-null then that's all, folks. */ if (pstate->p_pre_columnref_hook != NULL) @@ -552,7 +577,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) } /* - * Try to find the name as a relation. Note that only + * Try to find the name as a relation. Note that only * relations already entered into the rangetable will be * recognized. * @@ -606,8 +631,8 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) node = ParseFuncOrColumn(pstate, list_make1(makeString(colname)), list_make1(node), - NIL, false, false, false, - NULL, true, cref->location); + NULL, + cref->location); } break; } @@ -651,8 +676,8 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) node = ParseFuncOrColumn(pstate, list_make1(makeString(colname)), list_make1(node), - NIL, false, false, false, - NULL, true, cref->location); + NULL, + cref->location); } break; } @@ -709,8 +734,8 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) node = ParseFuncOrColumn(pstate, list_make1(makeString(colname)), list_make1(node), - NIL, false, false, false, - NULL, true, cref->location); + NULL, + cref->location); } break; } @@ -751,19 +776,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) switch (crerr) { case CRERR_NO_COLUMN: - if (relname) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), - errmsg("column %s.%s does not exist", - relname, colname), - parser_errposition(pstate, cref->location))); - - else - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), - errmsg("column \"%s\" does not exist", - colname), - parser_errposition(pstate, cref->location))); + errorMissingColumn(pstate, relname, colname, cref->location); break; case CRERR_NO_RTE: errorMissingRTE(pstate, makeRangeVar(nspname, relname, @@ -795,7 +808,7 @@ transformParamRef(ParseState *pstate, ParamRef *pref) Node *result; /* - * The core parser knows nothing about Params. If a hook is supplied, + * The core parser knows nothing about Params. If a hook is supplied, * call it. If not, or if the hook returns NULL, throw a generic error. */ if (pstate->p_paramref_hook != NULL) @@ -855,7 +868,7 @@ transformAExprOp(ParseState *pstate, A_Expr *a) else n->arg = (Expr *) lexpr; - result = transformExpr(pstate, (Node *) n); + result = transformExprRecurse(pstate, (Node *) n); } else if (lexpr && IsA(lexpr, RowExpr) && rexpr && IsA(rexpr, SubLink) && @@ -872,14 +885,14 @@ transformAExprOp(ParseState *pstate, A_Expr *a) s->testexpr = lexpr; s->operName = a->name; s->location = a->location; - result = transformExpr(pstate, (Node *) s); + result = transformExprRecurse(pstate, (Node *) s); } else if (lexpr && IsA(lexpr, RowExpr) && rexpr && IsA(rexpr, RowExpr)) { - /* "row op row" */ - lexpr = transformExpr(pstate, lexpr); - rexpr = transformExpr(pstate, rexpr); + /* ROW() op ROW() is handled specially */ + lexpr = transformExprRecurse(pstate, lexpr); + rexpr = transformExprRecurse(pstate, rexpr); Assert(IsA(lexpr, RowExpr)); Assert(IsA(rexpr, RowExpr)); @@ -892,8 +905,8 @@ transformAExprOp(ParseState *pstate, A_Expr *a) else { /* Ordinary scalar operator */ - lexpr = transformExpr(pstate, lexpr); - rexpr = transformExpr(pstate, rexpr); + lexpr = transformExprRecurse(pstate, lexpr); + rexpr = transformExprRecurse(pstate, rexpr); result = (Node *) make_op(pstate, a->name, @@ -908,8 +921,8 @@ transformAExprOp(ParseState *pstate, A_Expr *a) static Node * transformAExprAnd(ParseState *pstate, A_Expr *a) { - Node *lexpr = transformExpr(pstate, a->lexpr); - Node *rexpr = transformExpr(pstate, a->rexpr); + Node *lexpr = transformExprRecurse(pstate, a->lexpr); + Node *rexpr = transformExprRecurse(pstate, a->rexpr); lexpr = coerce_to_boolean(pstate, lexpr, "AND"); rexpr = coerce_to_boolean(pstate, rexpr, "AND"); @@ -922,8 +935,8 @@ transformAExprAnd(ParseState *pstate, A_Expr *a) static Node * transformAExprOr(ParseState *pstate, A_Expr *a) { - Node *lexpr = transformExpr(pstate, a->lexpr); - Node *rexpr = transformExpr(pstate, a->rexpr); + Node *lexpr = transformExprRecurse(pstate, a->lexpr); + Node *rexpr = transformExprRecurse(pstate, a->rexpr); lexpr = coerce_to_boolean(pstate, lexpr, "OR"); rexpr = coerce_to_boolean(pstate, rexpr, "OR"); @@ -936,7 +949,7 @@ transformAExprOr(ParseState *pstate, A_Expr *a) static Node * transformAExprNot(ParseState *pstate, A_Expr *a) { - Node *rexpr = transformExpr(pstate, a->rexpr); + Node *rexpr = transformExprRecurse(pstate, a->rexpr); rexpr = coerce_to_boolean(pstate, rexpr, "NOT"); @@ -948,8 +961,8 @@ transformAExprNot(ParseState *pstate, A_Expr *a) static Node * transformAExprOpAny(ParseState *pstate, A_Expr *a) { - Node *lexpr = transformExpr(pstate, a->lexpr); - Node *rexpr = transformExpr(pstate, a->rexpr); + Node *lexpr = transformExprRecurse(pstate, a->lexpr); + Node *rexpr = transformExprRecurse(pstate, a->rexpr); return (Node *) make_scalar_array_op(pstate, a->name, @@ -962,8 +975,8 @@ transformAExprOpAny(ParseState *pstate, A_Expr *a) static Node * transformAExprOpAll(ParseState *pstate, A_Expr *a) { - Node *lexpr = transformExpr(pstate, a->lexpr); - Node *rexpr = transformExpr(pstate, a->rexpr); + Node *lexpr = transformExprRecurse(pstate, a->lexpr); + Node *rexpr = transformExprRecurse(pstate, a->rexpr); return (Node *) make_scalar_array_op(pstate, a->name, @@ -976,13 +989,13 @@ transformAExprOpAll(ParseState *pstate, A_Expr *a) static Node * transformAExprDistinct(ParseState *pstate, A_Expr *a) { - Node *lexpr = transformExpr(pstate, a->lexpr); - Node *rexpr = transformExpr(pstate, a->rexpr); + Node *lexpr = transformExprRecurse(pstate, a->lexpr); + Node *rexpr = transformExprRecurse(pstate, a->rexpr); if (lexpr && IsA(lexpr, RowExpr) && rexpr && IsA(rexpr, RowExpr)) { - /* "row op row" */ + /* ROW() op ROW() is handled specially */ return make_row_distinct_op(pstate, a->name, (RowExpr *) lexpr, (RowExpr *) rexpr, @@ -1002,8 +1015,8 @@ transformAExprDistinct(ParseState *pstate, A_Expr *a) static Node * transformAExprNullIf(ParseState *pstate, A_Expr *a) { - Node *lexpr = transformExpr(pstate, a->lexpr); - Node *rexpr = transformExpr(pstate, a->rexpr); + Node *lexpr = transformExprRecurse(pstate, a->lexpr); + Node *rexpr = transformExprRecurse(pstate, a->rexpr); OpExpr *result; result = (OpExpr *) make_op(pstate, @@ -1041,7 +1054,7 @@ transformAExprOf(ParseState *pstate, A_Expr *a) * Checking an expression for match to a list of type names. Will result * in a boolean constant node. */ - Node *lexpr = transformExpr(pstate, a->lexpr); + Node *lexpr = transformExprRecurse(pstate, a->lexpr); Const *result; ListCell *telem; Oid ltype, @@ -1081,7 +1094,6 @@ transformAExprIn(ParseState *pstate, A_Expr *a) List *rvars; List *rnonvars; bool useOr; - bool haveRowExpr; ListCell *l; /* @@ -1094,24 +1106,21 @@ transformAExprIn(ParseState *pstate, A_Expr *a) /* * We try to generate a ScalarArrayOpExpr from IN/NOT IN, but this is only - * possible if the inputs are all scalars (no RowExprs) and there is a - * suitable array type available. If not, we fall back to a boolean - * condition tree with multiple copies of the lefthand expression. Also, - * any IN-list items that contain Vars are handled as separate boolean - * conditions, because that gives the planner more scope for optimization - * on such clauses. + * possible if there is a suitable array type available. If not, we fall + * back to a boolean condition tree with multiple copies of the lefthand + * expression. Also, any IN-list items that contain Vars are handled as + * separate boolean conditions, because that gives the planner more scope + * for optimization on such clauses. * - * First step: transform all the inputs, and detect whether any are - * RowExprs or contain Vars. + * First step: transform all the inputs, and detect whether any contain + * Vars. */ - lexpr = transformExpr(pstate, a->lexpr); - haveRowExpr = (lexpr && IsA(lexpr, RowExpr)); + lexpr = transformExprRecurse(pstate, a->lexpr); rexprs = rvars = rnonvars = NIL; foreach(l, (List *) a->rexpr) { - Node *rexpr = transformExpr(pstate, lfirst(l)); + Node *rexpr = transformExprRecurse(pstate, lfirst(l)); - haveRowExpr |= (rexpr && IsA(rexpr, RowExpr)); rexprs = lappend(rexprs, rexpr); if (contain_vars_of_level(rexpr, 0)) rvars = lappend(rvars, rexpr); @@ -1121,16 +1130,16 @@ transformAExprIn(ParseState *pstate, A_Expr *a) /* * ScalarArrayOpExpr is only going to be useful if there's more than one - * non-Var righthand item. Also, it won't work for RowExprs. + * non-Var righthand item. */ - if (!haveRowExpr && list_length(rnonvars) > 1) + if (list_length(rnonvars) > 1) { List *allexprs; Oid scalar_type; Oid array_type; /* - * Try to select a common type for the array elements. Note that + * Try to select a common type for the array elements. Note that * since the LHS' type is first in the list, it will be preferred when * there is doubt (eg, when all the RHS items are unknown literals). * @@ -1139,8 +1148,13 @@ transformAExprIn(ParseState *pstate, A_Expr *a) allexprs = list_concat(list_make1(lexpr), rnonvars); scalar_type = select_common_type(pstate, allexprs, NULL, NULL); - /* Do we have an array type to use? */ - if (OidIsValid(scalar_type)) + /* + * Do we have an array type to use? Aside from the case where there + * isn't one, we don't risk using ScalarArrayOpExpr when the common + * type is RECORD, because the RowExpr comparison logic below can cope + * with some cases of non-identical row types. + */ + if (OidIsValid(scalar_type) && scalar_type != RECORDOID) array_type = get_array_type(scalar_type); else array_type = InvalidOid; @@ -1191,14 +1205,10 @@ transformAExprIn(ParseState *pstate, A_Expr *a) Node *rexpr = (Node *) lfirst(l); Node *cmp; - if (haveRowExpr) + if (IsA(lexpr, RowExpr) && + IsA(rexpr, RowExpr)) { - if (!IsA(lexpr, RowExpr) || - !IsA(rexpr, RowExpr)) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("arguments of row IN must all be row expressions"), - parser_errposition(pstate, a->location))); + /* ROW() op ROW() is handled specially */ cmp = make_row_comparison_op(pstate, a->name, (List *) copyObject(((RowExpr *) lexpr)->args), @@ -1206,11 +1216,14 @@ transformAExprIn(ParseState *pstate, A_Expr *a) a->location); } else + { + /* Ordinary scalar operator */ cmp = (Node *) make_op(pstate, a->name, copyObject(lexpr), rexpr, a->location); + } cmp = coerce_to_boolean(pstate, cmp, "IN"); if (result == NULL) @@ -1234,20 +1247,35 @@ transformFuncCall(ParseState *pstate, FuncCall *fn) targs = NIL; foreach(args, fn->args) { - targs = lappend(targs, transformExpr(pstate, - (Node *) lfirst(args))); + targs = lappend(targs, transformExprRecurse(pstate, + (Node *) lfirst(args))); + } + + /* + * When WITHIN GROUP is used, we treat its ORDER BY expressions as + * additional arguments to the function, for purposes of function lookup + * and argument type coercion. So, transform each such expression and add + * them to the targs list. We don't explicitly mark where each argument + * came from, but ParseFuncOrColumn can tell what's what by reference to + * list_length(fn->agg_order). + */ + if (fn->agg_within_group) + { + Assert(fn->agg_order != NIL); + foreach(args, fn->agg_order) + { + SortBy *arg = (SortBy *) lfirst(args); + + targs = lappend(targs, transformExpr(pstate, arg->node, + EXPR_KIND_ORDER_BY)); + } } /* ... and hand off to ParseFuncOrColumn */ return ParseFuncOrColumn(pstate, fn->funcname, targs, - fn->agg_order, - fn->agg_star, - fn->agg_distinct, - fn->func_variadic, - fn->over, - false, + fn, fn->location); } @@ -1271,7 +1299,7 @@ transformCaseExpr(ParseState *pstate, CaseExpr *c) newc = makeNode(CaseExpr); /* transform the test expression, if any */ - arg = transformExpr(pstate, (Node *) c->arg); + arg = transformExprRecurse(pstate, (Node *) c->arg); /* generate placeholder for test expression */ if (arg) @@ -1324,14 +1352,14 @@ transformCaseExpr(ParseState *pstate, CaseExpr *c) warg, w->location); } - neww->expr = (Expr *) transformExpr(pstate, warg); + neww->expr = (Expr *) transformExprRecurse(pstate, warg); neww->expr = (Expr *) coerce_to_boolean(pstate, (Node *) neww->expr, "CASE/WHEN"); warg = (Node *) w->result; - neww->result = (Expr *) transformExpr(pstate, warg); + neww->result = (Expr *) transformExprRecurse(pstate, warg); neww->location = w->location; newargs = lappend(newargs, neww); @@ -1350,7 +1378,7 @@ transformCaseExpr(ParseState *pstate, CaseExpr *c) n->location = -1; defresult = (Node *) n; } - newc->defresult = (Expr *) transformExpr(pstate, defresult); + newc->defresult = (Expr *) transformExprRecurse(pstate, defresult); /* * Note: default result is considered the most significant type in @@ -1393,16 +1421,97 @@ transformSubLink(ParseState *pstate, SubLink *sublink) { Node *result = (Node *) sublink; Query *qtree; + const char *err; /* If we already transformed this node, do nothing */ if (IsA(sublink->subselect, Query)) return result; + /* + * Check to see if the sublink is in an invalid place within the query. We + * allow sublinks everywhere in SELECT/INSERT/UPDATE/DELETE, but generally + * not in utility statements. + */ + err = NULL; + switch (pstate->p_expr_kind) + { + case EXPR_KIND_NONE: + Assert(false); /* can't happen */ + break; + case EXPR_KIND_OTHER: + /* Accept sublink here; caller must throw error if wanted */ + break; + case EXPR_KIND_JOIN_ON: + case EXPR_KIND_JOIN_USING: + case EXPR_KIND_FROM_SUBSELECT: + case EXPR_KIND_FROM_FUNCTION: + case EXPR_KIND_WHERE: + case EXPR_KIND_HAVING: + case EXPR_KIND_FILTER: + case EXPR_KIND_WINDOW_PARTITION: + case EXPR_KIND_WINDOW_ORDER: + case EXPR_KIND_WINDOW_FRAME_RANGE: + case EXPR_KIND_WINDOW_FRAME_ROWS: + case EXPR_KIND_SELECT_TARGET: + case EXPR_KIND_INSERT_TARGET: + case EXPR_KIND_UPDATE_SOURCE: + case EXPR_KIND_UPDATE_TARGET: + case EXPR_KIND_GROUP_BY: + case EXPR_KIND_ORDER_BY: + case EXPR_KIND_DISTINCT_ON: + case EXPR_KIND_LIMIT: + case EXPR_KIND_OFFSET: + case EXPR_KIND_RETURNING: + case EXPR_KIND_VALUES: + /* okay */ + break; + case EXPR_KIND_CHECK_CONSTRAINT: + case EXPR_KIND_DOMAIN_CHECK: + err = _("cannot use subquery in check constraint"); + break; + case EXPR_KIND_COLUMN_DEFAULT: + case EXPR_KIND_FUNCTION_DEFAULT: + err = _("cannot use subquery in DEFAULT expression"); + break; + case EXPR_KIND_INDEX_EXPRESSION: + err = _("cannot use subquery in index expression"); + break; + case EXPR_KIND_INDEX_PREDICATE: + err = _("cannot use subquery in index predicate"); + break; + case EXPR_KIND_ALTER_COL_TRANSFORM: + err = _("cannot use subquery in transform expression"); + break; + case EXPR_KIND_EXECUTE_PARAMETER: + err = _("cannot use subquery in EXECUTE parameter"); + break; + case EXPR_KIND_TRIGGER_WHEN: + err = _("cannot use subquery in trigger WHEN condition"); + break; + + /* + * There is intentionally no default: case here, so that the + * compiler will warn if we add a new ParseExprKind without + * extending this switch. If we do see an unrecognized value at + * runtime, the behavior will be the same as for EXPR_KIND_OTHER, + * which is sane anyway. + */ + } + if (err) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg_internal("%s", err), + parser_errposition(pstate, sublink->location))); + pstate->p_hasSubLinks = true; + + /* + * OK, let's transform the sub-SELECT. + */ qtree = parse_sub_analyze(sublink->subselect, pstate, NULL, false); /* - * Check that we got something reasonable. Many of these conditions are + * Check that we got something reasonable. Many of these conditions are * impossible given restrictions of the grammar, but check 'em anyway. */ if (!IsA(qtree, Query) || @@ -1463,7 +1572,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink) /* * Transform lefthand expression, and convert to a list */ - lefthand = transformExpr(pstate, sublink->testexpr); + lefthand = transformExprRecurse(pstate, sublink->testexpr); if (lefthand && IsA(lefthand, RowExpr)) left_list = ((RowExpr *) lefthand)->args; else @@ -1570,7 +1679,7 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a, } else { - newe = transformExpr(pstate, e); + newe = transformExprRecurse(pstate, e); /* * Check for sub-array expressions, if we haven't already found @@ -1686,13 +1795,19 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a, static Node * transformRowExpr(ParseState *pstate, RowExpr *r) { - RowExpr *newr = makeNode(RowExpr); + RowExpr *newr; char fname[16]; int fnum; ListCell *lc; + /* If we already transformed this node, do nothing */ + if (OidIsValid(r->row_typeid)) + return (Node *) r; + + newr = makeNode(RowExpr); + /* Transform the field expressions */ - newr->args = transformExpressionList(pstate, r->args); + newr->args = transformExpressionList(pstate, r->args, pstate->p_expr_kind); /* Barring later casting, we consider the type RECORD */ newr->row_typeid = RECORDOID; @@ -1725,7 +1840,7 @@ transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c) Node *e = (Node *) lfirst(args); Node *newe; - newe = transformExpr(pstate, e); + newe = transformExprRecurse(pstate, e); newargs = lappend(newargs, newe); } @@ -1764,7 +1879,7 @@ transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m) Node *e = (Node *) lfirst(args); Node *newe; - newe = transformExpr(pstate, e); + newe = transformExprRecurse(pstate, e); newargs = lappend(newargs, newe); } @@ -1791,20 +1906,27 @@ transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m) static Node * transformXmlExpr(ParseState *pstate, XmlExpr *x) { - XmlExpr *newx = makeNode(XmlExpr); + XmlExpr *newx; ListCell *lc; int i; + /* If we already transformed this node, do nothing */ + if (OidIsValid(x->type)) + return (Node *) x; + + newx = makeNode(XmlExpr); newx->op = x->op; if (x->name) newx->name = map_sql_identifier_to_xml_name(x->name, false, false); else newx->name = NULL; newx->xmloption = x->xmloption; + newx->type = XMLOID; /* this just marks the node as transformed */ + newx->typmod = -1; newx->location = x->location; /* - * gram.y built the named args as a list of ResTarget. Transform each, + * gram.y built the named args as a list of ResTarget. Transform each, * and break the names out as a separate list. */ newx->named_args = NIL; @@ -1818,7 +1940,7 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x) Assert(IsA(r, ResTarget)); - expr = transformExpr(pstate, r->val); + expr = transformExprRecurse(pstate, r->val); if (r->name) argname = map_sql_identifier_to_xml_name(r->name, false, false); @@ -1864,7 +1986,7 @@ transformXmlExpr(ParseState *pstate, XmlExpr *x) Node *e = (Node *) lfirst(lc); Node *newe; - newe = transformExpr(pstate, e); + newe = transformExprRecurse(pstate, e); switch (x->op) { case IS_XMLCONCAT: @@ -1927,7 +2049,7 @@ transformXmlSerialize(ParseState *pstate, XmlSerialize *xs) xexpr = makeNode(XmlExpr); xexpr->op = IS_XMLSERIALIZE; xexpr->args = list_make1(coerce_to_specific_type(pstate, - transformExpr(pstate, xs->expr), + transformExprRecurse(pstate, xs->expr), XMLOID, "XMLSERIALIZE")); @@ -1990,7 +2112,7 @@ transformBooleanTest(ParseState *pstate, BooleanTest *b) clausename = NULL; /* keep compiler quiet */ } - b->arg = (Expr *) transformExpr(pstate, (Node *) b->arg); + b->arg = (Expr *) transformExprRecurse(pstate, (Node *) b->arg); b->arg = (Expr *) coerce_to_boolean(pstate, (Node *) b->arg, @@ -2073,9 +2195,9 @@ transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte, int location) vnum = RTERangeTablePosn(pstate, rte, &sublevels_up); /* - * Build the appropriate referencing node. Note that if the RTE is a + * Build the appropriate referencing node. Note that if the RTE is a * function returning scalar, we create just a plain reference to the - * function value, not a composite containing a single column. This is + * function value, not a composite containing a single column. This is * pretty inconsistent at first sight, but it's what we've done * historically. One argument for it is that "rel" and "rel.*" mean the * same thing for composite relations, so why not for scalar functions... @@ -2101,7 +2223,7 @@ static Node * transformTypeCast(ParseState *pstate, TypeCast *tc) { Node *result; - Node *expr = transformExpr(pstate, tc->arg); + Node *expr = transformExprRecurse(pstate, tc->arg); Oid inputType = exprType(expr); Oid targetType; int32 targetTypmod; @@ -2149,7 +2271,7 @@ transformCollateClause(ParseState *pstate, CollateClause *c) Oid argtype; newc = makeNode(CollateExpr); - newc->arg = (Expr *) transformExpr(pstate, c->arg); + newc->arg = (Expr *) transformExprRecurse(pstate, c->arg); argtype = exprType((Node *) newc->arg); @@ -2259,7 +2381,7 @@ make_row_comparison_op(ParseState *pstate, List *opname, /* * Now we must determine which row comparison semantics (= <> < <= > >=) - * apply to this set of operators. We look for btree opfamilies + * apply to this set of operators. We look for btree opfamilies * containing the operators, and see which interpretations (strategy * numbers) exist for each operator. */ @@ -2452,3 +2574,89 @@ make_distinct_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree, return result; } + +/* + * Produce a string identifying an expression by kind. + * + * Note: when practical, use a simple SQL keyword for the result. If that + * doesn't work well, check call sites to see whether custom error message + * strings are required. + */ +const char * +ParseExprKindName(ParseExprKind exprKind) +{ + switch (exprKind) + { + case EXPR_KIND_NONE: + return "invalid expression context"; + case EXPR_KIND_OTHER: + return "extension expression"; + case EXPR_KIND_JOIN_ON: + return "JOIN/ON"; + case EXPR_KIND_JOIN_USING: + return "JOIN/USING"; + case EXPR_KIND_FROM_SUBSELECT: + return "sub-SELECT in FROM"; + case EXPR_KIND_FROM_FUNCTION: + return "function in FROM"; + case EXPR_KIND_WHERE: + return "WHERE"; + case EXPR_KIND_HAVING: + return "HAVING"; + case EXPR_KIND_FILTER: + return "FILTER"; + case EXPR_KIND_WINDOW_PARTITION: + return "window PARTITION BY"; + case EXPR_KIND_WINDOW_ORDER: + return "window ORDER BY"; + case EXPR_KIND_WINDOW_FRAME_RANGE: + return "window RANGE"; + case EXPR_KIND_WINDOW_FRAME_ROWS: + return "window ROWS"; + case EXPR_KIND_SELECT_TARGET: + return "SELECT"; + case EXPR_KIND_INSERT_TARGET: + return "INSERT"; + case EXPR_KIND_UPDATE_SOURCE: + case EXPR_KIND_UPDATE_TARGET: + return "UPDATE"; + case EXPR_KIND_GROUP_BY: + return "GROUP BY"; + case EXPR_KIND_ORDER_BY: + return "ORDER BY"; + case EXPR_KIND_DISTINCT_ON: + return "DISTINCT ON"; + case EXPR_KIND_LIMIT: + return "LIMIT"; + case EXPR_KIND_OFFSET: + return "OFFSET"; + case EXPR_KIND_RETURNING: + return "RETURNING"; + case EXPR_KIND_VALUES: + return "VALUES"; + case EXPR_KIND_CHECK_CONSTRAINT: + case EXPR_KIND_DOMAIN_CHECK: + return "CHECK"; + case EXPR_KIND_COLUMN_DEFAULT: + case EXPR_KIND_FUNCTION_DEFAULT: + return "DEFAULT"; + case EXPR_KIND_INDEX_EXPRESSION: + return "index expression"; + case EXPR_KIND_INDEX_PREDICATE: + return "index predicate"; + case EXPR_KIND_ALTER_COL_TRANSFORM: + return "USING"; + case EXPR_KIND_EXECUTE_PARAMETER: + return "EXECUTE"; + case EXPR_KIND_TRIGGER_WHEN: + return "WHEN"; + + /* + * There is intentionally no default: case here, so that the + * compiler will warn if we add a new ParseExprKind without + * extending this switch. If we do see an unrecognized value at + * runtime, we'll fall through to the "unrecognized" return. + */ + } + return "unrecognized expression kind"; +} diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index b051707d7e..9ebd3fd43b 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -3,7 +3,7 @@ * parse_func.c * handle function calls in parser * - * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -14,12 +14,16 @@ */ #include "postgres.h" +#include "access/htup_details.h" +#include "catalog/pg_aggregate.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" #include "funcapi.h" +#include "lib/stringinfo.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "parser/parse_agg.h" +#include "parser/parse_clause.h" #include "parser/parse_coerce.h" #include "parser/parse_func.h" #include "parser/parse_relation.h" @@ -30,6 +34,9 @@ #include "utils/syscache.h" +static void unify_hypothetical_args(ParseState *pstate, + List *fargs, int numAggregatedArgs, + Oid *actual_arg_types, Oid *declared_arg_types); static Oid FuncNameAsType(List *funcname); static Node *ParseComplexProjection(ParseState *pstate, char *funcname, Node *first_arg, int location); @@ -45,24 +52,30 @@ static Node *ParseComplexProjection(ParseState *pstate, char *funcname, * a function of a single complex-type argument can be written like a * column reference, allowing functions to act like computed columns. * - * Hence, both cases come through here. The is_column parameter tells us - * which syntactic construct is actually being dealt with, but this is - * intended to be used only to deliver an appropriate error message, - * not to affect the semantics. When is_column is true, we should have - * a single argument (the putative table), unqualified function name - * equal to the column name, and no aggregate or variadic decoration. - * Also, when is_column is true, we return NULL on failure rather than + * Hence, both cases come through here. If fn is null, we're dealing with + * column syntax not function syntax, but in principle that should not + * affect the lookup behavior, only which error messages we deliver. + * The FuncCall struct is needed however to carry various decoration that + * applies to aggregate and window functions. + * + * Also, when fn is null, we return NULL on failure rather than * reporting a no-such-function error. * - * The argument expressions (in fargs) must have been transformed already. - * But the agg_order expressions, if any, have not been. + * The argument expressions (in fargs) must have been transformed + * already. However, nothing in *fn has been transformed. */ Node * ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, - List *agg_order, bool agg_star, bool agg_distinct, - bool func_variadic, - WindowDef *over, bool is_column, int location) + FuncCall *fn, int location) { + bool is_column = (fn == NULL); + List *agg_order = (fn ? fn->agg_order : NIL); + Expr *agg_filter = NULL; + bool agg_within_group = (fn ? fn->agg_within_group : false); + bool agg_star = (fn ? fn->agg_star : false); + bool agg_distinct = (fn ? fn->agg_distinct : false); + bool func_variadic = (fn ? fn->func_variadic : false); + WindowDef *over = (fn ? fn->over : NULL); Oid rettype; Oid funcid; ListCell *l; @@ -77,11 +90,21 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, Node *retval; bool retset; int nvargs; + Oid vatype; FuncDetailCode fdresult; + char aggkind = 0; + + /* + * If there's an aggregate filter, transform it using transformWhereClause + */ + if (fn && fn->agg_filter != NULL) + agg_filter = (Expr *) transformWhereClause(pstate, fn->agg_filter, + EXPR_KIND_FILTER, + "FILTER"); /* * Most of the rest of the parser just assumes that functions do not have - * more than FUNC_MAX_ARGS parameters. We have to test here to protect + * more than FUNC_MAX_ARGS parameters. We have to test here to protect * against array overruns, etc. Of course, this may not be a function, * but the test doesn't hurt. */ @@ -98,10 +121,12 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, * Extract arg type info in preparation for function lookup. * * If any arguments are Param markers of type VOID, we discard them from - * the parameter list. This is a hack to allow the JDBC driver to not - * have to distinguish "input" and "output" parameter symbols while - * parsing function-call constructs. We can't use foreach() because we - * may modify the list ... + * the parameter list. This is a hack to allow the JDBC driver to not have + * to distinguish "input" and "output" parameter symbols while parsing + * function-call constructs. Don't do this if dealing with column syntax, + * nor if we had WITHIN GROUP (because in that case it's critical to keep + * the argument count unchanged). We can't use foreach() because we may + * modify the list ... */ nargs = 0; for (l = list_head(fargs); l != NULL; l = nextl) @@ -111,7 +136,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, nextl = lnext(l); - if (argtype == VOIDOID && IsA(arg, Param) &&!is_column) + if (argtype == VOIDOID && IsA(arg, Param) && + !is_column && !agg_within_group) { fargs = list_delete_ptr(fargs, arg); continue; @@ -172,8 +198,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, * the "function call" could be a projection. We also check that there * wasn't any aggregate or variadic decoration, nor an argument name. */ - if (nargs == 1 && agg_order == NIL && !agg_star && !agg_distinct && - over == NULL && !func_variadic && argnames == NIL && + if (nargs == 1 && agg_order == NIL && agg_filter == NULL && !agg_star && + !agg_distinct && over == NULL && !func_variadic && argnames == NIL && list_length(funcname) == 1) { Oid argtype = actual_arg_types[0]; @@ -212,7 +238,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, fdresult = func_get_detail(funcname, fargs, argnames, nargs, actual_arg_types, !func_variadic, true, - &funcid, &rettype, &retset, &nvargs, + &funcid, &rettype, &retset, + &nvargs, &vatype, &declared_arg_types, &argdefaults); if (fdresult == FUNCDETAIL_COERCION) { @@ -243,12 +270,24 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, errmsg("DISTINCT specified, but %s is not an aggregate function", NameListToString(funcname)), parser_errposition(pstate, location))); + if (agg_within_group) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("WITHIN GROUP specified, but %s is not an aggregate function", + NameListToString(funcname)), + parser_errposition(pstate, location))); if (agg_order != NIL) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("ORDER BY specified, but %s is not an aggregate function", NameListToString(funcname)), parser_errposition(pstate, location))); + if (agg_filter) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("FILTER specified, but %s is not an aggregate function", + NameListToString(funcname)), + parser_errposition(pstate, location))); if (over) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), @@ -256,8 +295,181 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, NameListToString(funcname)), parser_errposition(pstate, location))); } - else if (!(fdresult == FUNCDETAIL_AGGREGATE || - fdresult == FUNCDETAIL_WINDOWFUNC)) + else if (fdresult == FUNCDETAIL_AGGREGATE) + { + /* + * It's an aggregate; fetch needed info from the pg_aggregate entry. + */ + HeapTuple tup; + Form_pg_aggregate classForm; + int catDirectArgs; + + tup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(funcid)); + if (!HeapTupleIsValid(tup)) /* should not happen */ + elog(ERROR, "cache lookup failed for aggregate %u", funcid); + classForm = (Form_pg_aggregate) GETSTRUCT(tup); + aggkind = classForm->aggkind; + catDirectArgs = classForm->aggnumdirectargs; + ReleaseSysCache(tup); + + /* Now check various disallowed cases. */ + if (AGGKIND_IS_ORDERED_SET(aggkind)) + { + int numAggregatedArgs; + int numDirectArgs; + + if (!agg_within_group) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("WITHIN GROUP is required for ordered-set aggregate %s", + NameListToString(funcname)), + parser_errposition(pstate, location))); + if (over) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("OVER is not supported for ordered-set aggregate %s", + NameListToString(funcname)), + parser_errposition(pstate, location))); + /* gram.y rejects DISTINCT + WITHIN GROUP */ + Assert(!agg_distinct); + /* gram.y rejects VARIADIC + WITHIN GROUP */ + Assert(!func_variadic); + + /* + * Since func_get_detail was working with an undifferentiated list + * of arguments, it might have selected an aggregate that doesn't + * really match because it requires a different division of direct + * and aggregated arguments. Check that the number of direct + * arguments is actually OK; if not, throw an "undefined function" + * error, similarly to the case where a misplaced ORDER BY is used + * in a regular aggregate call. + */ + numAggregatedArgs = list_length(agg_order); + numDirectArgs = nargs - numAggregatedArgs; + Assert(numDirectArgs >= 0); + + if (!OidIsValid(vatype)) + { + /* Test is simple if aggregate isn't variadic */ + if (numDirectArgs != catDirectArgs) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("function %s does not exist", + func_signature_string(funcname, nargs, + argnames, + actual_arg_types)), + errhint("There is an ordered-set aggregate %s, but it requires %d direct arguments, not %d.", + NameListToString(funcname), + catDirectArgs, numDirectArgs), + parser_errposition(pstate, location))); + } + else + { + /* + * If it's variadic, we have two cases depending on whether + * the agg was "... ORDER BY VARIADIC" or "..., VARIADIC ORDER + * BY VARIADIC". It's the latter if catDirectArgs equals + * pronargs; to save a catalog lookup, we reverse-engineer + * pronargs from the info we got from func_get_detail. + */ + int pronargs; + + pronargs = nargs; + if (nvargs > 1) + pronargs -= nvargs - 1; + if (catDirectArgs < pronargs) + { + /* VARIADIC isn't part of direct args, so still easy */ + if (numDirectArgs != catDirectArgs) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("function %s does not exist", + func_signature_string(funcname, nargs, + argnames, + actual_arg_types)), + errhint("There is an ordered-set aggregate %s, but it requires %d direct arguments, not %d.", + NameListToString(funcname), + catDirectArgs, numDirectArgs), + parser_errposition(pstate, location))); + } + else + { + /* + * Both direct and aggregated args were declared variadic. + * For a standard ordered-set aggregate, it's okay as long + * as there aren't too few direct args. For a + * hypothetical-set aggregate, we assume that the + * hypothetical arguments are those that matched the + * variadic parameter; there must be just as many of them + * as there are aggregated arguments. + */ + if (aggkind == AGGKIND_HYPOTHETICAL) + { + if (nvargs != 2 * numAggregatedArgs) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("function %s does not exist", + func_signature_string(funcname, nargs, + argnames, + actual_arg_types)), + errhint("To use the hypothetical-set aggregate %s, the number of hypothetical direct arguments (here %d) must match the number of ordering columns (here %d).", + NameListToString(funcname), + nvargs - numAggregatedArgs, numAggregatedArgs), + parser_errposition(pstate, location))); + } + else + { + if (nvargs <= numAggregatedArgs) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("function %s does not exist", + func_signature_string(funcname, nargs, + argnames, + actual_arg_types)), + errhint("There is an ordered-set aggregate %s, but it requires at least %d direct arguments.", + NameListToString(funcname), + catDirectArgs), + parser_errposition(pstate, location))); + } + } + } + + /* Check type matching of hypothetical arguments */ + if (aggkind == AGGKIND_HYPOTHETICAL) + unify_hypothetical_args(pstate, fargs, numAggregatedArgs, + actual_arg_types, declared_arg_types); + } + else + { + /* Normal aggregate, so it can't have WITHIN GROUP */ + if (agg_within_group) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s is not an ordered-set aggregate, so it cannot have WITHIN GROUP", + NameListToString(funcname)), + parser_errposition(pstate, location))); + } + } + else if (fdresult == FUNCDETAIL_WINDOWFUNC) + { + /* + * True window functions must be called with a window definition. + */ + if (!over) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("window function %s requires an OVER clause", + NameListToString(funcname)), + parser_errposition(pstate, location))); + /* And, per spec, WITHIN GROUP isn't allowed */ + if (agg_within_group) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("window function %s cannot have WITHIN GROUP", + NameListToString(funcname)), + parser_errposition(pstate, location))); + } + else { /* * Oops. Time to die. @@ -280,7 +492,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, errhint("Could not choose a best candidate function. " "You might need to add explicit type casts."), parser_errposition(pstate, location))); - else if (list_length(agg_order) > 1) + else if (list_length(agg_order) > 1 && !agg_within_group) { /* It's agg(x, ORDER BY y,z) ... perhaps misplaced ORDER BY */ ereport(ERROR, @@ -308,7 +520,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, * If there are default arguments, we have to include their types in * actual_arg_types for the purpose of checking generic type consistency. * However, we do NOT put them into the generated parse node, because - * their actual values might change before the query gets run. The + * their actual values might change before the query gets run. The * planner has to insert the up-to-date values at plan time. */ nargsplusdefs = nargs; @@ -344,10 +556,21 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, make_fn_arguments(pstate, fargs, actual_arg_types, declared_arg_types); /* + * If the function isn't actually variadic, forget any VARIADIC decoration + * on the call. (Perhaps we should throw an error instead, but + * historically we've allowed people to write that.) + */ + if (!OidIsValid(vatype)) + { + Assert(nvargs == 0); + func_variadic = false; + } + + /* * If it's a variadic function call, transform the last nvargs arguments * into an array --- unless it's an "any" variadic. */ - if (nvargs > 0 && declared_arg_types[nargs - 1] != ANYOID) + if (nvargs > 0 && vatype != ANYOID) { ArrayExpr *newa = makeNode(ArrayExpr); int non_var_args = nargs - nvargs; @@ -372,6 +595,27 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, newa->location = exprLocation((Node *) vargs); fargs = lappend(fargs, newa); + + /* We could not have had VARIADIC marking before ... */ + Assert(!func_variadic); + /* ... but now, it's a VARIADIC call */ + func_variadic = true; + } + + /* + * If an "any" variadic is called with explicit VARIADIC marking, insist + * that the variadic parameter be of some array type. + */ + if (nargs > 0 && vatype == ANYOID && func_variadic) + { + Oid va_arr_typid = actual_arg_types[nargs - 1]; + + if (!OidIsValid(get_base_element_type(va_arr_typid))) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("VARIADIC argument must be an array"), + parser_errposition(pstate, + exprLocation((Node *) llast(fargs))))); } /* build the appropriate output structure */ @@ -382,6 +626,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, funcexpr->funcid = funcid; funcexpr->funcresulttype = rettype; funcexpr->funcretset = retset; + funcexpr->funcvariadic = func_variadic; funcexpr->funcformat = COERCE_EXPLICIT_CALL; /* funccollid and inputcollid will be set by parse_collate.c */ funcexpr->args = fargs; @@ -397,16 +642,20 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, aggref->aggfnoid = funcid; aggref->aggtype = rettype; /* aggcollid and inputcollid will be set by parse_collate.c */ - /* args, aggorder, aggdistinct will be set by transformAggregateCall */ + /* aggdirectargs and args will be set by transformAggregateCall */ + /* aggorder and aggdistinct will be set by transformAggregateCall */ + aggref->aggfilter = agg_filter; aggref->aggstar = agg_star; + aggref->aggvariadic = func_variadic; + aggref->aggkind = aggkind; /* agglevelsup will be set by transformAggregateCall */ aggref->location = location; /* * Reject attempt to call a parameterless aggregate without (*) - * syntax. This is mere pedantry but some folks insisted ... + * syntax. This is mere pedantry but some folks insisted ... */ - if (fargs == NIL && !agg_star) + if (fargs == NIL && !agg_star && !agg_within_group) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("%s(*) must be used to call a parameterless aggregate function", @@ -420,10 +669,13 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, parser_errposition(pstate, location))); /* - * Currently it's not possible to define an aggregate with named - * arguments, so this case should be impossible. Check anyway because - * the planner and executor wouldn't cope with NamedArgExprs in an - * Aggref node. + * We might want to support named arguments later, but disallow it for + * now. We'd need to figure out the parsed representation (should the + * NamedArgExprs go above or below the TargetEntry nodes?) and then + * teach the planner to reorder the list properly. Or maybe we could + * make transformAggregateCall do that? However, if you'd also like + * to allow default arguments for aggregates, we'd need to do it in + * planning to avoid semantic problems. */ if (argnames != NIL) ereport(ERROR, @@ -441,14 +693,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, /* window function */ WindowFunc *wfunc = makeNode(WindowFunc); - /* - * True window functions must be called with a window definition. - */ - if (!over) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("window function call requires an OVER clause"), - parser_errposition(pstate, location))); + Assert(over); /* lack of this was checked above */ + Assert(!agg_within_group); /* also checked above */ wfunc->winfnoid = funcid; wfunc->wintype = rettype; @@ -457,6 +703,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, /* winref will be set by transformWindowFuncCall */ wfunc->winstar = agg_star; wfunc->winagg = (fdresult == FUNCDETAIL_AGGREGATE); + wfunc->aggfilter = agg_filter; wfunc->location = location; /* @@ -470,7 +717,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, /* * Reject attempt to call a parameterless aggregate without (*) - * syntax. This is mere pedantry but some folks insisted ... + * syntax. This is mere pedantry but some folks insisted ... */ if (wfunc->winagg && fargs == NIL && !agg_star) ereport(ERROR, @@ -488,21 +735,19 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, errmsg("aggregate ORDER BY is not implemented for window functions"), parser_errposition(pstate, location))); - if (retset) - ereport(ERROR, - (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), - errmsg("window functions cannot return sets"), - parser_errposition(pstate, location))); - /* - * We might want to support this later, but for now reject it because - * the planner and executor wouldn't cope with NamedArgExprs in a - * WindowFunc node. + * FILTER is not yet supported with true window functions */ - if (argnames != NIL) + if (!wfunc->winagg && agg_filter) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("window functions cannot use named arguments"), + errmsg("FILTER is not implemented for non-aggregate window functions"), + parser_errposition(pstate, location))); + + if (retset) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("window functions cannot return sets"), parser_errposition(pstate, location))); /* parse_agg.c does additional window-func-specific processing */ @@ -650,7 +895,7 @@ func_select_candidate(int nargs, * matches" in the exact-match heuristic; it also makes it possible to do * something useful with the type-category heuristics. Note that this * makes it difficult, but not impossible, to use functions declared to - * take a domain as an input datatype. Such a function will be selected + * take a domain as an input datatype. Such a function will be selected * over the base-type function only if it is an exact match at all * argument positions, and so was already chosen by our caller. * @@ -774,7 +1019,7 @@ func_select_candidate(int nargs, /* * The next step examines each unknown argument position to see if we can - * determine a "type category" for it. If any candidate has an input + * determine a "type category" for it. If any candidate has an input * datatype of STRING category, use STRING category (this bias towards * STRING is appropriate since unknown-type literals look like strings). * Otherwise, if all the candidates agree on the type category of this @@ -785,7 +1030,7 @@ func_select_candidate(int nargs, * the candidates takes a preferred datatype within the category. * * Having completed this examination, remove candidates that accept the - * wrong category at any unknown position. Also, if at least one + * wrong category at any unknown position. Also, if at least one * candidate accepted a preferred type at a position, remove candidates * that accept non-preferred types. If just one candidate remains, return * that one. However, if this rule turns out to reject all candidates, @@ -914,7 +1159,7 @@ func_select_candidate(int nargs, * type, and see if that gives us a unique match. If so, use that match. * * NOTE: for a binary operator with one unknown and one non-unknown input, - * we already tried this heuristic in binary_oper_exact(). However, that + * we already tried this heuristic in binary_oper_exact(). However, that * code only finds exact matches, whereas here we will handle matches that * involve coercion, polymorphic type resolution, etc. */ @@ -1012,6 +1257,7 @@ func_get_detail(List *funcname, Oid *rettype, /* return value */ bool *retset, /* return value */ int *nvargs, /* return value */ + Oid *vatype, /* return value */ Oid **true_typeids, /* return value */ List **argdefaults) /* optional return value */ { @@ -1023,13 +1269,15 @@ func_get_detail(List *funcname, *rettype = InvalidOid; *retset = false; *nvargs = 0; + *vatype = InvalidOid; *true_typeids = NULL; if (argdefaults) *argdefaults = NIL; /* Get list of possible candidates from namespace search */ raw_candidates = FuncnameGetCandidates(funcname, nargs, fargnames, - expand_variadic, expand_defaults); + expand_variadic, expand_defaults, + false); /* * Quickly check if there is an exact match to the input datatypes (there @@ -1080,7 +1328,7 @@ func_get_detail(List *funcname, * * NB: it's important that this code does not exceed what coerce_type * can do, because the caller will try to apply coerce_type if we - * return FUNCDETAIL_COERCION. If we return that result for something + * return FUNCDETAIL_COERCION. If we return that result for something * coerce_type can't handle, we'll cause infinite recursion between * this module and coerce_type! */ @@ -1133,6 +1381,7 @@ func_get_detail(List *funcname, *rettype = targetType; *retset = false; *nvargs = 0; + *vatype = InvalidOid; *true_typeids = argtypes; return FUNCDETAIL_COERCION; } @@ -1230,6 +1479,7 @@ func_get_detail(List *funcname, pform = (Form_pg_proc) GETSTRUCT(ftup); *rettype = pform->prorettype; *retset = pform->proretset; + *vatype = pform->provariadic; /* fetch default args if caller wants 'em */ if (argdefaults && best_candidate->ndargs > 0) { @@ -1256,7 +1506,7 @@ func_get_detail(List *funcname, { /* * This is a bit tricky in named notation, since the supplied - * arguments could replace any subset of the defaults. We + * arguments could replace any subset of the defaults. We * work by making a bitmapset of the argnumbers of defaulted * arguments, then scanning the defaults list and selecting * the needed items. (This assumes that defaulted arguments @@ -1314,6 +1564,101 @@ func_get_detail(List *funcname, /* + * unify_hypothetical_args() + * + * Ensure that each hypothetical direct argument of a hypothetical-set + * aggregate has the same type as the corresponding aggregated argument. + * Modify the expressions in the fargs list, if necessary, and update + * actual_arg_types[]. + * + * If the agg declared its args non-ANY (even ANYELEMENT), we need only a + * sanity check that the declared types match; make_fn_arguments will coerce + * the actual arguments to match the declared ones. But if the declaration + * is ANY, nothing will happen in make_fn_arguments, so we need to fix any + * mismatch here. We use the same type resolution logic as UNION etc. + */ +static void +unify_hypothetical_args(ParseState *pstate, + List *fargs, + int numAggregatedArgs, + Oid *actual_arg_types, + Oid *declared_arg_types) +{ + Node *args[FUNC_MAX_ARGS]; + int numDirectArgs, + numNonHypotheticalArgs; + int i; + ListCell *lc; + + numDirectArgs = list_length(fargs) - numAggregatedArgs; + numNonHypotheticalArgs = numDirectArgs - numAggregatedArgs; + /* safety check (should only trigger with a misdeclared agg) */ + if (numNonHypotheticalArgs < 0) + elog(ERROR, "incorrect number of arguments to hypothetical-set aggregate"); + + /* Deconstruct fargs into an array for ease of subscripting */ + i = 0; + foreach(lc, fargs) + { + args[i++] = (Node *) lfirst(lc); + } + + /* Check each hypothetical arg and corresponding aggregated arg */ + for (i = numNonHypotheticalArgs; i < numDirectArgs; i++) + { + int aargpos = numDirectArgs + (i - numNonHypotheticalArgs); + Oid commontype; + + /* A mismatch means AggregateCreate didn't check properly ... */ + if (declared_arg_types[i] != declared_arg_types[aargpos]) + elog(ERROR, "hypothetical-set aggregate has inconsistent declared argument types"); + + /* No need to unify if make_fn_arguments will coerce */ + if (declared_arg_types[i] != ANYOID) + continue; + + /* + * Select common type, giving preference to the aggregated argument's + * type (we'd rather coerce the direct argument once than coerce all + * the aggregated values). + */ + commontype = select_common_type(pstate, + list_make2(args[aargpos], args[i]), + "WITHIN GROUP", + NULL); + + /* + * Perform the coercions. We don't need to worry about NamedArgExprs + * here because they aren't supported with aggregates. + */ + args[i] = coerce_type(pstate, + args[i], + actual_arg_types[i], + commontype, -1, + COERCION_IMPLICIT, + COERCE_IMPLICIT_CAST, + -1); + actual_arg_types[i] = commontype; + args[aargpos] = coerce_type(pstate, + args[aargpos], + actual_arg_types[aargpos], + commontype, -1, + COERCION_IMPLICIT, + COERCE_IMPLICIT_CAST, + -1); + actual_arg_types[aargpos] = commontype; + } + + /* Reconstruct fargs from array */ + i = 0; + foreach(lc, fargs) + { + lfirst(lc) = args[i++]; + } +} + + +/* * make_fn_arguments() * * Given the actual argument expressions for a function, and the desired @@ -1388,7 +1733,7 @@ FuncNameAsType(List *funcname) Oid result; Type typtup; - typtup = LookupTypeName(NULL, makeTypeNameFromNameList(funcname), NULL); + typtup = LookupTypeName(NULL, makeTypeNameFromNameList(funcname), NULL, false); if (typtup == NULL) return InvalidOid; @@ -1406,7 +1751,7 @@ FuncNameAsType(List *funcname) * ParseComplexProjection - * handles function calls with a single argument that is of complex type. * If the function call is actually a column projection, return a suitably - * transformed expression tree. If not, return NULL. + * transformed expression tree. If not, return NULL. */ static Node * ParseComplexProjection(ParseState *pstate, char *funcname, Node *first_arg, @@ -1480,7 +1825,7 @@ ParseComplexProjection(ParseState *pstate, char *funcname, Node *first_arg, * The result is something like "foo(integer)". * * If argnames isn't NIL, it is a list of C strings representing the actual - * arg names for the last N arguments. This must be considered part of the + * arg names for the last N arguments. This must be considered part of the * function signature too, when dealing with named-notation function calls. * * This is typically used in the construction of function-not-found error @@ -1547,7 +1892,7 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError) { FuncCandidateList clist; - clist = FuncnameGetCandidates(funcname, nargs, NIL, false, false); + clist = FuncnameGetCandidates(funcname, nargs, NIL, false, false, noError); while (clist) { @@ -1567,27 +1912,6 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError) } /* - * LookupTypeNameOid - * Convenience routine to look up a type, silently accepting shell types - */ -static Oid -LookupTypeNameOid(const TypeName *typename) -{ - Oid result; - Type typtup; - - typtup = LookupTypeName(NULL, typename, NULL); - if (typtup == NULL) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("type \"%s\" does not exist", - TypeNameToString(typename)))); - result = typeTypeId(typtup); - ReleaseSysCache(typtup); - return result; -} - -/* * LookupFuncNameTypeNames * Like LookupFuncName, but the argument types are specified by a * list of TypeName nodes. @@ -1614,7 +1938,7 @@ LookupFuncNameTypeNames(List *funcname, List *argtypes, bool noError) { TypeName *t = (TypeName *) lfirst(args_item); - argoids[i] = LookupTypeNameOid(t); + argoids[i] = LookupTypeNameOid(NULL, t, noError); args_item = lnext(args_item); } @@ -1654,7 +1978,7 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError) { TypeName *t = (TypeName *) lfirst(lc); - argoids[i] = LookupTypeNameOid(t); + argoids[i] = LookupTypeNameOid(NULL, t, noError); i++; } diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c index 80dbdd19e4..1e3d1f68fa 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -3,7 +3,7 @@ * parse_node.c * various routines that make nodes for querytrees * - * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -15,6 +15,7 @@ #include "postgres.h" #include "access/heapam.h" +#include "access/htup_details.h" #include "catalog/pg_type.h" #include "mb/pg_wchar.h" #include "nodes/makefuncs.h" @@ -98,8 +99,8 @@ free_parsestate(ParseState *pstate) * is a dummy (always 0, in fact). * * The locations stored in raw parsetrees are byte offsets into the source - * string. We have to convert them to 1-based character indexes for reporting - * to clients. (We do things this way to avoid unnecessary overhead in the + * string. We have to convert them to 1-based character indexes for reporting + * to clients. (We do things this way to avoid unnecessary overhead in the * normal non-error case: computing character indexes would be much more * expensive than storing token offsets.) */ @@ -128,7 +129,7 @@ parser_errposition(ParseState *pstate, int location) * Sometimes the parser calls functions that aren't part of the parser * subsystem and can't reasonably be passed a ParseState; yet we would * like any errors thrown in those functions to be tagged with a parse - * error location. Use this function to set up an error context stack + * error location. Use this function to set up an error context stack * entry that will accomplish that. Usage pattern: * * declare a local variable "ParseCallbackState pcbstate" @@ -144,10 +145,10 @@ setup_parser_errposition_callback(ParseCallbackState *pcbstate, /* Setup error traceback support for ereport() */ pcbstate->pstate = pstate; pcbstate->location = location; - pcbstate->errcontext.callback = pcb_error_callback; - pcbstate->errcontext.arg = (void *) pcbstate; - pcbstate->errcontext.previous = error_context_stack; - error_context_stack = &pcbstate->errcontext; + pcbstate->errcallback.callback = pcb_error_callback; + pcbstate->errcallback.arg = (void *) pcbstate; + pcbstate->errcallback.previous = error_context_stack; + error_context_stack = &pcbstate->errcallback; } /* @@ -157,7 +158,7 @@ void cancel_parser_errposition_callback(ParseCallbackState *pcbstate) { /* Pop the error context stack */ - error_context_stack = pcbstate->errcontext.previous; + error_context_stack = pcbstate->errcallback.previous; } /* @@ -220,11 +221,23 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod) * If the input is a domain, smash to base type, and extract the actual * typmod to be applied to the base type. Subscripting a domain is an * operation that necessarily works on the base array type, not the domain - * itself. (Note that we provide no method whereby the creator of a + * itself. (Note that we provide no method whereby the creator of a * domain over an array type could hide its ability to be subscripted.) */ *arrayType = getBaseTypeAndTypmod(*arrayType, arrayTypmod); + /* + * We treat int2vector and oidvector as though they were domains over + * int2[] and oid[]. This is needed because array slicing could create an + * array that doesn't satisfy the dimensionality constraints of the + * xxxvector type; so we want the result of a slice operation to be + * considered to be of the more general type. + */ + if (*arrayType == INT2VECTOROID) + *arrayType = INT2ARRAYOID; + else if (*arrayType == OIDVECTOROID) + *arrayType = OIDARRAYOID; + /* Get the type tuple for the array */ type_tuple_array = SearchSysCache1(TYPEOID, ObjectIdGetDatum(*arrayType)); if (!HeapTupleIsValid(type_tuple_array)) @@ -256,12 +269,13 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod) * * In an array assignment, we are given a destination array value plus a * source value that is to be assigned to a single element or a slice of - * that array. We produce an expression that represents the new array value + * that array. We produce an expression that represents the new array value * with the source data inserted into the right part of the array. * * For both cases, if the source array is of a domain-over-array type, * the result is of the base array type or its element type; essentially, * we must fold a domain to its base type before applying subscripting. + * (Note that int2vector and oidvector are treated as domains here.) * * pstate Parse state * arrayBase Already-transformed expression for the array as a whole @@ -328,7 +342,7 @@ transformArraySubscripts(ParseState *pstate, { if (ai->lidx) { - subexpr = transformExpr(pstate, ai->lidx); + subexpr = transformExpr(pstate, ai->lidx, pstate->p_expr_kind); /* If it's not int4 already, try to coerce */ subexpr = coerce_to_target_type(pstate, subexpr, exprType(subexpr), @@ -355,7 +369,7 @@ transformArraySubscripts(ParseState *pstate, } lowerIndexpr = lappend(lowerIndexpr, subexpr); } - subexpr = transformExpr(pstate, ai->uidx); + subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind); /* If it's not int4 already, try to coerce */ subexpr = coerce_to_target_type(pstate, subexpr, exprType(subexpr), diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c index bb5f04031d..b65b632f17 100644 --- a/src/backend/parser/parse_oper.c +++ b/src/backend/parser/parse_oper.c @@ -3,7 +3,7 @@ * parse_oper.c * handle operator things for parser * - * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -15,6 +15,7 @@ #include "postgres.h" +#include "access/htup_details.h" #include "catalog/pg_operator.h" #include "catalog/pg_type.h" #include "lib/stringinfo.h" @@ -147,12 +148,12 @@ LookupOperNameTypeNames(ParseState *pstate, List *opername, if (oprleft == NULL) leftoid = InvalidOid; else - leftoid = typenameTypeId(pstate, oprleft); + leftoid = LookupTypeNameOid(pstate, oprleft, noError); if (oprright == NULL) rightoid = InvalidOid; else - rightoid = typenameTypeId(pstate, oprright); + rightoid = LookupTypeNameOid(pstate, oprright, noError); return LookupOperName(pstate, opername, leftoid, rightoid, noError, location); @@ -406,7 +407,7 @@ oper(ParseState *pstate, List *opname, Oid ltypeId, Oid rtypeId, FuncCandidateList clist; /* Get binary operators of given name */ - clist = OpernameGetCandidates(opname, 'b'); + clist = OpernameGetCandidates(opname, 'b', false); /* No operators found? Then fail... */ if (clist != NULL) @@ -446,7 +447,7 @@ oper(ParseState *pstate, List *opname, Oid ltypeId, Oid rtypeId, * * This is tighter than oper() because it will not return an operator that * requires coercion of the input datatypes (but binary-compatible operators - * are accepted). Otherwise, the semantics are the same. + * are accepted). Otherwise, the semantics are the same. */ Operator compatible_oper(ParseState *pstate, List *op, Oid arg1, Oid arg2, @@ -552,7 +553,7 @@ right_oper(ParseState *pstate, List *op, Oid arg, bool noError, int location) FuncCandidateList clist; /* Get postfix operators of given name */ - clist = OpernameGetCandidates(op, 'r'); + clist = OpernameGetCandidates(op, 'r', false); /* No operators found? Then fail... */ if (clist != NULL) @@ -630,7 +631,7 @@ left_oper(ParseState *pstate, List *op, Oid arg, bool noError, int location) FuncCandidateList clist; /* Get prefix operators of given name */ - clist = OpernameGetCandidates(op, 'l'); + clist = OpernameGetCandidates(op, 'l', false); /* No operators found? Then fail... */ if (clist != NULL) @@ -979,7 +980,7 @@ make_scalar_array_op(ParseState *pstate, List *opname, * mapping is pretty expensive to compute, especially for ambiguous operators; * this is mainly because there are a *lot* of instances of popular operator * names such as "=", and we have to check each one to see which is the - * best match. So once we have identified the correct mapping, we save it + * best match. So once we have identified the correct mapping, we save it * in a cache that need only be flushed on pg_operator or pg_cast change. * (pg_cast must be considered because changes in the set of implicit casts * affect the set of applicable operators for any given input datatype.) @@ -1026,7 +1027,7 @@ make_oper_cache_key(OprCacheKey *key, List *opname, Oid ltypeId, Oid rtypeId) if (schemaname) { /* search only in exact schema given */ - key->search_path[0] = LookupExplicitNamespace(schemaname); + key->search_path[0] = LookupExplicitNamespace(schemaname, false); } else { diff --git a/src/backend/parser/parse_param.c b/src/backend/parser/parse_param.c index cfe72629ea..41b755a1fa 100644 --- a/src/backend/parser/parse_param.c +++ b/src/backend/parser/parse_param.c @@ -12,7 +12,7 @@ * Note that other approaches to parameters are possible using the parser * hooks defined in ParseState. * - * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -57,6 +57,7 @@ static Node *variable_coerce_param_hook(ParseState *pstate, Param *param, Oid targetTypeId, int32 targetTypeMod, int location); static bool check_parameter_resolution_walker(Node *node, ParseState *pstate); +static bool query_contains_extern_params_walker(Node *node, void *context); /* @@ -255,7 +256,7 @@ variable_coerce_param_hook(ParseState *pstate, Param *param, * of parsing with parse_variable_parameters. * * Note: this code intentionally does not check that all parameter positions - * were used, nor that all got non-UNKNOWN types assigned. Caller of parser + * were used, nor that all got non-UNKNOWN types assigned. Caller of parser * should enforce that if it's important. */ void @@ -316,3 +317,38 @@ check_parameter_resolution_walker(Node *node, ParseState *pstate) return expression_tree_walker(node, check_parameter_resolution_walker, (void *) pstate); } + +/* + * Check to see if a fully-parsed query tree contains any PARAM_EXTERN Params. + */ +bool +query_contains_extern_params(Query *query) +{ + return query_tree_walker(query, + query_contains_extern_params_walker, + NULL, 0); +} + +static bool +query_contains_extern_params_walker(Node *node, void *context) +{ + if (node == NULL) + return false; + if (IsA(node, Param)) + { + Param *param = (Param *) node; + + if (param->paramkind == PARAM_EXTERN) + return true; + return false; + } + if (IsA(node, Query)) + { + /* Recurse into RTE subquery or not-yet-planned sublink subquery */ + return query_tree_walker((Query *) node, + query_contains_extern_params_walker, + context, 0); + } + return expression_tree_walker(node, query_contains_extern_params_walker, + context); +} diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 4b4cc2cae6..d8fbd640a2 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -8,7 +8,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Portions Copyright (c) 2012-2014, TransLattice, Inc. - * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -21,6 +21,7 @@ #include <ctype.h> +#include "access/htup_details.h" #include "access/sysattr.h" #include "catalog/heap.h" #include "catalog/namespace.h" @@ -48,6 +49,8 @@ static RangeTblEntry *scanNameSpaceForRefname(ParseState *pstate, const char *refname, int location); static RangeTblEntry *scanNameSpaceForRelid(ParseState *pstate, Oid relid, int location); +static void check_lateral_ref_ok(ParseState *pstate, ParseNamespaceItem *nsitem, + int location); static void markRTEForSelectPriv(ParseState *pstate, RangeTblEntry *rte, int rtindex, AttrNumber col); static void expandRelation(Oid relid, Alias *eref, @@ -55,12 +58,15 @@ static void expandRelation(Oid relid, Alias *eref, int location, bool include_dropped, List **colnames, List **colvars); static void expandTupleDesc(TupleDesc tupdesc, Alias *eref, + int count, int offset, int rtindex, int sublevels_up, int location, bool include_dropped, List **colnames, List **colvars); #ifndef PGXC static int specialAttNum(const char *attname); #endif +static bool isQueryUsingTempRelation_walker(Node *node, void *context); + /* * refnameRangeTblEntry @@ -79,9 +85,9 @@ static int specialAttNum(const char *attname); * * A qualified refname (schemaname != NULL) can only match a relation RTE * that (a) has no alias and (b) is for the same relation identified by - * schemaname.refname. In this case we convert schemaname.refname to a + * schemaname.refname. In this case we convert schemaname.refname to a * relation OID and search by relid, rather than by alias name. This is - * peculiar, but it's what SQL92 says to do. + * peculiar, but it's what SQL says to do. */ RangeTblEntry * refnameRangeTblEntry(ParseState *pstate, @@ -141,6 +147,18 @@ refnameRangeTblEntry(ParseState *pstate, * Search the query's table namespace for an RTE matching the * given unqualified refname. Return the RTE if a unique match, or NULL * if no match. Raise error if multiple matches. + * + * Note: it might seem that we shouldn't have to worry about the possibility + * of multiple matches; after all, the SQL standard disallows duplicate table + * aliases within a given SELECT level. Historically, however, Postgres has + * been laxer than that. For example, we allow + * SELECT ... FROM tab1 x CROSS JOIN (tab2 x CROSS JOIN tab3 y) z + * on the grounds that the aliased join (z) hides the aliases within it, + * therefore there is no conflict between the two RTEs named "x". However, + * if tab3 is a LATERAL subquery, then from within the subquery both "x"es + * are visible. Rather than rejecting queries that used to work, we allow + * this situation, and complain only if there's actually an ambiguous + * reference to "x". */ static RangeTblEntry * scanNameSpaceForRefname(ParseState *pstate, const char *refname, int location) @@ -148,9 +166,17 @@ scanNameSpaceForRefname(ParseState *pstate, const char *refname, int location) RangeTblEntry *result = NULL; ListCell *l; - foreach(l, pstate->p_relnamespace) + foreach(l, pstate->p_namespace) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l); + RangeTblEntry *rte = nsitem->p_rte; + + /* Ignore columns-only items */ + if (!nsitem->p_rel_visible) + continue; + /* If not inside LATERAL, ignore lateral-only items */ + if (nsitem->p_lateral_only && !pstate->p_lateral_active) + continue; if (strcmp(rte->eref->aliasname, refname) == 0) { @@ -160,6 +186,7 @@ scanNameSpaceForRefname(ParseState *pstate, const char *refname, int location) errmsg("table reference \"%s\" is ambiguous", refname), parser_errposition(pstate, location))); + check_lateral_ref_ok(pstate, nsitem, location); result = rte; } } @@ -168,9 +195,8 @@ scanNameSpaceForRefname(ParseState *pstate, const char *refname, int location) /* * Search the query's table namespace for a relation RTE matching the - * given relation OID. Return the RTE if a unique match, or NULL - * if no match. Raise error if multiple matches (which shouldn't - * happen if the namespace was checked correctly when it was created). + * given relation OID. Return the RTE if a unique match, or NULL + * if no match. Raise error if multiple matches. * * See the comments for refnameRangeTblEntry to understand why this * acts the way it does. @@ -181,9 +207,17 @@ scanNameSpaceForRelid(ParseState *pstate, Oid relid, int location) RangeTblEntry *result = NULL; ListCell *l; - foreach(l, pstate->p_relnamespace) + foreach(l, pstate->p_namespace) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l); + RangeTblEntry *rte = nsitem->p_rte; + + /* Ignore columns-only items */ + if (!nsitem->p_rel_visible) + continue; + /* If not inside LATERAL, ignore lateral-only items */ + if (nsitem->p_lateral_only && !pstate->p_lateral_active) + continue; /* yes, the test for alias == NULL should be there... */ if (rte->rtekind == RTE_RELATION && @@ -196,6 +230,7 @@ scanNameSpaceForRelid(ParseState *pstate, Oid relid, int location) errmsg("table reference %u is ambiguous", relid), parser_errposition(pstate, location))); + check_lateral_ref_ok(pstate, nsitem, location); result = rte; } } @@ -258,13 +293,13 @@ isFutureCTE(ParseState *pstate, const char *refname) } /* - * searchRangeTable + * searchRangeTableForRel * See if any RangeTblEntry could possibly match the RangeVar. * If so, return a pointer to the RangeTblEntry; else return NULL. * * This is different from refnameRangeTblEntry in that it considers every * entry in the ParseState's rangetable(s), not only those that are currently - * visible in the p_relnamespace lists. This behavior is invalid per the SQL + * visible in the p_namespace list(s). This behavior is invalid per the SQL * spec, and it may give ambiguous results (there might be multiple equally * valid matches, but only one will be returned). This must be used ONLY * as a heuristic in giving suitable error messages. See errorMissingRTE. @@ -273,7 +308,7 @@ isFutureCTE(ParseState *pstate, const char *refname) * and matches on alias. */ static RangeTblEntry * -searchRangeTable(ParseState *pstate, RangeVar *relation) +searchRangeTableForRel(ParseState *pstate, RangeVar *relation) { const char *refname = relation->relname; Oid relId = InvalidOid; @@ -287,8 +322,8 @@ searchRangeTable(ParseState *pstate, RangeVar *relation) * relation. * * NB: It's not critical that RangeVarGetRelid return the correct answer - * here in the face of concurrent DDL. If it doesn't, the worst case - * scenario is a less-clear error message. Also, the tables involved in + * here in the face of concurrent DDL. If it doesn't, the worst case + * scenario is a less-clear error message. Also, the tables involved in * the query are already locked, which reduces the number of cases in * which surprising behavior can occur. So we do the name lookup * unlocked. @@ -326,15 +361,19 @@ searchRangeTable(ParseState *pstate, RangeVar *relation) } /* - * Check for relation-name conflicts between two relnamespace lists. + * Check for relation-name conflicts between two namespace lists. * Raise an error if any is found. * * Note: we assume that each given argument does not contain conflicts * itself; we just want to know if the two can be merged together. * - * Per SQL92, two alias-less plain relation RTEs do not conflict even if + * Per SQL, two alias-less plain relation RTEs do not conflict even if * they have the same eref->aliasname (ie, same relation name), if they * are for different relation OIDs (implying they are in different schemas). + * + * We ignore the lateral-only flags in the namespace items: the lists must + * not conflict, even when all items are considered visible. However, + * columns-only items should be ignored. */ void checkNameSpaceConflicts(ParseState *pstate, List *namespace1, @@ -344,20 +383,27 @@ checkNameSpaceConflicts(ParseState *pstate, List *namespace1, foreach(l1, namespace1) { - RangeTblEntry *rte1 = (RangeTblEntry *) lfirst(l1); + ParseNamespaceItem *nsitem1 = (ParseNamespaceItem *) lfirst(l1); + RangeTblEntry *rte1 = nsitem1->p_rte; const char *aliasname1 = rte1->eref->aliasname; ListCell *l2; + if (!nsitem1->p_rel_visible) + continue; + foreach(l2, namespace2) { - RangeTblEntry *rte2 = (RangeTblEntry *) lfirst(l2); + ParseNamespaceItem *nsitem2 = (ParseNamespaceItem *) lfirst(l2); + RangeTblEntry *rte2 = nsitem2->p_rte; + if (!nsitem2->p_rel_visible) + continue; if (strcmp(rte2->eref->aliasname, aliasname1) != 0) continue; /* definitely no conflict */ if (rte1->rtekind == RTE_RELATION && rte1->alias == NULL && rte2->rtekind == RTE_RELATION && rte2->alias == NULL && rte1->relid != rte2->relid) - continue; /* no conflict per SQL92 rule */ + continue; /* no conflict per SQL rule */ ereport(ERROR, (errcode(ERRCODE_DUPLICATE_ALIAS), errmsg("table name \"%s\" specified more than once", @@ -367,8 +413,39 @@ checkNameSpaceConflicts(ParseState *pstate, List *namespace1, } /* + * Complain if a namespace item is currently disallowed as a LATERAL reference. + * This enforces both SQL:2008's rather odd idea of what to do with a LATERAL + * reference to the wrong side of an outer join, and our own prohibition on + * referencing the target table of an UPDATE or DELETE as a lateral reference + * in a FROM/USING clause. + * + * Convenience subroutine to avoid multiple copies of a rather ugly ereport. + */ +static void +check_lateral_ref_ok(ParseState *pstate, ParseNamespaceItem *nsitem, + int location) +{ + if (nsitem->p_lateral_only && !nsitem->p_lateral_ok) + { + /* SQL:2008 demands this be an error, not an invisible item */ + RangeTblEntry *rte = nsitem->p_rte; + char *refname = rte->eref->aliasname; + + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmsg("invalid reference to FROM-clause entry for table \"%s\"", + refname), + (rte == pstate->p_target_rangetblentry) ? + errhint("There is an entry for table \"%s\", but it cannot be referenced from this part of the query.", + refname) : + errdetail("The combining JOIN type must be INNER or LEFT for a LATERAL reference."), + parser_errposition(pstate, location))); + } +} + +/* * given an RTE, return RT index (starting with 1) of the entry, - * and optionally get its nesting depth (0 = current). If sublevels_up + * and optionally get its nesting depth (0 = current). If sublevels_up * is NULL, only consider rels at the current nesting level. * Raises error if RTE not found. */ @@ -519,6 +596,16 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname, { /* quick check to see if name could be a system column */ attnum = specialAttNum(colname); + + /* In constraint check, no system column is allowed except tableOid */ + if (pstate->p_expr_kind == EXPR_KIND_CHECK_CONSTRAINT && + attnum < InvalidAttrNumber && attnum != TableOidAttributeNumber) + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmsg("system column \"%s\" reference in check constraint is invalid", + colname), + parser_errposition(pstate, location))); + if (attnum != InvalidAttrNumber) { /* now check to see if column actually is defined */ @@ -555,11 +642,19 @@ colNameToVar(ParseState *pstate, char *colname, bool localonly, { ListCell *l; - foreach(l, pstate->p_varnamespace) + foreach(l, pstate->p_namespace) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l); + RangeTblEntry *rte = nsitem->p_rte; Node *newresult; + /* Ignore table-only items */ + if (!nsitem->p_cols_visible) + continue; + /* If not inside LATERAL, ignore lateral-only items */ + if (nsitem->p_lateral_only && !pstate->p_lateral_active) + continue; + /* use orig_pstate here to get the right sublevels_up */ newresult = scanRTEForColumn(orig_pstate, rte, colname, location); @@ -570,7 +665,8 @@ colNameToVar(ParseState *pstate, char *colname, bool localonly, (errcode(ERRCODE_AMBIGUOUS_COLUMN), errmsg("column reference \"%s\" is ambiguous", colname), - parser_errposition(orig_pstate, location))); + parser_errposition(pstate, location))); + check_lateral_ref_ok(pstate, nsitem, location); result = newresult; } } @@ -585,6 +681,40 @@ colNameToVar(ParseState *pstate, char *colname, bool localonly, } /* + * searchRangeTableForCol + * See if any RangeTblEntry could possibly provide the given column name. + * If so, return a pointer to the RangeTblEntry; else return NULL. + * + * This is different from colNameToVar in that it considers every entry in + * the ParseState's rangetable(s), not only those that are currently visible + * in the p_namespace list(s). This behavior is invalid per the SQL spec, + * and it may give ambiguous results (there might be multiple equally valid + * matches, but only one will be returned). This must be used ONLY as a + * heuristic in giving suitable error messages. See errorMissingColumn. + */ +static RangeTblEntry * +searchRangeTableForCol(ParseState *pstate, char *colname, int location) +{ + ParseState *orig_pstate = pstate; + + while (pstate != NULL) + { + ListCell *l; + + foreach(l, pstate->p_rtable) + { + RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + + if (scanRTEForColumn(orig_pstate, rte, colname, location)) + return rte; + } + + pstate = pstate->parentParseState; + } + return NULL; +} + +/* * markRTEForSelectPriv * Mark the specified column of an RTE as requiring SELECT privilege * @@ -686,14 +816,15 @@ markRTEForSelectPriv(ParseState *pstate, RangeTblEntry *rte, * The aliasvar could be either a Var or a COALESCE expression, * but in the latter case we should already have marked the two * referent variables as being selected, due to their use in the - * JOIN clause. So we need only be concerned with the simple Var - * case. + * JOIN clause. So we need only be concerned with the Var case. + * But we do need to drill down through implicit coercions. */ Var *aliasvar; Assert(col > 0 && col <= list_length(rte->joinaliasvars)); aliasvar = (Var *) list_nth(rte->joinaliasvars, col - 1); - if (IsA(aliasvar, Var)) + aliasvar = (Var *) strip_implicit_coercions((Node *) aliasvar); + if (aliasvar && IsA(aliasvar, Var)) markVarForSelectPriv(pstate, aliasvar, NULL); } } @@ -722,8 +853,7 @@ markVarForSelectPriv(ParseState *pstate, Var *var, RangeTblEntry *rte) /* * buildRelationAliases * Construct the eref column name list for a relation RTE. - * This code is also used for the case of a function RTE returning - * a named composite type. + * This code is also used for function RTEs. * * tupdesc: the physical column information * alias: the user-supplied alias, or NULL if none @@ -732,6 +862,8 @@ markVarForSelectPriv(ParseState *pstate, Var *var, RangeTblEntry *rte) * eref->colnames is filled in. Also, alias->colnames is rebuilt to insert * empty strings for any dropped columns, so that it will be one-to-one with * physical column numbers. + * + * It is an error for there to be more aliases present than required. */ static void buildRelationAliases(TupleDesc tupdesc, Alias *alias, Alias *eref) @@ -795,37 +927,24 @@ buildRelationAliases(TupleDesc tupdesc, Alias *alias, Alias *eref) } /* - * buildScalarFunctionAlias - * Construct the eref column name list for a function RTE, + * chooseScalarFunctionAlias + * Select the column alias for a function in a function RTE, * when the function returns a scalar type (not composite or RECORD). * * funcexpr: transformed expression tree for the function call - * funcname: function name (used only for error message) - * alias: the user-supplied alias, or NULL if none - * eref: the eref Alias to store column names in + * funcname: function name (as determined by FigureColname) + * alias: the user-supplied alias for the RTE, or NULL if none + * nfuncs: the number of functions appearing in the function RTE * - * eref->colnames is filled in. + * Note that the name we choose might be overridden later, if the user-given + * alias includes column alias names. That's of no concern here. */ -static void -buildScalarFunctionAlias(Node *funcexpr, char *funcname, - Alias *alias, Alias *eref) +static char * +chooseScalarFunctionAlias(Node *funcexpr, char *funcname, + Alias *alias, int nfuncs) { char *pname; - Assert(eref->colnames == NIL); - - /* Use user-specified column alias if there is one. */ - if (alias && alias->colnames != NIL) - { - if (list_length(alias->colnames) != 1) - ereport(ERROR, - (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("too many column aliases specified for function %s", - funcname))); - eref->colnames = copyObject(alias->colnames); - return; - } - /* * If the expression is a simple function call, and the function has a * single OUT parameter that is named, use the parameter's name. @@ -834,17 +953,21 @@ buildScalarFunctionAlias(Node *funcexpr, char *funcname, { pname = get_func_result_name(((FuncExpr *) funcexpr)->funcid); if (pname) - { - eref->colnames = list_make1(makeString(pname)); - return; - } + return pname; } /* - * Otherwise use the previously-determined alias (not necessarily the - * function name!) + * If there's just one function in the RTE, and the user gave an RTE alias + * name, use that name. (This makes FROM func() AS foo use "foo" as the + * column name as well as the table alias.) + */ + if (nfuncs == 1 && alias) + return alias->aliasname; + + /* + * Otherwise use the function name. */ - eref->colnames = list_make1(makeString(eref->aliasname)); + return funcname; } /* @@ -926,7 +1049,7 @@ addRangeTableEntry(ParseState *pstate, /* * Get the rel's OID. This access also ensures that we have an up-to-date - * relcache entry for the rel. Since this is typically the first access + * relcache entry for the rel. Since this is typically the first access * to a rel in a statement, be careful to get the right access level * depending on whether we're doing SELECT FOR UPDATE/SHARE. */ @@ -998,16 +1121,13 @@ addRangeTableEntry(ParseState *pstate, */ heap_close(rel, NoLock); - /*---------- - * Flags: - * - this RTE should be expanded to include descendant tables, - * - this RTE is in the FROM clause, - * - this RTE should be checked for appropriate access rights. + /* + * Set flags and access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. - *---------- */ + rte->lateral = false; rte->inh = inh; rte->inFromCl = inFromCl; @@ -1079,16 +1199,13 @@ addRangeTableEntryForRelation(ParseState *pstate, rte->eref = makeAlias(refname, NIL); buildRelationAliases(rel->rd_att, alias, rte->eref); - /*---------- - * Flags: - * - this RTE should be expanded to include descendant tables, - * - this RTE is in the FROM clause, - * - this RTE should be checked for appropriate access rights. + /* + * Set flags and access permissions. * * The initial default on access checks is always check-for-READ-access, * which is the right thing for all except target tables. - *---------- */ + rte->lateral = false; rte->inh = inh; rte->inFromCl = inFromCl; @@ -1117,6 +1234,7 @@ RangeTblEntry * addRangeTableEntryForSubquery(ParseState *pstate, Query *subquery, Alias *alias, + bool lateral, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); @@ -1160,15 +1278,12 @@ addRangeTableEntryForSubquery(ParseState *pstate, rte->eref = eref; - /*---------- - * Flags: - * - this RTE should be expanded to include descendant tables, - * - this RTE is in the FROM clause, - * - this RTE should be checked for appropriate access rights. + /* + * Set flags and access permissions. * * Subqueries are never checked for access rights. - *---------- */ + rte->lateral = lateral; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; @@ -1188,126 +1303,241 @@ addRangeTableEntryForSubquery(ParseState *pstate, } /* - * Add an entry for a function to the pstate's range table (p_rtable). + * Add an entry for a function (or functions) to the pstate's range table + * (p_rtable). * * This is just like addRangeTableEntry() except that it makes a function RTE. */ RangeTblEntry * addRangeTableEntryForFunction(ParseState *pstate, - char *funcname, - Node *funcexpr, + List *funcnames, + List *funcexprs, + List *coldeflists, RangeFunction *rangefunc, + bool lateral, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); - TypeFuncClass functypclass; - Oid funcrettype; - TupleDesc tupdesc; Alias *alias = rangefunc->alias; - List *coldeflist = rangefunc->coldeflist; Alias *eref; + char *aliasname; + int nfuncs = list_length(funcexprs); + TupleDesc *functupdescs; + TupleDesc tupdesc; + ListCell *lc1, + *lc2, + *lc3; + int i; + int j; + int funcno; + int natts, + totalatts; rte->rtekind = RTE_FUNCTION; rte->relid = InvalidOid; rte->subquery = NULL; - rte->funcexpr = funcexpr; - rte->funccoltypes = NIL; - rte->funccoltypmods = NIL; - rte->funccolcollations = NIL; + rte->functions = NIL; /* we'll fill this list below */ + rte->funcordinality = rangefunc->ordinality; rte->alias = alias; - eref = makeAlias(alias ? alias->aliasname : funcname, NIL); - rte->eref = eref; - - /* - * Now determine if the function returns a simple or composite type. - */ - functypclass = get_expr_result_type(funcexpr, - &funcrettype, - &tupdesc); - /* - * A coldeflist is required if the function returns RECORD and hasn't got - * a predetermined record type, and is prohibited otherwise. + * Choose the RTE alias name. We default to using the first function's + * name even when there's more than one; which is maybe arguable but beats + * using something constant like "table". */ - if (coldeflist != NIL) - { - if (functypclass != TYPEFUNC_RECORD) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("a column definition list is only allowed for functions returning \"record\""), - parser_errposition(pstate, exprLocation(funcexpr)))); - } + if (alias) + aliasname = alias->aliasname; else + aliasname = linitial(funcnames); + + eref = makeAlias(aliasname, NIL); + rte->eref = eref; + + /* Process each function ... */ + functupdescs = (TupleDesc *) palloc(nfuncs * sizeof(TupleDesc)); + + totalatts = 0; + funcno = 0; + forthree(lc1, funcexprs, lc2, funcnames, lc3, coldeflists) { - if (functypclass == TYPEFUNC_RECORD) + Node *funcexpr = (Node *) lfirst(lc1); + char *funcname = (char *) lfirst(lc2); + List *coldeflist = (List *) lfirst(lc3); + RangeTblFunction *rtfunc = makeNode(RangeTblFunction); + TypeFuncClass functypclass; + Oid funcrettype; + + /* Initialize RangeTblFunction node */ + rtfunc->funcexpr = funcexpr; + rtfunc->funccolnames = NIL; + rtfunc->funccoltypes = NIL; + rtfunc->funccoltypmods = NIL; + rtfunc->funccolcollations = NIL; + rtfunc->funcparams = NULL; /* not set until planning */ + + /* + * Now determine if the function returns a simple or composite type. + */ + functypclass = get_expr_result_type(funcexpr, + &funcrettype, + &tupdesc); + + /* + * A coldeflist is required if the function returns RECORD and hasn't + * got a predetermined record type, and is prohibited otherwise. + */ + if (coldeflist != NIL) + { + if (functypclass != TYPEFUNC_RECORD) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("a column definition list is only allowed for functions returning \"record\""), + parser_errposition(pstate, + exprLocation((Node *) coldeflist)))); + } + else + { + if (functypclass == TYPEFUNC_RECORD) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("a column definition list is required for functions returning \"record\""), + parser_errposition(pstate, exprLocation(funcexpr)))); + } + + if (functypclass == TYPEFUNC_COMPOSITE) + { + /* Composite data type, e.g. a table's row type */ + Assert(tupdesc); + } + else if (functypclass == TYPEFUNC_SCALAR) + { + /* Base data type, i.e. scalar */ + tupdesc = CreateTemplateTupleDesc(1, false); + TupleDescInitEntry(tupdesc, + (AttrNumber) 1, + chooseScalarFunctionAlias(funcexpr, funcname, + alias, nfuncs), + funcrettype, + -1, + 0); + } + else if (functypclass == TYPEFUNC_RECORD) + { + ListCell *col; + + /* + * Use the column definition list to construct a tupdesc and fill + * in the RangeTblFunction's lists. + */ + tupdesc = CreateTemplateTupleDesc(list_length(coldeflist), false); + i = 1; + foreach(col, coldeflist) + { + ColumnDef *n = (ColumnDef *) lfirst(col); + char *attrname; + Oid attrtype; + int32 attrtypmod; + Oid attrcollation; + + attrname = n->colname; + if (n->typeName->setof) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("column \"%s\" cannot be declared SETOF", + attrname), + parser_errposition(pstate, n->location))); + typenameTypeIdAndMod(pstate, n->typeName, + &attrtype, &attrtypmod); + attrcollation = GetColumnDefCollation(pstate, n, attrtype); + TupleDescInitEntry(tupdesc, + (AttrNumber) i, + attrname, + attrtype, + attrtypmod, + 0); + TupleDescInitEntryCollation(tupdesc, + (AttrNumber) i, + attrcollation); + rtfunc->funccolnames = lappend(rtfunc->funccolnames, + makeString(pstrdup(attrname))); + rtfunc->funccoltypes = lappend_oid(rtfunc->funccoltypes, + attrtype); + rtfunc->funccoltypmods = lappend_int(rtfunc->funccoltypmods, + attrtypmod); + rtfunc->funccolcollations = lappend_oid(rtfunc->funccolcollations, + attrcollation); + + i++; + } + + /* + * Ensure that the coldeflist defines a legal set of names (no + * duplicates) and datatypes (no pseudo-types, for instance). + */ + CheckAttributeNamesTypes(tupdesc, RELKIND_COMPOSITE_TYPE, false); + } + else ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("a column definition list is required for functions returning \"record\""), + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("function \"%s\" in FROM has unsupported return type %s", + funcname, format_type_be(funcrettype)), parser_errposition(pstate, exprLocation(funcexpr)))); - } - if (functypclass == TYPEFUNC_COMPOSITE) - { - /* Composite data type, e.g. a table's row type */ - Assert(tupdesc); - /* Build the column alias list */ - buildRelationAliases(tupdesc, alias, eref); - } - else if (functypclass == TYPEFUNC_SCALAR) - { - /* Base data type, i.e. scalar */ - buildScalarFunctionAlias(funcexpr, funcname, alias, eref); + /* Finish off the RangeTblFunction and add it to the RTE's list */ + rtfunc->funccolcount = tupdesc->natts; + rte->functions = lappend(rte->functions, rtfunc); + + /* Save the tupdesc for use below */ + functupdescs[funcno] = tupdesc; + totalatts += tupdesc->natts; + funcno++; } - else if (functypclass == TYPEFUNC_RECORD) + + /* + * If there's more than one function, or we want an ordinality column, we + * have to produce a merged tupdesc. + */ + if (nfuncs > 1 || rangefunc->ordinality) { - ListCell *col; + if (rangefunc->ordinality) + totalatts++; - /* - * Use the column definition list to form the alias list and - * funccoltypes/funccoltypmods/funccolcollations lists. - */ - foreach(col, coldeflist) + /* Merge the tuple descs of each function into a composite one */ + tupdesc = CreateTemplateTupleDesc(totalatts, false); + natts = 0; + for (i = 0; i < nfuncs; i++) { - ColumnDef *n = (ColumnDef *) lfirst(col); - char *attrname; - Oid attrtype; - int32 attrtypmod; - Oid attrcollation; - - attrname = pstrdup(n->colname); - if (n->typeName->setof) - ereport(ERROR, - (errcode(ERRCODE_INVALID_TABLE_DEFINITION), - errmsg("column \"%s\" cannot be declared SETOF", - attrname), - parser_errposition(pstate, n->typeName->location))); - typenameTypeIdAndMod(pstate, n->typeName, &attrtype, &attrtypmod); - attrcollation = GetColumnDefCollation(pstate, n, attrtype); - eref->colnames = lappend(eref->colnames, makeString(attrname)); - rte->funccoltypes = lappend_oid(rte->funccoltypes, attrtype); - rte->funccoltypmods = lappend_int(rte->funccoltypmods, attrtypmod); - rte->funccolcollations = lappend_oid(rte->funccolcollations, - attrcollation); + for (j = 1; j <= functupdescs[i]->natts; j++) + TupleDescCopyEntry(tupdesc, ++natts, functupdescs[i], j); } + + /* Add the ordinality column if needed */ + if (rangefunc->ordinality) + TupleDescInitEntry(tupdesc, + (AttrNumber) ++natts, + "ordinality", + INT8OID, + -1, + 0); + + Assert(natts == totalatts); } else - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("function \"%s\" in FROM has unsupported return type %s", - funcname, format_type_be(funcrettype)), - parser_errposition(pstate, exprLocation(funcexpr)))); + { + /* We can just use the single function's tupdesc as-is */ + tupdesc = functupdescs[0]; + } - /*---------- - * Flags: - * - this RTE should be expanded to include descendant tables, - * - this RTE is in the FROM clause, - * - this RTE should be checked for appropriate access rights. + /* Use the tupdesc while assigning column aliases for the RTE */ + buildRelationAliases(tupdesc, alias, eref); + + /* + * Set flags and access permissions. * - * Functions are never checked for access rights (at least, not by - * the RTE permissions mechanism). - *---------- + * Functions are never checked for access rights (at least, not by the RTE + * permissions mechanism). */ + rte->lateral = lateral; rte->inh = false; /* never true for functions */ rte->inFromCl = inFromCl; @@ -1336,6 +1566,7 @@ addRangeTableEntryForValues(ParseState *pstate, List *exprs, List *collations, Alias *alias, + bool lateral, bool inFromCl) { RangeTblEntry *rte = makeNode(RangeTblEntry); @@ -1373,15 +1604,12 @@ addRangeTableEntryForValues(ParseState *pstate, rte->eref = eref; - /*---------- - * Flags: - * - this RTE should be expanded to include descendant tables, - * - this RTE is in the FROM clause, - * - this RTE should be checked for appropriate access rights. + /* + * Set flags and access permissions. * * Subqueries are never checked for access rights. - *---------- */ + rte->lateral = lateral; rte->inh = false; /* never true for values RTEs */ rte->inFromCl = inFromCl; @@ -1444,15 +1672,12 @@ addRangeTableEntryForJoin(ParseState *pstate, rte->eref = eref; - /*---------- - * Flags: - * - this RTE should be expanded to include descendant tables, - * - this RTE is in the FROM clause, - * - this RTE should be checked for appropriate access rights. + /* + * Set flags and access permissions. * * Joins are never checked for access rights. - *---------- */ + rte->lateral = false; rte->inh = false; /* never true for joins */ rte->inFromCl = inFromCl; @@ -1556,15 +1781,12 @@ addRangeTableEntryForCTE(ParseState *pstate, rte->eref = eref; - /*---------- - * Flags: - * - this RTE should be expanded to include descendant tables, - * - this RTE is in the FROM clause, - * - this RTE should be checked for appropriate access rights. + /* + * Set flags and access permissions. * * Subqueries are never checked for access rights. - *---------- */ + rte->lateral = false; rte->inh = false; /* never true for subqueries */ rte->inFromCl = inFromCl; @@ -1633,8 +1855,13 @@ isLockedRefname(ParseState *pstate, const char *refname) /* * Add the given RTE as a top-level entry in the pstate's join list - * and/or name space lists. (We assume caller has checked for any - * namespace conflicts.) + * and/or namespace list. (We assume caller has checked for any + * namespace conflicts.) The RTE is always marked as unconditionally + * visible, that is, not LATERAL-only. + * + * Note: some callers know that they can find the new ParseNamespaceItem + * at the end of the pstate->p_namespace list. This is a bit ugly but not + * worth complicating this function's signature for. */ void addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte, @@ -1649,10 +1876,18 @@ addRTEtoQuery(ParseState *pstate, RangeTblEntry *rte, rtr->rtindex = rtindex; pstate->p_joinlist = lappend(pstate->p_joinlist, rtr); } - if (addToRelNameSpace) - pstate->p_relnamespace = lappend(pstate->p_relnamespace, rte); - if (addToVarNameSpace) - pstate->p_varnamespace = lappend(pstate->p_varnamespace, rte); + if (addToRelNameSpace || addToVarNameSpace) + { + ParseNamespaceItem *nsitem; + + nsitem = (ParseNamespaceItem *) palloc(sizeof(ParseNamespaceItem)); + nsitem->p_rte = rte; + nsitem->p_rel_visible = addToRelNameSpace; + nsitem->p_cols_visible = addToVarNameSpace; + nsitem->p_lateral_only = false; + nsitem->p_lateral_ok = true; + pstate->p_namespace = lappend(pstate->p_namespace, nsitem); + } } /* @@ -1736,77 +1971,117 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, case RTE_FUNCTION: { /* Function RTE */ - TypeFuncClass functypclass; - Oid funcrettype; - TupleDesc tupdesc; - - functypclass = get_expr_result_type(rte->funcexpr, - &funcrettype, - &tupdesc); - if (functypclass == TYPEFUNC_COMPOSITE) - { - /* Composite data type, e.g. a table's row type */ - Assert(tupdesc); - expandTupleDesc(tupdesc, rte->eref, - rtindex, sublevels_up, location, - include_dropped, colnames, colvars); - } - else if (functypclass == TYPEFUNC_SCALAR) - { - /* Base data type, i.e. scalar */ - if (colnames) - *colnames = lappend(*colnames, - linitial(rte->eref->colnames)); + int atts_done = 0; + ListCell *lc; - if (colvars) + foreach(lc, rte->functions) + { + RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc); + TypeFuncClass functypclass; + Oid funcrettype; + TupleDesc tupdesc; + + functypclass = get_expr_result_type(rtfunc->funcexpr, + &funcrettype, + &tupdesc); + if (functypclass == TYPEFUNC_COMPOSITE) { - Var *varnode; - - varnode = makeVar(rtindex, 1, - funcrettype, -1, - exprCollation(rte->funcexpr), - sublevels_up); - varnode->location = location; - - *colvars = lappend(*colvars, varnode); + /* Composite data type, e.g. a table's row type */ + Assert(tupdesc); + expandTupleDesc(tupdesc, rte->eref, + rtfunc->funccolcount, atts_done, + rtindex, sublevels_up, location, + include_dropped, colnames, colvars); } - } - else if (functypclass == TYPEFUNC_RECORD) - { - if (colnames) - *colnames = copyObject(rte->eref->colnames); - if (colvars) + else if (functypclass == TYPEFUNC_SCALAR) { - ListCell *l1; - ListCell *l2; - ListCell *l3; - int attnum = 0; - - forthree(l1, rte->funccoltypes, - l2, rte->funccoltypmods, - l3, rte->funccolcollations) + /* Base data type, i.e. scalar */ + if (colnames) + *colnames = lappend(*colnames, + list_nth(rte->eref->colnames, + atts_done)); + + if (colvars) { - Oid attrtype = lfirst_oid(l1); - int32 attrtypmod = lfirst_int(l2); - Oid attrcollation = lfirst_oid(l3); Var *varnode; - attnum++; - varnode = makeVar(rtindex, - attnum, - attrtype, - attrtypmod, - attrcollation, + varnode = makeVar(rtindex, atts_done + 1, + funcrettype, -1, + exprCollation(rtfunc->funcexpr), sublevels_up); varnode->location = location; + *colvars = lappend(*colvars, varnode); } } + else if (functypclass == TYPEFUNC_RECORD) + { + if (colnames) + { + List *namelist; + + /* extract appropriate subset of column list */ + namelist = list_copy_tail(rte->eref->colnames, + atts_done); + namelist = list_truncate(namelist, + rtfunc->funccolcount); + *colnames = list_concat(*colnames, namelist); + } + + if (colvars) + { + ListCell *l1; + ListCell *l2; + ListCell *l3; + int attnum = atts_done; + + forthree(l1, rtfunc->funccoltypes, + l2, rtfunc->funccoltypmods, + l3, rtfunc->funccolcollations) + { + Oid attrtype = lfirst_oid(l1); + int32 attrtypmod = lfirst_int(l2); + Oid attrcollation = lfirst_oid(l3); + Var *varnode; + + attnum++; + varnode = makeVar(rtindex, + attnum, + attrtype, + attrtypmod, + attrcollation, + sublevels_up); + varnode->location = location; + *colvars = lappend(*colvars, varnode); + } + } + } + else + { + /* addRangeTableEntryForFunction should've caught this */ + elog(ERROR, "function in FROM has unsupported return type"); + } + atts_done += rtfunc->funccolcount; } - else + + /* Append the ordinality column if any */ + if (rte->funcordinality) { - /* addRangeTableEntryForFunction should've caught this */ - elog(ERROR, "function in FROM has unsupported return type"); + if (colnames) + *colnames = lappend(*colnames, + llast(rte->eref->colnames)); + + if (colvars) + { + Var *varnode = makeVar(rtindex, + atts_done + 1, + INT8OID, + -1, + InvalidOid, + sublevels_up); + + *colvars = lappend(*colvars, varnode); + } } } break; @@ -1870,10 +2145,10 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, * deleted columns in the join; but we have to check since * this routine is also used by the rewriter, and joins * found in stored rules might have join columns for - * since-deleted columns. This will be signaled by a NULL - * Const in the alias-vars list. + * since-deleted columns. This will be signaled by a null + * pointer in the alias-vars list. */ - if (IsA(avar, Const)) + if (avar == NULL) { if (include_dropped) { @@ -1881,8 +2156,16 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, *colnames = lappend(*colnames, makeString(pstrdup(""))); if (colvars) + { + /* + * Can't use join's column type here (it might + * be dropped!); but it doesn't really matter + * what type the Const claims to be. + */ *colvars = lappend(*colvars, - copyObject(avar)); + makeNullConst(INT4OID, -1, + InvalidOid)); + } } continue; } @@ -1967,7 +2250,8 @@ expandRelation(Oid relid, Alias *eref, int rtindex, int sublevels_up, /* Get the tupledesc and turn it over to expandTupleDesc */ rel = relation_open(relid, AccessShareLock); - expandTupleDesc(rel->rd_att, eref, rtindex, sublevels_up, + expandTupleDesc(rel->rd_att, eref, rel->rd_att->natts, 0, + rtindex, sublevels_up, location, include_dropped, colnames, colvars); relation_close(rel, AccessShareLock); @@ -1975,18 +2259,35 @@ expandRelation(Oid relid, Alias *eref, int rtindex, int sublevels_up, /* * expandTupleDesc -- expandRTE subroutine + * + * Generate names and/or Vars for the first "count" attributes of the tupdesc, + * and append them to colnames/colvars. "offset" is added to the varattno + * that each Var would otherwise have, and we also skip the first "offset" + * entries in eref->colnames. (These provisions allow use of this code for + * an individual composite-returning function in an RTE_FUNCTION RTE.) */ static void -expandTupleDesc(TupleDesc tupdesc, Alias *eref, +expandTupleDesc(TupleDesc tupdesc, Alias *eref, int count, int offset, int rtindex, int sublevels_up, int location, bool include_dropped, List **colnames, List **colvars) { - int maxattrs = tupdesc->natts; - int numaliases = list_length(eref->colnames); + ListCell *aliascell = list_head(eref->colnames); int varattno; - for (varattno = 0; varattno < maxattrs; varattno++) + if (colnames) + { + int i; + + for (i = 0; i < offset; i++) + { + if (aliascell) + aliascell = lnext(aliascell); + } + } + + Assert(count <= tupdesc->natts); + for (varattno = 0; varattno < count; varattno++) { Form_pg_attribute attr = tupdesc->attrs[varattno]; @@ -2006,6 +2307,8 @@ expandTupleDesc(TupleDesc tupdesc, Alias *eref, makeNullConst(INT4OID, -1, InvalidOid)); } } + if (aliascell) + aliascell = lnext(aliascell); continue; } @@ -2013,10 +2316,16 @@ expandTupleDesc(TupleDesc tupdesc, Alias *eref, { char *label; - if (varattno < numaliases) - label = strVal(list_nth(eref->colnames, varattno)); + if (aliascell) + { + label = strVal(lfirst(aliascell)); + aliascell = lnext(aliascell); + } else + { + /* If we run out of aliases, use the underlying name */ label = NameStr(attr->attname); + } *colnames = lappend(*colnames, makeString(pstrdup(label))); } @@ -2024,7 +2333,7 @@ expandTupleDesc(TupleDesc tupdesc, Alias *eref, { Var *varnode; - varnode = makeVar(rtindex, attr->attnum, + varnode = makeVar(rtindex, varattno + offset + 1, attr->atttypid, attr->atttypmod, attr->attcollation, sublevels_up); @@ -2188,62 +2497,93 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, case RTE_FUNCTION: { /* Function RTE */ - TypeFuncClass functypclass; - Oid funcrettype; - TupleDesc tupdesc; - - functypclass = get_expr_result_type(rte->funcexpr, - &funcrettype, - &tupdesc); + ListCell *lc; + int atts_done = 0; - if (functypclass == TYPEFUNC_COMPOSITE) + /* Identify which function covers the requested column */ + foreach(lc, rte->functions) { - /* Composite data type, e.g. a table's row type */ - Form_pg_attribute att_tup; + RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc); - Assert(tupdesc); - /* this is probably a can't-happen case */ - if (attnum < 1 || attnum > tupdesc->natts) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), - errmsg("column %d of relation \"%s\" does not exist", - attnum, - rte->eref->aliasname))); + if (attnum > atts_done && + attnum <= atts_done + rtfunc->funccolcount) + { + TypeFuncClass functypclass; + Oid funcrettype; + TupleDesc tupdesc; - att_tup = tupdesc->attrs[attnum - 1]; + attnum -= atts_done; /* now relative to this func */ + functypclass = get_expr_result_type(rtfunc->funcexpr, + &funcrettype, + &tupdesc); - /* - * If dropped column, pretend it ain't there. See notes - * in scanRTEForColumn. - */ - if (att_tup->attisdropped) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), - errmsg("column \"%s\" of relation \"%s\" does not exist", - NameStr(att_tup->attname), - rte->eref->aliasname))); - *vartype = att_tup->atttypid; - *vartypmod = att_tup->atttypmod; - *varcollid = att_tup->attcollation; + if (functypclass == TYPEFUNC_COMPOSITE) + { + /* Composite data type, e.g. a table's row type */ + Form_pg_attribute att_tup; + + Assert(tupdesc); + Assert(attnum <= tupdesc->natts); + att_tup = tupdesc->attrs[attnum - 1]; + + /* + * If dropped column, pretend it ain't there. See + * notes in scanRTEForColumn. + */ + if (att_tup->attisdropped) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" of relation \"%s\" does not exist", + NameStr(att_tup->attname), + rte->eref->aliasname))); + *vartype = att_tup->atttypid; + *vartypmod = att_tup->atttypmod; + *varcollid = att_tup->attcollation; + } + else if (functypclass == TYPEFUNC_SCALAR) + { + /* Base data type, i.e. scalar */ + *vartype = funcrettype; + *vartypmod = -1; + *varcollid = exprCollation(rtfunc->funcexpr); + } + else if (functypclass == TYPEFUNC_RECORD) + { + *vartype = list_nth_oid(rtfunc->funccoltypes, + attnum - 1); + *vartypmod = list_nth_int(rtfunc->funccoltypmods, + attnum - 1); + *varcollid = list_nth_oid(rtfunc->funccolcollations, + attnum - 1); + } + else + { + /* + * addRangeTableEntryForFunction should've caught + * this + */ + elog(ERROR, "function in FROM has unsupported return type"); + } + return; + } + atts_done += rtfunc->funccolcount; } - else if (functypclass == TYPEFUNC_SCALAR) + + /* If we get here, must be looking for the ordinality column */ + if (rte->funcordinality && attnum == atts_done + 1) { - /* Base data type, i.e. scalar */ - *vartype = funcrettype; + *vartype = INT8OID; *vartypmod = -1; - *varcollid = exprCollation(rte->funcexpr); - } - else if (functypclass == TYPEFUNC_RECORD) - { - *vartype = list_nth_oid(rte->funccoltypes, attnum - 1); - *vartypmod = list_nth_int(rte->funccoltypmods, attnum - 1); - *varcollid = list_nth_oid(rte->funccolcollations, attnum - 1); - } - else - { - /* addRangeTableEntryForFunction should've caught this */ - elog(ERROR, "function in FROM has unsupported return type"); + *varcollid = InvalidOid; + return; } + + /* this probably can't happen ... */ + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column %d of relation \"%s\" does not exist", + attnum, + rte->eref->aliasname))); } break; case RTE_VALUES: @@ -2271,6 +2611,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars)); aliasvar = (Node *) list_nth(rte->joinaliasvars, attnum - 1); + Assert(aliasvar != NULL); *vartype = exprType(aliasvar); *vartypmod = exprTypmod(aliasvar); *varcollid = exprCollation(aliasvar); @@ -2333,7 +2674,7 @@ get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum) * but one in a stored rule might contain columns that were * dropped from the underlying tables, if said columns are * nowhere explicitly referenced in the rule. This will be - * signaled to us by a NULL Const in the joinaliasvars list. + * signaled to us by a null pointer in the joinaliasvars list. */ Var *aliasvar; @@ -2342,42 +2683,63 @@ get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum) elog(ERROR, "invalid varattno %d", attnum); aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1); - result = IsA(aliasvar, Const); + result = (aliasvar == NULL); } break; case RTE_FUNCTION: { /* Function RTE */ - Oid funcrettype = exprType(rte->funcexpr); - Oid funcrelid = typeidTypeRelid(funcrettype); + ListCell *lc; + int atts_done = 0; - if (OidIsValid(funcrelid)) - { - /* - * Composite data type, i.e. a table's row type - * - * Same as ordinary relation RTE - */ - HeapTuple tp; - Form_pg_attribute att_tup; - - tp = SearchSysCache2(ATTNUM, - ObjectIdGetDatum(funcrelid), - Int16GetDatum(attnum)); - if (!HeapTupleIsValid(tp)) /* shouldn't happen */ - elog(ERROR, "cache lookup failed for attribute %d of relation %u", - attnum, funcrelid); - att_tup = (Form_pg_attribute) GETSTRUCT(tp); - result = att_tup->attisdropped; - ReleaseSysCache(tp); - } - else + /* + * Dropped attributes are only possible with functions that + * return named composite types. In such a case we have to + * look up the result type to see if it currently has this + * column dropped. So first, loop over the funcs until we + * find the one that covers the requested column. + */ + foreach(lc, rte->functions) { - /* - * Must be a base data type, i.e. scalar - */ - result = false; + RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc); + + if (attnum > atts_done && + attnum <= atts_done + rtfunc->funccolcount) + { + TypeFuncClass functypclass; + Oid funcrettype; + TupleDesc tupdesc; + + functypclass = get_expr_result_type(rtfunc->funcexpr, + &funcrettype, + &tupdesc); + if (functypclass == TYPEFUNC_COMPOSITE) + { + /* Composite data type, e.g. a table's row type */ + Form_pg_attribute att_tup; + + Assert(tupdesc); + Assert(attnum - atts_done <= tupdesc->natts); + att_tup = tupdesc->attrs[attnum - atts_done - 1]; + return att_tup->attisdropped; + } + /* Otherwise, it can't have any dropped columns */ + return false; + } + atts_done += rtfunc->funccolcount; } + + /* If we get here, must be looking for the ordinality column */ + if (rte->funcordinality && attnum == atts_done + 1) + return false; + + /* this probably can't happen ... */ + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column %d of relation \"%s\" does not exist", + attnum, + rte->eref->aliasname))); + result = false; /* keep compiler quiet */ } break; default: @@ -2570,10 +2932,10 @@ errorMissingRTE(ParseState *pstate, RangeVar *relation) /* * Check to see if there are any potential matches in the query's - * rangetable. (Note: cases involving a bad schema name in the RangeVar + * rangetable. (Note: cases involving a bad schema name in the RangeVar * will throw error immediately here. That seems OK.) */ - rte = searchRangeTable(pstate, relation); + rte = searchRangeTableForRel(pstate, relation); /* * If we found a match that has an alias and the alias is visible in the @@ -2610,3 +2972,91 @@ errorMissingRTE(ParseState *pstate, RangeVar *relation) relation->relname), parser_errposition(pstate, relation->location))); } + +/* + * Generate a suitable error about a missing column. + * + * Since this is a very common type of error, we work rather hard to + * produce a helpful message. + */ +void +errorMissingColumn(ParseState *pstate, + char *relname, char *colname, int location) +{ + RangeTblEntry *rte; + + /* + * If relname was given, just play dumb and report it. (In practice, a + * bad qualification name should end up at errorMissingRTE, not here, so + * no need to work hard on this case.) + */ + if (relname) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column %s.%s does not exist", relname, colname), + parser_errposition(pstate, location))); + + /* + * Otherwise, search the entire rtable looking for possible matches. If + * we find one, emit a hint about it. + * + * TODO: improve this code (and also errorMissingRTE) to mention using + * LATERAL if appropriate. + */ + rte = searchRangeTableForCol(pstate, colname, location); + + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" does not exist", colname), + rte ? errhint("There is a column named \"%s\" in table \"%s\", but it cannot be referenced from this part of the query.", + colname, rte->eref->aliasname) : 0, + parser_errposition(pstate, location))); +} + + +/* + * Examine a fully-parsed query, and return TRUE iff any relation underlying + * the query is a temporary relation (table, view, or materialized view). + */ +bool +isQueryUsingTempRelation(Query *query) +{ + return isQueryUsingTempRelation_walker((Node *) query, NULL); +} + +static bool +isQueryUsingTempRelation_walker(Node *node, void *context) +{ + if (node == NULL) + return false; + + if (IsA(node, Query)) + { + Query *query = (Query *) node; + ListCell *rtable; + + foreach(rtable, query->rtable) + { + RangeTblEntry *rte = lfirst(rtable); + + if (rte->rtekind == RTE_RELATION) + { + Relation rel = heap_open(rte->relid, AccessShareLock); + char relpersistence = rel->rd_rel->relpersistence; + + heap_close(rel, AccessShareLock); + if (relpersistence == RELPERSISTENCE_TEMP) + return true; + } + } + + return query_tree_walker(query, + isQueryUsingTempRelation_walker, + context, + QTW_IGNORE_JOINALIASES); + } + + return expression_tree_walker(node, + isQueryUsingTempRelation_walker, + context); +} diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index feec677050..3cd29df324 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -3,7 +3,7 @@ * parse_target.c * handle target lists * - * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -57,14 +57,14 @@ static Node *transformAssignmentSubscripts(ParseState *pstate, Node *rhs, int location); static List *ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, - bool targetlist); + bool make_target_entry); static List *ExpandAllTables(ParseState *pstate, int location); static List *ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind, - bool targetlist); + bool make_target_entry, ParseExprKind exprKind); static List *ExpandSingleTable(ParseState *pstate, RangeTblEntry *rte, - int location, bool targetlist); + int location, bool make_target_entry); static List *ExpandRowReference(ParseState *pstate, Node *expr, - bool targetlist); + bool make_target_entry); static int FigureColnameInternal(Node *node, char **name); @@ -76,6 +76,7 @@ static int FigureColnameInternal(Node *node, char **name); * * node the (untransformed) parse tree for the value expression. * expr the transformed expression, or NULL if caller didn't do it yet. + * exprKind expression kind (EXPR_KIND_SELECT_TARGET, etc) * colname the column name to be assigned, or NULL if none yet set. * resjunk true if the target should be marked resjunk, ie, it is not * wanted in the final projected tuple. @@ -84,12 +85,13 @@ TargetEntry * transformTargetEntry(ParseState *pstate, Node *node, Node *expr, + ParseExprKind exprKind, char *colname, bool resjunk) { /* Transform the node if caller didn't do it already */ if (expr == NULL) - expr = transformExpr(pstate, node); + expr = transformExpr(pstate, node, exprKind); if (colname == NULL && !resjunk) { @@ -111,11 +113,13 @@ transformTargetEntry(ParseState *pstate, * transformTargetList() * Turns a list of ResTarget's into a list of TargetEntry's. * - * At this point, we don't care whether we are doing SELECT, INSERT, - * or UPDATE; we just transform the given expressions (the "val" fields). + * At this point, we don't care whether we are doing SELECT, UPDATE, + * or RETURNING; we just transform the given expressions (the "val" fields). + * However, our subroutines care, so we need the exprKind parameter. */ List * -transformTargetList(ParseState *pstate, List *targetlist) +transformTargetList(ParseState *pstate, List *targetlist, + ParseExprKind exprKind) { List *p_target = NIL; ListCell *o_target; @@ -151,7 +155,7 @@ transformTargetList(ParseState *pstate, List *targetlist) /* It is something.*, expand into multiple items */ p_target = list_concat(p_target, ExpandIndirectionStar(pstate, ind, - true)); + true, exprKind)); continue; } } @@ -163,6 +167,7 @@ transformTargetList(ParseState *pstate, List *targetlist) transformTargetEntry(pstate, res->val, NULL, + exprKind, res->name, false)); } @@ -177,10 +182,11 @@ transformTargetList(ParseState *pstate, List *targetlist) * This is the identical transformation to transformTargetList, except that * the input list elements are bare expressions without ResTarget decoration, * and the output elements are likewise just expressions without TargetEntry - * decoration. We use this for ROW() and VALUES() constructs. + * decoration. We use this for ROW() and VALUES() constructs. */ List * -transformExpressionList(ParseState *pstate, List *exprlist) +transformExpressionList(ParseState *pstate, List *exprlist, + ParseExprKind exprKind) { List *result = NIL; ListCell *lc; @@ -216,7 +222,7 @@ transformExpressionList(ParseState *pstate, List *exprlist) /* It is something.*, expand into multiple items */ result = list_concat(result, ExpandIndirectionStar(pstate, ind, - false)); + false, exprKind)); continue; } } @@ -225,7 +231,7 @@ transformExpressionList(ParseState *pstate, List *exprlist) * Not "something.*", so transform as a single expression */ result = lappend(result, - transformExpr(pstate, e)); + transformExpr(pstate, e, exprKind)); } return result; @@ -305,6 +311,7 @@ markTargetListOrigin(ParseState *pstate, TargetEntry *tle, Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars)); aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1); + /* We intentionally don't strip implicit coercions here */ markTargetListOrigin(pstate, tle, aliasvar, netlevelsup); } break; @@ -346,7 +353,7 @@ markTargetListOrigin(ParseState *pstate, TargetEntry *tle, /* * transformAssignedExpr() - * This is used in INSERT and UPDATE statements only. It prepares an + * This is used in INSERT and UPDATE statements only. It prepares an * expression for assignment to a column of the target table. * This includes coercing the given value to the target column's type * (if necessary), and dealing with any subfield names or subscripts @@ -355,6 +362,7 @@ markTargetListOrigin(ParseState *pstate, TargetEntry *tle, * * pstate parse state * expr expression to be modified + * exprKind indicates which type of statement we're dealing with * colname target column name (ie, name of attribute to be assigned to) * attrno target attribute number * indirection subscripts/field names for target column, if any @@ -364,22 +372,33 @@ markTargetListOrigin(ParseState *pstate, TargetEntry *tle, * * Note: location points at the target column name (SET target or INSERT * column name list entry), and must therefore be -1 in an INSERT that - * omits the column name list. So we should usually prefer to use + * omits the column name list. So we should usually prefer to use * exprLocation(expr) for errors that can happen in a default INSERT. */ Expr * transformAssignedExpr(ParseState *pstate, Expr *expr, + ParseExprKind exprKind, char *colname, int attrno, List *indirection, int location) { + Relation rd = pstate->p_target_relation; Oid type_id; /* type of value provided */ Oid attrtype; /* type of target column */ int32 attrtypmod; Oid attrcollation; /* collation of target column */ - Relation rd = pstate->p_target_relation; + ParseExprKind sv_expr_kind; + + /* + * Save and restore identity of expression type we're parsing. We must + * set p_expr_kind here because we can parse subscripts without going + * through transformExpr(). + */ + Assert(exprKind != EXPR_KIND_NONE); + sv_expr_kind = pstate->p_expr_kind; + pstate->p_expr_kind = exprKind; Assert(rd != NULL); if (attrno <= 0) @@ -428,7 +447,7 @@ transformAssignedExpr(ParseState *pstate, /* * If there is indirection on the target column, prepare an array or - * subfield assignment expression. This will generate a new column value + * subfield assignment expression. This will generate a new column value * that the source value has been inserted into, which can then be placed * in the new tuple constructed by INSERT or UPDATE. */ @@ -496,6 +515,8 @@ transformAssignedExpr(ParseState *pstate, parser_errposition(pstate, exprLocation(orig_expr)))); } + pstate->p_expr_kind = sv_expr_kind; + return expr; } @@ -526,6 +547,7 @@ updateTargetListEntry(ParseState *pstate, /* Fix up expression as needed */ tle->expr = transformAssignedExpr(pstate, tle->expr, + EXPR_KIND_UPDATE_TARGET, colname, attrno, indirection, @@ -533,7 +555,7 @@ updateTargetListEntry(ParseState *pstate, /* * Set the resno to identify the target column --- the rewriter and - * planner depend on this. We also set the resname to identify the target + * planner depend on this. We also set the resname to identify the target * column, but this is only for debugging purposes; it should not be * relied on. (In particular, it might be out of date in a stored rule.) */ @@ -822,18 +844,20 @@ transformAssignmentSubscripts(ParseState *pstate, /* If target was a domain over array, need to coerce up to the domain */ if (arrayType != targetTypeId) { + Oid resulttype = exprType(result); + result = coerce_to_target_type(pstate, - result, exprType(result), + result, resulttype, targetTypeId, targetTypMod, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST, -1); - /* probably shouldn't fail, but check */ + /* can fail if we had int2vector/oidvector, but not for true domains */ if (result == NULL) ereport(ERROR, (errcode(ERRCODE_CANNOT_COERCE), errmsg("cannot cast type %s to %s", - format_type_be(exprType(result)), + format_type_be(resulttype), format_type_be(targetTypeId)), parser_errposition(pstate, location))); } @@ -952,7 +976,7 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos) */ static List * ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, - bool targetlist) + bool make_target_entry) { List *fields = cref->fields; int numnames = list_length(fields); @@ -965,9 +989,9 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * (e.g., SELECT * FROM emp, dept) * * Since the grammar only accepts bare '*' at top level of SELECT, we - * need not handle the targetlist==false case here. + * need not handle the make_target_entry==false case here. */ - Assert(targetlist); + Assert(make_target_entry); return ExpandAllTables(pstate, cref->location); } else @@ -979,7 +1003,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * * Note: this code is a lot like transformColumnRef; it's tempting to * call that instead and then replace the resulting whole-row Var with - * a list of Vars. However, that would leave us with the RTE's + * a list of Vars. However, that would leave us with the RTE's * selectedCols bitmap showing the whole row as needing select * permission, as well as the individual columns. That would be * incorrect (since columns added later shouldn't need select @@ -998,7 +1022,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, } crserr = CRSERR_NO_RTE; /* - * Give the PreParseColumnRefHook, if any, first shot. If it returns + * Give the PreParseColumnRefHook, if any, first shot. If it returns * non-null then we should use that expression. */ if (pstate->p_pre_columnref_hook != NULL) @@ -1007,7 +1031,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, node = (*pstate->p_pre_columnref_hook) (pstate, cref); if (node != NULL) - return ExpandRowReference(pstate, node, targetlist); + return ExpandRowReference(pstate, node, make_target_entry); } switch (numnames) @@ -1070,7 +1094,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, errmsg("column reference \"%s\" is ambiguous", NameListToString(cref->fields)), parser_errposition(pstate, cref->location))); - return ExpandRowReference(pstate, node, targetlist); + return ExpandRowReference(pstate, node, make_target_entry); } } @@ -1105,7 +1129,7 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, /* * OK, expand the RTE into fields. */ - return ExpandSingleTable(pstate, rte, cref->location, targetlist); + return ExpandSingleTable(pstate, rte, cref->location, make_target_entry); } } @@ -1113,9 +1137,10 @@ ExpandColumnRefStar(ParseState *pstate, ColumnRef *cref, * ExpandAllTables() * Transforms '*' (in the target list) into a list of targetlist entries. * - * tlist entries are generated for each relation appearing in the query's - * varnamespace. We do not consider relnamespace because that would include - * input tables of aliasless JOINs, NEW/OLD pseudo-entries, etc. + * tlist entries are generated for each relation visible for unqualified + * column name access. We do not consider qualified-name-only entries because + * that would include input tables of aliasless JOINs, NEW/OLD pseudo-entries, + * etc. * * The referenced relations/columns are marked as requiring SELECT access. */ @@ -1123,25 +1148,42 @@ static List * ExpandAllTables(ParseState *pstate, int location) { List *target = NIL; + bool found_table = false; ListCell *l; - /* Check for SELECT *; */ - if (!pstate->p_varnamespace) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("SELECT * with no tables specified is not valid"), - parser_errposition(pstate, location))); - - foreach(l, pstate->p_varnamespace) + foreach(l, pstate->p_namespace) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); - int rtindex = RTERangeTablePosn(pstate, rte, NULL); + ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(l); + RangeTblEntry *rte = nsitem->p_rte; + + /* Ignore table-only items */ + if (!nsitem->p_cols_visible) + continue; + /* Should not have any lateral-only items when parsing targetlist */ + Assert(!nsitem->p_lateral_only); + /* Remember we found a p_cols_visible item */ + found_table = true; target = list_concat(target, - expandRelAttrs(pstate, rte, rtindex, 0, + expandRelAttrs(pstate, + rte, + RTERangeTablePosn(pstate, rte, + NULL), + 0, location)); } + /* + * Check for "SELECT *;". We do it this way, rather than checking for + * target == NIL, because we want to allow SELECT * FROM a zero_column + * table. + */ + if (!found_table) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("SELECT * with no tables specified is not valid"), + parser_errposition(pstate, location))); + return target; } @@ -1153,10 +1195,12 @@ ExpandAllTables(ParseState *pstate, int location) * The code is shared between the case of foo.* at the top level in a SELECT * target list (where we want TargetEntry nodes in the result) and foo.* in * a ROW() or VALUES() construct (where we want just bare expressions). + * For robustness, we use a separate "make_target_entry" flag to control + * this rather than relying on exprKind. */ static List * ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind, - bool targetlist) + bool make_target_entry, ParseExprKind exprKind) { Node *expr; @@ -1166,10 +1210,10 @@ ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind, list_length(ind->indirection) - 1); /* And transform that */ - expr = transformExpr(pstate, (Node *) ind); + expr = transformExpr(pstate, (Node *) ind, exprKind); /* Expand the rowtype expression into individual fields */ - return ExpandRowReference(pstate, expr, targetlist); + return ExpandRowReference(pstate, expr, make_target_entry); } /* @@ -1183,14 +1227,14 @@ ExpandIndirectionStar(ParseState *pstate, A_Indirection *ind, */ static List * ExpandSingleTable(ParseState *pstate, RangeTblEntry *rte, - int location, bool targetlist) + int location, bool make_target_entry) { int sublevels_up; int rtindex; rtindex = RTERangeTablePosn(pstate, rte, &sublevels_up); - if (targetlist) + if (make_target_entry) { /* expandRelAttrs handles permissions marking */ return expandRelAttrs(pstate, rte, rtindex, sublevels_up, @@ -1232,7 +1276,7 @@ ExpandSingleTable(ParseState *pstate, RangeTblEntry *rte, */ static List * ExpandRowReference(ParseState *pstate, Node *expr, - bool targetlist) + bool make_target_entry) { List *result = NIL; TupleDesc tupleDesc; @@ -1241,7 +1285,7 @@ ExpandRowReference(ParseState *pstate, Node *expr, /* * If the rowtype expression is a whole-row Var, we can expand the fields - * as simple Vars. Note: if the RTE is a relation, this case leaves us + * as simple Vars. Note: if the RTE is a relation, this case leaves us * with the RTE's selectedCols bitmap showing the whole row as needing * select permission, as well as the individual columns. However, we can * only get here for weird notations like (table.*).*, so it's not worth @@ -1255,7 +1299,7 @@ ExpandRowReference(ParseState *pstate, Node *expr, RangeTblEntry *rte; rte = GetRTEByRangeTablePosn(pstate, var->varno, var->varlevelsup); - return ExpandSingleTable(pstate, rte, var->location, targetlist); + return ExpandSingleTable(pstate, rte, var->location, make_target_entry); } /* @@ -1300,7 +1344,7 @@ ExpandRowReference(ParseState *pstate, Node *expr, /* save attribute's collation for parse_collate.c */ fselect->resultcollid = att->attcollation; - if (targetlist) + if (make_target_entry) { /* add TargetEntry decoration */ TargetEntry *te; @@ -1323,7 +1367,7 @@ ExpandRowReference(ParseState *pstate, Node *expr, * Get the tuple descriptor for a Var of type RECORD, if possible. * * Since no actual table or view column is allowed to have type RECORD, such - * a Var must refer to a JOIN or FUNCTION RTE or to a subquery output. We + * a Var must refer to a JOIN or FUNCTION RTE or to a subquery output. We * drill down to find the ultimate defining expression and attempt to infer * the tupdesc from it. We ereport if we can't determine the tupdesc. * @@ -1406,7 +1450,7 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup) { /* * Recurse into the sub-select to see what its Var refers - * to. We have to build an additional level of ParseState + * to. We have to build an additional level of ParseState * to keep in step with varlevelsup in the subselect. */ ParseState mypstate; @@ -1425,6 +1469,8 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup) /* Join RTE --- recursively inspect the alias variable */ Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars)); expr = (Node *) list_nth(rte->joinaliasvars, attnum - 1); + Assert(expr != NULL); + /* We intentionally don't strip implicit coercions here */ if (IsA(expr, Var)) return expandRecordVariable(pstate, (Var *) expr, netlevelsup); /* else fall through to inspect the expression */ @@ -1483,7 +1529,7 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup) /* * We now have an expression we can't expand any more, so see if - * get_expr_result_type() can do anything with it. If not, pass to + * get_expr_result_type() can do anything with it. If not, pass to * lookup_rowtype_tupdesc() which will probably fail, but will give an * appropriate error message while failing. */ diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c index 871a7d1ce3..d0803dfafd 100644 --- a/src/backend/parser/parse_type.c +++ b/src/backend/parser/parse_type.c @@ -3,7 +3,7 @@ * parse_type.c * handle type operations for parser * - * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -14,6 +14,7 @@ */ #include "postgres.h" +#include "access/htup_details.h" #include "catalog/namespace.h" #include "catalog/pg_type.h" #include "lib/stringinfo.h" @@ -34,7 +35,7 @@ static int32 typenameTypeMod(ParseState *pstate, const TypeName *typeName, /* * LookupTypeName * Given a TypeName object, lookup the pg_type syscache entry of the type. - * Returns NULL if no such type can be found. If the type is found, + * Returns NULL if no such type can be found. If the type is found, * the typmod value represented in the TypeName struct is computed and * stored into *typmod_p. * @@ -47,7 +48,7 @@ static int32 typenameTypeMod(ParseState *pstate, const TypeName *typeName, * * typmod_p can be passed as NULL if the caller does not care to know the * typmod value, but the typmod decoration (if any) will be validated anyway, - * except in the case where the type is not found. Note that if the type is + * except in the case where the type is not found. Note that if the type is * found but is a shell, and there is typmod decoration, an error will be * thrown --- this is intentional. * @@ -55,7 +56,7 @@ static int32 typenameTypeMod(ParseState *pstate, const TypeName *typeName, */ Type LookupTypeName(ParseState *pstate, const TypeName *typeName, - int32 *typmod_p) + int32 *typmod_p, bool missing_ok) { Oid typoid; HeapTuple tup; @@ -112,27 +113,35 @@ LookupTypeName(ParseState *pstate, const TypeName *typeName, * Look up the field. * * XXX: As no lock is taken here, this might fail in the presence of - * concurrent DDL. But taking a lock would carry a performance + * concurrent DDL. But taking a lock would carry a performance * penalty and would also require a permissions check. */ - relid = RangeVarGetRelid(rel, NoLock, false); + relid = RangeVarGetRelid(rel, NoLock, missing_ok); attnum = get_attnum(relid, field); if (attnum == InvalidAttrNumber) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), - errmsg("column \"%s\" of relation \"%s\" does not exist", - field, rel->relname), - parser_errposition(pstate, typeName->location))); - typoid = get_atttype(relid, attnum); + { + if (missing_ok) + typoid = InvalidOid; + else + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" of relation \"%s\" does not exist", + field, rel->relname), + parser_errposition(pstate, typeName->location))); + } + else + { + typoid = get_atttype(relid, attnum); - /* this construct should never have an array indicator */ - Assert(typeName->arrayBounds == NIL); + /* this construct should never have an array indicator */ + Assert(typeName->arrayBounds == NIL); - /* emit nuisance notice (intentionally not errposition'd) */ - ereport(NOTICE, - (errmsg("type reference %s converted to %s", - TypeNameToString(typeName), - format_type_be(typoid)))); + /* emit nuisance notice (intentionally not errposition'd) */ + ereport(NOTICE, + (errmsg("type reference %s converted to %s", + TypeNameToString(typeName), + format_type_be(typoid)))); + } } else { @@ -148,10 +157,13 @@ LookupTypeName(ParseState *pstate, const TypeName *typeName, /* Look in specific schema only */ Oid namespaceId; - namespaceId = LookupExplicitNamespace(schemaname); - typoid = GetSysCacheOid2(TYPENAMENSP, - PointerGetDatum(typname), - ObjectIdGetDatum(namespaceId)); + namespaceId = LookupExplicitNamespace(schemaname, missing_ok); + if (OidIsValid(namespaceId)) + typoid = GetSysCacheOid2(TYPENAMENSP, + PointerGetDatum(typname), + ObjectIdGetDatum(namespaceId)); + else + typoid = InvalidOid; } else { @@ -184,6 +196,43 @@ LookupTypeName(ParseState *pstate, const TypeName *typeName, } /* + * LookupTypeNameOid + * Given a TypeName object, lookup the pg_type syscache entry of the type. + * Returns InvalidOid if no such type can be found. If the type is found, + * return its Oid. + * + * NB: direct callers of this function need to be aware that the type OID + * returned may correspond to a shell type. Most code should go through + * typenameTypeId instead. + * + * pstate is only used for error location info, and may be NULL. + */ +Oid +LookupTypeNameOid(ParseState *pstate, const TypeName *typeName, bool missing_ok) +{ + Oid typoid; + Type tup; + + tup = LookupTypeName(pstate, typeName, NULL, missing_ok); + if (tup == NULL) + { + if (!missing_ok) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("type \"%s\" does not exist", + TypeNameToString(typeName)), + parser_errposition(pstate, typeName->location))); + + return InvalidOid; + } + + typoid = HeapTupleGetOid(tup); + ReleaseSysCache(tup); + + return typoid; +} + +/* * typenameType - given a TypeName, return a Type structure and typmod * * This is equivalent to LookupTypeName, except that this will report @@ -195,7 +244,7 @@ typenameType(ParseState *pstate, const TypeName *typeName, int32 *typmod_p) { Type tup; - tup = LookupTypeName(pstate, typeName, typmod_p); + tup = LookupTypeName(pstate, typeName, typmod_p, false); if (tup == NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), @@ -312,8 +361,7 @@ typenameTypeMod(ParseState *pstate, const TypeName *typeName, Type typ) if (IsA(&ac->val, Integer)) { - cstr = (char *) palloc(32); - snprintf(cstr, 32, "%ld", (long) ac->val.val.ival); + cstr = psprintf("%ld", (long) ac->val.val.ival); } else if (IsA(&ac->val, Float) || IsA(&ac->val, String)) @@ -471,7 +519,7 @@ GetColumnDefCollation(ParseState *pstate, ColumnDef *coldef, Oid typeOid) { Oid result; Oid typcollation = get_typcollation(typeOid); - int location = -1; + int location = coldef->location; if (coldef->collClause) { @@ -577,7 +625,7 @@ typeTypeCollation(Type typ) /* * Given a type structure and a string, returns the internal representation - * of that string. The "string" can be NULL to perform conversion of a NULL + * of that string. The "string" can be NULL to perform conversion of a NULL * (which might result in failure, if the input function rejects NULLs). */ Datum @@ -601,7 +649,7 @@ stringTypeDatum(Type tp, char *string, int32 atttypmod) * instability in the input function is that comparison of Const nodes * relies on bytewise comparison of the datums, so if the input function * leaves garbage then subexpressions that should be identical may not get - * recognized as such. See pgsql-hackers discussion of 2008-04-04. + * recognized as such. See pgsql-hackers discussion of 2008-04-04. */ if (string && !typform->typbyval) { @@ -648,7 +696,7 @@ pts_error_callback(void *arg) /* * Currently we just suppress any syntax error position report, rather - * than transforming to an "internal query" error. It's unlikely that a + * than transforming to an "internal query" error. It's unlikely that a * type name is complex enough to need positioning. */ errposition(0); @@ -658,9 +706,12 @@ pts_error_callback(void *arg) * Given a string that is supposed to be a SQL-compatible type declaration, * such as "int4" or "integer" or "character varying(32)", parse * the string and convert it to a type OID and type modifier. + * If missing_ok is true, InvalidOid is returned rather than raising an error + * when the type name is not found. */ void -parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p) +parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p, + bool missing_ok) { StringInfoData buf; List *raw_parsetree_list; @@ -669,6 +720,7 @@ parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p) TypeCast *typecast; TypeName *typeName; ErrorContextCallback ptserrcontext; + Type tup; /* make sure we give useful error for empty input */ if (strspn(str, " \t\n\r\f") == strlen(str)) @@ -705,12 +757,12 @@ parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p) stmt->groupClause != NIL || stmt->havingClause != NULL || stmt->windowClause != NIL || - stmt->withClause != NULL || stmt->valuesLists != NIL || stmt->sortClause != NIL || stmt->limitOffset != NULL || stmt->limitCount != NULL || stmt->lockingClause != NIL || + stmt->withClause != NULL || stmt->op != SETOP_NONE) goto fail; if (list_length(stmt->targetList) != 1) @@ -734,7 +786,28 @@ parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p) if (typeName->setof) goto fail; - typenameTypeIdAndMod(NULL, typeName, typeid_p, typmod_p); + tup = LookupTypeName(NULL, typeName, typmod_p, missing_ok); + if (tup == NULL) + { + if (!missing_ok) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("type \"%s\" does not exist", + TypeNameToString(typeName)), + parser_errposition(NULL, typeName->location))); + *typeid_p = InvalidOid; + } + else + { + if (!((Form_pg_type) GETSTRUCT(tup))->typisdefined) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("type \"%s\" is only a shell", + TypeNameToString(typeName)), + parser_errposition(NULL, typeName->location))); + *typeid_p = HeapTupleGetOid(tup); + ReleaseSysCache(tup); + } pfree(buf.data); diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index ec00730eec..0dca7b1214 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -21,7 +21,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Portions Copyright (c) 2012-2014, TransLattice, Inc. - * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 2010-2012 Postgres-XC Development Group * @@ -32,6 +32,7 @@ #include "postgres.h" +#include "access/htup_details.h" #include "access/reloptions.h" #include "catalog/dependency.h" #include "catalog/heap.h" @@ -97,6 +98,7 @@ typedef struct RangeVar *relation; /* relation to create */ Relation rel; /* opened/locked rel, if ALTER */ List *inhRelations; /* relations to inherit from */ + bool isforeign; /* true if CREATE/ALTER FOREIGN TABLE */ bool isalter; /* true if altering existing table */ bool hasoids; /* does relation have an OID column? */ List *columns; /* ColumnDef items */ @@ -147,9 +149,9 @@ static void transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_clause); static void transformOfType(CreateStmtContext *cxt, TypeName *ofTypename); -static char *chooseIndexName(const RangeVar *relation, IndexStmt *index_stmt); static IndexStmt *generateClonedIndexStmt(CreateStmtContext *cxt, - Relation parent_index, AttrNumber *attmap); + Relation source_idx, + const AttrNumber *attmap, int attmap_length); static List *get_collation(Oid collation, Oid actual_datatype); static List *get_opclass(Oid opclass, Oid actual_datatype); static void transformIndexConstraints(CreateStmtContext *cxt); @@ -178,7 +180,7 @@ static PGXCSubCluster *makeSubCluster(List *nodelist); * will be the transformed CreateStmt, but there may be additional actions * to be done before and after the actual DefineRelation() call. * - * SQL92 allows constraints to be scattered all over, so thumb through + * SQL allows constraints to be scattered all over, so thumb through * the columns and collect all constraints into one place. * If there are any implied indices (e.g. UNIQUE or PRIMARY KEY) * then expand those into multiple IndexStmt blocks. @@ -208,7 +210,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString) stmt = (CreateStmt *) copyObject(stmt); /* - * Look up the creation namespace. This also checks permissions on the + * Look up the creation namespace. This also checks permissions on the * target namespace, locks it against concurrent drops, checks for a * preexisting relation in that namespace with the same name, and updates * stmt->relation->relpersistence if the select namespace is temporary. @@ -234,7 +236,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString) * If the target relation name isn't schema-qualified, make it so. This * prevents some corner cases in which added-on rewritten commands might * think they should apply to other relations that have the same name and - * are earlier in the search path. But a local temp table is effectively + * are earlier in the search path. But a local temp table is effectively * specified to be in pg_temp, so no need for anything extra in that case. */ if (stmt->relation->schemaname == NULL @@ -247,9 +249,15 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString) cxt.pstate = pstate; if (IsA(stmt, CreateForeignTableStmt)) + { cxt.stmtType = "CREATE FOREIGN TABLE"; + cxt.isforeign = true; + } else + { cxt.stmtType = "CREATE TABLE"; + cxt.isforeign = false; + } cxt.relation = stmt->relation; cxt.rel = NULL; cxt.inhRelations = stmt->inhRelations; @@ -262,7 +270,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString) cxt.blist = NIL; cxt.alist = NIL; cxt.pkey = NULL; - cxt.hasoids = interpretOidsOption(stmt->options); + cxt.hasoids = interpretOidsOption(stmt->options, true); #ifdef PGXC #ifdef XCP cxt.fallback_source = FBS_NONE; @@ -539,7 +547,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) "seq", snamespaceid); - ereport(NOTICE, + ereport(DEBUG1, (errmsg("%s will create implicit sequence \"%s\" for serial column \"%s.%s\"", cxt->stmtType, sname, cxt->relation->relname, column->colname))); @@ -607,16 +615,9 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) castnode->typeName = SystemTypeName("regclass"); castnode->arg = (Node *) snamenode; castnode->location = -1; - funccallnode = makeNode(FuncCall); - funccallnode->funcname = SystemFuncName("nextval"); - funccallnode->args = list_make1(castnode); - funccallnode->agg_order = NIL; - funccallnode->agg_star = false; - funccallnode->agg_distinct = false; - funccallnode->func_variadic = false; - funccallnode->over = NULL; - funccallnode->location = -1; - + funccallnode = makeFuncCall(SystemFuncName("nextval"), + list_make1(castnode), + -1); constraint = makeNode(Constraint); constraint->contype = CONSTR_DEFAULT; constraint->location = -1; @@ -681,11 +682,23 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) break; case CONSTR_CHECK: + if (cxt->isforeign) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("constraints are not supported on foreign tables"), + parser_errposition(cxt->pstate, + constraint->location))); cxt->ckconstraints = lappend(cxt->ckconstraints, constraint); break; case CONSTR_PRIMARY: case CONSTR_UNIQUE: + if (cxt->isforeign) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("constraints are not supported on foreign tables"), + parser_errposition(cxt->pstate, + constraint->location))); if (constraint->keys == NIL) constraint->keys = list_make1(makeString(column->colname)); cxt->ixconstraints = lappend(cxt->ixconstraints, constraint); @@ -697,6 +710,12 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) break; case CONSTR_FOREIGN: + if (cxt->isforeign) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("constraints are not supported on foreign tables"), + parser_errposition(cxt->pstate, + constraint->location))); /* * Fill in the current attribute's name and throw it into the @@ -721,8 +740,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) } /* - * Generate ALTER FOREIGN TABLE ALTER COLUMN statement which adds - * per-column foreign data wrapper options for this column. + * If needed, generate ALTER FOREIGN TABLE ALTER COLUMN statement to add + * per-column foreign data wrapper options to this column after creation. */ if (column->fdwoptions != NIL) { @@ -753,6 +772,13 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) static void transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint) { + if (cxt->isforeign) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("constraints are not supported on foreign tables"), + parser_errposition(cxt->pstate, + constraint->location))); + switch (constraint->contype) { case CONSTR_PRIMARY: @@ -801,22 +827,31 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla Relation relation; TupleDesc tupleDesc; TupleConstr *constr; + AttrNumber *attmap; AclResult aclresult; char *comment; ParseCallbackState pcbstate; - setup_parser_errposition_callback(&pcbstate, cxt->pstate, table_like_clause->relation->location); + setup_parser_errposition_callback(&pcbstate, cxt->pstate, + table_like_clause->relation->location); + + /* we could support LIKE in many cases, but worry about it another day */ + if (cxt->isforeign) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("LIKE is not supported for creating foreign tables"))); relation = relation_openrv(table_like_clause->relation, AccessShareLock); - if (relation->rd_rel->relkind != RELKIND_RELATION - && relation->rd_rel->relkind != RELKIND_VIEW - && relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE - && relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE) + if (relation->rd_rel->relkind != RELKIND_RELATION && + relation->rd_rel->relkind != RELKIND_VIEW && + relation->rd_rel->relkind != RELKIND_MATVIEW && + relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE && + relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is not a table, view, composite type, or foreign table", - table_like_clause->relation->relname))); + errmsg("\"%s\" is not a table, view, materialized view, composite type, or foreign table", + RelationGetRelationName(relation)))); cancel_parser_errposition_callback(&pcbstate); @@ -876,6 +911,13 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla constr = tupleDesc->constr; /* + * Initialize column number map for map_variable_attnos(). We need this + * since dropped columns in the source table aren't copied, so the new + * table can have different column numbers. + */ + attmap = (AttrNumber *) palloc0(sizeof(AttrNumber) * tupleDesc->natts); + + /* * Insert the copied attributes into the cxt for the new table definition. */ for (parent_attno = 1; parent_attno <= tupleDesc->natts; @@ -886,7 +928,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla ColumnDef *def; /* - * Ignore dropped columns in the parent. + * Ignore dropped columns in the parent. attmap entry is left zero. */ if (attribute->attisdropped) continue; @@ -911,6 +953,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla def->collClause = NULL; def->collOid = attribute->attcollation; def->constraints = NIL; + def->location = -1; /* * Add to column list @@ -931,6 +974,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla cxt->fallback_source = FBS_COLDEF; } #endif + attmap[parent_attno - 1] = list_length(cxt->columns); /* * Copy default, if present and the default has been requested @@ -990,22 +1034,39 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla /* * Copy CHECK constraints if requested, being careful to adjust attribute - * numbers + * numbers so they match the child. */ if ((table_like_clause->options & CREATE_TABLE_LIKE_CONSTRAINTS) && tupleDesc->constr) { - AttrNumber *attmap = varattnos_map_schema(tupleDesc, cxt->columns); int ccnum; for (ccnum = 0; ccnum < tupleDesc->constr->num_check; ccnum++) { char *ccname = tupleDesc->constr->check[ccnum].ccname; char *ccbin = tupleDesc->constr->check[ccnum].ccbin; - Node *ccbin_node = stringToNode(ccbin); Constraint *n = makeNode(Constraint); + Node *ccbin_node; + bool found_whole_row; - change_varattnos_of_a_node(ccbin_node, attmap); + ccbin_node = map_variable_attnos(stringToNode(ccbin), + 1, 0, + attmap, tupleDesc->natts, + &found_whole_row); + + /* + * We reject whole-row variables because the whole point of LIKE + * is that the new table's rowtype might later diverge from the + * parent's. So, while translation might be possible right now, + * it wouldn't be possible to guarantee it would work in future. + */ + if (found_whole_row) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot convert whole-row table reference"), + errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".", + ccname, + RelationGetRelationName(relation)))); n->contype = CONSTR_CHECK; n->location = -1; @@ -1041,7 +1102,6 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla if ((table_like_clause->options & CREATE_TABLE_LIKE_INDEXES) && relation->rd_rel->relhasindex) { - AttrNumber *attmap = varattnos_map_schema(tupleDesc, cxt->columns); List *parent_indexes; ListCell *l; @@ -1056,35 +1116,19 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla parent_index = index_open(parent_index_oid, AccessShareLock); /* Build CREATE INDEX statement to recreate the parent_index */ - index_stmt = generateClonedIndexStmt(cxt, parent_index, attmap); + index_stmt = generateClonedIndexStmt(cxt, parent_index, + attmap, tupleDesc->natts); - /* Copy comment on index */ + /* Copy comment on index, if requested */ if (table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS) { comment = GetComment(parent_index_oid, RelationRelationId, 0); - if (comment != NULL) - { - CommentStmt *stmt; - - /* - * We have to assign the index a name now, so that we can - * reference it in CommentStmt. - */ - if (index_stmt->idxname == NULL) - index_stmt->idxname = chooseIndexName(cxt->relation, - index_stmt); - - stmt = makeNode(CommentStmt); - stmt->objtype = OBJECT_INDEX; - stmt->objname = - list_make2(makeString(cxt->relation->schemaname), - makeString(index_stmt->idxname)); - stmt->objargs = NIL; - stmt->comment = comment; - - cxt->alist = lappend(cxt->alist, stmt); - } + /* + * We make use of IndexStmt's idxcomment option, so as not to + * need to know now what name the index will have. + */ + index_stmt->idxcomment = comment; } /* Save it in the inh_indexes list for the time being */ @@ -1096,7 +1140,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla /* * Close the parent rel, but keep our AccessShareLock on it until xact - * commit. That will prevent someone else from deleting or ALTERing the + * commit. That will prevent someone else from deleting or ALTERing the * parent before the child is committed. */ heap_close(relation, NoLock); @@ -1139,6 +1183,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename) n->collClause = NULL; n->collOid = attr->attcollation; n->constraints = NIL; + n->location = -1; cxt->columns = lappend(cxt->columns, n); } DecrTupleDescRefCount(tupdesc); @@ -1147,35 +1192,12 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename) } /* - * chooseIndexName - * - * Compute name for an index. This must match code in indexcmds.c. - * - * XXX this is inherently broken because the indexes aren't created - * immediately, so we fail to resolve conflicts when the same name is - * derived for multiple indexes. However, that's a reasonably uncommon - * situation, so we'll live with it for now. - */ -static char * -chooseIndexName(const RangeVar *relation, IndexStmt *index_stmt) -{ - Oid namespaceId; - List *colnames; - - namespaceId = RangeVarGetCreationNamespace(relation); - colnames = ChooseIndexColumnNames(index_stmt->indexParams); - return ChooseIndexName(relation->relname, namespaceId, - colnames, index_stmt->excludeOpNames, - index_stmt->primary, index_stmt->isconstraint); -} - -/* * Generate an IndexStmt node using information from an already existing index * "source_idx". Attribute numbers should be adjusted according to attmap. */ static IndexStmt * generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx, - AttrNumber *attmap) + const AttrNumber *attmap, int attmap_length) { Oid source_relid = RelationGetRelid(source_idx); Form_pg_attribute *attrs = RelationGetDescr(source_idx)->attrs; @@ -1232,7 +1254,10 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx, index->tableSpace = get_tablespace_name(idxrelrec->reltablespace); else index->tableSpace = NULL; + index->excludeOpNames = NIL; + index->idxcomment = NULL; index->indexOid = InvalidOid; + index->oldNode = InvalidOid; index->unique = idxrec->indisunique; index->primary = idxrec->indisprimary; index->concurrent = false; @@ -1363,14 +1388,26 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx, { /* Expressional index */ Node *indexkey; + bool found_whole_row; if (indexpr_item == NULL) elog(ERROR, "too few entries in indexprs list"); indexkey = (Node *) lfirst(indexpr_item); indexpr_item = lnext(indexpr_item); - /* OK to modify indexkey since we are working on a private copy */ - change_varattnos_of_a_node(indexkey, attmap); + /* Adjust Vars to match new table's column numbering */ + indexkey = map_variable_attnos(indexkey, + 1, 0, + attmap, attmap_length, + &found_whole_row); + + /* As in transformTableLikeClause, reject whole-row variables */ + if (found_whole_row) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot convert whole-row table reference"), + errdetail("Index \"%s\" contains a whole-row table reference.", + RelationGetRelationName(source_idx)))); iparam->name = NULL; iparam->expr = indexkey; @@ -1427,12 +1464,28 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx, if (!isnull) { char *pred_str; + Node *pred_tree; + bool found_whole_row; /* Convert text string to node tree */ pred_str = TextDatumGetCString(datum); - index->whereClause = (Node *) stringToNode(pred_str); - /* Adjust attribute numbers */ - change_varattnos_of_a_node(index->whereClause, attmap); + pred_tree = (Node *) stringToNode(pred_str); + + /* Adjust Vars to match new table's column numbering */ + pred_tree = map_variable_attnos(pred_tree, + 1, 0, + attmap, attmap_length, + &found_whole_row); + + /* As in transformTableLikeClause, reject whole-row variables */ + if (found_whole_row) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot convert whole-row table reference"), + errdetail("Index \"%s\" contains a whole-row table reference.", + RelationGetRelationName(source_idx)))); + + index->whereClause = pred_tree; } /* Clean up */ @@ -1561,8 +1614,8 @@ transformIndexConstraints(CreateStmtContext *cxt) /* * Scan the index list and remove any redundant index specifications. This * can happen if, for instance, the user writes UNIQUE PRIMARY KEY. A - * strict reading of SQL92 would suggest raising an error instead, but - * that strikes me as too anal-retentive. - tgl 2001-02-14 + * strict reading of SQL would suggest raising an error instead, but that + * strikes me as too anal-retentive. - tgl 2001-02-14 * * XXX in ALTER TABLE case, it'd be nice to look for duplicate * pre-existing indexes, too. @@ -1669,7 +1722,9 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) index->whereClause = constraint->where_clause; index->indexParams = NIL; index->excludeOpNames = NIL; + index->idxcomment = NULL; index->indexOid = InvalidOid; + index->oldNode = InvalidOid; index->concurrent = false; /* @@ -1733,18 +1788,12 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) index_name, RelationGetRelationName(heap_rel)), parser_errposition(cxt->pstate, constraint->location))); - if (!index_form->indisvalid) + if (!IndexIsValid(index_form)) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("index \"%s\" is not valid", index_name), parser_errposition(cxt->pstate, constraint->location))); - if (!index_form->indisready) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("index \"%s\" is not ready", index_name), - parser_errposition(cxt->pstate, constraint->location))); - if (!index_form->indisunique) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), @@ -1779,7 +1828,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) parser_errposition(cxt->pstate, constraint->location))); /* - * Insist on it being a btree. That's the only kind that supports + * Insist on it being a btree. That's the only kind that supports * uniqueness at the moment anyway; but we must have an index that * exactly matches what you'd get from plain ADD CONSTRAINT syntax, * else dump and reload will produce a different index (breaking @@ -1799,14 +1848,14 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) for (i = 0; i < index_form->indnatts; i++) { - int2 attnum = index_form->indkey.values[i]; + int16 attnum = index_form->indkey.values[i]; Form_pg_attribute attform; char *attname; Oid defopclass; /* * We shouldn't see attnum == 0 here, since we already rejected - * expression indexes. If we do, SystemAttributeDefinition will + * expression indexes. If we do, SystemAttributeDefinition will * throw an error. */ if (attnum > 0) @@ -1820,7 +1869,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) attname = pstrdup(NameStr(attform->attname)); /* - * Insist on default opclass and sort options. While the index + * Insist on default opclass and sort options. While the index * would still work as a constraint with non-default settings, it * might not provide exactly the same uniqueness semantics as * you'd get from a normally-created constraint; and there's also @@ -2274,17 +2323,21 @@ transformFKConstraints(CreateStmtContext *cxt, * transformIndexStmt - parse analysis for CREATE INDEX and ALTER TABLE * * Note: this is a no-op for an index not using either index expressions or - * a predicate expression. There are several code paths that create indexes + * a predicate expression. There are several code paths that create indexes * without bothering to call this, because they know they don't have any * such expressions to deal with. + * + * To avoid race conditions, it's important that this function rely only on + * the passed-in relid (and not on stmt->relation) to determine the target + * relation. */ IndexStmt * -transformIndexStmt(IndexStmt *stmt, const char *queryString) +transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString) { - Relation rel; ParseState *pstate; RangeTblEntry *rte; ListCell *l; + Relation rel; /* * We must not scribble on the passed-in IndexStmt, so copy it. (This is @@ -2292,26 +2345,17 @@ transformIndexStmt(IndexStmt *stmt, const char *queryString) */ stmt = (IndexStmt *) copyObject(stmt); - /* - * Open the parent table with appropriate locking. We must do this - * because addRangeTableEntry() would acquire only AccessShareLock, - * leaving DefineIndex() needing to do a lock upgrade with consequent risk - * of deadlock. Make sure this stays in sync with the type of lock - * DefineIndex() wants. If we are being called by ALTER TABLE, we will - * already hold a higher lock. - */ - rel = heap_openrv(stmt->relation, - (stmt->concurrent ? ShareUpdateExclusiveLock : ShareLock)); - /* Set up pstate */ pstate = make_parsestate(NULL); pstate->p_sourcetext = queryString; /* * Put the parent table into the rtable so that the expressions can refer - * to its fields without qualification. + * to its fields without qualification. Caller is responsible for locking + * relation, but we still need to open it. */ - rte = addRangeTableEntry(pstate, stmt->relation, NULL, false, true); + rel = relation_open(relid, NoLock); + rte = addRangeTableEntryForRelation(pstate, rel, NULL, false, true); /* no to join list, yes to namespaces */ addRTEtoQuery(pstate, rte, false, true, true); @@ -2321,6 +2365,7 @@ transformIndexStmt(IndexStmt *stmt, const char *queryString) { stmt->whereClause = transformWhereClause(pstate, stmt->whereClause, + EXPR_KIND_INDEX_PREDICATE, "WHERE"); /* we have to fix its collations too */ assign_expr_collations(pstate, stmt->whereClause); @@ -2338,15 +2383,20 @@ transformIndexStmt(IndexStmt *stmt, const char *queryString) ielem->indexcolname = FigureIndexColname(ielem->expr); /* Now do parse transformation of the expression */ - ielem->expr = transformExpr(pstate, ielem->expr); + ielem->expr = transformExpr(pstate, ielem->expr, + EXPR_KIND_INDEX_EXPRESSION); /* We have to fix its collations too */ assign_expr_collations(pstate, ielem->expr); /* - * We check only that the result type is legitimate; this is for - * consistency with what transformWhereClause() checks for the - * predicate. DefineIndex() will make more checks. + * transformExpr() should have already rejected subqueries, + * aggregates, and window functions, based on the EXPR_KIND_ for + * an index expression. + * + * Also reject expressions returning sets; this is for consistency + * with what transformWhereClause() checks for the predicate. + * DefineIndex() will make more checks. */ if (expression_returns_set(ielem->expr)) ereport(ERROR, @@ -2356,7 +2406,8 @@ transformIndexStmt(IndexStmt *stmt, const char *queryString) } /* - * Check that only the base rel is mentioned. + * Check that only the base rel is mentioned. (This should be dead code + * now that add_missing_from is history.) */ if (list_length(pstate->p_rtable) != 1) ereport(ERROR, @@ -2365,7 +2416,7 @@ transformIndexStmt(IndexStmt *stmt, const char *queryString) free_parsestate(pstate); - /* Close relation, but keep the lock */ + /* Close relation */ heap_close(rel, NoLock); return stmt; @@ -2395,12 +2446,17 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, /* * To avoid deadlock, make sure the first thing we do is grab - * AccessExclusiveLock on the target relation. This will be needed by + * AccessExclusiveLock on the target relation. This will be needed by * DefineQueryRewrite(), and we don't want to grab a lesser lock * beforehand. */ rel = heap_openrv(stmt->relation, AccessExclusiveLock); + if (rel->rd_rel->relkind == RELKIND_MATVIEW) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("rules on materialized views are not supported"))); + /* Set up pstate */ pstate = make_parsestate(NULL); pstate->p_sourcetext = queryString; @@ -2451,25 +2507,17 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, /* take care of the where clause */ *whereClause = transformWhereClause(pstate, (Node *) copyObject(stmt->whereClause), + EXPR_KIND_WHERE, "WHERE"); /* we have to fix its collations too */ assign_expr_collations(pstate, *whereClause); + /* this is probably dead code without add_missing_from: */ if (list_length(pstate->p_rtable) != 2) /* naughty, naughty... */ ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("rule WHERE condition cannot contain references to other relations"))); - /* aggregates not allowed (but subselects are okay) */ - if (pstate->p_hasAggs) - ereport(ERROR, - (errcode(ERRCODE_GROUPING_ERROR), - errmsg("cannot use aggregate function in rule WHERE condition"))); - if (pstate->p_hasWindowFuncs) - ereport(ERROR, - (errcode(ERRCODE_WINDOWING_ERROR), - errmsg("cannot use window function in rule WHERE condition"))); - /* * 'instead nothing' rules with a qualification need a query rangetable so * the rewrite handler can add the negated rule qualification to the @@ -2693,9 +2741,14 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, * Returns a List of utility commands to be done in sequence. One of these * will be the transformed AlterTableStmt, but there may be additional actions * to be done before and after the actual AlterTable() call. + * + * To avoid race conditions, it's important that this function rely only on + * the passed-in relid (and not on stmt->relation) to determine the target + * relation. */ List * -transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString) +transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, + const char *queryString) { Relation rel; ParseState *pstate; @@ -2707,7 +2760,6 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString) List *newcmds = NIL; bool skipValidation = true; AlterTableCmd *newcmd; - LOCKMODE lockmode; /* * We must not scribble on the passed-in AlterTableStmt, so copy it. (This @@ -2715,36 +2767,24 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString) */ stmt = (AlterTableStmt *) copyObject(stmt); - /* - * Determine the appropriate lock level for this list of subcommands. - */ - lockmode = AlterTableGetLockLevel(stmt->cmds); - - /* - * Acquire appropriate lock on the target relation, which will be held - * until end of transaction. This ensures any decisions we make here - * based on the state of the relation will still be good at execution. We - * must get lock now because execution will later require it; taking a - * lower grade lock now and trying to upgrade later risks deadlock. Any - * new commands we add after this must not upgrade the lock level - * requested here. - */ - rel = relation_openrv_extended(stmt->relation, lockmode, stmt->missing_ok); - if (rel == NULL) - { - /* this message is consistent with relation_openrv */ - ereport(NOTICE, - (errmsg("relation \"%s\" does not exist, skipping", - stmt->relation->relname))); - return NIL; - } + /* Caller is responsible for locking the relation */ + rel = relation_open(relid, NoLock); /* Set up pstate and CreateStmtContext */ pstate = make_parsestate(NULL); pstate->p_sourcetext = queryString; cxt.pstate = pstate; - cxt.stmtType = "ALTER TABLE"; + if (stmt->relkind == OBJECT_FOREIGN_TABLE) + { + cxt.stmtType = "ALTER FOREIGN TABLE"; + cxt.isforeign = true; + } + else + { + cxt.stmtType = "ALTER TABLE"; + cxt.isforeign = false; + } cxt.relation = stmt->relation; cxt.rel = rel; cxt.inhRelations = NIL; @@ -2862,7 +2902,7 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString) IndexStmt *idxstmt = (IndexStmt *) lfirst(l); Assert(IsA(idxstmt, IndexStmt)); - idxstmt = transformIndexStmt(idxstmt, queryString); + idxstmt = transformIndexStmt(relid, idxstmt, queryString); newcmd = makeNode(AlterTableCmd); newcmd->subtype = OidIsValid(idxstmt->indexOid) ? AT_AddIndexConstraint : AT_AddIndex; newcmd->def = (Node *) idxstmt; @@ -2886,7 +2926,7 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString) newcmds = lappend(newcmds, newcmd); } - /* Close rel but keep lock */ + /* Close rel */ relation_close(rel, NoLock); /* @@ -3080,7 +3120,7 @@ transformColumnType(CreateStmtContext *cxt, ColumnDef *column) * that the logic we use for determining forward references is * presently quite incomplete. * - * SQL92 also allows constraints to make forward references, so thumb through + * SQL also allows constraints to make forward references, so thumb through * the table columns and move forward references to a posterior alter-table * command. * diff --git a/src/backend/parser/parser.c b/src/backend/parser/parser.c index f9ec2b220a..663296683a 100644 --- a/src/backend/parser/parser.c +++ b/src/backend/parser/parser.c @@ -10,7 +10,7 @@ * analyze.c and related files. * * - * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION @@ -65,7 +65,7 @@ raw_parser(const char *str) * Intermediate filter between parser and core lexer (core_yylex in scan.l). * * The filter is needed because in some cases the standard SQL grammar - * requires more than one token lookahead. We reduce these cases to one-token + * requires more than one token lookahead. We reduce these cases to one-token * lookahead by combining tokens here, in order to keep the grammar LALR(1). * * Using a filter is simpler than trying to recognize multiword tokens @@ -133,7 +133,7 @@ base_yylex(YYSTYPE *lvalp, YYLTYPE *llocp, core_yyscan_t yyscanner) case WITH: /* - * WITH TIME must be reduced to one token + * WITH TIME and WITH ORDINALITY must each be reduced to one token */ cur_yylval = lvalp->core_yystype; cur_yylloc = *llocp; @@ -143,6 +143,9 @@ base_yylex(YYSTYPE *lvalp, YYLTYPE *llocp, core_yyscan_t yyscanner) case TIME: cur_token = WITH_TIME; break; + case ORDINALITY: + cur_token = WITH_ORDINALITY; + break; default: /* save the lookahead token for next time */ yyextra->lookahead_token = next_token; diff --git a/src/backend/parser/scan.l b/src/backend/parser/scan.l index 6b216b5378..146ae3db2d 100644 --- a/src/backend/parser/scan.l +++ b/src/backend/parser/scan.l @@ -21,7 +21,7 @@ * Postgres 9.2, this check is made automatically by the Makefile.) * * - * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION @@ -42,7 +42,13 @@ /* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */ #undef fprintf -#define fprintf(file, fmt, msg) ereport(ERROR, (errmsg_internal("%s", msg))) +#define fprintf(file, fmt, msg) fprintf_to_ereport(fmt, msg) + +static void +fprintf_to_ereport(const char *fmt, const char *msg) +{ + ereport(ERROR, (errmsg_internal("%s", msg))); +} /* * GUC variables. This is a DIRECT violation of the warning given at the @@ -91,6 +97,7 @@ static bool is_utf16_surrogate_first(pg_wchar c); static bool is_utf16_surrogate_second(pg_wchar c); static pg_wchar surrogate_pair_to_codepoint(pg_wchar first, pg_wchar second); static void addunicode(pg_wchar c, yyscan_t yyscanner); +static bool check_uescapechar(unsigned char escape); #define yyerror(msg) scanner_yyerror(msg, yyscanner) @@ -144,8 +151,13 @@ extern void core_yyset_column(int column_no, yyscan_t yyscanner); * <xe> extended quoted strings (support backslash escape sequences) * <xdolq> $foo$ quoted strings * <xui> quoted identifier with Unicode escapes + * <xuiend> end of a quoted identifier with Unicode escapes, UESCAPE can follow * <xus> quoted string with Unicode escapes + * <xusend> end of a quoted string with Unicode escapes, UESCAPE can follow * <xeu> Unicode surrogate pair in extended quoted string + * + * Remember to add an <<EOF>> case whenever you add a new exclusive state! + * The default one is probably not the right thing. */ %x xb @@ -156,7 +168,9 @@ extern void core_yyset_column(int column_no, yyscan_t yyscanner); %x xq %x xdolq %x xui +%x xuiend %x xus +%x xusend %x xeu /* @@ -273,17 +287,17 @@ xdinside [^"]+ /* Unicode escapes */ uescape [uU][eE][sS][cC][aA][pP][eE]{whitespace}*{quote}[^']{quote} /* error rule to avoid backup */ -uescapefail ("-"|[uU][eE][sS][cC][aA][pP][eE]{whitespace}*"-"|[uU][eE][sS][cC][aA][pP][eE]{whitespace}*{quote}[^']|[uU][eE][sS][cC][aA][pP][eE]{whitespace}*{quote}|[uU][eE][sS][cC][aA][pP][eE]{whitespace}*|[uU][eE][sS][cC][aA][pP]|[uU][eE][sS][cC][aA]|[uU][eE][sS][cC]|[uU][eE][sS]|[uU][eE]|[uU]) +uescapefail [uU][eE][sS][cC][aA][pP][eE]{whitespace}*"-"|[uU][eE][sS][cC][aA][pP][eE]{whitespace}*{quote}[^']|[uU][eE][sS][cC][aA][pP][eE]{whitespace}*{quote}|[uU][eE][sS][cC][aA][pP][eE]{whitespace}*|[uU][eE][sS][cC][aA][pP]|[uU][eE][sS][cC][aA]|[uU][eE][sS][cC]|[uU][eE][sS]|[uU][eE]|[uU] /* Quoted identifier with Unicode escapes */ xuistart [uU]&{dquote} -xuistop1 {dquote}{whitespace}*{uescapefail}? -xuistop2 {dquote}{whitespace}*{uescape} /* Quoted string with Unicode escapes */ xusstart [uU]&{quote} -xusstop1 {quote}{whitespace}*{uescapefail}? -xusstop2 {quote}{whitespace}*{uescape} + +/* Optional UESCAPE after a quoted string or identifier with Unicode escapes. */ +xustop1 {uescapefail}? +xustop2 {uescape} /* error rule to avoid backup */ xufailed [uU]& @@ -530,15 +544,32 @@ other . yylval->str = litbufdup(yyscanner); return SCONST; } -<xus>{xusstop1} { +<xus>{quotestop} | +<xus>{quotefail} { /* throw back all but the quote */ yyless(1); + /* xusend state looks for possible UESCAPE */ + BEGIN(xusend); + } +<xusend>{whitespace} { /* stay in xusend state over whitespace */ } +<xusend>{other} | +<xusend>{xustop1} | +<xusend><<EOF>> { + /* no UESCAPE after the quote, throw back everything */ + yyless(0); BEGIN(INITIAL); yylval->str = litbuf_udeescape('\\', yyscanner); return SCONST; } -<xus>{xusstop2} { +<xusend>{xustop2} { + /* found UESCAPE after the end quote */ BEGIN(INITIAL); + if (!check_uescapechar(yytext[yyleng-2])) + { + SET_YYLLOC(); + ADVANCE_YYLLOC(yyleng-2); + yyerror("invalid Unicode escape character"); + } yylval->str = litbuf_udeescape(yytext[yyleng-2], yyscanner); return SCONST; } @@ -696,29 +727,49 @@ other . yylval->str = ident; return IDENT; } -<xui>{xuistop1} { - char *ident; +<xui>{dquote} { + yyless(1); + /* xuiend state looks for possible UESCAPE */ + BEGIN(xuiend); + } +<xuiend>{whitespace} { /* stay in xuiend state over whitespace */ } +<xuiend>{other} | +<xuiend>{xustop1} | +<xuiend><<EOF>> { + /* no UESCAPE after the quote, throw back everything */ + char *ident; + int identlen; + + yyless(0); BEGIN(INITIAL); if (yyextra->literallen == 0) yyerror("zero-length delimited identifier"); ident = litbuf_udeescape('\\', yyscanner); - if (yyextra->literallen >= NAMEDATALEN) - truncate_identifier(ident, yyextra->literallen, true); + identlen = strlen(ident); + if (identlen >= NAMEDATALEN) + truncate_identifier(ident, identlen, true); yylval->str = ident; - /* throw back all but the quote */ - yyless(1); return IDENT; } -<xui>{xuistop2} { - char *ident; +<xuiend>{xustop2} { + /* found UESCAPE after the end quote */ + char *ident; + int identlen; BEGIN(INITIAL); if (yyextra->literallen == 0) yyerror("zero-length delimited identifier"); + if (!check_uescapechar(yytext[yyleng-2])) + { + SET_YYLLOC(); + ADVANCE_YYLLOC(yyleng-2); + yyerror("invalid Unicode escape character"); + } ident = litbuf_udeescape(yytext[yyleng - 2], yyscanner); - if (yyextra->literallen >= NAMEDATALEN) - truncate_identifier(ident, yyextra->literallen, true); + identlen = strlen(ident); + if (identlen >= NAMEDATALEN) + truncate_identifier(ident, identlen, true); yylval->str = ident; return IDENT; } @@ -1197,22 +1248,29 @@ addunicode(pg_wchar c, core_yyscan_t yyscanner) addlit(buf, pg_mblen(buf), yyscanner); } -static char * -litbuf_udeescape(unsigned char escape, core_yyscan_t yyscanner) +/* is 'escape' acceptable as Unicode escape character (UESCAPE syntax) ? */ +static bool +check_uescapechar(unsigned char escape) { - char *new; - char *litbuf, *in, *out; - pg_wchar pair_first = 0; - if (isxdigit(escape) || escape == '+' || escape == '\'' || escape == '"' || scanner_isspace(escape)) { - ADVANCE_YYLLOC(yyextra->literallen + yyleng + 1); - yyerror("invalid Unicode escape character"); + return false; } + else + return true; +} + +/* like litbufdup, but handle unicode escapes */ +static char * +litbuf_udeescape(unsigned char escape, core_yyscan_t yyscanner) +{ + char *new; + char *litbuf, *in, *out; + pg_wchar pair_first = 0; /* Make literalbuf null-terminated to simplify the scanning loop */ litbuf = yyextra->literalbuf; diff --git a/src/backend/parser/scansup.c b/src/backend/parser/scansup.c index 6101457a10..e9fa5dd0b0 100644 --- a/src/backend/parser/scansup.c +++ b/src/backend/parser/scansup.c @@ -4,7 +4,7 @@ * support routines for the lex/flex scanner, used by both the normal * backend as well as the bootstrap backend * - * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * @@ -56,6 +56,8 @@ scanstr(const char *s) * appear in pairs, so there should be another character. */ i++; + /* The bootstrap parser is not as smart, so check here. */ + Assert(s[i] == '\''); newStr[j] = s[i]; } else if (s[i] == '\\') @@ -130,8 +132,10 @@ downcase_truncate_identifier(const char *ident, int len, bool warn) { char *result; int i; + bool enc_is_single_byte; result = palloc(len + 1); + enc_is_single_byte = pg_database_encoding_max_length() == 1; /* * SQL99 specifies Unicode-aware case normalization, which we don't yet @@ -139,8 +143,8 @@ downcase_truncate_identifier(const char *ident, int len, bool warn) * locale-aware translation. However, there are some locales where this * is not right either (eg, Turkish may do strange things with 'i' and * 'I'). Our current compromise is to use tolower() for characters with - * the high bit set, and use an ASCII-only downcasing for 7-bit - * characters. + * the high bit set, as long as they aren't part of a multi-byte + * character, and use an ASCII-only downcasing for 7-bit characters. */ for (i = 0; i < len; i++) { @@ -148,7 +152,7 @@ downcase_truncate_identifier(const char *ident, int len, bool warn) if (ch >= 'A' && ch <= 'Z') ch += 'a' - 'A'; - else if (IS_HIGHBIT_SET(ch) && isupper(ch)) + else if (enc_is_single_byte && IS_HIGHBIT_SET(ch) && isupper(ch)) ch = tolower(ch); result[i] = (char) ch; } |
