]> granicus.if.org Git - postgresql/blobdiff - src/backend/parser/analyze.c
Give a suitable HINT when an INSERT's data source is a RowExpr containing
[postgresql] / src / backend / parser / analyze.c
index 3135d8524675545d3d0cc5542b9538065fc890db..a1ad3e20e5bf90dfbe46e1a6d969f09d6bb868d6 100644 (file)
  * optimizable statements.
  *
  *
- * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.367 2007/06/23 22:12:51 tgl Exp $
+ *     $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.404 2010/09/18 18:37:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "postgres.h"
 
+#include "access/sysattr.h"
 #include "catalog/pg_type.h"
 #include "nodes/makefuncs.h"
-#include "optimizer/clauses.h"
+#include "nodes/nodeFuncs.h"
 #include "optimizer/var.h"
 #include "parser/analyze.h"
 #include "parser/parse_agg.h"
 #include "parser/parse_clause.h"
 #include "parser/parse_coerce.h"
-#include "parser/parse_expr.h"
+#include "parser/parse_cte.h"
+#include "parser/parse_oper.h"
+#include "parser/parse_param.h"
 #include "parser/parse_relation.h"
 #include "parser/parse_target.h"
 #include "parser/parsetree.h"
-
-
-typedef struct
-{
-       Oid                *paramTypes;
-       int                     numParams;
-} check_parameter_resolution_context;
+#include "rewrite/rewriteManip.h"
+#include "utils/rel.h"
 
 
 static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
 static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt);
 static List *transformInsertRow(ParseState *pstate, List *exprlist,
                                   List *stmtcols, List *icolumns, List *attrnos);
+static int     count_rowexpr_columns(ParseState *pstate, Node *expr);
 static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
 static Query *transformValuesClause(ParseState *pstate, SelectStmt *stmt);
 static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
-static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt);
-static void getSetColTypes(ParseState *pstate, Node *node,
-                          List **colTypes, List **colTypmods);
+static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
+                                                 bool isTopLevel, List **colInfo);
+static void determineRecursiveColTypes(ParseState *pstate,
+                                                  Node *larg, List *lcolinfo);
 static void applyColumnNames(List *dst, List *src);
 static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
 static List *transformReturningList(ParseState *pstate, List *returningList);
 static Query *transformDeclareCursorStmt(ParseState *pstate,
                                                   DeclareCursorStmt *stmt);
 static Query *transformExplainStmt(ParseState *pstate,
-                                                  ExplainStmt *stmt);
-static void transformLockingClause(Query *qry, LockingClause *lc);
-static bool check_parameter_resolution_walker(Node *node,
-                                                               check_parameter_resolution_context *context);
+                                        ExplainStmt *stmt);
+static void transformLockingClause(ParseState *pstate, Query *qry,
+                                          LockingClause *lc, bool pushedDown);
 
 
 /*
  * parse_analyze
  *             Analyze a raw parse tree and transform it to Query form.
  *
- * If available, pass the source text from which the raw parse tree was
- * generated; it's OK to pass NULL if this is not available.
- *
  * 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.
  */
@@ -88,10 +84,12 @@ parse_analyze(Node *parseTree, const char *sourceText,
        ParseState *pstate = make_parsestate(NULL);
        Query      *query;
 
+       Assert(sourceText != NULL); /* required as of 8.4 */
+
        pstate->p_sourcetext = sourceText;
-       pstate->p_paramtypes = paramTypes;
-       pstate->p_numparams = numParams;
-       pstate->p_variableparams = false;
+
+       if (numParams > 0)
+               parse_fixed_parameters(pstate, paramTypes, numParams);
 
        query = transformStmt(pstate, parseTree);
 
@@ -114,27 +112,18 @@ parse_analyze_varparams(Node *parseTree, const char *sourceText,
        ParseState *pstate = make_parsestate(NULL);
        Query      *query;
 
-       pstate->p_sourcetext = sourceText;
-       pstate->p_paramtypes = *paramTypes;
-       pstate->p_numparams = *numParams;
-       pstate->p_variableparams = true;
+       Assert(sourceText != NULL); /* required as of 8.4 */
 
-       query = transformStmt(pstate, parseTree);
+       pstate->p_sourcetext = sourceText;
 
-       *paramTypes = pstate->p_paramtypes;
-       *numParams = pstate->p_numparams;
+       parse_variable_parameters(pstate, paramTypes, numParams);
 
-       free_parsestate(pstate);
+       query = transformStmt(pstate, parseTree);
 
        /* make sure all is well with parameter types */
-       if (*numParams > 0)
-       {
-               check_parameter_resolution_context context;
+       check_variable_parameters(pstate, query);
 
-               context.paramTypes = *paramTypes;
-               context.numParams = *numParams;
-               check_parameter_resolution_walker((Node *) query, &context);
-       }
+       free_parsestate(pstate);
 
        return query;
 }
@@ -144,11 +133,16 @@ parse_analyze_varparams(Node *parseTree, const char *sourceText,
  *             Entry point for recursively analyzing a sub-statement.
  */
 Query *
-parse_sub_analyze(Node *parseTree, ParseState *parentParseState)
+parse_sub_analyze(Node *parseTree, ParseState *parentParseState,
+                                 CommonTableExpr *parentCTE,
+                                 bool locked_from_parent)
 {
        ParseState *pstate = make_parsestate(parentParseState);
        Query      *query;
 
+       pstate->p_parent_cte = parentCTE;
+       pstate->p_locked_from_parent = locked_from_parent;
+
        query = transformStmt(pstate, parseTree);
 
        free_parsestate(pstate);
@@ -227,6 +221,56 @@ transformStmt(ParseState *pstate, Node *parseTree)
        return result;
 }
 
+/*
+ * analyze_requires_snapshot
+ *             Returns true if a snapshot must be set before doing parse analysis
+ *             on the given raw parse tree.
+ *
+ * Classification here should match transformStmt(); but we also have to
+ * allow a NULL input (for Parse/Bind of an empty query string).
+ */
+bool
+analyze_requires_snapshot(Node *parseTree)
+{
+       bool            result;
+
+       if (parseTree == NULL)
+               return false;
+
+       switch (nodeTag(parseTree))
+       {
+                       /*
+                        * Optimizable statements
+                        */
+               case T_InsertStmt:
+               case T_DeleteStmt:
+               case T_UpdateStmt:
+               case T_SelectStmt:
+                       result = true;
+                       break;
+
+                       /*
+                        * Special cases
+                        */
+               case T_DeclareCursorStmt:
+                       /* yes, because it's analyzed just like SELECT */
+                       result = true;
+                       break;
+
+               case T_ExplainStmt:
+                       /* yes, because we must analyze the contained statement */
+                       result = true;
+                       break;
+
+               default:
+                       /* other utility statements don't have any real parse analysis */
+                       result = false;
+                       break;
+       }
+
+       return result;
+}
+
 /*
  * transformDeleteStmt -
  *       transforms a Delete Statement
@@ -267,6 +311,9 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
        qry->hasAggs = pstate->p_hasAggs;
        if (pstate->p_hasAggs)
                parseCheckAggregates(pstate, qry);
+       qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
+       if (pstate->p_hasWindowFuncs)
+               parseCheckWindowFuncs(pstate, qry);
 
        return qry;
 }
@@ -320,6 +367,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
                pstate->p_relnamespace = NIL;
                sub_varnamespace = pstate->p_varnamespace;
                pstate->p_varnamespace = NIL;
+               /* There can't be any outer WITH to worry about */
+               Assert(pstate->p_ctenamespace == NIL);
        }
        else
        {
@@ -374,6 +423,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
                 * bugs of just that nature...)
                 */
                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;
 
@@ -382,13 +432,16 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
                free_parsestate(sub_pstate);
 
                /* The grammar should have produced a SELECT, but it might have INTO */
-               Assert(IsA(selectQuery, Query));
-               Assert(selectQuery->commandType == CMD_SELECT);
-               Assert(selectQuery->utilityStmt == NULL);
+               if (!IsA(selectQuery, Query) ||
+                       selectQuery->commandType != CMD_SELECT ||
+                       selectQuery->utilityStmt != NULL)
+                       elog(ERROR, "unexpected non-SELECT command in INSERT ... SELECT");
                if (selectQuery->intoClause)
                        ereport(ERROR,
                                        (errcode(ERRCODE_SYNTAX_ERROR),
-                                        errmsg("INSERT ... SELECT cannot specify INTO")));
+                                        errmsg("INSERT ... SELECT cannot specify INTO"),
+                                        parser_errposition(pstate,
+                                                  exprLocation((Node *) selectQuery->intoClause))));
 
                /*
                 * Make the source be a subquery in the INSERT's rangetable, and add
@@ -431,11 +484,12 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
                                exprType((Node *) tle->expr) == UNKNOWNOID)
                                expr = tle->expr;
                        else
-                               expr = (Expr *) makeVar(rtr->rtindex,
-                                                                               tle->resno,
-                                                                               exprType((Node *) tle->expr),
-                                                                               exprTypmod((Node *) tle->expr),
-                                                                               0);
+                       {
+                               Var                *var = makeVarFromTargetEntry(rtr->rtindex, tle);
+
+                               var->location = exprLocation((Node *) tle->expr);
+                               expr = (Expr *) var;
+                       }
                        exprList = lappend(exprList, expr);
                }
 
@@ -455,6 +509,13 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
                List       *exprsLists = NIL;
                int                     sublist_length = -1;
 
+               /* process the WITH clause */
+               if (selectStmt->withClause)
+               {
+                       qry->hasRecursive = selectStmt->withClause->recursive;
+                       qry->cteList = transformWithClause(pstate, selectStmt->withClause);
+               }
+
                foreach(lc, selectStmt->valuesLists)
                {
                        List       *sublist = (List *) lfirst(lc);
@@ -476,7 +537,9 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
                        {
                                ereport(ERROR,
                                                (errcode(ERRCODE_SYNTAX_ERROR),
-                                                errmsg("VALUES lists must all be the same length")));
+                                                errmsg("VALUES lists must all be the same length"),
+                                                parser_errposition(pstate,
+                                                                                       exprLocation((Node *) sublist))));
                        }
 
                        /* Prepare row for assignment to target table */
