postgresql/src/backend/parser/analyze.c

3599 lines
109 KiB
C

/*-------------------------------------------------------------------------
*
* 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-2024, 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 "commands/defrem.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/queryjumble.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_merge.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 "utils/backend_status.h"
#include "utils/builtins.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 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 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_fixedparams
* 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_fixedparams(RawStmt *parseTree, const char *sourceText,
const 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)
setup_parse_fixed_parameters(pstate, paramTypes, numParams);
pstate->p_queryEnv = queryEnv;
query = transformTopLevelStmt(pstate, parseTree);
if (IsQueryIdEnabled())
jstate = JumbleQuery(query);
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,
QueryEnvironment *queryEnv)
{
ParseState *pstate = make_parsestate(NULL);
Query *query;
JumbleState *jstate = NULL;
Assert(sourceText != NULL); /* required as of 8.4 */
pstate->p_sourcetext = sourceText;
setup_parse_variable_parameters(pstate, paramTypes, numParams);
pstate->p_queryEnv = queryEnv;
query = transformTopLevelStmt(pstate, parseTree);
/* make sure all is well with parameter types */
check_variable_parameters(pstate, query);
if (IsQueryIdEnabled())
jstate = JumbleQuery(query);
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_withcb
*
* This variant is used when the caller supplies their own parser callback to
* resolve parameters and possibly other things.
*/
Query *
parse_analyze_withcb(RawStmt *parseTree, const char *sourceText,
ParserSetupHook parserSetup,
void *parserSetupArg,
QueryEnvironment *queryEnv)
{
ParseState *pstate = make_parsestate(NULL);
Query *query;
JumbleState *jstate = NULL;
Assert(sourceText != NULL); /* required as of 8.4 */
pstate->p_sourcetext = sourceText;
pstate->p_queryEnv = queryEnv;
(*parserSetup) (pstate, parserSetupArg);
query = transformTopLevelStmt(pstate, parseTree);
if (IsQueryIdEnabled())
jstate = JumbleQuery(query);
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:
case T_MergeStmt:
(void) test_raw_expression_coverage(parseTree, NULL);
break;
default:
break;
}
#endif /* RAW_EXPRESSION_COVERAGE_TEST */
/*
* Caution: when changing the set of statement types that have non-default
* processing here, see also stmt_requires_parse_analysis() and
* analyze_requires_snapshot().
*/
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_MergeStmt:
result = transformMergeStmt(pstate, (MergeStmt *) 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;
}
/*
* stmt_requires_parse_analysis
* Returns true if parse analysis will do anything non-trivial
* with the given raw parse tree.
*
* Generally, this should return true for any statement type for which
* transformStmt() does more than wrap a CMD_UTILITY Query around it.
* When it returns false, the caller can assume that there is no situation
* in which parse analysis of the raw statement could need to be re-done.
*
* Currently, since the rewriter and planner do nothing for CMD_UTILITY
* Queries, a false result means that the entire parse analysis/rewrite/plan
* pipeline will never need to be re-done. If that ever changes, callers
* will likely need adjustment.
*/
bool
stmt_requires_parse_analysis(RawStmt *parseTree)
{
bool result;
switch (nodeTag(parseTree->stmt))
{
/*
* Optimizable statements
*/
case T_InsertStmt:
case T_DeleteStmt:
case T_UpdateStmt:
case T_MergeStmt:
case T_SelectStmt:
case T_ReturnStmt:
case T_PLAssignStmt:
result = true;
break;
/*
* Special cases
*/
case T_DeclareCursorStmt:
case T_ExplainStmt:
case T_CreateTableAsStmt:
case T_CallStmt:
result = true;
break;
default:
/* all other statements just get wrapped in a CMD_UTILITY Query */
result = false;
break;
}
return result;
}
/*
* analyze_requires_snapshot
* Returns true if a snapshot must be set before doing parse analysis
* on the given raw parse tree.
*/
bool
analyze_requires_snapshot(RawStmt *parseTree)
{
/*
* Currently, this should return true in exactly the same cases that
* stmt_requires_parse_analysis() does, so we just invoke that function
* rather than duplicating it. We keep the two entry points separate for
* clarity of callers, since from the callers' standpoint these are
* different conditions.
*
* While there may someday be a statement type for which transformStmt()
* does something nontrivial and yet no snapshot is needed for that
* processing, it seems likely that making such a choice would be fragile.
* If you want to install an exception, document the reasoning for it in a
* comment.
*/
return stmt_requires_parse_analysis(parseTree);
}
/*
* 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,
EXPR_KIND_RETURNING);
/* done building the range table and jointree */
qry->rtable = pstate->p_rtable;
qry->rteperminfos = pstate->p_rteperminfos;
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_rteperminfos;
List *sub_namespace;
List *icolumns;
List *attrnos;
ParseNamespaceItem *nsitem;
RTEPermissionInfo *perminfo;
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/rteperminfos/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_rteperminfos = pstate->p_rteperminfos;
pstate->p_rteperminfos = NIL;
sub_namespace = pstate->p_namespace;
pstate->p_namespace = NIL;
}
else
{
sub_rtable = NIL; /* not used, but keep compiler quiet */
sub_rteperminfos = NIL;
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_rteperminfos = sub_rteperminfos;
sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */
sub_pstate->p_nullingrels = NIL;
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(pstate, 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.
*/
perminfo = pstate->p_target_nsitem->p_perminfo;
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);
perminfo->insertedCols = bms_add_member(perminfo->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,
EXPR_KIND_RETURNING);
/* done building the range table and jointree */
qry->rtable = pstate->p_rtable;
qry->rteperminfos = pstate->p_rteperminfos;
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
*/
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)
{
/*
* We need to remove top-level FieldStores and SubscriptingRefs,
* as well as any CoerceToDomain appearing above one of those ---
* but not a CoerceToDomain that isn't above one of those.
*/
while (expr)
{
Expr *subexpr = expr;
while (IsA(subexpr, CoerceToDomain))
{
subexpr = ((CoerceToDomain *) subexpr)->arg;
}
if (IsA(subexpr, FieldStore))
{
FieldStore *fstore = (FieldStore *) subexpr;
expr = (Expr *) linitial(fstore->newvals);
}
else if (IsA(subexpr, SubscriptingRef))
{
SubscriptingRef *sbsref = (SubscriptingRef *) subexpr;
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;
/* 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->rteperminfos = pstate->p_rteperminfos;
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, true, -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->rteperminfos = pstate->p_rteperminfos;
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. Note, if changing
* this, add_setop_child_rel_equivalences() will need to be updated.
*/
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->rteperminfos = pstate->p_rteperminfos;
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,
&ltargetlist);
/*
* 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 <query
* expression> 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->rteperminfos = pstate->p_rteperminfos;
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,
EXPR_KIND_RETURNING);
/*
* 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->rteperminfos = pstate->p_rteperminfos;
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/MERGE/INSERT ... ON CONFLICT UPDATE
*/
List *
transformUpdateTargetList(ParseState *pstate, List *origTlist)
{
List *tlist = NIL;
RTEPermissionInfo *target_perminfo;
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_perminfo = pstate->p_target_nsitem->p_perminfo;
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)),
(origTarget->indirection != NIL &&
strcmp(origTarget->name, pstate->p_target_nsitem->p_names->aliasname) == 0) ?
errhint("SET target columns cannot be qualified with the relation name.") : 0,
parser_errposition(pstate, origTarget->location)));
updateTargetListEntry(pstate, tle, origTarget->name,
attrno,
origTarget->indirection,
origTarget->location);
/* Mark the target column as requiring update permissions */
target_perminfo->updatedCols = bms_add_member(target_perminfo->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/MERGE
*/
List *
transformReturningList(ParseState *pstate, List *returningList,
ParseExprKind exprKind)
{
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, exprKind);
/*
* 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->rteperminfos = pstate->p_rteperminfos;
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;
bool generic_plan = false;
Oid *paramTypes = NULL;
int numParams = 0;
/*
* If we have no external source of parameter definitions, and the
* GENERIC_PLAN option is specified, then accept variable parameter
* definitions (similarly to PREPARE, for example).
*/
if (pstate->p_paramref_hook == NULL)
{
ListCell *lc;
foreach(lc, stmt->options)
{
DefElem *opt = (DefElem *) lfirst(lc);
if (strcmp(opt->defname, "generic_plan") == 0)
generic_plan = defGetBoolean(opt);
/* don't "break", as we want the last value */
}
if (generic_plan)
setup_parse_variable_parameters(pstate, &paramTypes, &numParams);
}
/* transform contained query, allowing SELECT INTO */
stmt->query = (Node *) transformOptionalSelectInto(pstate, stmt->query);
/* make sure all is well with parameter types */
if (generic_plan)
check_variable_parameters(pstate, (Query *) 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:
{
RTEPermissionInfo *perminfo;
applyLockingClause(qry, i,
lc->strength,
lc->waitPolicy,
pushedDown);
perminfo = getRTEPermissionInfo(qry->rteperminfos, rte);
perminfo->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 = rte->eref->aliasname;
++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.
*
* Subquery and values RTEs without aliases are never visible
* as relation names and must always be skipped.
*/
if (rte->alias == NULL)
{
if (rte->rtekind == RTE_JOIN)
{
if (rte->join_using_alias == NULL)
continue;
rtename = rte->join_using_alias->aliasname;
}
else if (rte->rtekind == RTE_SUBQUERY ||
rte->rtekind == RTE_VALUES)
continue;
}
if (strcmp(rtename, thisrel->relname) == 0)
{
switch (rte->rtekind)
{
case RTE_RELATION:
{
RTEPermissionInfo *perminfo;
applyLockingClause(qry, i,
lc->strength,
lc->waitPolicy,
pushedDown);
perminfo = getRTEPermissionInfo(qry->rteperminfos, rte);
perminfo->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 */