/*------------------------------------------------------------------------- * * analyze.c * transform the raw parse tree into a query tree * * For optimizable statements, we are careful to obtain a suitable lock on * each referenced table, and other modules of the backend preserve or * re-obtain these locks before depending on the results. It is therefore * okay to do significant semantic analysis of these statements. For * utility commands, no locks are obtained here (and if they were, we could * not be sure we'd still have them at execution). Hence the general rule * for utility commands is to just dump them into a Query node untransformed. * DECLARE CURSOR, EXPLAIN, and CREATE TABLE AS are exceptions because they * contain optimizable statements, which we should transform. * * * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * src/backend/parser/analyze.c * *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/sysattr.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" #include "miscadmin.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "optimizer/optimizer.h" #include "parser/analyze.h" #include "parser/parse_agg.h" #include "parser/parse_clause.h" #include "parser/parse_coerce.h" #include "parser/parse_collate.h" #include "parser/parse_cte.h" #include "parser/parse_expr.h" #include "parser/parse_func.h" #include "parser/parse_oper.h" #include "parser/parse_param.h" #include "parser/parse_relation.h" #include "parser/parse_target.h" #include "parser/parse_type.h" #include "parser/parsetree.h" #include "rewrite/rewriteManip.h" #include "utils/backend_status.h" #include "utils/builtins.h" #include "utils/guc.h" #include "utils/queryjumble.h" #include "utils/rel.h" #include "utils/syscache.h" /* Hook for plugins to get control at end of parse analysis */ post_parse_analyze_hook_type post_parse_analyze_hook = NULL; static Query *transformOptionalSelectInto(ParseState *pstate, Node *parseTree); 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, bool strip_indirection); static OnConflictExpr *transformOnConflictClause(ParseState *pstate, OnConflictClause *onConflictClause); 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, bool isTopLevel, List **targetlist); static void determineRecursiveColTypes(ParseState *pstate, Node *larg, List *nrtargetlist); static Query *transformReturnStmt(ParseState *pstate, ReturnStmt *stmt); static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt); static List *transformReturningList(ParseState *pstate, List *returningList); static List *transformUpdateTargetList(ParseState *pstate, List *targetList); static Query *transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt); static Query *transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt); static Query *transformExplainStmt(ParseState *pstate, ExplainStmt *stmt); static Query *transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt); static Query *transformCallStmt(ParseState *pstate, CallStmt *stmt); static void transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, bool pushedDown); #ifdef RAW_EXPRESSION_COVERAGE_TEST static bool test_raw_expression_coverage(Node *node, void *context); #endif /* * parse_analyze * Analyze a raw parse tree and transform it to Query form. * * 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 * transformation, while utility-type statements are simply hung off * a dummy CMD_UTILITY Query node. */ Query * parse_analyze(RawStmt *parseTree, const char *sourceText, Oid *paramTypes, int numParams, QueryEnvironment *queryEnv) { ParseState *pstate = make_parsestate(NULL); Query *query; JumbleState *jstate = NULL; Assert(sourceText != NULL); /* required as of 8.4 */ pstate->p_sourcetext = sourceText; if (numParams > 0) parse_fixed_parameters(pstate, paramTypes, numParams); pstate->p_queryEnv = queryEnv; query = transformTopLevelStmt(pstate, parseTree); if (IsQueryIdEnabled()) jstate = JumbleQuery(query, sourceText); if (post_parse_analyze_hook) (*post_parse_analyze_hook) (pstate, query, jstate); free_parsestate(pstate); pgstat_report_query_id(query->queryId, false); return query; } /* * parse_analyze_varparams * * This variant is used when it's okay to deduce information about $n * symbol datatypes from context. The passed-in paramTypes[] array can * be modified or enlarged (via repalloc). */ Query * parse_analyze_varparams(RawStmt *parseTree, const char *sourceText, Oid **paramTypes, int *numParams) { ParseState *pstate = make_parsestate(NULL); Query *query; JumbleState *jstate = NULL; Assert(sourceText != NULL); /* required as of 8.4 */ pstate->p_sourcetext = sourceText; parse_variable_parameters(pstate, paramTypes, numParams); query = transformTopLevelStmt(pstate, parseTree); /* make sure all is well with parameter types */ check_variable_parameters(pstate, query); if (IsQueryIdEnabled()) jstate = JumbleQuery(query, sourceText); if (post_parse_analyze_hook) (*post_parse_analyze_hook) (pstate, query, jstate); free_parsestate(pstate); pgstat_report_query_id(query->queryId, false); return query; } /* * parse_sub_analyze * Entry point for recursively analyzing a sub-statement. */ Query * parse_sub_analyze(Node *parseTree, ParseState *parentParseState, CommonTableExpr *parentCTE, bool locked_from_parent, bool resolve_unknowns) { ParseState *pstate = make_parsestate(parentParseState); Query *query; pstate->p_parent_cte = parentCTE; pstate->p_locked_from_parent = locked_from_parent; pstate->p_resolve_unknowns = resolve_unknowns; query = transformStmt(pstate, parseTree); free_parsestate(pstate); return query; } /* * transformTopLevelStmt - * transform a Parse tree into a Query tree. * * This function is just responsible for transferring statement location data * from the RawStmt into the finished Query. */ Query * transformTopLevelStmt(ParseState *pstate, RawStmt *parseTree) { Query *result; /* We're at top level, so allow SELECT INTO */ result = transformOptionalSelectInto(pstate, parseTree->stmt); result->stmt_location = parseTree->stmt_location; result->stmt_len = parseTree->stmt_len; return result; } /* * transformOptionalSelectInto - * If SELECT has INTO, convert it to CREATE TABLE AS. * * The only thing we do here that we don't do in transformStmt() is to * convert SELECT ... INTO into CREATE TABLE AS. Since utility statements * aren't allowed within larger statements, this is only allowed at the top * of the parse tree, and so we only try it before entering the recursive * transformStmt() processing. */ static Query * transformOptionalSelectInto(ParseState *pstate, Node *parseTree) { if (IsA(parseTree, SelectStmt)) { SelectStmt *stmt = (SelectStmt *) parseTree; /* If it's a set-operation tree, drill down to leftmost SelectStmt */ while (stmt && stmt->op != SETOP_NONE) stmt = stmt->larg; Assert(stmt && IsA(stmt, SelectStmt) && stmt->larg == NULL); if (stmt->intoClause) { CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt); ctas->query = parseTree; ctas->into = stmt->intoClause; ctas->objtype = OBJECT_TABLE; ctas->is_select_into = true; /* * Remove the intoClause from the SelectStmt. This makes it safe * for transformSelectStmt to complain if it finds intoClause set * (implying that the INTO appeared in a disallowed place). */ stmt->intoClause = NULL; parseTree = (Node *) ctas; } } return transformStmt(pstate, parseTree); } /* * transformStmt - * recursively transform a Parse tree into a Query tree. */ Query * transformStmt(ParseState *pstate, Node *parseTree) { Query *result; /* * We apply RAW_EXPRESSION_COVERAGE_TEST testing to basic DML statements; * we can't just run it on everything because raw_expression_tree_walker() * doesn't claim to handle utility statements. */ #ifdef RAW_EXPRESSION_COVERAGE_TEST switch (nodeTag(parseTree)) { case T_SelectStmt: case T_InsertStmt: case T_UpdateStmt: case T_DeleteStmt: (void) test_raw_expression_coverage(parseTree, NULL); break; default: break; } #endif /* RAW_EXPRESSION_COVERAGE_TEST */ switch (nodeTag(parseTree)) { /* * Optimizable statements */ case T_InsertStmt: result = transformInsertStmt(pstate, (InsertStmt *) parseTree); break; case T_DeleteStmt: result = transformDeleteStmt(pstate, (DeleteStmt *) parseTree); break; case T_UpdateStmt: result = transformUpdateStmt(pstate, (UpdateStmt *) parseTree); break; case T_SelectStmt: { SelectStmt *n = (SelectStmt *) parseTree; if (n->valuesLists) result = transformValuesClause(pstate, n); else if (n->op == SETOP_NONE) result = transformSelectStmt(pstate, n); else result = transformSetOperationStmt(pstate, n); } break; case T_ReturnStmt: result = transformReturnStmt(pstate, (ReturnStmt *) parseTree); break; case T_PLAssignStmt: result = transformPLAssignStmt(pstate, (PLAssignStmt *) parseTree); break; /* * Special cases */ case T_DeclareCursorStmt: result = transformDeclareCursorStmt(pstate, (DeclareCursorStmt *) parseTree); break; case T_ExplainStmt: result = transformExplainStmt(pstate, (ExplainStmt *) parseTree); break; case T_CreateTableAsStmt: result = transformCreateTableAsStmt(pstate, (CreateTableAsStmt *) parseTree); break; case T_CallStmt: result = transformCallStmt(pstate, (CallStmt *) parseTree); break; default: /* * other statements don't require any transformation; just return * the original parsetree with a Query node plastered on top. */ result = makeNode(Query); result->commandType = CMD_UTILITY; result->utilityStmt = (Node *) parseTree; break; } /* Mark as original query until we learn differently */ result->querySource = QSRC_ORIGINAL; result->canSetTag = true; 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(). */ bool analyze_requires_snapshot(RawStmt *parseTree) { bool result; switch (nodeTag(parseTree->stmt)) { /* * Optimizable statements */ case T_InsertStmt: case T_DeleteStmt: case T_UpdateStmt: case T_SelectStmt: case T_PLAssignStmt: result = true; break; /* * Special cases */ case T_DeclareCursorStmt: case T_ExplainStmt: case T_CreateTableAsStmt: /* 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 */ static Query * transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) { Query *qry = makeNode(Query); ParseNamespaceItem *nsitem; Node *qual; qry->commandType = CMD_DELETE; /* process the WITH clause independently of all else */ if (stmt->withClause) { qry->hasRecursive = stmt->withClause->recursive; qry->cteList = transformWithClause(pstate, stmt->withClause); qry->hasModifyingCTE = pstate->p_hasModifyingCTE; } /* set up range table with just the result rel */ qry->resultRelation = setTargetTable(pstate, stmt->relation, stmt->relation->inh, true, ACL_DELETE); nsitem = pstate->p_target_nsitem; /* there's no DISTINCT in DELETE */ qry->distinctClause = NIL; /* subqueries in USING cannot access the result relation */ nsitem->p_lateral_only = true; nsitem->p_lateral_ok = false; /* * The USING clause is non-standard SQL syntax, and is equivalent in * functionality to the FROM list that can be specified for UPDATE. The * USING keyword is used rather than FROM because FROM is already a * keyword in the DELETE syntax. */ transformFromClause(pstate, stmt->usingClause); /* remaining clauses can reference the result relation normally */ nsitem->p_lateral_only = false; nsitem->p_lateral_ok = true; qual = transformWhereClause(pstate, stmt->whereClause, EXPR_KIND_WHERE, "WHERE"); qry->returningList = transformReturningList(pstate, stmt->returningList); /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; qry->hasTargetSRFs = pstate->p_hasTargetSRFs; qry->hasAggs = pstate->p_hasAggs; assign_query_collations(pstate, qry); /* this must be done after collations, for reliable comparison of exprs */ if (pstate->p_hasAggs) parseCheckAggregates(pstate, qry); return qry; } /* * transformInsertStmt - * transform an Insert Statement */ static Query * transformInsertStmt(ParseState *pstate, InsertStmt *stmt) { Query *qry = makeNode(Query); SelectStmt *selectStmt = (SelectStmt *) stmt->selectStmt; List *exprList = NIL; bool isGeneralSelect; List *sub_rtable; List *sub_namespace; List *icolumns; List *attrnos; ParseNamespaceItem *nsitem; RangeTblEntry *rte; ListCell *icols; ListCell *attnos; ListCell *lc; bool isOnConflictUpdate; AclMode targetPerms; /* There can't be any outer WITH to worry about */ Assert(pstate->p_ctenamespace == NIL); qry->commandType = CMD_INSERT; pstate->p_is_insert = true; /* process the WITH clause independently of all else */ if (stmt->withClause) { qry->hasRecursive = stmt->withClause->recursive; qry->cteList = transformWithClause(pstate, stmt->withClause); qry->hasModifyingCTE = pstate->p_hasModifyingCTE; } qry->override = stmt->override; isOnConflictUpdate = (stmt->onConflictClause && stmt->onConflictClause->action == ONCONFLICT_UPDATE); /* * We have three cases to deal with: DEFAULT VALUES (selectStmt == NULL), * VALUES list, or general SELECT input. We special-case VALUES, both for * efficiency and so we can handle DEFAULT specifications. * * The grammar allows attaching ORDER BY, LIMIT, FOR UPDATE, or WITH to a * VALUES clause. If we have any of those, treat it as a general SELECT; * so it will work, but you can't use DEFAULT items together with those. */ isGeneralSelect = (selectStmt && (selectStmt->valuesLists == NIL || selectStmt->sortClause != NIL || selectStmt->limitOffset != NULL || selectStmt->limitCount != NULL || selectStmt->lockingClause != NIL || selectStmt->withClause != NULL)); /* * If a non-nil rangetable/namespace was passed in, and we are doing * INSERT/SELECT, arrange to pass the rangetable/namespace down to the * SELECT. This can only happen if we are inside a CREATE RULE, and in * that case we want the rule's OLD and NEW rtable entries to appear as * part of the SELECT's rtable, not as outer references for it. (Kluge!) * The SELECT's joinlist is not affected however. We must do this before * adding the target table to the INSERT's rtable. */ if (isGeneralSelect) { sub_rtable = pstate->p_rtable; pstate->p_rtable = NIL; sub_namespace = pstate->p_namespace; pstate->p_namespace = NIL; } else { sub_rtable = NIL; /* not used, but keep compiler quiet */ sub_namespace = NIL; } /* * Must get write lock on INSERT target table before scanning SELECT, else * we will grab the wrong kind of initial lock if the target table is also * mentioned in the SELECT part. Note that the target table is not added * to the joinlist or namespace. */ targetPerms = ACL_INSERT; if (isOnConflictUpdate) targetPerms |= ACL_UPDATE; qry->resultRelation = setTargetTable(pstate, stmt->relation, false, false, targetPerms); /* Validate stmt->cols list, or build default list if no list given */ icolumns = checkInsertTargets(pstate, stmt->cols, &attrnos); Assert(list_length(icolumns) == list_length(attrnos)); /* * Determine which variant of INSERT we have. */ if (selectStmt == NULL) { /* * We have INSERT ... DEFAULT VALUES. We can handle this case by * emitting an empty targetlist --- all columns will be defaulted when * the planner expands the targetlist. */ exprList = NIL; } else if (isGeneralSelect) { /* * We make the sub-pstate a child of the outer pstate so that it can * see any Param definitions supplied from above. Since the outer * pstate's rtable and namespace are presently empty, there are no * side-effects of exposing names the sub-SELECT shouldn't be able to * see. */ ParseState *sub_pstate = make_parsestate(pstate); Query *selectQuery; /* * Process the source SELECT. * * It is important that this be handled just like a standalone SELECT; * otherwise the behavior of SELECT within INSERT might be different * from a stand-alone SELECT. (Indeed, Postgres up through 6.5 had * bugs of just that nature...) * * The sole exception is that we prevent resolving unknown-type * outputs as TEXT. This does not change the semantics since if the * column type matters semantically, it would have been resolved to * something else anyway. Doing this lets us resolve such outputs as * the target column's type, which we handle below. */ sub_pstate->p_rtable = sub_rtable; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_namespace = sub_namespace; sub_pstate->p_resolve_unknowns = false; selectQuery = transformStmt(sub_pstate, stmt->selectStmt); free_parsestate(sub_pstate); /* The grammar should have produced a SELECT */ if (!IsA(selectQuery, Query) || selectQuery->commandType != CMD_SELECT) elog(ERROR, "unexpected non-SELECT command in INSERT ... SELECT"); /* * Make the source be a subquery in the INSERT's rangetable, and add * it to the INSERT's joinlist (but not the namespace). */ nsitem = addRangeTableEntryForSubquery(pstate, selectQuery, makeAlias("*SELECT*", NIL), false, false); addNSItemToQuery(pstate, nsitem, true, false, false); /*---------- * Generate an expression list for the INSERT that selects all the * non-resjunk columns from the subquery. (INSERT's tlist must be * separate from the subquery's tlist because we may add columns, * insert datatype coercions, etc.) * * HACK: unknown-type constants and params in the SELECT's targetlist * are copied up as-is rather than being referenced as subquery * outputs. This is to ensure that when we try to coerce them to * the target column's datatype, the right things happen (see * special cases in coerce_type). Otherwise, this fails: * INSERT INTO foo SELECT 'bar', ... FROM baz *---------- */ exprList = NIL; foreach(lc, selectQuery->targetList) { TargetEntry *tle = (TargetEntry *) lfirst(lc); Expr *expr; if (tle->resjunk) continue; if (tle->expr && (IsA(tle->expr, Const) || IsA(tle->expr, Param)) && exprType((Node *) tle->expr) == UNKNOWNOID) expr = tle->expr; else { Var *var = makeVarFromTargetEntry(nsitem->p_rtindex, tle); var->location = exprLocation((Node *) tle->expr); expr = (Expr *) var; } exprList = lappend(exprList, expr); } /* Prepare row for assignment to target table */ exprList = transformInsertRow(pstate, exprList, stmt->cols, icolumns, attrnos, false); } else if (list_length(selectStmt->valuesLists) > 1) { /* * Process INSERT ... VALUES with multiple VALUES sublists. We * generate a VALUES RTE holding the transformed expression lists, and * build up a targetlist containing Vars that reference the VALUES * RTE. */ List *exprsLists = NIL; List *coltypes = NIL; List *coltypmods = NIL; List *colcollations = NIL; int sublist_length = -1; bool lateral = false; Assert(selectStmt->intoClause == NULL); foreach(lc, selectStmt->valuesLists) { List *sublist = (List *) lfirst(lc); /* * Do basic expression transformation (same as a ROW() expr, but * allow SetToDefault at top level) */ sublist = transformExpressionList(pstate, sublist, EXPR_KIND_VALUES, true); /* * All the sublists must be the same length, *after* * transformation (which might expand '*' into multiple items). * The VALUES RTE can't handle anything different. */ if (sublist_length < 0) { /* Remember post-transformation length of first sublist */ sublist_length = list_length(sublist); } else if (sublist_length != list_length(sublist)) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("VALUES lists must all be the same length"), parser_errposition(pstate, exprLocation((Node *) sublist)))); } /* * Prepare row for assignment to target table. We process any * indirection on the target column specs normally but then strip * off the resulting field/array assignment nodes, since we don't * want the parsed statement to contain copies of those in each * VALUES row. (It's annoying to have to transform the * indirection specs over and over like this, but avoiding it * would take some really messy refactoring of * transformAssignmentIndirection.) */ sublist = transformInsertRow(pstate, sublist, stmt->cols, icolumns, attrnos, true); /* * We must assign collations now because assign_query_collations * doesn't process rangetable entries. We just assign all the * collations independently in each row, and don't worry about * whether they are consistent vertically. The outer INSERT query * isn't going to care about the collations of the VALUES columns, * so it's not worth the effort to identify a common collation for * each one here. (But note this does have one user-visible * consequence: INSERT ... VALUES won't complain about conflicting * explicit COLLATEs in a column, whereas the same VALUES * construct in another context would complain.) */ assign_list_collations(pstate, sublist); exprsLists = lappend(exprsLists, sublist); } /* * Construct column type/typmod/collation lists for the VALUES RTE. * Every expression in each column has been coerced to the type/typmod * of the corresponding target column or subfield, so it's sufficient * to look at the exprType/exprTypmod of the first row. We don't care * about the collation labeling, so just fill in InvalidOid for that. */ foreach(lc, (List *) linitial(exprsLists)) { Node *val = (Node *) lfirst(lc); coltypes = lappend_oid(coltypes, exprType(val)); coltypmods = lappend_int(coltypmods, exprTypmod(val)); colcollations = lappend_oid(colcollations, InvalidOid); } /* * Ordinarily there can't be any current-level Vars in the expression * lists, because the namespace was empty ... but if we're inside * CREATE RULE, then NEW/OLD references might appear. In that case we * have to mark the VALUES RTE as LATERAL. */ if (list_length(pstate->p_rtable) != 1 && contain_vars_of_level((Node *) exprsLists, 0)) lateral = true; /* * Generate the VALUES RTE */ nsitem = addRangeTableEntryForValues(pstate, exprsLists, coltypes, coltypmods, colcollations, NULL, lateral, true); addNSItemToQuery(pstate, nsitem, true, false, false); /* * Generate list of Vars referencing the RTE */ exprList = expandNSItemVars(nsitem, 0, -1, NULL); /* * Re-apply any indirection on the target column specs to the Vars */ exprList = transformInsertRow(pstate, exprList, stmt->cols, icolumns, attrnos, false); } else { /* * Process INSERT ... VALUES with a single VALUES sublist. We treat * this case separately for efficiency. The sublist is just computed * directly as the Query's targetlist, with no VALUES RTE. So it * works just like a SELECT without any FROM. */ List *valuesLists = selectStmt->valuesLists; Assert(list_length(valuesLists) == 1); Assert(selectStmt->intoClause == NULL); /* * Do basic expression transformation (same as a ROW() expr, but allow * SetToDefault at top level) */ exprList = transformExpressionList(pstate, (List *) linitial(valuesLists), EXPR_KIND_VALUES_SINGLE, true); /* Prepare row for assignment to target table */ exprList = transformInsertRow(pstate, exprList, stmt->cols, icolumns, attrnos, false); } /* * 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_nsitem->p_rte; qry->targetList = NIL; Assert(list_length(exprList) <= list_length(icolumns)); forthree(lc, exprList, icols, icolumns, attnos, attrnos) { Expr *expr = (Expr *) lfirst(lc); ResTarget *col = lfirst_node(ResTarget, icols); AttrNumber attr_num = (AttrNumber) lfirst_int(attnos); TargetEntry *tle; tle = makeTargetEntry(expr, attr_num, col->name, false); qry->targetList = lappend(qry->targetList, tle); rte->insertedCols = bms_add_member(rte->insertedCols, attr_num - FirstLowInvalidHeapAttributeNumber); } /* * If we have any clauses yet to process, set the query namespace to * contain only the target relation, removing any entries added in a * sub-SELECT or VALUES list. */ if (stmt->onConflictClause || stmt->returningList) { pstate->p_namespace = NIL; addNSItemToQuery(pstate, pstate->p_target_nsitem, false, true, true); } /* Process ON CONFLICT, if any. */ if (stmt->onConflictClause) qry->onConflict = transformOnConflictClause(pstate, stmt->onConflictClause); /* Process RETURNING, if any. */ if (stmt->returningList) qry->returningList = transformReturningList(pstate, stmt->returningList); /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; qry->hasSubLinks = pstate->p_hasSubLinks; assign_query_collations(pstate, qry); return qry; } /* * Prepare an INSERT row for assignment to the target table. * * exprlist: transformed expressions for source values; these might come from * a VALUES row, or be Vars referencing a sub-SELECT or VALUES RTE output. * stmtcols: original target-columns spec for INSERT (we just test for NIL) * icolumns: effective target-columns spec (list of ResTarget) * attrnos: integer column numbers (must be same length as icolumns) * strip_indirection: if true, remove any field/array assignment nodes */ static List * transformInsertRow(ParseState *pstate, List *exprlist, List *stmtcols, List *icolumns, List *attrnos, bool strip_indirection) { List *result; ListCell *lc; ListCell *icols; ListCell *attnos; /* * Check length of expr list. It must not have more expressions than * there are target columns. We allow fewer, but only if no explicit * columns list was given (the remaining columns are implicitly * defaulted). Note we must check this *after* transformation because * that could expand '*' into multiple items. */ if (list_length(exprlist) > list_length(icolumns)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), 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"), ((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. */ result = NIL; forthree(lc, exprlist, icols, icolumns, attnos, attrnos) { Expr *expr = (Expr *) lfirst(lc); ResTarget *col = lfirst_node(ResTarget, icols); int attno = lfirst_int(attnos); expr = transformAssignedExpr(pstate, expr, EXPR_KIND_INSERT_TARGET, col->name, attno, col->indirection, col->location); if (strip_indirection) { while (expr) { if (IsA(expr, FieldStore)) { FieldStore *fstore = (FieldStore *) expr; expr = (Expr *) linitial(fstore->newvals); } else if (IsA(expr, SubscriptingRef)) { SubscriptingRef *sbsref = (SubscriptingRef *) expr; if (sbsref->refassgnexpr == NULL) break; expr = sbsref->refassgnexpr; } else break; } } result = lappend(result, expr); } return result; } /* * transformOnConflictClause - * transforms an OnConflictClause in an INSERT */ static OnConflictExpr * transformOnConflictClause(ParseState *pstate, OnConflictClause *onConflictClause) { ParseNamespaceItem *exclNSItem = NULL; List *arbiterElems; Node *arbiterWhere; Oid arbiterConstraint; List *onConflictSet = NIL; Node *onConflictWhere = NULL; int exclRelIndex = 0; List *exclRelTlist = NIL; OnConflictExpr *result; /* * If this is ON CONFLICT ... UPDATE, first create the range table entry * for the EXCLUDED pseudo relation, so that that will be present while * processing arbiter expressions. (You can't actually reference it from * there, but this provides a useful error message if you try.) */ if (onConflictClause->action == ONCONFLICT_UPDATE) { Relation targetrel = pstate->p_target_relation; RangeTblEntry *exclRte; exclNSItem = addRangeTableEntryForRelation(pstate, targetrel, RowExclusiveLock, makeAlias("excluded", NIL), false, false); exclRte = exclNSItem->p_rte; exclRelIndex = exclNSItem->p_rtindex; /* * relkind is set to composite to signal that we're not dealing with * an actual relation, and no permission checks are required on it. * (We'll check the actual target relation, instead.) */ exclRte->relkind = RELKIND_COMPOSITE_TYPE; exclRte->requiredPerms = 0; /* other permissions fields in exclRte are already empty */ /* Create EXCLUDED rel's targetlist for use by EXPLAIN */ exclRelTlist = BuildOnConflictExcludedTargetlist(targetrel, exclRelIndex); } /* Process the arbiter clause, ON CONFLICT ON (...) */ transformOnConflictArbiter(pstate, onConflictClause, &arbiterElems, &arbiterWhere, &arbiterConstraint); /* Process DO UPDATE */ if (onConflictClause->action == ONCONFLICT_UPDATE) { /* * Expressions in the UPDATE targetlist need to be handled like UPDATE * not INSERT. We don't need to save/restore this because all INSERT * expressions have been parsed already. */ pstate->p_is_insert = false; /* * Add the EXCLUDED pseudo relation to the query namespace, making it * available in the UPDATE subexpressions. */ addNSItemToQuery(pstate, exclNSItem, false, true, true); /* * Now transform the UPDATE subexpressions. */ onConflictSet = transformUpdateTargetList(pstate, onConflictClause->targetList); onConflictWhere = transformWhereClause(pstate, onConflictClause->whereClause, EXPR_KIND_WHERE, "WHERE"); /* * Remove the EXCLUDED pseudo relation from the query namespace, since * it's not supposed to be available in RETURNING. (Maybe someday we * could allow that, and drop this step.) */ Assert((ParseNamespaceItem *) llast(pstate->p_namespace) == exclNSItem); pstate->p_namespace = list_delete_last(pstate->p_namespace); } /* Finally, build ON CONFLICT DO [NOTHING | UPDATE] expression */ result = makeNode(OnConflictExpr); result->action = onConflictClause->action; result->arbiterElems = arbiterElems; result->arbiterWhere = arbiterWhere; result->constraint = arbiterConstraint; result->onConflictSet = onConflictSet; result->onConflictWhere = onConflictWhere; result->exclRelIndex = exclRelIndex; result->exclRelTlist = exclRelTlist; return result; } /* * BuildOnConflictExcludedTargetlist * Create target list for the EXCLUDED pseudo-relation of ON CONFLICT, * representing the columns of targetrel with varno exclRelIndex. * * Note: Exported for use in the rewriter. */ List * BuildOnConflictExcludedTargetlist(Relation targetrel, Index exclRelIndex) { List *result = NIL; int attno; Var *var; TargetEntry *te; /* * Note that resnos of the tlist must correspond to attnos of the * underlying relation, hence we need entries for dropped columns too. */ for (attno = 0; attno < RelationGetNumberOfAttributes(targetrel); attno++) { Form_pg_attribute attr = TupleDescAttr(targetrel->rd_att, attno); char *name; if (attr->attisdropped) { /* * can't use atttypid here, but it doesn't really matter what type * the Const claims to be. */ var = (Var *) makeNullConst(INT4OID, -1, InvalidOid); name = NULL; } else { var = makeVar(exclRelIndex, attno + 1, attr->atttypid, attr->atttypmod, attr->attcollation, 0); name = pstrdup(NameStr(attr->attname)); } te = makeTargetEntry((Expr *) var, attno + 1, name, false); result = lappend(result, te); } /* * Add a whole-row-Var entry to support references to "EXCLUDED.*". Like * the other entries in the EXCLUDED tlist, its resno must match the Var's * varattno, else the wrong things happen while resolving references in * setrefs.c. This is against normal conventions for targetlists, but * it's okay since we don't use this as a real tlist. */ var = makeVar(exclRelIndex, InvalidAttrNumber, targetrel->rd_rel->reltype, -1, InvalidOid, 0); te = makeTargetEntry((Expr *) var, InvalidAttrNumber, NULL, true); result = lappend(result, te); 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 - * transforms a Select Statement * * Note: this covers only cases with no set operations and no VALUES lists; * see below for the other cases. */ static Query * transformSelectStmt(ParseState *pstate, SelectStmt *stmt) { Query *qry = makeNode(Query); Node *qual; ListCell *l; qry->commandType = CMD_SELECT; /* process the WITH clause independently of all else */ if (stmt->withClause) { qry->hasRecursive = stmt->withClause->recursive; qry->cteList = transformWithClause(pstate, stmt->withClause); qry->hasModifyingCTE = pstate->p_hasModifyingCTE; } /* Complain if we get called from someplace where INTO is not allowed */ if (stmt->intoClause) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("SELECT ... INTO is not allowed here"), parser_errposition(pstate, exprLocation((Node *) stmt->intoClause)))); /* 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); /* transform targetlist */ qry->targetList = transformTargetList(pstate, stmt->targetList, EXPR_KIND_SELECT_TARGET); /* mark column origins */ markTargetListOrigins(pstate, qry->targetList); /* transform WHERE */ qual = transformWhereClause(pstate, stmt->whereClause, EXPR_KIND_WHERE, "WHERE"); /* initial processing of HAVING clause is much like WHERE clause */ qry->havingQual = transformWhereClause(pstate, stmt->havingClause, EXPR_KIND_HAVING, "HAVING"); /* * 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. */ qry->sortClause = transformSortClause(pstate, stmt->sortClause, &qry->targetList, EXPR_KIND_ORDER_BY, false /* allow SQL92 rules */ ); qry->groupClause = transformGroupClause(pstate, stmt->groupClause, &qry->groupingSets, &qry->targetList, qry->sortClause, EXPR_KIND_GROUP_BY, false /* allow SQL92 rules */ ); qry->groupDistinct = stmt->groupDistinct; 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, EXPR_KIND_OFFSET, "OFFSET", stmt->limitOption); qry->limitCount = transformLimitClause(pstate, stmt->limitCount, EXPR_KIND_LIMIT, "LIMIT", stmt->limitOption); qry->limitOption = stmt->limitOption; /* transform window clauses after we have seen all window functions */ qry->windowClause = transformWindowDefinitions(pstate, pstate->p_windowdefs, &qry->targetList); /* resolve any still-unresolved output columns as being type text */ if (pstate->p_resolve_unknowns) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; qry->hasTargetSRFs = pstate->p_hasTargetSRFs; qry->hasAggs = pstate->p_hasAggs; foreach(l, stmt->lockingClause) { transformLockingClause(pstate, qry, (LockingClause *) lfirst(l), false); } assign_query_collations(pstate, qry); /* this must be done after collations, for reliable comparison of exprs */ if (pstate->p_hasAggs || qry->groupClause || qry->groupingSets || qry->havingQual) parseCheckAggregates(pstate, qry); return qry; } /* * transformValuesClause - * transforms a VALUES clause that's being used as a standalone SELECT * * We build a Query containing a VALUES RTE, rather as if one had written * SELECT * FROM (VALUES ...) AS "*VALUES*" */ static Query * transformValuesClause(ParseState *pstate, SelectStmt *stmt) { Query *qry = makeNode(Query); List *exprsLists = NIL; List *coltypes = NIL; List *coltypmods = NIL; List *colcollations = NIL; List **colexprs = NULL; int sublist_length = -1; bool lateral = false; ParseNamespaceItem *nsitem; ListCell *lc; ListCell *lc2; int i; qry->commandType = CMD_SELECT; /* Most SELECT stuff doesn't apply in a VALUES clause */ Assert(stmt->distinctClause == NIL); Assert(stmt->intoClause == NULL); Assert(stmt->targetList == NIL); Assert(stmt->fromClause == NIL); 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); qry->hasModifyingCTE = pstate->p_hasModifyingCTE; } /* * For each row of VALUES, transform the raw expressions. * * Note that the intermediate representation we build is column-organized * not row-organized. That simplifies the type and collation processing * below. */ foreach(lc, stmt->valuesLists) { List *sublist = (List *) lfirst(lc); /* * Do basic expression transformation (same as a ROW() expr, but here * we disallow SetToDefault) */ sublist = transformExpressionList(pstate, sublist, EXPR_KIND_VALUES, false); /* * All the sublists must be the same length, *after* transformation * (which might expand '*' into multiple items). The VALUES RTE can't * handle anything different. */ if (sublist_length < 0) { /* Remember post-transformation length of first sublist */ sublist_length = list_length(sublist); /* and allocate array for per-column lists */ colexprs = (List **) palloc0(sublist_length * sizeof(List *)); } else if (sublist_length != list_length(sublist)) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("VALUES lists must all be the same length"), parser_errposition(pstate, exprLocation((Node *) sublist)))); } /* Build per-column expression lists */ i = 0; foreach(lc2, sublist) { Node *col = (Node *) lfirst(lc2); colexprs[i] = lappend(colexprs[i], col); i++; } /* Release sub-list's cells to save memory */ list_free(sublist); /* Prepare an exprsLists element for this row */ exprsLists = lappend(exprsLists, NIL); } /* * Now resolve the common types of the columns, and coerce everything to * those types. Then identify the common typmod and common collation, if * any, of each column. * * We must do collation processing now because (1) assign_query_collations * doesn't process rangetable entries, and (2) we need to label the VALUES * RTE with column collations for use in the outer query. We don't * consider conflict of implicit collations to be an error here; instead * the column will just show InvalidOid as its collation, and you'll get a * failure later if that results in failure to resolve a collation. * * Note we modify the per-column expression lists in-place. */ for (i = 0; i < sublist_length; i++) { Oid coltype; int32 coltypmod; Oid colcoll; coltype = select_common_type(pstate, colexprs[i], "VALUES", NULL); foreach(lc, colexprs[i]) { Node *col = (Node *) lfirst(lc); col = coerce_to_common_type(pstate, col, coltype, "VALUES"); lfirst(lc) = (void *) col; } coltypmod = select_common_typmod(pstate, colexprs[i], coltype); colcoll = select_common_collation(pstate, colexprs[i], true); coltypes = lappend_oid(coltypes, coltype); coltypmods = lappend_int(coltypmods, coltypmod); colcollations = lappend_oid(colcollations, colcoll); } /* * Finally, rearrange the coerced expressions into row-organized lists. */ for (i = 0; i < sublist_length; i++) { forboth(lc, colexprs[i], lc2, exprsLists) { Node *col = (Node *) lfirst(lc); List *sublist = lfirst(lc2); sublist = lappend(sublist, col); lfirst(lc2) = sublist; } list_free(colexprs[i]); } /* * Ordinarily there can't be any current-level Vars in the expression * lists, because the namespace was empty ... but if we're inside CREATE * RULE, then NEW/OLD references might appear. In that case we have to * mark the VALUES RTE as LATERAL. */ if (pstate->p_rtable != NIL && contain_vars_of_level((Node *) exprsLists, 0)) lateral = true; /* * Generate the VALUES RTE */ nsitem = addRangeTableEntryForValues(pstate, exprsLists, coltypes, coltypmods, colcollations, NULL, lateral, true); addNSItemToQuery(pstate, nsitem, true, true, true); /* * Generate a targetlist as though expanding "*" */ Assert(pstate->p_next_resno == 1); qry->targetList = expandNSItemAttrs(pstate, nsitem, 0, -1); /* * The grammar allows attaching ORDER BY, LIMIT, and FOR UPDATE to a * VALUES, so cope. */ qry->sortClause = transformSortClause(pstate, stmt->sortClause, &qry->targetList, EXPR_KIND_ORDER_BY, false /* allow SQL92 rules */ ); qry->limitOffset = transformLimitClause(pstate, stmt->limitOffset, EXPR_KIND_OFFSET, "OFFSET", stmt->limitOption); qry->limitCount = transformLimitClause(pstate, stmt->limitCount, EXPR_KIND_LIMIT, "LIMIT", stmt->limitOption); qry->limitOption = stmt->limitOption; if (stmt->lockingClause) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), /*------ translator: %s is a SQL row locking clause such as FOR UPDATE */ errmsg("%s cannot be applied to VALUES", LCS_asString(((LockingClause *) linitial(stmt->lockingClause))->strength)))); qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; assign_query_collations(pstate, qry); return qry; } /* * transformSetOperationStmt - * transforms a set-operations tree * * A set-operation tree is just a SELECT, but with UNION/INTERSECT/EXCEPT * structure to it. We must transform each leaf SELECT and build up a top- * level Query that contains the leaf SELECTs as subqueries in its rangetable. * The tree of set operations is converted into the setOperations field of * the top-level Query. */ static Query * transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) { Query *qry = makeNode(Query); SelectStmt *leftmostSelect; int leftmostRTI; Query *leftmostQuery; SetOperationStmt *sostmt; List *sortClause; Node *limitOffset; Node *limitCount; List *lockingClause; WithClause *withClause; Node *node; ListCell *left_tlist, *lct, *lcm, *lcc, *l; List *targetvars, *targetnames, *sv_namespace; int sv_rtable_length; ParseNamespaceItem *jnsitem; ParseNamespaceColumn *sortnscolumns; int sortcolindex; int tllen; qry->commandType = CMD_SELECT; /* * Find leftmost leaf SelectStmt. We currently only need to do this in * order to deliver a suitable error message if there's an INTO clause * there, implying the set-op tree is in a context that doesn't allow * INTO. (transformSetOperationTree would throw error anyway, but it * seems worth the trouble to throw a different error for non-leftmost * INTO, so we produce that error in transformSetOperationTree.) */ leftmostSelect = stmt->larg; while (leftmostSelect && leftmostSelect->op != SETOP_NONE) leftmostSelect = leftmostSelect->larg; Assert(leftmostSelect && IsA(leftmostSelect, SelectStmt) && leftmostSelect->larg == NULL); if (leftmostSelect->intoClause) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("SELECT ... INTO is not allowed here"), parser_errposition(pstate, exprLocation((Node *) leftmostSelect->intoClause)))); /* * We need to extract ORDER BY and other top-level clauses here and not * let transformSetOperationTree() see them --- else it'll just recurse * right back here! */ sortClause = stmt->sortClause; limitOffset = stmt->limitOffset; limitCount = stmt->limitCount; lockingClause = stmt->lockingClause; withClause = stmt->withClause; stmt->sortClause = NIL; stmt->limitOffset = NULL; stmt->limitCount = NULL; stmt->lockingClause = NIL; stmt->withClause = NULL; /* We don't support FOR UPDATE/SHARE with set ops at the moment. */ if (lockingClause) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), /*------ translator: %s is a SQL row locking clause such as FOR UPDATE */ errmsg("%s is not allowed with UNION/INTERSECT/EXCEPT", LCS_asString(((LockingClause *) linitial(lockingClause))->strength)))); /* Process the WITH clause independently of all else */ if (withClause) { qry->hasRecursive = withClause->recursive; qry->cteList = transformWithClause(pstate, withClause); qry->hasModifyingCTE = pstate->p_hasModifyingCTE; } /* * Recursively transform the components of the tree. */ sostmt = castNode(SetOperationStmt, transformSetOperationTree(pstate, stmt, true, NULL)); Assert(sostmt); qry->setOperations = (Node *) sostmt; /* * Re-find leftmost SELECT (now it's a sub-query in rangetable) */ node = sostmt->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 for outer query using column names of * leftmost select and common datatypes/collations of topmost set * operation. Also make lists of the dummy vars and their names for use * in parsing ORDER BY. * * Note: we use leftmostRTI as the varno of the dummy variables. It * shouldn't matter too much which RT index they have, as long as they * have one that corresponds to a real RT entry; else funny things may * happen when the tree is mashed by rule rewriting. */ qry->targetList = NIL; targetvars = NIL; targetnames = NIL; sortnscolumns = (ParseNamespaceColumn *) palloc0(list_length(sostmt->colTypes) * sizeof(ParseNamespaceColumn)); sortcolindex = 0; forfour(lct, sostmt->colTypes, lcm, sostmt->colTypmods, lcc, sostmt->colCollations, left_tlist, leftmostQuery->targetList) { Oid colType = lfirst_oid(lct); int32 colTypmod = lfirst_int(lcm); Oid colCollation = lfirst_oid(lcc); TargetEntry *lefttle = (TargetEntry *) lfirst(left_tlist); char *colName; TargetEntry *tle; Var *var; Assert(!lefttle->resjunk); colName = pstrdup(lefttle->resname); var = makeVar(leftmostRTI, lefttle->resno, colType, colTypmod, colCollation, 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, var); targetnames = lappend(targetnames, makeString(colName)); sortnscolumns[sortcolindex].p_varno = leftmostRTI; sortnscolumns[sortcolindex].p_varattno = lefttle->resno; sortnscolumns[sortcolindex].p_vartype = colType; sortnscolumns[sortcolindex].p_vartypmod = colTypmod; sortnscolumns[sortcolindex].p_varcollid = colCollation; sortnscolumns[sortcolindex].p_varnosyn = leftmostRTI; sortnscolumns[sortcolindex].p_varattnosyn = lefttle->resno; sortcolindex++; } /* * As a first step towards supporting sort clauses that are expressions * using the output columns, generate a namespace entry that makes the * output columns visible. A Join RTE node is handy for this, since we * can easily control the Vars generated upon matches. * * Note: we don't yet do anything useful with such cases, but at least * "ORDER BY upper(foo)" will draw the right error message rather than * "foo not found". */ sv_rtable_length = list_length(pstate->p_rtable); jnsitem = addRangeTableEntryForJoin(pstate, targetnames, sortnscolumns, JOIN_INNER, 0, targetvars, NIL, NIL, NULL, NULL, false); sv_namespace = pstate->p_namespace; pstate->p_namespace = NIL; /* add jnsitem to column namespace only */ addNSItemToQuery(pstate, jnsitem, false, false, true); /* * For now, we don't support resjunk sort clauses on the output of a * setOperation tree --- you can only use the SQL92-spec options of * selecting an output column by name or number. Enforce by checking that * transformSortClause doesn't add any items to tlist. */ tllen = list_length(qry->targetList); qry->sortClause = transformSortClause(pstate, sortClause, &qry->targetList, EXPR_KIND_ORDER_BY, false /* allow SQL92 rules */ ); /* restore namespace, remove join RTE from rtable */ pstate->p_namespace = sv_namespace; pstate->p_rtable = list_truncate(pstate->p_rtable, sv_rtable_length); if (tllen != list_length(qry->targetList)) ereport(ERROR, (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."), parser_errposition(pstate, exprLocation(list_nth(qry->targetList, tllen))))); qry->limitOffset = transformLimitClause(pstate, limitOffset, EXPR_KIND_OFFSET, "OFFSET", stmt->limitOption); qry->limitCount = transformLimitClause(pstate, limitCount, EXPR_KIND_LIMIT, "LIMIT", stmt->limitOption); qry->limitOption = stmt->limitOption; qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; qry->hasTargetSRFs = pstate->p_hasTargetSRFs; qry->hasAggs = pstate->p_hasAggs; foreach(l, lockingClause) { transformLockingClause(pstate, qry, (LockingClause *) lfirst(l), false); } assign_query_collations(pstate, qry); /* this must be done after collations, for reliable comparison of exprs */ if (pstate->p_hasAggs || qry->groupClause || qry->groupingSets || qry->havingQual) parseCheckAggregates(pstate, qry); return qry; } /* * Make a SortGroupClause node for a SetOperationStmt's groupClauses * * If require_hash is true, the caller is indicating that they need hash * support or they will fail. So look extra hard for hash support. */ SortGroupClause * makeSortGroupClauseForSetOp(Oid rescoltype, bool require_hash) { SortGroupClause *grpcl = makeNode(SortGroupClause); Oid sortop; Oid eqop; bool hashable; /* determine the eqop and optional sortop */ get_sort_group_operators(rescoltype, false, true, false, &sortop, &eqop, NULL, &hashable); /* * The type cache doesn't believe that record is hashable (see * cache_record_field_properties()), but if the caller really needs hash * support, we can assume it does. Worst case, if any components of the * record don't support hashing, we will fail at execution. */ if (require_hash && (rescoltype == RECORDOID || rescoltype == RECORDARRAYOID)) hashable = true; /* 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 */ grpcl->hashable = hashable; return grpcl; } /* * transformSetOperationTree * Recursively transform leaves and internal nodes of a set-op tree * * In addition to returning the transformed node, if targetlist isn't NULL * then we return a list of its non-resjunk TargetEntry nodes. For a leaf * set-op node these are the actual targetlist entries; otherwise they are * dummy entries created to carry the type, typmod, collation, and location * (for error messages) of each output column of the set-op node. This info * is needed only during the internal recursion of this function, so outside * callers pass NULL for targetlist. Note: the reason for passing the * actual targetlist entries of a leaf node is so that upper levels can * replace UNKNOWN Consts with properly-coerced constants. */ static Node * transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, bool isTopLevel, List **targetlist) { bool isLeaf; Assert(stmt && IsA(stmt, SelectStmt)); /* Guard against stack overflow due to overly complex set-expressions */ check_stack_depth(); /* * Validity-check both leaf and internal SELECTs for disallowed ops. */ if (stmt->intoClause) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), 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, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), /*------ translator: %s is a SQL row locking clause such as FOR UPDATE */ errmsg("%s is not allowed with UNION/INTERSECT/EXCEPT", LCS_asString(((LockingClause *) linitial(stmt->lockingClause))->strength)))); /* * If an internal node of a set-op tree has ORDER BY, LIMIT, FOR UPDATE, * or WITH clauses attached, we need to treat it like a leaf node to * generate an independent sub-Query tree. Otherwise, it can be * represented by a SetOperationStmt node underneath the parent Query. */ if (stmt->op == SETOP_NONE) { Assert(stmt->larg == NULL && stmt->rarg == NULL); isLeaf = true; } else { Assert(stmt->larg != NULL && stmt->rarg != NULL); if (stmt->sortClause || stmt->limitOffset || stmt->limitCount || stmt->lockingClause || stmt->withClause) isLeaf = true; else isLeaf = false; } if (isLeaf) { /* Process leaf SELECT */ Query *selectQuery; char selectName[32]; ParseNamespaceItem *nsitem; RangeTblRef *rtr; ListCell *tl; /* * Transform SelectStmt into a Query. * * This works the same as SELECT transformation normally would, except * that we prevent resolving unknown-type outputs as TEXT. This does * not change the subquery's semantics since if the column type * matters semantically, it would have been resolved to something else * anyway. Doing this lets us resolve such outputs using * select_common_type(), below. * * Note: previously transformed sub-queries don't affect the parsing * of this sub-query, because they are not in the toplevel pstate's * namespace list. */ selectQuery = parse_sub_analyze((Node *) stmt, pstate, NULL, false, false); /* * Check for bogus references to Vars on the current query level (but * upper-level references are okay). Normally this can't happen * because the namespace will be empty, but it could happen if we are * inside a rule. */ if (pstate->p_namespace) { 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"), parser_errposition(pstate, locate_var_of_level((Node *) selectQuery, 1)))); } /* * Extract a list of the non-junk TLEs for upper-level processing. */ if (targetlist) { *targetlist = NIL; foreach(tl, selectQuery->targetList) { TargetEntry *tle = (TargetEntry *) lfirst(tl); if (!tle->resjunk) *targetlist = lappend(*targetlist, tle); } } /* * Make the leaf query be a subquery in the top-level rangetable. */ snprintf(selectName, sizeof(selectName), "*SELECT* %d", list_length(pstate->p_rtable) + 1); nsitem = addRangeTableEntryForSubquery(pstate, selectQuery, makeAlias(selectName, NIL), false, false); /* * Return a RangeTblRef to replace the SelectStmt in the set-op tree. */ rtr = makeNode(RangeTblRef); rtr->rtindex = nsitem->p_rtindex; return (Node *) rtr; } else { /* Process an internal node (set operation node) */ SetOperationStmt *op = makeNode(SetOperationStmt); List *ltargetlist; List *rtargetlist; ListCell *ltl; ListCell *rtl; const char *context; bool recursive = (pstate->p_parent_cte && pstate->p_parent_cte->cterecursive); context = (stmt->op == SETOP_UNION ? "UNION" : (stmt->op == SETOP_INTERSECT ? "INTERSECT" : "EXCEPT")); op->op = stmt->op; op->all = stmt->all; /* * Recursively transform the left child node. */ op->larg = transformSetOperationTree(pstate, stmt->larg, false, <argetlist); /* * 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 && recursive) determineRecursiveColTypes(pstate, op->larg, ltargetlist); /* * Recursively transform the right child node. */ op->rarg = transformSetOperationTree(pstate, stmt->rarg, false, &rtargetlist); /* * Verify that the two children have the same number of non-junk * columns, and determine the types of the merged output columns. */ if (list_length(ltargetlist) != list_length(rtargetlist)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("each %s query must have the same number of columns", context), parser_errposition(pstate, exprLocation((Node *) rtargetlist)))); if (targetlist) *targetlist = NIL; op->colTypes = NIL; op->colTypmods = NIL; op->colCollations = NIL; op->groupClauses = NIL; forboth(ltl, ltargetlist, rtl, rtargetlist) { TargetEntry *ltle = (TargetEntry *) lfirst(ltl); TargetEntry *rtle = (TargetEntry *) lfirst(rtl); Node *lcolnode = (Node *) ltle->expr; Node *rcolnode = (Node *) rtle->expr; Oid lcoltype = exprType(lcolnode); Oid rcoltype = exprType(rcolnode); Node *bestexpr; int bestlocation; Oid rescoltype; int32 rescoltypmod; Oid rescolcoll; /* select common type, same as CASE et al */ rescoltype = select_common_type(pstate, list_make2(lcolnode, rcolnode), context, &bestexpr); bestlocation = exprLocation(bestexpr); /* * 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. * * For all non-UNKNOWN-type cases, we verify coercibility but we * don't modify the child's expression, for fear of changing the * child query's semantics. * * If a child expression is an UNKNOWN-type Const or Param, we * want to replace it with the coerced expression. This can only * happen when the child is a leaf set-op node. It's safe to * replace the expression because if the child query's semantics * depended on the type of this output column, it'd have already * coerced the UNKNOWN to something else. We want to do this * because (a) we want to verify that a Const is valid for the * target type, or resolve the actual type of an UNKNOWN Param, * and (b) we want to avoid unnecessary discrepancies between the * output type of the child query and the resolved target type. * Such a discrepancy would disable optimization in the planner. * * If it's some other UNKNOWN-type node, eg a Var, we do nothing * (knowing that coerce_to_common_type would fail). The planner * is sometimes able to fold an UNKNOWN Var to a constant before * it has to coerce the type, so failing now would just break * cases that might work. */ if (lcoltype != UNKNOWNOID) lcolnode = coerce_to_common_type(pstate, lcolnode, rescoltype, context); else if (IsA(lcolnode, Const) || IsA(lcolnode, Param)) { lcolnode = coerce_to_common_type(pstate, lcolnode, rescoltype, context); ltle->expr = (Expr *) lcolnode; } if (rcoltype != UNKNOWNOID) rcolnode = coerce_to_common_type(pstate, rcolnode, rescoltype, context); else if (IsA(rcolnode, Const) || IsA(rcolnode, Param)) { rcolnode = coerce_to_common_type(pstate, rcolnode, rescoltype, context); rtle->expr = (Expr *) rcolnode; } rescoltypmod = select_common_typmod(pstate, list_make2(lcolnode, rcolnode), rescoltype); /* * Select common collation. A common collation is required for * all set operators except UNION ALL; see SQL:2008 7.13 Syntax Rule 15c. (If we fail to identify a common * collation for a UNION ALL column, the colCollations element * will be set to InvalidOid, which may result in a runtime error * if something at a higher query level wants to use the column's * collation.) */ rescolcoll = select_common_collation(pstate, list_make2(lcolnode, rcolnode), (op->op == SETOP_UNION && op->all)); /* emit results */ op->colTypes = lappend_oid(op->colTypes, rescoltype); op->colTypmods = lappend_int(op->colTypmods, rescoltypmod); op->colCollations = lappend_oid(op->colCollations, rescolcoll); /* * 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) { ParseCallbackState pcbstate; setup_parser_errposition_callback(&pcbstate, pstate, bestlocation); /* If it's a recursive union, we need to require hashing support. */ op->groupClauses = lappend(op->groupClauses, makeSortGroupClauseForSetOp(rescoltype, recursive)); cancel_parser_errposition_callback(&pcbstate); } /* * Construct a dummy tlist entry to return. We use a SetToDefault * node for the expression, since it carries exactly the fields * needed, but any other expression node type would do as well. */ if (targetlist) { SetToDefault *rescolnode = makeNode(SetToDefault); TargetEntry *restle; rescolnode->typeId = rescoltype; rescolnode->typeMod = rescoltypmod; rescolnode->collation = rescolcoll; rescolnode->location = bestlocation; restle = makeTargetEntry((Expr *) rescolnode, 0, /* no need to set resno */ NULL, false); *targetlist = lappend(*targetlist, restle); } } return (Node *) op; } } /* * 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 *nrtargetlist) { Node *node; int leftmostRTI; Query *leftmostQuery; List *targetList; ListCell *left_tlist; ListCell *nrtl; 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; next_resno = 1; forboth(nrtl, nrtargetlist, left_tlist, leftmostQuery->targetList) { TargetEntry *nrtle = (TargetEntry *) lfirst(nrtl); TargetEntry *lefttle = (TargetEntry *) lfirst(left_tlist); char *colName; TargetEntry *tle; Assert(!lefttle->resjunk); colName = pstrdup(lefttle->resname); tle = makeTargetEntry(nrtle->expr, next_resno++, colName, false); targetList = lappend(targetList, tle); } /* Now build CTE's output column info using dummy targetlist */ analyzeCTETargetList(pstate, pstate->p_parent_cte, targetList); } /* * transformReturnStmt - * transforms a return statement */ static Query * transformReturnStmt(ParseState *pstate, ReturnStmt *stmt) { Query *qry = makeNode(Query); qry->commandType = CMD_SELECT; qry->isReturn = true; qry->targetList = list_make1(makeTargetEntry((Expr *) transformExpr(pstate, stmt->returnval, EXPR_KIND_SELECT_TARGET), 1, NULL, false)); if (pstate->p_resolve_unknowns) resolveTargetListUnknowns(pstate, qry->targetList); qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; qry->hasTargetSRFs = pstate->p_hasTargetSRFs; qry->hasAggs = pstate->p_hasAggs; assign_query_collations(pstate, qry); return qry; } /* * transformUpdateStmt - * transforms an update statement */ static Query * transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) { Query *qry = makeNode(Query); ParseNamespaceItem *nsitem; Node *qual; qry->commandType = CMD_UPDATE; pstate->p_is_insert = false; /* process the WITH clause independently of all else */ if (stmt->withClause) { qry->hasRecursive = stmt->withClause->recursive; qry->cteList = transformWithClause(pstate, stmt->withClause); qry->hasModifyingCTE = pstate->p_hasModifyingCTE; } qry->resultRelation = setTargetTable(pstate, stmt->relation, stmt->relation->inh, true, ACL_UPDATE); nsitem = pstate->p_target_nsitem; /* subqueries in FROM cannot access the result relation */ nsitem->p_lateral_only = true; nsitem->p_lateral_ok = false; /* * the FROM clause is non-standard SQL syntax. We used to be able to do * this with REPLACE in POSTQUEL so we keep the feature. */ transformFromClause(pstate, stmt->fromClause); /* remaining clauses can reference the result relation normally */ nsitem->p_lateral_only = false; nsitem->p_lateral_ok = true; qual = transformWhereClause(pstate, stmt->whereClause, EXPR_KIND_WHERE, "WHERE"); qry->returningList = transformReturningList(pstate, stmt->returningList); /* * Now we are done with SELECT-like processing, and can get on with * transforming the target list to match the UPDATE target columns. */ qry->targetList = transformUpdateTargetList(pstate, stmt->targetList); qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasTargetSRFs = pstate->p_hasTargetSRFs; qry->hasSubLinks = pstate->p_hasSubLinks; assign_query_collations(pstate, qry); return qry; } /* * transformUpdateTargetList - * handle SET clause in UPDATE/INSERT ... ON CONFLICT UPDATE */ static List * transformUpdateTargetList(ParseState *pstate, List *origTlist) { List *tlist = NIL; RangeTblEntry *target_rte; ListCell *orig_tl; ListCell *tl; tlist = transformTargetList(pstate, origTlist, EXPR_KIND_UPDATE_SOURCE); /* Prepare to assign non-conflicting resnos to resjunk attributes */ if (pstate->p_next_resno <= RelationGetNumberOfAttributes(pstate->p_target_relation)) pstate->p_next_resno = RelationGetNumberOfAttributes(pstate->p_target_relation) + 1; /* Prepare non-junk columns for assignment to target table */ target_rte = pstate->p_target_nsitem->p_rte; orig_tl = list_head(origTlist); foreach(tl, tlist) { TargetEntry *tle = (TargetEntry *) lfirst(tl); ResTarget *origTarget; int attrno; if (tle->resjunk) { /* * Resjunk nodes need no additional processing, but be sure they * have resnos that do not match any target columns; else rewriter * or planner might get confused. They don't need a resname * either. */ tle->resno = (AttrNumber) pstate->p_next_resno++; tle->resname = NULL; continue; } if (orig_tl == NULL) elog(ERROR, "UPDATE target count mismatch --- internal error"); origTarget = lfirst_node(ResTarget, orig_tl); attrno = attnameAttNum(pstate->p_target_relation, origTarget->name, true); if (attrno == InvalidAttrNumber) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" of relation \"%s\" does not exist", origTarget->name, RelationGetRelationName(pstate->p_target_relation)), parser_errposition(pstate, origTarget->location))); updateTargetListEntry(pstate, tle, origTarget->name, attrno, origTarget->indirection, origTarget->location); /* Mark the target column as requiring update permissions */ target_rte->updatedCols = bms_add_member(target_rte->updatedCols, attrno - FirstLowInvalidHeapAttributeNumber); orig_tl = lnext(origTlist, orig_tl); } if (orig_tl != NULL) elog(ERROR, "UPDATE target count mismatch --- internal error"); return tlist; } /* * transformReturningList - * handle a RETURNING clause in INSERT/UPDATE/DELETE */ static List * transformReturningList(ParseState *pstate, List *returningList) { List *rlist; int save_next_resno; if (returningList == NIL) return NIL; /* nothing to do */ /* * We need to assign resnos starting at one in the RETURNING list. Save * and restore the main tlist's value of p_next_resno, just in case * someone looks at it later (probably won't happen). */ save_next_resno = pstate->p_next_resno; pstate->p_next_resno = 1; /* transform RETURNING identically to a SELECT targetlist */ rlist = transformTargetList(pstate, returningList, EXPR_KIND_RETURNING); /* * Complain if the nonempty tlist expanded to nothing (which is possible * if it contains only a star-expansion of a zero-column table). If we * allow this, the parsed Query will look like it didn't have RETURNING, * with results that would probably surprise the user. */ if (rlist == NIL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("RETURNING must have at least one column"), parser_errposition(pstate, exprLocation(linitial(returningList))))); /* mark column origins */ markTargetListOrigins(pstate, rlist); /* resolve any still-unresolved output columns as being type text */ if (pstate->p_resolve_unknowns) resolveTargetListUnknowns(pstate, rlist); /* restore state */ pstate->p_next_resno = save_next_resno; return rlist; } /* * transformPLAssignStmt - * transform a PL/pgSQL assignment statement * * If there is no opt_indirection, the transformed statement looks like * "SELECT a_expr ...", except the expression has been cast to the type of * the target. With indirection, it's still a SELECT, but the expression will * incorporate FieldStore and/or assignment SubscriptingRef nodes to compute a * new value for a container-type variable represented by the target. The * expression references the target as the container source. */ static Query * transformPLAssignStmt(ParseState *pstate, PLAssignStmt *stmt) { Query *qry = makeNode(Query); ColumnRef *cref = makeNode(ColumnRef); List *indirection = stmt->indirection; int nnames = stmt->nnames; SelectStmt *sstmt = stmt->val; Node *target; Oid targettype; int32 targettypmod; Oid targetcollation; List *tlist; TargetEntry *tle; Oid type_id; Node *qual; ListCell *l; /* * First, construct a ColumnRef for the target variable. If the target * has more than one dotted name, we have to pull the extra names out of * the indirection list. */ cref->fields = list_make1(makeString(stmt->name)); cref->location = stmt->location; if (nnames > 1) { /* avoid munging the raw parsetree */ indirection = list_copy(indirection); while (--nnames > 0 && indirection != NIL) { Node *ind = (Node *) linitial(indirection); if (!IsA(ind, String)) elog(ERROR, "invalid name count in PLAssignStmt"); cref->fields = lappend(cref->fields, ind); indirection = list_delete_first(indirection); } } /* * Transform the target reference. Typically we will get back a Param * node, but there's no reason to be too picky about its type. */ target = transformExpr(pstate, (Node *) cref, EXPR_KIND_UPDATE_TARGET); targettype = exprType(target); targettypmod = exprTypmod(target); targetcollation = exprCollation(target); /* * The rest mostly matches transformSelectStmt, except that we needn't * consider WITH or INTO, and we build a targetlist our own way. */ qry->commandType = CMD_SELECT; pstate->p_is_insert = false; /* make FOR UPDATE/FOR SHARE info available to addRangeTableEntry */ pstate->p_locking_clause = sstmt->lockingClause; /* make WINDOW info available for window functions, too */ pstate->p_windowdefs = sstmt->windowClause; /* process the FROM clause */ transformFromClause(pstate, sstmt->fromClause); /* initially transform the targetlist as if in SELECT */ tlist = transformTargetList(pstate, sstmt->targetList, EXPR_KIND_SELECT_TARGET); /* we should have exactly one targetlist item */ if (list_length(tlist) != 1) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg_plural("assignment source returned %d column", "assignment source returned %d columns", list_length(tlist), list_length(tlist)))); tle = linitial_node(TargetEntry, tlist); /* * This next bit is similar to transformAssignedExpr; the key difference * is we use COERCION_PLPGSQL not COERCION_ASSIGNMENT. */ type_id = exprType((Node *) tle->expr); pstate->p_expr_kind = EXPR_KIND_UPDATE_TARGET; if (indirection) { tle->expr = (Expr *) transformAssignmentIndirection(pstate, target, stmt->name, false, targettype, targettypmod, targetcollation, indirection, list_head(indirection), (Node *) tle->expr, COERCION_PLPGSQL, exprLocation(target)); } else if (targettype != type_id && (targettype == RECORDOID || ISCOMPLEX(targettype)) && (type_id == RECORDOID || ISCOMPLEX(type_id))) { /* * Hack: do not let coerce_to_target_type() deal with inconsistent * composite types. Just pass the expression result through as-is, * and let the PL/pgSQL executor do the conversion its way. This is * rather bogus, but it's needed for backwards compatibility. */ } else { /* * For normal non-qualified target column, do type checking and * coercion. */ Node *orig_expr = (Node *) tle->expr; tle->expr = (Expr *) coerce_to_target_type(pstate, orig_expr, type_id, targettype, targettypmod, COERCION_PLPGSQL, COERCE_IMPLICIT_CAST, -1); /* With COERCION_PLPGSQL, this error is probably unreachable */ if (tle->expr == NULL) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("variable \"%s\" is of type %s" " but expression is of type %s", stmt->name, format_type_be(targettype), format_type_be(type_id)), errhint("You will need to rewrite or cast the expression."), parser_errposition(pstate, exprLocation(orig_expr)))); } pstate->p_expr_kind = EXPR_KIND_NONE; qry->targetList = list_make1(tle); /* transform WHERE */ qual = transformWhereClause(pstate, sstmt->whereClause, EXPR_KIND_WHERE, "WHERE"); /* initial processing of HAVING clause is much like WHERE clause */ qry->havingQual = transformWhereClause(pstate, sstmt->havingClause, EXPR_KIND_HAVING, "HAVING"); /* * 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. */ qry->sortClause = transformSortClause(pstate, sstmt->sortClause, &qry->targetList, EXPR_KIND_ORDER_BY, false /* allow SQL92 rules */ ); qry->groupClause = transformGroupClause(pstate, sstmt->groupClause, &qry->groupingSets, &qry->targetList, qry->sortClause, EXPR_KIND_GROUP_BY, false /* allow SQL92 rules */ ); if (sstmt->distinctClause == NIL) { qry->distinctClause = NIL; qry->hasDistinctOn = false; } else if (linitial(sstmt->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, sstmt->distinctClause, &qry->targetList, qry->sortClause); qry->hasDistinctOn = true; } /* transform LIMIT */ qry->limitOffset = transformLimitClause(pstate, sstmt->limitOffset, EXPR_KIND_OFFSET, "OFFSET", sstmt->limitOption); qry->limitCount = transformLimitClause(pstate, sstmt->limitCount, EXPR_KIND_LIMIT, "LIMIT", sstmt->limitOption); qry->limitOption = sstmt->limitOption; /* transform window clauses after we have seen all window functions */ qry->windowClause = transformWindowDefinitions(pstate, pstate->p_windowdefs, &qry->targetList); qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; qry->hasTargetSRFs = pstate->p_hasTargetSRFs; qry->hasAggs = pstate->p_hasAggs; foreach(l, sstmt->lockingClause) { transformLockingClause(pstate, qry, (LockingClause *) lfirst(l), false); } assign_query_collations(pstate, qry); /* this must be done after collations, for reliable comparison of exprs */ if (pstate->p_hasAggs || qry->groupClause || qry->groupingSets || qry->havingQual) parseCheckAggregates(pstate, qry); return qry; } /* * transformDeclareCursorStmt - * transform a DECLARE CURSOR Statement * * DECLARE CURSOR 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 * transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt) { Query *result; Query *query; if ((stmt->options & CURSOR_OPT_SCROLL) && (stmt->options & CURSOR_OPT_NO_SCROLL)) ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), /* translator: %s is a SQL keyword */ errmsg("cannot specify both %s and %s", "SCROLL", "NO SCROLL"))); if ((stmt->options & CURSOR_OPT_ASENSITIVE) && (stmt->options & CURSOR_OPT_INSENSITIVE)) ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), /* translator: %s is a SQL keyword */ errmsg("cannot specify both %s and %s", "ASENSITIVE", "INSENSITIVE"))); /* Transform contained query, not allowing SELECT INTO */ query = transformStmt(pstate, stmt->query); stmt->query = (Node *) query; /* Grammar should not have allowed anything but SELECT */ if (!IsA(query, Query) || query->commandType != CMD_SELECT) elog(ERROR, "unexpected non-SELECT command in DECLARE CURSOR"); /* * We also disallow data-modifying WITH in a cursor. (This could be * allowed, but the semantics of when the updates occur might be * surprising.) */ if (query->hasModifyingCTE) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("DECLARE CURSOR must not contain data-modifying statements in WITH"))); /* FOR UPDATE and WITH HOLD are not compatible */ if (query->rowMarks != NIL && (stmt->options & CURSOR_OPT_HOLD)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), /*------ translator: %s is a SQL row locking clause such as FOR UPDATE */ errmsg("DECLARE CURSOR WITH HOLD ... %s is not supported", LCS_asString(((RowMarkClause *) linitial(query->rowMarks))->strength)), errdetail("Holdable cursors must be READ ONLY."))); /* FOR UPDATE and SCROLL are not compatible */ if (query->rowMarks != NIL && (stmt->options & CURSOR_OPT_SCROLL)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), /*------ translator: %s is a SQL row locking clause such as FOR UPDATE */ errmsg("DECLARE SCROLL CURSOR ... %s is not supported", LCS_asString(((RowMarkClause *) linitial(query->rowMarks))->strength)), errdetail("Scrollable cursors must be READ ONLY."))); /* FOR UPDATE and INSENSITIVE are not compatible */ if (query->rowMarks != NIL && (stmt->options & CURSOR_OPT_INSENSITIVE)) ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), /*------ translator: %s is a SQL row locking clause such as FOR UPDATE */ errmsg("DECLARE INSENSITIVE CURSOR ... %s is not valid", LCS_asString(((RowMarkClause *) linitial(query->rowMarks))->strength)), errdetail("Insensitive cursors must be READ ONLY."))); /* represent the command as a utility Query */ result = makeNode(Query); result->commandType = CMD_UTILITY; result->utilityStmt = (Node *) stmt; return result; } /* * transformExplainStmt - * transform an EXPLAIN Statement * * 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; /* transform contained query, allowing SELECT INTO */ stmt->query = (Node *) transformOptionalSelectInto(pstate, stmt->query); /* represent the command as a utility Query */ result = makeNode(Query); result->commandType = CMD_UTILITY; result->utilityStmt = (Node *) stmt; return result; } /* * transformCreateTableAsStmt - * transform a CREATE TABLE AS, SELECT ... INTO, or CREATE MATERIALIZED VIEW * Statement * * As with DECLARE CURSOR and EXPLAIN, transform the contained statement now. */ static Query * transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt) { Query *result; Query *query; /* transform contained query, not allowing SELECT INTO */ query = transformStmt(pstate, stmt->query); stmt->query = (Node *) query; /* additional work needed for CREATE MATERIALIZED VIEW */ if (stmt->objtype == OBJECT_MATVIEW) { /* * Prohibit a data-modifying CTE in the query used to create a * materialized view. It's not sufficiently clear what the user would * want to happen if the MV is refreshed or incrementally maintained. */ if (query->hasModifyingCTE) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialized views must not use data-modifying statements in WITH"))); /* * Check whether any temporary database objects are used in the * creation query. It would be hard to refresh data or incrementally * maintain it if a source disappeared. */ if (isQueryUsingTempRelation(query)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialized views must not use temporary tables or views"))); /* * A materialized view would either need to save parameters for use in * maintaining/loading the data or prohibit them entirely. The latter * seems safer and more sane. */ if (query_contains_extern_params(query)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialized views may not be defined using bound parameters"))); /* * For now, we disallow unlogged materialized views, because it seems * like a bad idea for them to just go to empty after a crash. (If we * could mark them as unpopulated, that would be better, but that * requires catalog changes which crash recovery can't presently * handle.) */ if (stmt->into->rel->relpersistence == RELPERSISTENCE_UNLOGGED) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialized views cannot be unlogged"))); /* * At runtime, we'll need a copy of the parsed-but-not-rewritten Query * for purposes of creating the view's ON SELECT rule. We stash that * in the IntoClause because that's where intorel_startup() can * conveniently get it from. */ stmt->into->viewQuery = (Node *) copyObject(query); } /* represent the command as a utility Query */ result = makeNode(Query); result->commandType = CMD_UTILITY; result->utilityStmt = (Node *) stmt; return result; } /* * transform a CallStmt */ static Query * transformCallStmt(ParseState *pstate, CallStmt *stmt) { List *targs; ListCell *lc; Node *node; FuncExpr *fexpr; HeapTuple proctup; Datum proargmodes; bool isNull; List *outargs = NIL; Query *result; /* * First, do standard parse analysis on the procedure call and its * arguments, allowing us to identify the called procedure. */ targs = NIL; foreach(lc, stmt->funccall->args) { targs = lappend(targs, transformExpr(pstate, (Node *) lfirst(lc), EXPR_KIND_CALL_ARGUMENT)); } node = ParseFuncOrColumn(pstate, stmt->funccall->funcname, targs, pstate->p_last_srf, stmt->funccall, true, stmt->funccall->location); assign_expr_collations(pstate, node); fexpr = castNode(FuncExpr, node); proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fexpr->funcid)); if (!HeapTupleIsValid(proctup)) elog(ERROR, "cache lookup failed for function %u", fexpr->funcid); /* * Expand the argument list to deal with named-argument notation and * default arguments. For ordinary FuncExprs this'd be done during * planning, but a CallStmt doesn't go through planning, and there seems * no good reason not to do it here. */ fexpr->args = expand_function_arguments(fexpr->args, true, fexpr->funcresulttype, proctup); /* Fetch proargmodes; if it's null, there are no output args */ proargmodes = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_proargmodes, &isNull); if (!isNull) { /* * Split the list into input arguments in fexpr->args and output * arguments in stmt->outargs. INOUT arguments appear in both lists. */ ArrayType *arr; int numargs; char *argmodes; List *inargs; int i; arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */ numargs = list_length(fexpr->args); if (ARR_NDIM(arr) != 1 || ARR_DIMS(arr)[0] != numargs || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != CHAROID) elog(ERROR, "proargmodes is not a 1-D char array of length %d or it contains nulls", numargs); argmodes = (char *) ARR_DATA_PTR(arr); inargs = NIL; i = 0; foreach(lc, fexpr->args) { Node *n = lfirst(lc); switch (argmodes[i]) { case PROARGMODE_IN: case PROARGMODE_VARIADIC: inargs = lappend(inargs, n); break; case PROARGMODE_OUT: outargs = lappend(outargs, n); break; case PROARGMODE_INOUT: inargs = lappend(inargs, n); outargs = lappend(outargs, copyObject(n)); break; default: /* note we don't support PROARGMODE_TABLE */ elog(ERROR, "invalid argmode %c for procedure", argmodes[i]); break; } i++; } fexpr->args = inargs; } stmt->funcexpr = fexpr; stmt->outargs = outargs; ReleaseSysCache(proctup); /* represent the command as a utility Query */ result = makeNode(Query); result->commandType = CMD_UTILITY; result->utilityStmt = (Node *) stmt; return result; } /* * Produce a string representation of a LockClauseStrength value. * This should only be applied to valid values (not LCS_NONE). */ const char * LCS_asString(LockClauseStrength strength) { switch (strength) { case LCS_NONE: Assert(false); break; case LCS_FORKEYSHARE: return "FOR KEY SHARE"; case LCS_FORSHARE: return "FOR SHARE"; case LCS_FORNOKEYUPDATE: return "FOR NO KEY UPDATE"; case LCS_FORUPDATE: return "FOR UPDATE"; } return "FOR some"; /* shouldn't happen */ } /* * Check for features that are not supported with FOR [KEY] UPDATE/SHARE. * * exported so planner can check again after rewriting, query pullup, etc */ void CheckSelectLocking(Query *qry, LockClauseStrength strength) { Assert(strength != LCS_NONE); /* else caller error */ if (qry->setOperations) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), /*------ translator: %s is a SQL row locking clause such as FOR UPDATE */ errmsg("%s is not allowed with UNION/INTERSECT/EXCEPT", LCS_asString(strength)))); if (qry->distinctClause != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), /*------ translator: %s is a SQL row locking clause such as FOR UPDATE */ errmsg("%s is not allowed with DISTINCT clause", LCS_asString(strength)))); if (qry->groupClause != NIL || qry->groupingSets != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), /*------ translator: %s is a SQL row locking clause such as FOR UPDATE */ errmsg("%s is not allowed with GROUP BY clause", LCS_asString(strength)))); if (qry->havingQual != NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), /*------ translator: %s is a SQL row locking clause such as FOR UPDATE */ errmsg("%s is not allowed with HAVING clause", LCS_asString(strength)))); if (qry->hasAggs) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), /*------ translator: %s is a SQL row locking clause such as FOR UPDATE */ errmsg("%s is not allowed with aggregate functions", LCS_asString(strength)))); if (qry->hasWindowFuncs) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), /*------ translator: %s is a SQL row locking clause such as FOR UPDATE */ errmsg("%s is not allowed with window functions", LCS_asString(strength)))); if (qry->hasTargetSRFs) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), /*------ translator: %s is a SQL row locking clause such as FOR UPDATE */ errmsg("%s is not allowed with set-returning functions in the target list", LCS_asString(strength)))); } /* * Transform a FOR [KEY] UPDATE/SHARE clause * * This basically involves replacing names by integer relids. * * NB: if you need to change this, see also markQueryForLocking() * in rewriteHandler.c, and isLockedRefname() in parse_relation.c. */ static void transformLockingClause(ParseState *pstate, Query *qry, LockingClause *lc, bool pushedDown) { List *lockedRels = lc->lockedRels; ListCell *l; ListCell *rt; Index i; LockingClause *allrels; CheckSelectLocking(qry, lc->strength); /* make a clause we can pass down to subqueries to select all rels */ allrels = makeNode(LockingClause); allrels->lockedRels = NIL; /* indicates all rels */ allrels->strength = lc->strength; allrels->waitPolicy = lc->waitPolicy; if (lockedRels == NIL) { /* * Lock all regular tables used in query and its subqueries. We * examine inFromCl to exclude auto-added RTEs, particularly NEW/OLD * in rules. This is a bit of an abuse of a mostly-obsolete flag, but * it's convenient. We can't rely on the namespace mechanism that has * largely replaced inFromCl, since for example we need to lock * base-relation RTEs even if they are masked by upper joins. */ i = 0; foreach(rt, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt); ++i; if (!rte->inFromCl) continue; switch (rte->rtekind) { case RTE_RELATION: applyLockingClause(qry, i, lc->strength, lc->waitPolicy, pushedDown); rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, lc->waitPolicy, pushedDown); /* * FOR UPDATE/SHARE of subquery is propagated to all of * 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, true); break; default: /* ignore JOIN, SPECIAL, FUNCTION, VALUES, CTE RTEs */ break; } } } else { /* * Lock just the named tables. As above, we allow locking any base * relation regardless of alias-visibility rules, so we need to * examine inFromCl to exclude OLD/NEW. */ foreach(l, lockedRels) { 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), /*------ translator: %s is a SQL row locking clause such as FOR UPDATE */ errmsg("%s must specify unqualified relation names", LCS_asString(lc->strength)), parser_errposition(pstate, thisrel->location))); i = 0; foreach(rt, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt); char *rtename; ++i; if (!rte->inFromCl) continue; /* * A join RTE without an alias is not visible as a relation * name and needs to be skipped (otherwise it might hide a * base relation with the same name), except if it has a USING * alias, which *is* visible. */ if (rte->rtekind == RTE_JOIN && rte->alias == NULL) { if (rte->join_using_alias == NULL) continue; rtename = rte->join_using_alias->aliasname; } else rtename = rte->eref->aliasname; if (strcmp(rtename, thisrel->relname) == 0) { switch (rte->rtekind) { case RTE_RELATION: applyLockingClause(qry, i, lc->strength, lc->waitPolicy, pushedDown); rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, lc->waitPolicy, pushedDown); /* see comment above */ transformLockingClause(pstate, rte->subquery, allrels, true); break; case RTE_JOIN: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), /*------ translator: %s is a SQL row locking clause such as FOR UPDATE */ errmsg("%s cannot be applied to a join", LCS_asString(lc->strength)), parser_errposition(pstate, thisrel->location))); break; case RTE_FUNCTION: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), /*------ translator: %s is a SQL row locking clause such as FOR UPDATE */ errmsg("%s cannot be applied to a function", LCS_asString(lc->strength)), parser_errposition(pstate, thisrel->location))); break; case RTE_TABLEFUNC: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), /*------ translator: %s is a SQL row locking clause such as FOR UPDATE */ errmsg("%s cannot be applied to a table function", LCS_asString(lc->strength)), parser_errposition(pstate, thisrel->location))); break; case RTE_VALUES: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), /*------ translator: %s is a SQL row locking clause such as FOR UPDATE */ errmsg("%s cannot be applied to VALUES", LCS_asString(lc->strength)), parser_errposition(pstate, thisrel->location))); break; case RTE_CTE: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), /*------ translator: %s is a SQL row locking clause such as FOR UPDATE */ errmsg("%s cannot be applied to a WITH query", LCS_asString(lc->strength)), parser_errposition(pstate, thisrel->location))); break; case RTE_NAMEDTUPLESTORE: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), /*------ translator: %s is a SQL row locking clause such as FOR UPDATE */ errmsg("%s cannot be applied to a named tuplestore", LCS_asString(lc->strength)), parser_errposition(pstate, thisrel->location))); break; /* Shouldn't be possible to see RTE_RESULT here */ default: elog(ERROR, "unrecognized RTE type: %d", (int) rte->rtekind); break; } break; /* out of foreach loop */ } } if (rt == NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE), /*------ translator: %s is a SQL row locking clause such as FOR UPDATE */ errmsg("relation \"%s\" in %s clause not found in FROM clause", thisrel->relname, LCS_asString(lc->strength)), parser_errposition(pstate, thisrel->location))); } } } /* * Record locking info for a single rangetable item */ void applyLockingClause(Query *qry, Index rtindex, LockClauseStrength strength, LockWaitPolicy waitPolicy, bool pushedDown) { RowMarkClause *rc; Assert(strength != LCS_NONE); /* else caller error */ /* 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_parse_rowmark(qry, rtindex)) != NULL) { /* * If the same RTE is specified with more than one locking strength, * use the strongest. (Reasonable, since you can't take both a shared * and exclusive lock at the same time; it'll end up being exclusive * anyway.) * * Similarly, if the same RTE is specified with more than one lock * wait policy, consider that NOWAIT wins over SKIP LOCKED, which in * turn wins over waiting for the lock (the default). This 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.) Having NOWAIT win over SKIP * LOCKED is reasonable since the former throws an error in case of * coming across a locked tuple, which may be undesirable in some * cases but it seems better than silently returning inconsistent * results. * * And of course pushedDown becomes false if any clause is explicit. */ rc->strength = Max(rc->strength, strength); rc->waitPolicy = Max(rc->waitPolicy, waitPolicy); rc->pushedDown &= pushedDown; return; } /* Make a new RowMarkClause */ rc = makeNode(RowMarkClause); rc->rti = rtindex; rc->strength = strength; rc->waitPolicy = waitPolicy; rc->pushedDown = pushedDown; qry->rowMarks = lappend(qry->rowMarks, rc); } /* * Coverage testing for raw_expression_tree_walker(). * * When enabled, we run raw_expression_tree_walker() over every DML statement * submitted to parse analysis. Without this provision, that function is only * applied in limited cases involving CTEs, and we don't really want to have * to test everything inside as well as outside a CTE. */ #ifdef RAW_EXPRESSION_COVERAGE_TEST static bool test_raw_expression_coverage(Node *node, void *context) { if (node == NULL) return false; return raw_expression_tree_walker(node, test_raw_expression_coverage, context); } #endif /* RAW_EXPRESSION_COVERAGE_TEST */