@@ -495,7 +558,9 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
                if (pstate->p_joinlist != NIL)
                        ereport(ERROR,
                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                        errmsg("VALUES must not contain table references")));
+                                        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
@@ -508,7 +573,9 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
                        ereport(ERROR,
                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                         errmsg("VALUES must not contain OLD or NEW references"),
-                                        errhint("Use SELECT ... UNION ALL ... instead.")));
+                                        errhint("Use SELECT ... UNION ALL ... instead."),
+                                        parser_errposition(pstate,
+                                                         locate_var_of_level((Node *) exprsLists, 0))));
 
                /*
                 * Generate the VALUES RTE
@@ -523,7 +590,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
                /*
                 * Generate list of Vars referencing the RTE
                 */
-               expandRTE(rte, rtr->rtindex, 0, false, NULL, &exprList);
+               expandRTE(rte, rtr->rtindex, 0, -1, false, NULL, &exprList);
        }
        else
        {
@@ -542,6 +609,13 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 
                Assert(list_length(valuesLists) == 1);
 
+               /* process the WITH clause */
+               if (selectStmt->withClause)
+               {
+                       qry->hasRecursive = selectStmt->withClause->recursive;
+                       qry->cteList = transformWithClause(pstate, selectStmt->withClause);
+               }
+
                /* Do basic expression transformation (same as a ROW() expr) */
                exprList = transformExpressionList(pstate,
                                                                                   (List *) linitial(valuesLists));
@@ -554,7 +628,9 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 
        /*
         * Generate query's target list using the computed list of expressions.
+        * Also, mark all the target columns as needing insert permissions.
         */
+       rte = pstate->p_target_rangetblentry;
        qry->targetList = NIL;
        icols = list_head(icolumns);
        attnos = list_head(attrnos);
@@ -562,17 +638,22 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
        {
                Expr       *expr = (Expr *) lfirst(lc);
                ResTarget  *col;
+               AttrNumber      attr_num;
                TargetEntry *tle;
 
                col = (ResTarget *) lfirst(icols);
                Assert(IsA(col, ResTarget));
+               attr_num = (AttrNumber) lfirst_int(attnos);
 
                tle = makeTargetEntry(expr,
-                                                         (AttrNumber) lfirst_int(attnos),
+                                                         attr_num,
                                                          col->name,
                                                          false);
                qry->targetList = lappend(qry->targetList, tle);
 
+               rte->modifiedCols = bms_add_member(rte->modifiedCols,
+                                                         attr_num - FirstLowInvalidHeapAttributeNumber);
+
                icols = lnext(icols);
                attnos = lnext(attnos);
        }
@@ -602,7 +683,15 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
        if (pstate->p_hasAggs)
                ereport(ERROR,
                                (errcode(ERRCODE_GROUPING_ERROR),
-                                errmsg("cannot use aggregate function in VALUES")));
+                                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))));
 
        return qry;
 }
@@ -632,12 +721,33 @@ transformInsertRow(ParseState *pstate, List *exprlist,
        if (list_length(exprlist) > list_length(icolumns))
                ereport(ERROR,
                                (errcode(ERRCODE_SYNTAX_ERROR),
-                                errmsg("INSERT has more expressions than target columns")));
+                                errmsg("INSERT has more expressions than target columns"),
+                                parser_errposition(pstate,
+                                                                       exprLocation(list_nth(exprlist,
+                                                                                                 list_length(icolumns))))));
        if (stmtcols != NIL &&
                list_length(exprlist) < list_length(icolumns))
