]> 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 cdac02b71db69399e00b4a63eefe0d2f9f481ad0..a1ad3e20e5bf90dfbe46e1a6d969f09d6bb868d6 100644 (file)
  * optimizable statements.
  *
  *
- * Portions Copyright (c) 1996-2008, 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.384 2008/12/13 02:00:19 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 "nodes/nodeFuncs.h"
@@ -34,6 +35,7 @@
 #include "parser/parse_coerce.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"
@@ -45,11 +47,14 @@ 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,
-                                                                          List **colInfo);
+                                                 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);
@@ -57,9 +62,8 @@ static Query *transformDeclareCursorStmt(ParseState *pstate,
                                                   DeclareCursorStmt *stmt);
 static Query *transformExplainStmt(ParseState *pstate,
                                         ExplainStmt *stmt);
-static void transformLockingClause(ParseState *pstate,
-                                                                  Query *qry, LockingClause *lc);
-static bool check_parameter_resolution_walker(Node *node, ParseState *pstate);
+static void transformLockingClause(ParseState *pstate, Query *qry,
+                                          LockingClause *lc, bool pushedDown);
 
 
 /*
@@ -80,12 +84,12 @@ parse_analyze(Node *parseTree, const char *sourceText,
        ParseState *pstate = make_parsestate(NULL);
        Query      *query;
 
-       Assert(sourceText != NULL);                             /* required as of 8.4 */
+       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);
 
@@ -108,21 +112,16 @@ parse_analyze_varparams(Node *parseTree, const char *sourceText,
        ParseState *pstate = make_parsestate(NULL);
        Query      *query;
 
-       Assert(sourceText != NULL);                             /* required as of 8.4 */
+       Assert(sourceText != NULL); /* required as of 8.4 */
 
        pstate->p_sourcetext = sourceText;
-       pstate->p_paramtypes = *paramTypes;
-       pstate->p_numparams = *numParams;
-       pstate->p_variableparams = true;
+
+       parse_variable_parameters(pstate, paramTypes, numParams);
 
        query = transformStmt(pstate, parseTree);
 
        /* make sure all is well with parameter types */
-       if (pstate->p_numparams > 0)
-               check_parameter_resolution_walker((Node *) query, pstate);
-
-       *paramTypes = pstate->p_paramtypes;
-       *numParams = pstate->p_numparams;
+       check_variable_parameters(pstate, query);
 
        free_parsestate(pstate);
 
@@ -134,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);
@@ -222,13 +226,17 @@ transformStmt(ParseState *pstate, Node *parseTree)
  *             Returns true if a snapshot must be set before doing parse analysis
  *             on the given raw parse tree.
  *
- * Classification here should match transformStmt().
+ * 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))
        {
                        /*
@@ -250,15 +258,12 @@ analyze_requires_snapshot(Node *parseTree)
                        break;
 
                case T_ExplainStmt:
-                       /*
-                        * We only need a snapshot in varparams case, but it doesn't seem
-                        * worth complicating this function's API to distinguish that.
-                        */
+                       /* yes, because we must analyze the contained statement */
                        result = true;
                        break;
 
                default:
-                       /* utility statements don't have any active parse analysis */
+                       /* other utility statements don't have any real parse analysis */
                        result = false;
                        break;
        }
@@ -306,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;
 }
@@ -415,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;
 