+       {
+               /*
+                * We can get here for cases like INSERT ... SELECT (a,b,c) FROM ...
+                * where the user accidentally created a RowExpr instead of separate
+                * columns.  Add a suitable hint if that seems to be the problem,
+                * because the main error message is quite misleading for this case.
+                * (If there's no stmtcols, you'll get something about data type
+                * mismatch, which is less misleading so we don't worry about giving
+                * a hint in that case.)
+                */
                ereport(ERROR,
                                (errcode(ERRCODE_SYNTAX_ERROR),
-                                errmsg("INSERT has more target columns than expressions")));
+                                errmsg("INSERT has more target columns than expressions"),
+                                ((list_length(exprlist) == 1 &&
+                                  count_rowexpr_columns(pstate, linitial(exprlist)) ==
+                                  list_length(icolumns)) ?
+                                 errhint("The insertion source is a row expression containing the same number of columns expected by the INSERT. Did you accidentally use extra parentheses?") : 0),
+                                parser_errposition(pstate,
+                                                                       exprLocation(list_nth(icolumns,
+                                                                                                 list_length(exprlist))))));
+       }
 
        /*
         * Prepare columns for assignment to target table.
@@ -668,6 +778,49 @@ transformInsertRow(ParseState *pstate, List *exprlist,
        return result;
 }
 
+/*
+ * count_rowexpr_columns -
+ *       get number of columns contained in a ROW() expression;
+ *       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
+ * because that's what we'll get in the INSERT ... SELECT (...) case.
+ */
+static int
+count_rowexpr_columns(ParseState *pstate, Node *expr)
+{
+       if (expr == NULL)
+               return -1;
+       if (IsA(expr, RowExpr))
+               return list_length(((RowExpr *) expr)->args);
+       if (IsA(expr, Var))
+       {
+               Var                *var = (Var *) expr;
+               AttrNumber      attnum = var->varattno;
+
+               if (attnum > 0 && var->vartype == RECORDOID)
+               {
+                       RangeTblEntry *rte;
+
+                       rte = GetRTEByRangeTablePosn(pstate, var->varno, var->varlevelsup);
+                       if (rte->rtekind == RTE_SUBQUERY)
+                       {
+                               /* Subselect-in-FROM: examine sub-select's output expr */
+                               TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,
+                                                                                                       attnum);
+
+                               if (ste == NULL || ste->resjunk)
+                                       return -1;
+                               expr = (Node *) ste->expr;
+                               if (IsA(expr, RowExpr))
+                                       return list_length(((RowExpr *) expr)->args);
+                       }
+               }
+       }
+       return -1;
+}
+
 
 /*
  * transformSelectStmt -
@@ -685,9 +838,19 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 
        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);
+       }
+
        /* make FOR UPDATE/FOR SHARE info available to addRangeTableEntry */
        pstate->p_locking_clause = stmt->lockingClause;
 
+       /* make WINDOW info available for window functions, too */
+       pstate->p_windowdefs = stmt->windowClause;
+
        /* process the FROM clause */
        transformFromClause(pstate, stmt->fromClause);
 
@@ -708,28 +871,57 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 
        /*
         * Transform sorting/grouping stuff.  Do ORDER BY first because both
-        * transformGroupClause and transformDistinctClause need the results.
+        * transformGroupClause and transformDistinctClause need the results. Note
+        * that these functions can also change the targetList, so it's passed to
+        * them by reference.
         */
        qry->sortClause = transformSortClause(pstate,
                                                                                  stmt->sortClause,
                                                                                  &qry->targetList,
-                                                                                 true /* fix unknowns */ );
+                                                                                 true /* fix unknowns */ ,
+                                                                                 false /* allow SQL92 rules */ );
 
        qry->groupClause = transformGroupClause(pstate,
                                                                                        stmt->groupClause,
                                                                                        &qry->targetList,
-                                                                                       qry->sortClause);
+                                                                                       qry->sortClause,
+                                                                                       false /* allow SQL92 rules */ );
 
-       qry->distinctClause = transformDistinctClause(pstate,
-                                                                                                 stmt->distinctClause,
-                                                                                                 &qry->targetList,
-                                                                                                 &qry->sortClause);
+       if (stmt->distinctClause == NIL)
+       {
+               qry->distinctClause = NIL;
+               qry->hasDistinctOn = false;
+       }
+       else if (linitial(stmt->distinctClause) == NULL)
+       {
+               /* We had SELECT DISTINCT */
+               qry->distinctClause = transformDistinctClause(pstate,
+                                                                                                         &qry->targetList,
+                                                                                                         qry->sortClause,
+                                                                                                         false);
+               qry->hasDistinctOn = false;
+       }
+       else
+       {
+               /* We had SELECT DISTINCT ON */
+               qry->distinctClause = transformDistinctOnClause(pstate,
+                                                                                                               stmt->distinctClause,
+                                                                                                               &qry->targetList,
+                                                                                                               qry->sortClause);
+               qry->hasDistinctOn = true;
+       }
 
+       /* transform LIMIT */
        qry->limitOffset = transformLimitClause(pstate, stmt->limitOffset,
                                                                                        "OFFSET");
        qry->limitCount = transformLimitClause(pstate, stmt->limitCount,
                                                                                   "LIMIT");
 
+       /* transform window clauses after we have seen all window functions */
+       qry->windowClause = transformWindowDefinitions(pstate,
+                                                                                                  pstate->p_windowdefs,
+                                                                                                  &qry->targetList);
+
        /* handle any SELECT INTO/CREATE TABLE AS spec */
        if (stmt->intoClause)
        {
@@ -745,10 +937,14 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
        qry->hasAggs = pstate->p_hasAggs;
        if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
                parseCheckAggregates(pstate, qry);
+       qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
+       if (pstate->p_hasWindowFuncs)
+               parseCheckWindowFuncs(pstate, qry);
 
        foreach(l, stmt->lockingClause)
        {
-               transformLockingClause(qry, (LockingClause *) lfirst(l));
+               transformLockingClause(pstate, qry,
+                                                          (LockingClause *) lfirst(l), false);
        }
 
        return qry;
@@ -766,7 +962,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
 {
        Query      *qry = makeNode(Query);
        List       *exprsLists = NIL;
-       List      **coltype_lists = NULL;
+       List      **colexprs = NULL;
        Oid                *coltypes = NULL;
        int                     sublist_length = -1;
        List       *newExprsLists;
@@ -785,8 +981,16 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
        Assert(stmt->whereClause == NULL);
        Assert(stmt->groupClause == NIL);
        Assert(stmt->havingClause == NULL);
+       Assert(stmt->windowClause == NIL);
        Assert(stmt->op == SETOP_NONE);
 
+       /* process the WITH clause independently of all else */
+       if (stmt->withClause)
+       {
+               qry->hasRecursive = stmt->withClause->recursive;
+               qry->cteList = transformWithClause(pstate, stmt->withClause);
+       }
+
        /*
         * For each row of VALUES, transform the raw expressions and gather type
         * information.  This is also a handy place to reject DEFAULT nodes, which
@@ -808,19 +1012,22 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
                {
                        /* Remember post-transformation length of first sublist */
                        sublist_length = list_length(sublist);
-                       /* and allocate arrays for column-type info */
-                       coltype_lists = (List **) palloc0(sublist_length * sizeof(List *));
+                       /* and allocate arrays for per-column info */
+                       colexprs = (List **) palloc0(sublist_length * sizeof(List *));
                        coltypes = (Oid *) palloc0(sublist_length * sizeof(Oid));
                }
                else if (sublist_length != list_length(sublist))
                {
                        ereport(ERROR,
                                        (errcode(ERRCODE_SYNTAX_ERROR),
-                                        errmsg("VALUES lists must all be the same length")));
+                                        errmsg("VALUES lists must all be the same length"),
+                                        parser_errposition(pstate,
+                                                                               exprLocation((Node *) sublist))));
                }
 
                exprsLists = lappend(exprsLists, sublist);
 
+               /* Check for DEFAULT and build per-column expression lists */
                i = 0;
                foreach(lc2, sublist)
                {
@@ -829,8 +1036,9 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
                        if (IsA(col, SetToDefault))
                                ereport(ERROR,
                                                (errcode(ERRCODE_SYNTAX_ERROR),
-                                                errmsg("DEFAULT can only appear in a VALUES list within INSERT")));
-                       coltype_lists[i] = lappend_oid(coltype_lists[i], exprType(col));
+                                                errmsg("DEFAULT can only appear in a VALUES list within INSERT"),
+                                                parser_errposition(pstate, exprLocation(col))));
+                       colexprs[i] = lappend(colexprs[i], col);
                        i++;
                }
        }
@@ -841,7 +1049,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
         */
        for (i = 0; i < sublist_length; i++)
        {
-               coltypes[i] = select_common_type(coltype_lists[i], "VALUES");
+               coltypes[i] = select_common_type(pstate, colexprs[i], "VALUES", NULL);
        }
 
        newExprsLists = NIL;
@@ -878,7 +1086,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
         * Generate a targetlist as though expanding "*"
         */
        Assert(pstate->p_next_resno == 1);
-       qry->targetList = expandRelAttrs(pstate, rte, rtr->rtindex, 0);
+       qry->targetList = expandRelAttrs(pstate, rte, rtr->rtindex, 0, -1);
 
        /*
         * The grammar allows attaching ORDER BY, LIMIT, and FOR UPDATE to a
@@ -887,7 +1095,8 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
        qry->sortClause = transformSortClause(pstate,
                                                                                  stmt->sortClause,
                                                                                  &qry->targetList,
-                                                                                 true /* fix unknowns */ );
+                                                                                 true /* fix unknowns */ ,
+                                                                                 false /* allow SQL92 rules */ );
 
        qry->limitOffset = transformLimitClause(pstate, stmt->limitOffset,
                                                                                        "OFFSET");
@@ -916,7 +1125,9 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
        if (list_length(pstate->p_joinlist) != 1)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                errmsg("VALUES must not contain table references")));
+                                errmsg("VALUES must not contain table references"),
+                                parser_errposition(pstate,
+                                                  locate_var_of_level((Node *) newExprsLists, 0))));
 
        /*
         * Another thing we can't currently support is NEW/OLD references in rules
@@ -929,7 +1140,9 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("VALUES must not contain OLD or NEW references"),
-                                errhint("Use SELECT ... UNION ALL ... instead.")));
+                                errhint("Use SELECT ... UNION ALL ... instead."),
+                                parser_errposition(pstate,
+                                                  locate_var_of_level((Node *) newExprsLists, 0))));
 
        qry->rtable = pstate->p_rtable;
        qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
@@ -939,7 +1152,15 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
        if (pstate->p_hasAggs)
                ereport(ERROR,
                                (errcode(ERRCODE_GROUPING_ERROR),
-                                errmsg("cannot use aggregate function in VALUES")));
+                                errmsg("cannot use aggregate function in VALUES"),
+                                parser_errposition(pstate,
+                                                  locate_agg_of_level((Node *) newExprsLists, 0))));
+       if (pstate->p_hasWindowFuncs)
+               ereport(ERROR,
+                               (errcode(ERRCODE_WINDOWING_ERROR),
+                                errmsg("cannot use window function in VALUES"),
+                                parser_errposition(pstate,
+                                                               locate_windowfunc((Node *) newExprsLists))));
 
        return qry;
 }
@@ -962,6 +1183,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
        int                     leftmostRTI;
        Query      *leftmostQuery;
        SetOperationStmt *sostmt;
+       List       *socolinfo;
        List       *intoColNames = NIL;
        List       *sortClause;
        Node       *limitOffset;
@@ -975,13 +1197,20 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
        List       *targetvars,
                           *targetnames,
                           *sv_relnamespace,
-                          *sv_varnamespace,
-                          *sv_rtable;
+                          *sv_varnamespace;
+       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);
+       }
+
        /*
         * Find leftmost leaf SelectStmt; extract the one-time-only items from it
         * and from the top-level node.
@@ -1024,7 +1253,9 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
        /*
         * Recursively transform the components of the tree.
         */
-       sostmt = (SetOperationStmt *) transformSetOperationTree(pstate, stmt);
+       sostmt = (SetOperationStmt *) transformSetOperationTree(pstate, stmt,
+                                                                                                                       true,
+                                                                                                                       &socolinfo);
        Assert(sostmt && IsA(sostmt, SetOperationStmt));
        qry->setOperations = (Node *) sostmt;
 
@@ -1062,21 +1293,22 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
                TargetEntry *lefttle = (TargetEntry *) lfirst(left_tlist);
                char       *colName;
                TargetEntry *tle;
-               Expr       *expr;
+               Var                *var;
 
                Assert(!lefttle->resjunk);
                colName = pstrdup(lefttle->resname);
-               expr = (Expr *) makeVar(leftmostRTI,
-                                                               lefttle->resno,
-                                                               colType,
-                                                               colTypmod,
-                                                               0);
-               tle = makeTargetEntry(expr,
+               var = makeVar(leftmostRTI,
+                                         lefttle->resno,
+                                         colType,
+                                         colTypmod,
+                                         0);
+               var->location = exprLocation((Node *) lefttle->expr);
+               tle = makeTargetEntry((Expr *) var,
                                                          (AttrNumber) pstate->p_next_resno++,
                                                          colName,
                                                          false);
                qry->targetList = lappend(qry->targetList, tle);
-               targetvars = lappend(targetvars, expr);
+               targetvars = lappend(targetvars, var);
                targetnames = lappend(targetnames, makeString(colName));
                left_tlist = lnext(left_tlist);
        }
@@ -1091,16 +1323,15 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
         * "ORDER BY upper(foo)" will draw the right error message rather than
         * "foo not found".
         */
-       jrte = addRangeTableEntryForJoin(NULL,
+       sv_rtable_length = list_length(pstate->p_rtable);
+
+       jrte = addRangeTableEntryForJoin(pstate,
                                                                         targetnames,
                                                                         JOIN_INNER,
                                                                         targetvars,
                                                                         NULL,
                                                                         false);
 
-       sv_rtable = pstate->p_rtable;
-       pstate->p_rtable = list_make1(jrte);
-
        sv_relnamespace = pstate->p_relnamespace;
        pstate->p_relnamespace = NIL;           /* no qualified names allowed */
 
@@ -1118,9 +1349,10 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
        qry->sortClause = transformSortClause(pstate,
                                                                                  sortClause,
                                                                                  &qry->targetList,
-                                                                                 false /* no unknowns expected */ );
+                                                                                 false /* no unknowns expected */ ,
+                                                                                 false /* allow SQL92 rules */ );
 
-       pstate->p_rtable = sv_rtable;
+       pstate->p_rtable = list_truncate(pstate->p_rtable, sv_rtable_length);
        pstate->p_relnamespace = sv_relnamespace;
        pstate->p_varnamespace = sv_varnamespace;
 
@@ -1129,7 +1361,9 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("invalid UNION/INTERSECT/EXCEPT ORDER BY clause"),
                                 errdetail("Only result column names can be used, not expressions or functions."),
-                                errhint("Add the expression/function to every SELECT, or move the UNION into a FROM clause.")));
+                                errhint("Add the expression/function to every SELECT, or move the UNION into a FROM clause."),
+                                parser_errposition(pstate,
+                                                  exprLocation(list_nth(qry->targetList, tllen)))));
 
        qry->limitOffset = transformLimitClause(pstate, limitOffset,
                                                                                        "OFFSET");