@@ -432,7 +441,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
                                        (errcode(ERRCODE_SYNTAX_ERROR),
                                         errmsg("INSERT ... SELECT cannot specify INTO"),
                                         parser_errposition(pstate,
-                                                                               exprLocation((Node *) selectQuery->intoClause))));
+                                                  exprLocation((Node *) selectQuery->intoClause))));
 
                /*
                 * Make the source be a subquery in the INSERT's rangetable, and add
@@ -476,11 +485,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
                                expr = tle->expr;
                        else
                        {
-                               Var        *var = 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;
                        }
@@ -554,7 +560,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                         errmsg("VALUES must not contain table references"),
                                         parser_errposition(pstate,
-                                                                               locate_var_of_level((Node *) exprsLists, 0))));
+                                                         locate_var_of_level((Node *) exprsLists, 0))));
 
                /*
                 * Another thing we can't currently support is NEW/OLD references in
@@ -569,7 +575,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
                                         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))));
+                                                         locate_var_of_level((Node *) exprsLists, 0))));
 
                /*
                 * Generate the VALUES RTE
@@ -622,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);
@@ -630,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);
        }
@@ -673,6 +686,12 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
                                 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;
 }
@@ -705,15 +724,30 @@ transformInsertRow(ParseState *pstate, List *exprlist,
                                 errmsg("INSERT has more expressions than target columns"),
                                 parser_errposition(pstate,
                                                                        exprLocation(list_nth(exprlist,
-                                                                                                                 list_length(icolumns))))));
+                                                                                                 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"),
+                                ((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))))));
+                                                                                                 list_length(exprlist))))));
+       }
 
        /*
         * Prepare columns for assignment to target table.
@@ -744,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 -
@@ -761,16 +838,19 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 
        qry->commandType = CMD_SELECT;
 
-       /* make FOR UPDATE/FOR SHARE info available to addRangeTableEntry */
-       pstate->p_locking_clause = stmt->lockingClause;
-
-       /* process the WITH clause */
+       /* 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);
 
@@ -791,19 +871,21 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 
        /*
         * Transform sorting/grouping stuff.  Do ORDER BY first because both
-        * transformGroupClause and transformDistinctClause need the results.
-        * Note that these functions can also change the targetList, so it's
-        * passed to them by reference.
+        * 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 */ );
 
        if (stmt->distinctClause == NIL)
        {
@@ -815,7 +897,8 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
                /* We had SELECT DISTINCT */
                qry->distinctClause = transformDistinctClause(pstate,
                                                                                                          &qry->targetList,
-                                                                                                         qry->sortClause);
+                                                                                                         qry->sortClause,
+                                                                                                         false);
                qry->hasDistinctOn = false;
        }
        else
@@ -834,6 +917,11 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
        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)
        {
@@ -849,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(pstate, qry, (LockingClause *) lfirst(l));
+               transformLockingClause(pstate, qry,
+                                                          (LockingClause *) lfirst(l), false);
        }
 
        return qry;
@@ -889,9 +981,10 @@ 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 */
+       /* process the WITH clause independently of all else */
        if (stmt->withClause)
        {
                qry->hasRecursive = stmt->withClause->recursive;
@@ -1002,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");
@@ -1033,7 +1127,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("VALUES must not contain table references"),
                                 parser_errposition(pstate,
-                                                                       locate_var_of_level((Node *) newExprsLists, 0))));
+                                                  locate_var_of_level((Node *) newExprsLists, 0))));
 
        /*
         * Another thing we can't currently support is NEW/OLD references in rules
@@ -1048,7 +1142,7 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
                                 errmsg("VALUES must not contain OLD or NEW references"),
                                 errhint("Use SELECT ... UNION ALL ... instead."),
                                 parser_errposition(pstate,
-                                                                       locate_var_of_level((Node *) newExprsLists, 0))));
+                                                  locate_var_of_level((Node *) newExprsLists, 0))));
 
        qry->rtable = pstate->p_rtable;
        qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
@@ -1060,7 +1154,13 @@ transformValuesClause(ParseState *pstate, SelectStmt *stmt)
                                (errcode(ERRCODE_GROUPING_ERROR),
                                 errmsg("cannot use aggregate function in VALUES"),
                                 parser_errposition(pstate,
-                                                                       locate_agg_of_level((Node *) newExprsLists, 0))));
+                                                  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;
 }
@@ -1097,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.
@@ -1143,17 +1250,11 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT")));
 
-       /* process the WITH clause */
-       if (stmt->withClause)
-       {
-               qry->hasRecursive = stmt->withClause->recursive;
-               qry->cteList = transformWithClause(pstate, stmt->withClause);
-       }
-
        /*
         * Recursively transform the components of the tree.
         */
        sostmt = (SetOperationStmt *) transformSetOperationTree(pstate, stmt,
+                                                                                                                       true,
                                                                                                                        &socolinfo);
        Assert(sostmt && IsA(sostmt, SetOperationStmt));
        qry->setOperations = (Node *) sostmt;