@@ -1156,10 +1390,14 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
        qry->hasAggs = pstate->p_hasAggs;
        if (pstate->p_hasAggs || qry->groupClause || qry->havingQual)
                parseCheckAggregates(pstate, qry);
+       qry->hasWindowFuncs = pstate->p_hasWindowFuncs;
+       if (pstate->p_hasWindowFuncs)
+               parseCheckWindowFuncs(pstate, qry);
 
        foreach(l, lockingClause)
        {
-               transformLockingClause(qry, (LockingClause *) lfirst(l));
+               transformLockingClause(pstate, qry,
+                                                          (LockingClause *) lfirst(l), false);
        }
 
        return qry;
@@ -1168,9 +1406,17 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
 /*
  * transformSetOperationTree
  *             Recursively transform leaves and internal nodes of a set-op tree
+ *
+ * In addition to returning the transformed node, we return a list of
+ * expression nodes showing the type, typmod, and location (for error messages)
+ * of each output column of the set-op node.  This is used only during the
+ * internal recursion of this function.  At the upper levels we use
+ * SetToDefault nodes for this purpose, since they carry exactly the fields
+ * needed, but any other expression node type would do as well.
  */
 static Node *
-transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
+transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
+                                                 bool isTopLevel, List **colInfo)
 {
        bool            isLeaf;
 
@@ -1182,7 +1428,10 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
        if (stmt->intoClause)
                ereport(ERROR,
                                (errcode(ERRCODE_SYNTAX_ERROR),
-                                errmsg("INTO is only allowed on first SELECT of UNION/INTERSECT/EXCEPT")));
+                                errmsg("INTO is only allowed on first SELECT of UNION/INTERSECT/EXCEPT"),
+                                parser_errposition(pstate,
+                                                                 exprLocation((Node *) stmt->intoClause))));
+
        /* We don't support FOR UPDATE/SHARE with set ops at the moment. */
        if (stmt->lockingClause)
                ereport(ERROR,
@@ -1190,7 +1439,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
                                 errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT")));
 
        /*
-        * If an internal node of a set-op tree has ORDER BY, UPDATE, or LIMIT
+        * 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.
@@ -1217,6 +1466,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
                char            selectName[32];
                RangeTblEntry *rte;
                RangeTblRef *rtr;
+               ListCell   *tl;
 
                /*
                 * Transform SelectStmt into a Query.
@@ -1225,7 +1475,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
                 * of this sub-query, because they are not in the toplevel pstate's
                 * namespace list.
                 */
-               selectQuery = parse_sub_analyze((Node *) stmt, pstate);
+               selectQuery = parse_sub_analyze((Node *) stmt, pstate, NULL, false);
 
                /*
                 * Check for bogus references to Vars on the current query level (but
@@ -1238,7 +1488,21 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
                        if (contain_vars_of_level((Node *) selectQuery, 1))
                                ereport(ERROR,
                                                (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
-                                                errmsg("UNION/INTERSECT/EXCEPT member statement cannot refer to other relations of same query level")));
+                                                errmsg("UNION/INTERSECT/EXCEPT member statement cannot refer to other relations of same query level"),
+                                                parser_errposition(pstate,
+                                                        locate_var_of_level((Node *) selectQuery, 1))));
+               }
+
+               /*
+                * Extract a list of the result expressions for upper-level checking.
+                */
+               *colInfo = NIL;
+               foreach(tl, selectQuery->targetList)
+               {
+                       TargetEntry *tle = (TargetEntry *) lfirst(tl);
+
+                       if (!tle->resjunk)
+                               *colInfo = lappend(*colInfo, tle->expr);
                }
 
                /*
@@ -1264,14 +1528,10 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
        {
                /* Process an internal node (set operation node) */
                SetOperationStmt *op = makeNode(SetOperationStmt);
-               List       *lcoltypes;
-               List       *rcoltypes;
-               List       *lcoltypmods;
-               List       *rcoltypmods;
-               ListCell   *lct;
-               ListCell   *rct;
-               ListCell   *lcm;
-               ListCell   *rcm;
+               List       *lcolinfo;
+               List       *rcolinfo;
+               ListCell   *lci;
+               ListCell   *rci;
                const char *context;
 
                context = (stmt->op == SETOP_UNION ? "UNION" :
@@ -1282,52 +1542,133 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
                op->all = stmt->all;
 
                /*
-                * Recursively transform the child nodes.
+                * Recursively transform the left child node.
+                */
+               op->larg = transformSetOperationTree(pstate, stmt->larg,
+                                                                                        false,
+                                                                                        &lcolinfo);
+
+               /*
+                * If we are processing a recursive union query, now is the time to
+                * examine the non-recursive term's output columns and mark the
+                * containing CTE as having those result columns.  We should do this
+                * only at the topmost setop of the CTE, of course.
+                */
+               if (isTopLevel &&
+                       pstate->p_parent_cte &&
+                       pstate->p_parent_cte->cterecursive)
+                       determineRecursiveColTypes(pstate, op->larg, lcolinfo);
+
+               /*
+                * Recursively transform the right child node.
                 */
-               op->larg = transformSetOperationTree(pstate, stmt->larg);
-               op->rarg = transformSetOperationTree(pstate, stmt->rarg);
+               op->rarg = transformSetOperationTree(pstate, stmt->rarg,
+                                                                                        false,
+                                                                                        &rcolinfo);
 
                /*
                 * Verify that the two children have the same number of non-junk
                 * columns, and determine the types of the merged output columns.
                 */
-               getSetColTypes(pstate, op->larg, &lcoltypes, &lcoltypmods);
-               getSetColTypes(pstate, op->rarg, &rcoltypes, &rcoltypmods);
-               if (list_length(lcoltypes) != list_length(rcoltypes))
+               if (list_length(lcolinfo) != list_length(rcolinfo))
                        ereport(ERROR,
                                        (errcode(ERRCODE_SYNTAX_ERROR),
                                 errmsg("each %s query must have the same number of columns",
-                                               context)));
-               Assert(list_length(lcoltypes) == list_length(lcoltypmods));
-               Assert(list_length(rcoltypes) == list_length(rcoltypmods));
+                                               context),
+                                        parser_errposition(pstate,
+                                                                               exprLocation((Node *) rcolinfo))));
 
+               *colInfo = NIL;
                op->colTypes = NIL;
                op->colTypmods = NIL;
-               /* don't have a "foreach4", so chase two of the lists by hand */
-               lcm = list_head(lcoltypmods);
-               rcm = list_head(rcoltypmods);
-               forboth(lct, lcoltypes, rct, rcoltypes)
+               op->groupClauses = NIL;
+               forboth(lci, lcolinfo, rci, rcolinfo)
                {
-                       Oid                     lcoltype = lfirst_oid(lct);
-                       Oid                     rcoltype = lfirst_oid(rct);
-                       int32           lcoltypmod = lfirst_int(lcm);
-                       int32           rcoltypmod = lfirst_int(rcm);
+                       Node       *lcolnode = (Node *) lfirst(lci);
+                       Node       *rcolnode = (Node *) lfirst(rci);
+                       Oid                     lcoltype = exprType(lcolnode);
+                       Oid                     rcoltype = exprType(rcolnode);
+                       int32           lcoltypmod = exprTypmod(lcolnode);
+                       int32           rcoltypmod = exprTypmod(rcolnode);
+                       Node       *bestexpr;
+                       SetToDefault *rescolnode;
                        Oid                     rescoltype;
                        int32           rescoltypmod;
 
                        /* select common type, same as CASE et al */
-                       rescoltype = select_common_type(list_make2_oid(lcoltype, rcoltype),
-                                                                                       context);
+                       rescoltype = select_common_type(pstate,
+                                                                                       list_make2(lcolnode, rcolnode),
+                                                                                       context,
+                                                                                       &bestexpr);
                        /* if same type and same typmod, use typmod; else default */
                        if (lcoltype == rcoltype && lcoltypmod == rcoltypmod)
                                rescoltypmod = lcoltypmod;
                        else
                                rescoltypmod = -1;
+
+                       /*
+                        * 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.
+                        *
+                        * The if-tests might look wrong, but they are correct: we should
+                        * verify if the input is non-UNKNOWN *or* if it is an UNKNOWN
+                        * Const (to verify the literal is valid for the target data type)
+                        * or Param (to possibly resolve the Param's type).  We should do
+                        * nothing if the input is say an UNKNOWN Var, which can happen in
+                        * some cases.  The planner is sometimes able to fold the Var to a
+                        * constant before it has to coerce the type, so failing now would
+                        * just break cases that might work.
+                        */
+                       if (lcoltype != UNKNOWNOID ||
+                               IsA(lcolnode, Const) ||IsA(lcolnode, Param))
+                               (void) coerce_to_common_type(pstate, lcolnode,
+                                                                                        rescoltype, context);
+                       if (rcoltype != UNKNOWNOID ||
+                               IsA(rcolnode, Const) ||IsA(rcolnode, Param))
+                               (void) coerce_to_common_type(pstate, rcolnode,
+                                                                                        rescoltype, context);
+
+                       /* emit results */
+                       rescolnode = makeNode(SetToDefault);
+                       rescolnode->typeId = rescoltype;
+                       rescolnode->typeMod = rescoltypmod;
+                       rescolnode->location = exprLocation(bestexpr);
+                       *colInfo = lappend(*colInfo, rescolnode);
+
                        op->colTypes = lappend_oid(op->colTypes, rescoltype);
                        op->colTypmods = lappend_int(op->colTypmods, rescoltypmod);
 
-                       lcm = lnext(lcm);
-                       rcm = lnext(rcm);
+                       /*
+                        * For all cases except UNION ALL, identify the grouping operators
+                        * (and, if available, sorting operators) that will be used to
+                        * eliminate duplicates.
+                        */
+                       if (op->op != SETOP_UNION || !op->all)
+                       {
+                               SortGroupClause *grpcl = makeNode(SortGroupClause);
+                               Oid                     sortop;
+                               Oid                     eqop;
+                               ParseCallbackState pcbstate;
+
+                               setup_parser_errposition_callback(&pcbstate, pstate,
+                                                                                                 rescolnode->location);
+
+                               /* determine the eqop and optional sortop */
+                               get_sort_group_operators(rescoltype,
+                                                                                false, true, false,
+                                                                                &sortop, &eqop, NULL);
+
+                               cancel_parser_errposition_callback(&pcbstate);
+
+                               /* we don't have a tlist yet, so can't assign sortgrouprefs */
+                               grpcl->tleSortGroupRef = 0;
+                               grpcl->eqop = eqop;
+                               grpcl->sortop = sortop;
+                               grpcl->nulls_first = false;             /* OK with or without sortop */
+
+                               op->groupClauses = lappend(op->groupClauses, grpcl);
+                       }
                }
 
                return (Node *) op;
@@ -1335,47 +1676,58 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt)
 }
 
 /*
- * getSetColTypes
- *       Get output column types/typmods of an (already transformed) set-op node
+ * Process the outputs of the non-recursive term of a recursive union
+ * to set up the parent CTE's columns
  */
 static void
-getSetColTypes(ParseState *pstate, Node *node,
-                          List **colTypes, List **colTypmods)
+determineRecursiveColTypes(ParseState *pstate, Node *larg, List *lcolinfo)
 {
-       *colTypes = NIL;
-       *colTypmods = NIL;
-       if (IsA(node, RangeTblRef))
-       {
-               RangeTblRef *rtr = (RangeTblRef *) node;
-               RangeTblEntry *rte = rt_fetch(rtr->rtindex, pstate->p_rtable);
-               Query      *selectQuery = rte->subquery;
-               ListCell   *tl;
+       Node       *node;
+       int                     leftmostRTI;
+       Query      *leftmostQuery;
+       List       *targetList;
+       ListCell   *left_tlist;
+       ListCell   *lci;
+       int                     next_resno;
 
-               Assert(selectQuery != NULL);
-               /* Get types of non-junk columns */
-               foreach(tl, selectQuery->targetList)
-               {
-                       TargetEntry *tle = (TargetEntry *) lfirst(tl);
+       /*
+        * Find leftmost leaf SELECT
+        */
+       node = larg;
+       while (node && IsA(node, SetOperationStmt))
+               node = ((SetOperationStmt *) node)->larg;
+       Assert(node && IsA(node, RangeTblRef));
+       leftmostRTI = ((RangeTblRef *) node)->rtindex;
+       leftmostQuery = rt_fetch(leftmostRTI, pstate->p_rtable)->subquery;
+       Assert(leftmostQuery != NULL);
 
-                       if (tle->resjunk)
-                               continue;
-                       *colTypes = lappend_oid(*colTypes,
-                                                                       exprType((Node *) tle->expr));
-                       *colTypmods = lappend_int(*colTypmods,
-                                                                         exprTypmod((Node *) tle->expr));
-               }
-       }
-       else if (IsA(node, SetOperationStmt))
+       /*
+        * Generate dummy targetlist using column names of leftmost select and
+        * dummy result expressions of the non-recursive term.
+        */
+       targetList = NIL;
+       left_tlist = list_head(leftmostQuery->targetList);
+       next_resno = 1;
+
+       foreach(lci, lcolinfo)
        {
-               SetOperationStmt *op = (SetOperationStmt *) node;
+               Expr       *lcolexpr = (Expr *) lfirst(lci);
+               TargetEntry *lefttle = (TargetEntry *) lfirst(left_tlist);
+               char       *colName;
+               TargetEntry *tle;
 
-               /* Result already computed during transformation of node */
-               Assert(op->colTypes != NIL);
-               *colTypes = op->colTypes;
-               *colTypmods = op->colTypmods;
+               Assert(!lefttle->resjunk);
+               colName = pstrdup(lefttle->resname);
+               tle = makeTargetEntry(lcolexpr,
+                                                         next_resno++,
+                                                         colName,
+                                                         false);
+               targetList = lappend(targetList, tle);
+               left_tlist = lnext(left_tlist);
        }
-       else
-               elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
+
+       /* Now build CTE's output column info using dummy targetlist */
+       analyzeCTETargetList(pstate, pstate->p_parent_cte, targetList);
 }
 
 /*
@@ -1425,6 +1777,7 @@ static Query *
 transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
 {
        Query      *qry = makeNode(Query);
+       RangeTblEntry *target_rte;
        Node       *qual;
        ListCell   *origTargetList;
        ListCell   *tl;
@@ -1462,7 +1815,15 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
        if (pstate->p_hasAggs)
                ereport(ERROR,
                                (errcode(ERRCODE_GROUPING_ERROR),
-                                errmsg("cannot use aggregate function in UPDATE")));
+                                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
@@ -1474,6 +1835,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
                pstate->p_next_resno = pstate->p_target_relation->rd_rel->relnatts + 1;
 
        /* Prepare non-junk columns for assignment to target table */