@@ -1222,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 */
 
@@ -1249,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;
 
@@ -1262,7 +1363,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
                                 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."),
                                 parser_errposition(pstate,
-                                                                       exprLocation(list_nth(qry->targetList, tllen)))));
+                                                  exprLocation(list_nth(qry->targetList, tllen)))));
 
        qry->limitOffset = transformLimitClause(pstate, limitOffset,
                                                                                        "OFFSET");
@@ -1289,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(pstate, qry, (LockingClause *) lfirst(l));
+               transformLockingClause(pstate, qry,
+                                                          (LockingClause *) lfirst(l), false);
        }
 
        return qry;
@@ -1311,7 +1416,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
  */
 static Node *
 transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
-                                                 List **colInfo)
+                                                 bool isTopLevel, List **colInfo)
 {
        bool            isLeaf;
 
@@ -1325,7 +1430,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
                                (errcode(ERRCODE_SYNTAX_ERROR),
                                 errmsg("INTO is only allowed on first SELECT of UNION/INTERSECT/EXCEPT"),
                                 parser_errposition(pstate,
-                                                                       exprLocation((Node *) stmt->intoClause))));
+                                                                 exprLocation((Node *) stmt->intoClause))));
 
        /* We don't support FOR UPDATE/SHARE with set ops at the moment. */
        if (stmt->lockingClause)
@@ -1334,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.
@@ -1370,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
@@ -1385,7 +1490,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
                                                (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
                                                 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))));
+                                                        locate_var_of_level((Node *) selectQuery, 1))));
                }
 
                /*
@@ -1437,11 +1542,28 @@ 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->rarg = transformSetOperationTree(pstate, stmt->rarg,
+                                                                                        false,
                                                                                         &rcolinfo);
 
                /*
@@ -1462,20 +1584,20 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
                op->groupClauses = NIL;
                forboth(lci, lcolinfo, rci, rcolinfo)
                {
-                       Node       *lcolinfo = (Node *) lfirst(lci);
-                       Node       *rcolinfo = (Node *) lfirst(rci);
-                       Oid                     lcoltype = exprType(lcolinfo);
-                       Oid                     rcoltype = exprType(rcolinfo);
-                       int32           lcoltypmod = exprTypmod(lcolinfo);
-                       int32           rcoltypmod = exprTypmod(rcolinfo);
+                       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 *rescolinfo;
+                       SetToDefault *rescolnode;
                        Oid                     rescoltype;
                        int32           rescoltypmod;
 
                        /* select common type, same as CASE et al */
                        rescoltype = select_common_type(pstate,
-                                                                                       list_make2(lcolinfo, rcolinfo),
+                                                                                       list_make2(lcolnode, rcolnode),
                                                                                        context,
                                                                                        &bestexpr);
                        /* if same type and same typmod, use typmod; else default */
@@ -1484,18 +1606,35 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
                        else
                                rescoltypmod = -1;
 