+       target_rte = pstate->p_target_rangetblentry;
        origTargetList = list_head(stmt->targetList);
 
        foreach(tl, qry->targetList)
@@ -1514,6 +1876,10 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
                                                          origTarget->indirection,
                                                          origTarget->location);
 
+               /* Mark the target column as requiring update permissions */
+               target_rte->modifiedCols = bms_add_member(target_rte->modifiedCols,
+                                                               attrno - FirstLowInvalidHeapAttributeNumber);
+
                origTargetList = lnext(origTargetList);
        }
        if (origTargetList != NULL)
@@ -1532,6 +1898,7 @@ transformReturningList(ParseState *pstate, List *returningList)
        List       *rlist;
        int                     save_next_resno;
        bool            save_hasAggs;
+       bool            save_hasWindowFuncs;
        int                     length_rtable;
 
        if (returningList == NIL)
@@ -1548,6 +1915,8 @@ transformReturningList(ParseState *pstate, List *returningList)
        /* 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 */
@@ -1559,13 +1928,34 @@ transformReturningList(ParseState *pstate, List *returningList)
        if (pstate->p_hasAggs)
                ereport(ERROR,
                                (errcode(ERRCODE_GROUPING_ERROR),
-                                errmsg("cannot use aggregate function in RETURNING")));
+                                errmsg("cannot use aggregate function in RETURNING"),
+                                parser_errposition(pstate,
+                                                                       locate_agg_of_level((Node *) rlist, 0))));
+       if (pstate->p_hasWindowFuncs)
+               ereport(ERROR,
+                               (errcode(ERRCODE_WINDOWING_ERROR),
+                                errmsg("cannot use window function in RETURNING"),
+                                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")));
+                       errmsg("RETURNING cannot contain references to other relations"),
+                                parser_errposition(pstate, vlocation)));
+       }
 
        /* mark column origins */
        markTargetListOrigins(pstate, rlist);
@@ -1573,6 +1963,7 @@ transformReturningList(ParseState *pstate, List *returningList)
        /* restore state */
        pstate->p_next_resno = save_next_resno;
        pstate->p_hasAggs = save_hasAggs;
+       pstate->p_hasWindowFuncs = save_hasWindowFuncs;
 
        return rlist;
 }
@@ -1605,24 +1996,41 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
 
        result = transformStmt(pstate, stmt->query);
 
+       /* Grammar should not have allowed anything but SELECT */
        if (!IsA(result, Query) ||
                result->commandType != CMD_SELECT ||
                result->utilityStmt != NULL)
-               elog(ERROR, "unexpected non-SELECT command in cursor statement");
+               elog(ERROR, "unexpected non-SELECT command in DECLARE CURSOR");
 
        /* But we must explicitly disallow DECLARE CURSOR ... SELECT INTO */
        if (result->intoClause)
                ereport(ERROR,
                                (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
-                                errmsg("DECLARE CURSOR cannot specify INTO")));
+                                errmsg("DECLARE CURSOR cannot specify INTO"),
+                                parser_errposition(pstate,
+                                                               exprLocation((Node *) result->intoClause))));
 
        /* FOR UPDATE and WITH HOLD are not compatible */
        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"),
+                                errmsg("DECLARE CURSOR WITH HOLD ... FOR UPDATE/SHARE is not supported"),
                                 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"),
+                                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"),
+                                errdetail("Insensitive cursors must be READ ONLY.")));
+
        /* We won't need the raw querytree any more */
        stmt->query = NULL;
 
@@ -1636,29 +2044,21 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
  * transformExplainStmt -
  *     transform an EXPLAIN Statement
  *
- * EXPLAIN is just like other utility statements in that we emit it as a
- * CMD_UTILITY Query node with no transformation of the raw parse tree.
- * However, if p_variableparams is set, it could be that the client is
- * expecting us to resolve parameter types in something like
- *             EXPLAIN SELECT * FROM tab WHERE col = $1
- * To deal with such cases, we run parse analysis and throw away the result;
- * this is a bit grotty but not worth contorting the rest of the system for.
- * (The approach we use for DECLARE CURSOR won't work because the statement
- * being explained isn't necessarily a SELECT, and in particular might rewrite
- * to multiple parsetrees.)
+ * EXPLAIN is like other utility statements in that we emit it as a
+ * CMD_UTILITY Query node; however, we must first transform the contained
+ * query.  We used to postpone that until execution, but it's really necessary
+ * to do it during the normal parse analysis phase to ensure that side effects
+ * of parser hooks happen at the expected time.
  */
 static Query *
 transformExplainStmt(ParseState *pstate, ExplainStmt *stmt)
 {
        Query      *result;
 
-       if (pstate->p_variableparams)
-       {
-               /* Since parse analysis scribbles on its input, copy the tree first! */
-               (void) transformStmt(pstate, copyObject(stmt->query));
-       }
+       /* transform contained query */
+       stmt->query = (Node *) transformStmt(pstate, stmt->query);
 
-       /* Now return the untransformed command as a utility Query */
+       /* represent the command as a utility Query */
        result = makeNode(Query);
        result->commandType = CMD_UTILITY;
        result->utilityStmt = (Node *) stmt;
@@ -1667,7 +2067,11 @@ transformExplainStmt(ParseState *pstate, ExplainStmt *stmt)
 }
 
 
-/* exported so planner can check again after rewriting, query pullup, etc */
+/*
+ * Check for features that are not supported together with FOR UPDATE/SHARE.
+ *
+ * exported so planner can check again after rewriting, query pullup, etc
+ */
 void
 CheckSelectLocking(Query *qry)
 {
@@ -1691,6 +2095,14 @@ CheckSelectLocking(Query *qry)
                ereport(ERROR,
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("SELECT FOR UPDATE/SHARE is not allowed with aggregate functions")));
+       if (qry->hasWindowFuncs)
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("SELECT FOR UPDATE/SHARE is not allowed with window functions")));
+       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")));
 }
 
 /*
@@ -1699,10 +2111,11 @@ CheckSelectLocking(Query *qry)
  * This basically involves replacing names by integer relids.
  *
  * NB: if you need to change this, see also markQueryForLocking()
- * in rewriteHandler.c.
+ * in rewriteHandler.c, and isLockedRefname() in parse_relation.c.
  */
 static void
-transformLockingClause(Query *qry, LockingClause *lc)
+transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc,
+                                          bool pushedDown)
 {
        List       *lockedRels = lc->lockedRels;
        ListCell   *l;
@@ -1730,19 +2143,26 @@ transformLockingClause(Query *qry, LockingClause *lc)
                        switch (rte->rtekind)
                        {
                                case RTE_RELATION:
-                                       applyLockingClause(qry, i, lc->forUpdate, lc->noWait);
+                                       applyLockingClause(qry, i,
+                                                                          lc->forUpdate, lc->noWait, pushedDown);
                                        rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
                                        break;
                                case RTE_SUBQUERY:
+                                       applyLockingClause(qry, i,
+                                                                          lc->forUpdate, lc->noWait, pushedDown);
 
                                        /*
                                         * FOR UPDATE/SHARE of subquery is propagated to all of
-                                        * subquery's rels
+                                        * subquery's rels, too.  We could do this later (based on
+                                        * the marking of the subquery RTE) but it is convenient
+                                        * to have local knowledge in each query level about which
+                                        * rels need to be opened with RowShareLock.
                                         */
-                                       transformLockingClause(rte->subquery, allrels);
+                                       transformLockingClause(pstate, rte->subquery,
+                                                                                  allrels, true);
                                        break;
                                default:
-                                       /* ignore JOIN, SPECIAL, FUNCTION RTEs */
+                                       /* ignore JOIN, SPECIAL, FUNCTION, VALUES, CTE RTEs */
                                        break;
                        }
                }
@@ -1752,7 +2172,14 @@ transformLockingClause(Query *qry, LockingClause *lc)
                /* just the named tables */
                foreach(l, lockedRels)
                {
-                       char       *relname = strVal(lfirst(l));
+                       RangeVar   *thisrel = (RangeVar *) lfirst(l);
+
+                       /* For simplicity we insist on unqualified alias names here */
+                       if (thisrel->catalogname || thisrel->schemaname)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_SYNTAX_ERROR),
+                                                errmsg("SELECT FOR UPDATE/SHARE must specify unqualified relation names"),
+                                                parser_errposition(pstate, thisrel->location)));
 
                        i = 0;
                        foreach(rt, qry->rtable)
@@ -1760,42 +2187,53 @@ transformLockingClause(Query *qry, LockingClause *lc)
                                RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
 
                                ++i;
-                               if (strcmp(rte->eref->aliasname, relname) == 0)
+                               if (strcmp(rte->eref->aliasname, thisrel->relname) == 0)
                                {
                                        switch (rte->rtekind)
                                        {
                                                case RTE_RELATION:
                                                        applyLockingClause(qry, i,
-                                                                                          lc->forUpdate, lc->noWait);
+                                                                                          lc->forUpdate, lc->noWait,
+                                                                                          pushedDown);
                                                        rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
                                                        break;
                                                case RTE_SUBQUERY:
-
-                                                       /*
-                                                        * FOR UPDATE/SHARE of subquery is propagated to
-                                                        * all of subquery's rels
-                                                        */
-                                                       transformLockingClause(rte->subquery, allrels);
+                                                       applyLockingClause(qry, i,
+                                                                                          lc->forUpdate, lc->noWait,
+                                                                                          pushedDown);
+                                                       /* see comment above */
+                                                       transformLockingClause(pstate, rte->subquery,
+                                                                                                  allrels, true);
                                                        break;
                                                case RTE_JOIN:
                                                        ereport(ERROR,
                                                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                                                        errmsg("SELECT FOR UPDATE/SHARE cannot be applied to a join")));
+                                                                        errmsg("SELECT FOR UPDATE/SHARE cannot be applied to a join"),
+                                                        parser_errposition(pstate, thisrel->location)));
                                                        break;
                                                case RTE_SPECIAL:
                                                        ereport(ERROR,
                                                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                                                        errmsg("SELECT FOR UPDATE/SHARE cannot be applied to NEW or OLD")));
+                                                                        errmsg("SELECT FOR UPDATE/SHARE cannot be applied to NEW or OLD"),
+                                                        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")));
+                                                                        errmsg("SELECT FOR UPDATE/SHARE cannot be applied to a function"),
+                                                        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")));
+                                                                        errmsg("SELECT FOR UPDATE/SHARE cannot be applied to VALUES"),
+                                                        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"),
+                                                        parser_errposition(pstate, thisrel->location)));
                                                        break;
                                                default:
                                                        elog(ERROR, "unrecognized RTE type: %d",
@@ -1809,7 +2247,8 @@ transformLockingClause(Query *qry, LockingClause *lc)
                                ereport(ERROR,
                                                (errcode(ERRCODE_UNDEFINED_TABLE),
                                                 errmsg("relation \"%s\" in FOR UPDATE/SHARE clause not found in FROM clause",
-                                                               relname)));
+                                                               thisrel->relname),
+                                                parser_errposition(pstate, thisrel->location)));
                }
        }
 }
@@ -1818,12 +2257,17 @@ transformLockingClause(Query *qry, LockingClause *lc)
  * Record locking info for a single rangetable item
  */
 void
-applyLockingClause(Query *qry, Index rtindex, bool forUpdate, bool noWait)
+applyLockingClause(Query *qry, Index rtindex,
+                                  bool forUpdate, bool noWait, bool pushedDown)
 {
        RowMarkClause *rc;
 
+       /* If it's an explicit clause, make sure hasForUpdate gets set */
+       if (!pushedDown)
+               qry->hasForUpdate = true;
+
        /* Check for pre-existing entry for same rtindex */
-       if ((rc = get_rowmark(qry, rtindex)) != NULL)
+       if ((rc = get_parse_rowmark(qry, rtindex)) != NULL)
        {
                /*
                 * If the same RTE is specified both FOR UPDATE and FOR SHARE, treat
@@ -1835,9 +2279,12 @@ applyLockingClause(Query *qry, Index rtindex, bool forUpdate, bool noWait)
                 * is a bit more debatable but raising an error doesn't seem helpful.
                 * (Consider for instance SELECT FOR UPDATE NOWAIT from a view that
                 * internally contains a plain FOR UPDATE spec.)
+                *
+                * And of course pushedDown becomes false if any clause is explicit.
                 */
                rc->forUpdate |= forUpdate;
                rc->noWait |= noWait;
+               rc->pushedDown &= pushedDown;
                return;
        }
 
@@ -1846,51 +2293,6 @@ applyLockingClause(Query *qry, Index rtindex, bool forUpdate, bool noWait)
        rc->rti = rtindex;
        rc->forUpdate = forUpdate;
        rc->noWait = noWait;
+       rc->pushedDown = pushedDown;
        qry->rowMarks = lappend(qry->rowMarks, rc);
 }
-
-
-/*
- * Traverse a fully-analyzed tree to verify that parameter symbols
- * match their types.  We need this because some Params might still
- * be UNKNOWN, if there wasn't anything to force their coercion,
- * and yet other instances seen later might have gotten coerced.
- */
-static bool
-check_parameter_resolution_walker(Node *node,
-                                                                 check_parameter_resolution_context *context)
-{
-       if (node == NULL)
-               return false;
-       if (IsA(node, Param))
-       {
-               Param      *param = (Param *) node;
-
-               if (param->paramkind == PARAM_EXTERN)
-               {
-                       int                     paramno = param->paramid;
-
-                       if (paramno <= 0 || /* shouldn't happen, but... */
-                               paramno > context->numParams)
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_UNDEFINED_PARAMETER),
-                                                errmsg("there is no parameter $%d", paramno)));
-
-                       if (param->paramtype != context->paramTypes[paramno - 1])
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
-                                        errmsg("could not determine data type of parameter $%d",
-                                                       paramno)));
-               }
-               return false;
-       }
-       if (IsA(node, Query))
-       {
-               /* Recurse into RTE subquery or not-yet-planned sublink subquery */
-               return query_tree_walker((Query *) node,
-                                                                check_parameter_resolution_walker,
-                                                                (void *) context, 0);
-       }
-       return expression_tree_walker(node, check_parameter_resolution_walker,
-                                                                 (void *) context);
-}