-                       /* verify the coercions are actually possible */
-                       (void) coerce_to_common_type(pstate, lcolinfo,
-                                                                                rescoltype, context);
-                       (void) coerce_to_common_type(pstate, rcolinfo,
-                                                                                rescoltype, context);
+                       /*
+                        * 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 */
-                       rescolinfo = makeNode(SetToDefault);
-                       rescolinfo->typeId = rescoltype;
-                       rescolinfo->typeMod = rescoltypmod;
-                       rescolinfo->location = exprLocation(bestexpr);
-                       *colInfo = lappend(*colInfo, rescolinfo);
+                       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);
@@ -1513,7 +1652,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
                                ParseCallbackState pcbstate;
 
                                setup_parser_errposition_callback(&pcbstate, pstate,
-                                                                                                 rescolinfo->location);
+                                                                                                 rescolnode->location);
 
                                /* determine the eqop and optional sortop */
                                get_sort_group_operators(rescoltype,
@@ -1536,6 +1675,61 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
        }
 }
 
+/*
+ * Process the outputs of the non-recursive term of a recursive union
+ * to set up the parent CTE's columns
+ */
+static void
+determineRecursiveColTypes(ParseState *pstate, Node *larg, List *lcolinfo)
+{
+       Node       *node;
+       int                     leftmostRTI;
+       Query      *leftmostQuery;
+       List       *targetList;
+       ListCell   *left_tlist;
+       ListCell   *lci;
+       int                     next_resno;
+
+       /*
+        * 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);
+
+       /*
+        * 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)
+       {
+               Expr       *lcolexpr = (Expr *) lfirst(lci);
+               TargetEntry *lefttle = (TargetEntry *) lfirst(left_tlist);
+               char       *colName;
+               TargetEntry *tle;
+
+               Assert(!lefttle->resjunk);
+               colName = pstrdup(lefttle->resname);
+               tle = makeTargetEntry(lcolexpr,
+                                                         next_resno++,
+                                                         colName,
+                                                         false);
+               targetList = lappend(targetList, tle);
+               left_tlist = lnext(left_tlist);
+       }
+
+       /* Now build CTE's output column info using dummy targetlist */
+       analyzeCTETargetList(pstate, pstate->p_parent_cte, targetList);
+}
+
 /*
  * Attach column names from a ColumnDef list to a TargetEntry list
  * (for CREATE TABLE AS)
@@ -1583,6 +1777,7 @@ static Query *
 transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
 {
        Query      *qry = makeNode(Query);
+       RangeTblEntry *target_rte;
        Node       *qual;
        ListCell   *origTargetList;
        ListCell   *tl;
@@ -1623,6 +1818,12 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
                                 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
@@ -1634,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)
@@ -1674,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)
@@ -1692,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)
@@ -1708,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 */
@@ -1722,12 +1931,18 @@ transformReturningList(ParseState *pstate, List *returningList)
                                 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;
+               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++)
@@ -1738,7 +1953,7 @@ transformReturningList(ParseState *pstate, List *returningList)
                }
                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)));
        }
 
@@ -1748,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;
 }
@@ -1792,7 +2008,7 @@ transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt)
                                (errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
                                 errmsg("DECLARE CURSOR cannot specify INTO"),
                                 parser_errposition(pstate,
-                                                                       exprLocation((Node *) result->intoClause))));
+                                                               exprLocation((Node *) result->intoClause))));
 
        /* FOR UPDATE and WITH HOLD are not compatible */
        if (result->rowMarks != NIL && (stmt->options & CURSOR_OPT_HOLD))
@@ -1828,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;
@@ -1859,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)
 {
@@ -1883,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")));
 }
 
 /*
@@ -1891,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, and isLockedRel() in parse_relation.c.
+ * in rewriteHandler.c, and isLockedRefname() in parse_relation.c.
  */
 static void
-transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc)
+transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc,
+                                          bool pushedDown)
 {
        List       *lockedRels = lc->lockedRels;
        ListCell   *l;
@@ -1922,43 +2143,26 @@ transformLockingClause(ParseState *pstate, 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(pstate, rte->subquery, allrels);
-                                       break;
-                               case RTE_CTE:
-                                       {
-                                               /*
-                                                * We allow FOR UPDATE/SHARE of a WITH query to be
-                                                * propagated into the WITH, but it doesn't seem
-                                                * very sane to allow this for a reference to an
-                                                * outer-level WITH.  And it definitely wouldn't
-                                                * work for a self-reference, since we're not done
-                                                * analyzing the CTE anyway.
-                                                */
-                                               CommonTableExpr *cte;
-
-                                               if (rte->ctelevelsup > 0 || rte->self_reference)
-                                                       ereport(ERROR,
-                                                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                                                        errmsg("SELECT FOR UPDATE/SHARE cannot be applied to an outer-level WITH query")));
-                                               cte = GetCTEForRTE(pstate, rte, -1);
-                                               /* should be analyzed by now */
-                                               Assert(IsA(cte->ctequery, Query));
-                                               transformLockingClause(pstate,
-                                                                                          (Query *) cte->ctequery,
-                                                                                          allrels);
-                                       }
+                                       transformLockingClause(pstate, rte->subquery,
+                                                                                  allrels, true);
                                        break;
                                default:
-                                       /* ignore JOIN, SPECIAL, FUNCTION RTEs */
+                                       /* ignore JOIN, SPECIAL, FUNCTION, VALUES, CTE RTEs */
                                        break;
                        }
                }
@@ -1989,66 +2193,47 @@ transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc)
                                        {
                                                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(pstate, 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"),
-                                                                        parser_errposition(pstate, thisrel->location)));
+                                                        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"),
-                                                                        parser_errposition(pstate, thisrel->location)));
+                                                        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"),
-                                                                        parser_errposition(pstate, thisrel->location)));
+                                                        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"),
-                                                                        parser_errposition(pstate, thisrel->location)));
+                                                        parser_errposition(pstate, thisrel->location)));
                                                        break;
                                                case RTE_CTE:
-                                                       {
-                                                               /*
-                                                                * We allow FOR UPDATE/SHARE of a WITH query
-                                                                * to be propagated into the WITH, but it
-                                                                * doesn't seem very sane to allow this for a
-                                                                * reference to an outer-level WITH.  And it
-                                                                * definitely wouldn't work for a
-                                                                * self-reference, since we're not done
-                                                                * analyzing the CTE anyway.
-                                                                */
-                                                               CommonTableExpr *cte;
-
-                                                               if (rte->ctelevelsup > 0 || rte->self_reference)
-                                                                       ereport(ERROR,
-                                                                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                                                                        errmsg("SELECT FOR UPDATE/SHARE cannot be applied to an outer-level WITH query"),
-                                                                                        parser_errposition(pstate, thisrel->location)));
-                                                               cte = GetCTEForRTE(pstate, rte, -1);
-                                                               /* should be analyzed by now */
-                                                               Assert(IsA(cte->ctequery, Query));
-                                                               transformLockingClause(pstate,
-                                                                                                          (Query *) cte->ctequery,
-                                                                                                          allrels);
-                                                       }
+                                                       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",
@@ -2072,12 +2257,17 @@ transformLockingClause(ParseState *pstate, 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
@@ -2089,65 +2279,20 @@ 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;
        }
 
        /* Make a new RowMarkClause */
        rc = makeNode(RowMarkClause);
        rc->rti = rtindex;
-       rc->prti = rtindex;
        rc->forUpdate = forUpdate;
        rc->noWait = noWait;
-       rc->isParent = false;
+       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, ParseState *pstate)
-{
-       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 > pstate->p_numparams)
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_UNDEFINED_PARAMETER),
-                                                errmsg("there is no parameter $%d", paramno),
-                                                parser_errposition(pstate, param->location)));
-
-                       if (param->paramtype != pstate->p_paramtypes[paramno - 1])
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_AMBIGUOUS_PARAMETER),
-                                        errmsg("could not determine data type of parameter $%d",
-                                                       paramno),
-                                                parser_errposition(pstate, param->location)));
-               }
-               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 *) pstate, 0);
-       }
-       return expression_tree_walker(node, check_parameter_resolution_walker,
-                                                                 (void *) pstate);
-}