#include "postgres.h"
#include "access/sysattr.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/var.h"
#include "parser/analyze.h"
#include "parser/parse_agg.h"
#include "parser/parse_clause.h"
#include "parser/parse_coerce.h"
#include "parser/parse_collate.h"
#include "parser/parse_cte.h"
#include "parser/parse_oper.h"
#include "parser/parse_param.h"
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
#include "utils/rel.h"
Go to the source code of this file.
Definition at line 295 of file analyze.c.
References nodeTag, NULL, T_CreateTableAsStmt, T_DeclareCursorStmt, T_DeleteStmt, T_ExplainStmt, T_InsertStmt, T_SelectStmt, and T_UpdateStmt.
Referenced by BuildCachedPlan(), exec_bind_message(), exec_parse_message(), and exec_simple_query().
{ bool result; if (parseTree == NULL) return false; switch (nodeTag(parseTree)) { /* * Optimizable statements */ case T_InsertStmt: case T_DeleteStmt: case T_UpdateStmt: case T_SelectStmt: result = true; break; /* * Special cases */ case T_DeclareCursorStmt: /* yes, because it's analyzed just like SELECT */ result = true; break; case T_ExplainStmt: case T_CreateTableAsStmt: /* yes, because we must analyze the contained statement */ result = true; break; default: /* other utility statements don't have any real parse analysis */ result = false; break; } return result; }
void applyLockingClause | ( | Query * | qry, | |
Index | rtindex, | |||
LockClauseStrength | strength, | |||
bool | noWait, | |||
bool | pushedDown | |||
) |
Definition at line 2371 of file analyze.c.
References get_parse_rowmark(), Query::hasForUpdate, lappend(), makeNode, Max, RowMarkClause::noWait, NULL, RowMarkClause::pushedDown, Query::rowMarks, RowMarkClause::rti, and RowMarkClause::strength.
Referenced by markQueryForLocking(), and transformLockingClause().
{ RowMarkClause *rc; /* If it's an explicit clause, make sure hasForUpdate gets set */ if (!pushedDown) qry->hasForUpdate = true; /* Check for pre-existing entry for same rtindex */ if ((rc = get_parse_rowmark(qry, rtindex)) != NULL) { /* * If the same RTE is specified for more than one locking strength, * treat is as the strongest. (Reasonable, since you can't take both a * shared and exclusive lock at the same time; it'll end up being * exclusive anyway.) * * We also consider that NOWAIT wins if it's specified both ways. This * is a bit more debatable but raising an error doesn't seem helpful. * (Consider for instance SELECT FOR UPDATE NOWAIT from a view that * internally contains a plain FOR UPDATE spec.) * * And of course pushedDown becomes false if any clause is explicit. */ rc->strength = Max(rc->strength, strength); rc->noWait |= noWait; rc->pushedDown &= pushedDown; return; } /* Make a new RowMarkClause */ rc = makeNode(RowMarkClause); rc->rti = rtindex; rc->strength = strength; rc->noWait = noWait; rc->pushedDown = pushedDown; qry->rowMarks = lappend(qry->rowMarks, rc); }
void CheckSelectLocking | ( | Query * | qry | ) |
Definition at line 2193 of file analyze.c.
References Query::distinctClause, ereport, errcode(), errmsg(), ERROR, expression_returns_set(), Query::groupClause, Query::hasAggs, Query::hasWindowFuncs, Query::havingQual, NIL, NULL, Query::setOperations, and Query::targetList.
Referenced by preprocess_rowmarks(), and transformLockingClause().
{ if (qry->setOperations) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("row-level locks are not allowed with UNION/INTERSECT/EXCEPT"))); if (qry->distinctClause != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("row-level locks are not allowed with DISTINCT clause"))); if (qry->groupClause != NIL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("row-level locks are not allowed with GROUP BY clause"))); if (qry->havingQual != NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("row-level locks are not allowed with HAVING clause"))); if (qry->hasAggs) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("row-level locks are not allowed with aggregate functions"))); if (qry->hasWindowFuncs) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("row-level locks are not allowed with window functions"))); if (expression_returns_set((Node *) qry->targetList)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("row-level locks are not allowed with set-returning functions in the target list"))); }
static int count_rowexpr_columns | ( | ParseState * | pstate, | |
Node * | expr | |||
) | [static] |
Definition at line 853 of file analyze.c.
References TargetEntry::expr, get_tle_by_resno(), GetRTEByRangeTablePosn(), IsA, list_length(), NULL, RECORDOID, TargetEntry::resjunk, RTE_SUBQUERY, RangeTblEntry::rtekind, RangeTblEntry::subquery, Query::targetList, Var::varattno, Var::varlevelsup, Var::varno, and Var::vartype.
Referenced by transformInsertRow().
{ 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; }
static void determineRecursiveColTypes | ( | ParseState * | pstate, | |
Node * | larg, | |||
List * | nrtargetlist | |||
) | [static] |
Definition at line 1821 of file analyze.c.
References analyzeCTETargetList(), Assert, TargetEntry::expr, IsA, lappend(), lfirst, list_head(), lnext, makeTargetEntry(), NULL, ParseState::p_parent_cte, ParseState::p_rtable, pstrdup(), TargetEntry::resjunk, TargetEntry::resname, rt_fetch, and Query::targetList.
Referenced by transformSetOperationTree().
{ 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; left_tlist = list_head(leftmostQuery->targetList); next_resno = 1; foreach(nrtl, nrtargetlist) { 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); left_tlist = lnext(left_tlist); } /* Now build CTE's output column info using dummy targetlist */ analyzeCTETargetList(pstate, pstate->p_parent_cte, targetList); }
Definition at line 87 of file analyze.c.
References Assert, free_parsestate(), make_parsestate(), NULL, ParseState::p_sourcetext, parse_fixed_parameters(), post_parse_analyze_hook, and transformTopLevelStmt().
Referenced by DefineView(), and pg_analyze_and_rewrite().
{ ParseState *pstate = make_parsestate(NULL); Query *query; Assert(sourceText != NULL); /* required as of 8.4 */ pstate->p_sourcetext = sourceText; if (numParams > 0) parse_fixed_parameters(pstate, paramTypes, numParams); query = transformTopLevelStmt(pstate, parseTree); if (post_parse_analyze_hook) (*post_parse_analyze_hook) (pstate, query); free_parsestate(pstate); return query; }
Query* parse_analyze_varparams | ( | Node * | parseTree, | |
const char * | sourceText, | |||
Oid ** | paramTypes, | |||
int * | numParams | |||
) |
Definition at line 118 of file analyze.c.
References Assert, check_variable_parameters(), free_parsestate(), make_parsestate(), NULL, ParseState::p_sourcetext, parse_variable_parameters(), post_parse_analyze_hook, and transformTopLevelStmt().
Referenced by exec_parse_message(), and PrepareQuery().
{ ParseState *pstate = make_parsestate(NULL); Query *query; Assert(sourceText != NULL); /* required as of 8.4 */ pstate->p_sourcetext = sourceText; parse_variable_parameters(pstate, paramTypes, numParams); query = transformTopLevelStmt(pstate, parseTree); /* make sure all is well with parameter types */ check_variable_parameters(pstate, query); if (post_parse_analyze_hook) (*post_parse_analyze_hook) (pstate, query); free_parsestate(pstate); return query; }
Query* parse_sub_analyze | ( | Node * | parseTree, | |
ParseState * | parentParseState, | |||
CommonTableExpr * | parentCTE, | |||
bool | locked_from_parent | |||
) |
Definition at line 148 of file analyze.c.
References free_parsestate(), make_parsestate(), ParseState::p_locked_from_parent, ParseState::p_parent_cte, and transformStmt().
Referenced by analyzeCTE(), transformRangeSubselect(), transformSetOperationTree(), and transformSubLink().
{ ParseState *pstate = make_parsestate(parentParseState); Query *query; pstate->p_parent_cte = parentCTE; pstate->p_locked_from_parent = locked_from_parent; query = transformStmt(pstate, parseTree); free_parsestate(pstate); return query; }
static Query * transformCreateTableAsStmt | ( | ParseState * | pstate, | |
CreateTableAsStmt * | stmt | |||
) | [static] |
Definition at line 2127 of file analyze.c.
References Query::commandType, copyObject(), ereport, errcode(), errmsg(), ERROR, Query::hasModifyingCTE, CreateTableAsStmt::into, isQueryUsingTempRelation(), makeNode, OBJECT_MATVIEW, CreateTableAsStmt::query, query_contains_extern_params(), CreateTableAsStmt::relkind, transformStmt(), Query::utilityStmt, and IntoClause::viewQuery.
Referenced by transformStmt().
{ Query *result; Query *query; /* transform contained query */ query = transformStmt(pstate, stmt->query); stmt->query = (Node *) query; /* additional work needed for CREATE MATERIALIZED VIEW */ if (stmt->relkind == OBJECT_MATVIEW) { /* * Prohibit a data-modifying CTE in the query used to create a * materialized view. It's not sufficiently clear what the user would * want to happen if the MV is refreshed or incrementally maintained. */ if (query->hasModifyingCTE) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialized views must not use data-modifying statements in WITH"))); /* * Check whether any temporary database objects are used in the * creation query. It would be hard to refresh data or incrementally * maintain it if a source disappeared. */ if (isQueryUsingTempRelation(query)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialized views must not use temporary tables or views"))); /* * A materialized view would either need to save parameters for use in * maintaining/loading the data or prohibit them entirely. The latter * seems safer and more sane. */ if (query_contains_extern_params(query)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialized views may not be defined using bound parameters"))); /* * At runtime, we'll need a copy of the parsed-but-not-rewritten Query * for purposes of creating the view's ON SELECT rule. We stash that * in the IntoClause because that's where intorel_startup() can * conveniently get it from. */ stmt->into->viewQuery = copyObject(query); } /* represent the command as a utility Query */ result = makeNode(Query); result->commandType = CMD_UTILITY; result->utilityStmt = (Node *) stmt; return result; }
static Query * transformDeclareCursorStmt | ( | ParseState * | pstate, | |
DeclareCursorStmt * | stmt | |||
) | [static] |
Definition at line 2031 of file analyze.c.
References CMD_SELECT, Query::commandType, CURSOR_OPT_HOLD, CURSOR_OPT_INSENSITIVE, CURSOR_OPT_NO_SCROLL, CURSOR_OPT_SCROLL, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, Query::hasModifyingCTE, IsA, NIL, NULL, DeclareCursorStmt::options, DeclareCursorStmt::query, Query::rowMarks, transformStmt(), and Query::utilityStmt.
Referenced by transformStmt().
{ Query *result; /* * Don't allow both SCROLL and NO SCROLL to be specified */ if ((stmt->options & CURSOR_OPT_SCROLL) && (stmt->options & CURSOR_OPT_NO_SCROLL)) ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_DEFINITION), errmsg("cannot specify both SCROLL and NO SCROLL"))); result = transformStmt(pstate, stmt->query); /* Grammar should not have allowed anything but SELECT */ if (!IsA(result, Query) || result->commandType != CMD_SELECT || result->utilityStmt != NULL) elog(ERROR, "unexpected non-SELECT command in 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 (result->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 (result->rowMarks != NIL && (stmt->options & CURSOR_OPT_HOLD)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("DECLARE CURSOR WITH HOLD ... FOR UPDATE/SHARE is not supported"), errdetail("Holdable cursors must be READ ONLY."))); /* FOR UPDATE and SCROLL are not compatible */ if (result->rowMarks != NIL && (stmt->options & CURSOR_OPT_SCROLL)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("DECLARE SCROLL CURSOR ... FOR UPDATE/SHARE is not supported"), errdetail("Scrollable cursors must be READ ONLY."))); /* FOR UPDATE and INSENSITIVE are not compatible */ if (result->rowMarks != NIL && (stmt->options & CURSOR_OPT_INSENSITIVE)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("DECLARE INSENSITIVE CURSOR ... FOR UPDATE/SHARE is not supported"), errdetail("Insensitive cursors must be READ ONLY."))); /* We won't need the raw querytree any more */ stmt->query = NULL; result->utilityStmt = (Node *) stmt; return result; }
static Query * transformDeleteStmt | ( | ParseState * | pstate, | |
DeleteStmt * | stmt | |||
) | [static] |
Definition at line 342 of file analyze.c.
References ACL_DELETE, assign_query_collations(), Query::commandType, Query::cteList, Query::distinctClause, EXPR_KIND_WHERE, Query::hasAggs, Query::hasModifyingCTE, Query::hasRecursive, Query::hasSubLinks, Query::hasWindowFuncs, RangeVar::inhOpt, interpretInhOption(), Query::jointree, makeFromExpr(), makeNode, ParseState::p_hasAggs, ParseState::p_hasModifyingCTE, ParseState::p_hasSubLinks, ParseState::p_hasWindowFuncs, ParseState::p_joinlist, ParseState::p_rtable, parseCheckAggregates(), WithClause::recursive, DeleteStmt::relation, Query::resultRelation, DeleteStmt::returningList, Query::returningList, Query::rtable, setTargetTable(), transformFromClause(), transformReturningList(), transformWhereClause(), transformWithClause(), DeleteStmt::usingClause, DeleteStmt::whereClause, and DeleteStmt::withClause.
Referenced by transformStmt().
{ Query *qry = makeNode(Query); 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, interpretInhOption(stmt->relation->inhOpt), true, ACL_DELETE); qry->distinctClause = NIL; /* * 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); qual = transformWhereClause(pstate, stmt->whereClause, EXPR_KIND_WHERE, "WHERE"); qry->returningList = transformReturningList(pstate, stmt->returningList); /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; qry->hasAggs = pstate->p_hasAggs; if (pstate->p_hasAggs) parseCheckAggregates(pstate, qry); assign_query_collations(pstate, qry); return qry; }
static Query * transformExplainStmt | ( | ParseState * | pstate, | |
ExplainStmt * | stmt | |||
) | [static] |
Definition at line 2103 of file analyze.c.
References Query::commandType, makeNode, ExplainStmt::query, transformTopLevelStmt(), and Query::utilityStmt.
Referenced by transformStmt().
{ Query *result; /* transform contained query, allowing SELECT INTO */ stmt->query = (Node *) transformTopLevelStmt(pstate, stmt->query); /* represent the command as a utility Query */ result = makeNode(Query); result->commandType = CMD_UTILITY; result->utilityStmt = (Node *) stmt; return result; }
static List * transformInsertRow | ( | ParseState * | pstate, | |
List * | exprlist, | |||
List * | stmtcols, | |||
List * | icolumns, | |||
List * | attrnos | |||
) | [static] |
Definition at line 767 of file analyze.c.
References Assert, count_rowexpr_columns(), ereport, errcode(), errhint(), errmsg(), ERROR, EXPR_KIND_INSERT_TARGET, exprLocation(), ResTarget::indirection, IsA, lappend(), lfirst, lfirst_int, linitial, list_head(), list_length(), list_nth(), lnext, ResTarget::location, ResTarget::name, NIL, parser_errposition(), and transformAssignedExpr().
Referenced by transformInsertStmt().
{ 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; icols = list_head(icolumns); attnos = list_head(attrnos); foreach(lc, exprlist) { Expr *expr = (Expr *) lfirst(lc); ResTarget *col; col = (ResTarget *) lfirst(icols); Assert(IsA(col, ResTarget)); expr = transformAssignedExpr(pstate, expr, EXPR_KIND_INSERT_TARGET, col->name, lfirst_int(attnos), col->indirection, col->location); result = lappend(result, expr); icols = lnext(icols); attnos = lnext(attnos); } return result; }
static Query * transformInsertStmt | ( | ParseState * | pstate, | |
InsertStmt * | stmt | |||
) | [static] |
Definition at line 398 of file analyze.c.
References ACL_INSERT, addRangeTableEntryForSubquery(), Assert, checkInsertTargets(), CMD_SELECT, InsertStmt::cols, Query::commandType, Query::cteList, elog, ERROR, TargetEntry::expr, exprLocation(), exprType(), free_parsestate(), Query::hasModifyingCTE, Query::hasRecursive, IsA, lappend(), lfirst, SelectStmt::limitCount, SelectStmt::limitOffset, list_length(), Var::location, SelectStmt::lockingClause, make_parsestate(), makeAlias(), makeNode, makeVarFromTargetEntry(), NIL, NULL, ParseState::p_ctenamespace, ParseState::p_hasModifyingCTE, ParseState::p_is_insert, ParseState::p_joinexprs, ParseState::p_joinlist, ParseState::p_namespace, ParseState::p_rtable, WithClause::recursive, InsertStmt::relation, TargetEntry::resjunk, Query::resultRelation, rt_fetch, InsertStmt::selectStmt, setTargetTable(), SelectStmt::sortClause, Query::targetList, transformInsertRow(), transformStmt(), transformWithClause(), Query::utilityStmt, SelectStmt::valuesLists, SelectStmt::withClause, and InsertStmt::withClause.
Referenced by transformStmt().
{ Query *qry = makeNode(Query); SelectStmt *selectStmt = (SelectStmt *) stmt->selectStmt; List *exprList = NIL; bool isGeneralSelect; List *sub_rtable; List *sub_namespace; List *icolumns; List *attrnos; RangeTblEntry *rte; RangeTblRef *rtr; ListCell *icols; ListCell *attnos; ListCell *lc; /* 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; } /* * We have three cases to deal with: DEFAULT VALUES (selectStmt == NULL), * VALUES list, or general SELECT input. We special-case VALUES, both for * efficiency and so we can handle DEFAULT specifications. * * The grammar allows attaching ORDER BY, LIMIT, FOR UPDATE, or WITH to a * VALUES clause. If we have any of those, treat it as a general SELECT; * so it will work, but you can't use DEFAULT items together with those. */ isGeneralSelect = (selectStmt && (selectStmt->valuesLists == NIL || selectStmt->sortClause != NIL || selectStmt->limitOffset != NULL || selectStmt->limitCount != NULL || selectStmt->lockingClause != NIL || selectStmt->withClause != NULL)); /* * If a non-nil rangetable/namespace was passed in, and we are doing * INSERT/SELECT, arrange to pass the rangetable/namespace down to the * SELECT. This can only happen if we are inside a CREATE RULE, and in * that case we want the rule's OLD and NEW rtable entries to appear as * part of the SELECT's rtable, not as outer references for it. (Kluge!) * The SELECT's joinlist is not affected however. We must do this before * adding the target table to the INSERT's rtable. */ if (isGeneralSelect) { sub_rtable = pstate->p_rtable; pstate->p_rtable = NIL; sub_namespace = pstate->p_namespace; pstate->p_namespace = NIL; } else { sub_rtable = NIL; /* not used, but keep compiler quiet */ sub_namespace = NIL; } /* * Must get write lock on INSERT target table before scanning SELECT, else * we will grab the wrong kind of initial lock if the target table is also * mentioned in the SELECT part. Note that the target table is not added * to the joinlist or namespace. */ qry->resultRelation = setTargetTable(pstate, stmt->relation, false, false, ACL_INSERT); /* 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...) */ sub_pstate->p_rtable = sub_rtable; sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_namespace = sub_namespace; 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 || selectQuery->utilityStmt != NULL) 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. */ rte = addRangeTableEntryForSubquery(pstate, selectQuery, makeAlias("*SELECT*", NIL), false, false); rtr = makeNode(RangeTblRef); /* assume new rte is at end */ rtr->rtindex = list_length(pstate->p_rtable); Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable)); pstate->p_joinlist = lappend(pstate->p_joinlist, rtr); /*---------- * 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(rtr->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); } 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 *collations = NIL; int sublist_length = -1; bool lateral = false; int i; Assert(selectStmt->intoClause == NULL); foreach(lc, selectStmt->valuesLists) { List *sublist = (List *) lfirst(lc); /* Do basic expression transformation (same as a ROW() expr) */ sublist = transformExpressionList(pstate, sublist, EXPR_KIND_VALUES); /* * 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 */ sublist = transformInsertRow(pstate, sublist, stmt->cols, icolumns, attrnos); /* * 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); } /* * Although we don't really need collation info, let's just make sure * we provide a correctly-sized list in the VALUES RTE. */ for (i = 0; i < sublist_length; i++) collations = lappend_oid(collations, 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 */ rte = addRangeTableEntryForValues(pstate, exprsLists, collations, NULL, lateral, true); rtr = makeNode(RangeTblRef); /* assume new rte is at end */ rtr->rtindex = list_length(pstate->p_rtable); Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable)); pstate->p_joinlist = lappend(pstate->p_joinlist, rtr); /* * Generate list of Vars referencing the RTE */ expandRTE(rte, rtr->rtindex, 0, -1, false, NULL, &exprList); } 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) */ exprList = transformExpressionList(pstate, (List *) linitial(valuesLists), EXPR_KIND_VALUES); /* Prepare row for assignment to target table */ exprList = transformInsertRow(pstate, exprList, stmt->cols, icolumns, attrnos); } /* * Generate query's target list using the computed list of expressions. * Also, mark all the target columns as needing insert permissions. */ rte = pstate->p_target_rangetblentry; qry->targetList = NIL; icols = list_head(icolumns); attnos = list_head(attrnos); foreach(lc, exprList) { Expr *expr = (Expr *) lfirst(lc); ResTarget *col; AttrNumber attr_num; TargetEntry *tle; col = (ResTarget *) lfirst(icols); Assert(IsA(col, ResTarget)); attr_num = (AttrNumber) lfirst_int(attnos); tle = makeTargetEntry(expr, attr_num, col->name, false); qry->targetList = lappend(qry->targetList, tle); rte->modifiedCols = bms_add_member(rte->modifiedCols, attr_num - FirstLowInvalidHeapAttributeNumber); icols = lnext(icols); attnos = lnext(attnos); } /* * If we have a RETURNING clause, we need to add the target relation to * the query namespace before processing it, so that Var references in * RETURNING will work. Also, remove any namespace entries added in a * sub-SELECT or VALUES list. */ if (stmt->returningList) { pstate->p_namespace = NIL; addRTEtoQuery(pstate, pstate->p_target_rangetblentry, false, true, true); qry->returningList = transformReturningList(pstate, stmt->returningList); } /* done building the range table and jointree */ qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; assign_query_collations(pstate, qry); return qry; }
static void transformLockingClause | ( | ParseState * | pstate, | |
Query * | qry, | |||
LockingClause * | lc, | |||
bool | pushedDown | |||
) | [static] |
Definition at line 2234 of file analyze.c.
References Alias::aliasname, applyLockingClause(), RangeVar::catalogname, CheckSelectLocking(), elog, RangeTblEntry::eref, ereport, errcode(), errmsg(), ERROR, lfirst, RangeVar::location, LockingClause::lockedRels, makeNode, NIL, LockingClause::noWait, NULL, parser_errposition(), RangeVar::relname, RangeTblEntry::requiredPerms, Query::rtable, RTE_CTE, RTE_FUNCTION, RTE_JOIN, RTE_RELATION, RTE_SUBQUERY, RTE_VALUES, RangeTblEntry::rtekind, RangeVar::schemaname, LockingClause::strength, and RangeTblEntry::subquery.
Referenced by transformSelectStmt(), and transformSetOperationStmt().
{ List *lockedRels = lc->lockedRels; ListCell *l; ListCell *rt; Index i; LockingClause *allrels; CheckSelectLocking(qry); /* 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->noWait = lc->noWait; if (lockedRels == NIL) { /* all regular tables used in query */ i = 0; foreach(rt, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt); ++i; switch (rte->rtekind) { case RTE_RELATION: applyLockingClause(qry, i, lc->strength, lc->noWait, pushedDown); rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, lc->noWait, 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 { /* just the named tables */ 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), errmsg("row-level locks must specify unqualified relation names"), parser_errposition(pstate, thisrel->location))); i = 0; foreach(rt, qry->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt); ++i; if (strcmp(rte->eref->aliasname, thisrel->relname) == 0) { switch (rte->rtekind) { case RTE_RELATION: applyLockingClause(qry, i, lc->strength, lc->noWait, pushedDown); rte->requiredPerms |= ACL_SELECT_FOR_UPDATE; break; case RTE_SUBQUERY: applyLockingClause(qry, i, lc->strength, lc->noWait, pushedDown); /* see comment above */ transformLockingClause(pstate, rte->subquery, allrels, true); break; case RTE_JOIN: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("row-level locks cannot be applied to a join"), parser_errposition(pstate, thisrel->location))); break; case RTE_FUNCTION: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("row-level locks cannot be applied to a function"), parser_errposition(pstate, thisrel->location))); break; case RTE_VALUES: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("row-level locks cannot be applied to VALUES"), parser_errposition(pstate, thisrel->location))); break; case RTE_CTE: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("row-level locks cannot be applied to a WITH query"), parser_errposition(pstate, thisrel->location))); break; 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), errmsg("relation \"%s\" in row-level lock clause not found in FROM clause", thisrel->relname), parser_errposition(pstate, thisrel->location))); } } }
static List * transformReturningList | ( | ParseState * | pstate, | |
List * | returningList | |||
) | [static] |
Definition at line 1990 of file analyze.c.
References EXPR_KIND_RETURNING, markTargetListOrigins(), NIL, ParseState::p_next_resno, and transformTargetList().
Referenced by transformDeleteStmt(), and transformUpdateStmt().
{ List *rlist; int save_next_resno; if (returningList == NIL) return NIL; /* nothing to do */ /* * We need to assign resnos starting at one in the RETURNING list. Save * and restore the main tlist's value of p_next_resno, just in case * someone looks at it later (probably won't happen). */ save_next_resno = pstate->p_next_resno; pstate->p_next_resno = 1; /* transform RETURNING identically to a SELECT targetlist */ rlist = transformTargetList(pstate, returningList, EXPR_KIND_RETURNING); /* mark column origins */ markTargetListOrigins(pstate, rlist); /* restore state */ pstate->p_next_resno = save_next_resno; return rlist; }
static Query * transformSelectStmt | ( | ParseState * | pstate, | |
SelectStmt * | stmt | |||
) | [static] |
Definition at line 895 of file analyze.c.
References assign_query_collations(), Query::commandType, Query::cteList, Query::distinctClause, SelectStmt::distinctClause, ereport, errcode(), errmsg(), ERROR, EXPR_KIND_GROUP_BY, EXPR_KIND_HAVING, EXPR_KIND_LIMIT, EXPR_KIND_OFFSET, EXPR_KIND_ORDER_BY, EXPR_KIND_SELECT_TARGET, EXPR_KIND_WHERE, exprLocation(), SelectStmt::fromClause, SelectStmt::groupClause, Query::groupClause, Query::hasAggs, Query::hasDistinctOn, Query::hasModifyingCTE, Query::hasRecursive, Query::hasSubLinks, Query::hasWindowFuncs, SelectStmt::havingClause, Query::havingQual, SelectStmt::intoClause, Query::jointree, lfirst, SelectStmt::limitCount, Query::limitCount, SelectStmt::limitOffset, Query::limitOffset, linitial, SelectStmt::lockingClause, makeFromExpr(), makeNode, markTargetListOrigins(), NIL, NULL, ParseState::p_hasAggs, ParseState::p_hasModifyingCTE, ParseState::p_hasSubLinks, ParseState::p_hasWindowFuncs, ParseState::p_joinlist, ParseState::p_locking_clause, ParseState::p_rtable, ParseState::p_windowdefs, parseCheckAggregates(), parser_errposition(), WithClause::recursive, Query::rtable, SelectStmt::sortClause, Query::sortClause, SelectStmt::targetList, Query::targetList, transformDistinctClause(), transformDistinctOnClause(), transformFromClause(), transformGroupClause(), transformLimitClause(), transformLockingClause(), transformSortClause(), transformTargetList(), transformWhereClause(), transformWindowDefinitions(), transformWithClause(), SelectStmt::whereClause, Query::windowClause, SelectStmt::windowClause, and SelectStmt::withClause.
Referenced by transformStmt().
{ 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, true /* fix unknowns */ , false /* allow SQL92 rules */ ); qry->groupClause = transformGroupClause(pstate, stmt->groupClause, &qry->targetList, qry->sortClause, EXPR_KIND_GROUP_BY, false /* allow SQL92 rules */ ); 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"); qry->limitCount = transformLimitClause(pstate, stmt->limitCount, EXPR_KIND_LIMIT, "LIMIT"); /* transform window clauses after we have seen all window functions */ qry->windowClause = transformWindowDefinitions(pstate, pstate->p_windowdefs, &qry->targetList); qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; qry->hasAggs = pstate->p_hasAggs; if (pstate->p_hasAggs || qry->groupClause || qry->havingQual) parseCheckAggregates(pstate, qry); foreach(l, stmt->lockingClause) { transformLockingClause(pstate, qry, (LockingClause *) lfirst(l), false); } assign_query_collations(pstate, qry); return qry; }
static Query * transformSetOperationStmt | ( | ParseState * | pstate, | |
SelectStmt * | stmt | |||
) | [static] |
Definition at line 1247 of file analyze.c.
References addRangeTableEntryForJoin(), addRTEtoQuery(), Assert, assign_query_collations(), SetOperationStmt::colCollations, SetOperationStmt::colTypes, SetOperationStmt::colTypmods, Query::commandType, Query::cteList, ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, TargetEntry::expr, EXPR_KIND_LIMIT, EXPR_KIND_OFFSET, EXPR_KIND_ORDER_BY, exprLocation(), forthree, Query::groupClause, Query::hasAggs, Query::hasModifyingCTE, Query::hasRecursive, Query::hasSubLinks, Query::hasWindowFuncs, Query::havingQual, SelectStmt::intoClause, IsA, JOIN_INNER, Query::jointree, lappend(), SetOperationStmt::larg, SelectStmt::larg, lfirst, lfirst_int, lfirst_oid, Query::limitCount, SelectStmt::limitCount, Query::limitOffset, SelectStmt::limitOffset, list_head(), list_length(), list_nth(), list_truncate(), lnext, Var::location, SelectStmt::lockingClause, makeFromExpr(), makeNode, makeString(), makeTargetEntry(), makeVar(), NULL, SelectStmt::op, ParseState::p_hasAggs, ParseState::p_hasModifyingCTE, ParseState::p_hasSubLinks, ParseState::p_hasWindowFuncs, ParseState::p_joinlist, ParseState::p_namespace, ParseState::p_next_resno, ParseState::p_rtable, parseCheckAggregates(), parser_errposition(), pstrdup(), WithClause::recursive, TargetEntry::resjunk, TargetEntry::resname, TargetEntry::resno, rt_fetch, Query::rtable, SETOP_NONE, Query::setOperations, Query::sortClause, SelectStmt::sortClause, Query::targetList, transformLimitClause(), transformLockingClause(), transformSetOperationTree(), transformSortClause(), transformWithClause(), and SelectStmt::withClause.
Referenced by transformStmt().
{ 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; RangeTblEntry *jrte; 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), errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT"))); /* 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 = (SetOperationStmt *) transformSetOperationTree(pstate, stmt, true, NULL); Assert(sostmt && IsA(sostmt, SetOperationStmt)); 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; left_tlist = list_head(leftmostQuery->targetList); forthree(lct, sostmt->colTypes, lcm, sostmt->colTypmods, lcc, sostmt->colCollations) { 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)); left_tlist = lnext(left_tlist); } /* * 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); jrte = addRangeTableEntryForJoin(pstate, targetnames, JOIN_INNER, targetvars, NULL, false); sv_namespace = pstate->p_namespace; pstate->p_namespace = NIL; /* add jrte to column namespace only */ addRTEtoQuery(pstate, jrte, false, false, true); /* * For now, we don't support resjunk sort clauses on the output of a * setOperation tree --- you can only use the SQL92-spec options of * selecting an output column by name or number. Enforce by checking that * transformSortClause doesn't add any items to tlist. */ tllen = list_length(qry->targetList); qry->sortClause = transformSortClause(pstate, sortClause, &qry->targetList, EXPR_KIND_ORDER_BY, false /* no unknowns expected */ , false /* allow SQL92 rules */ ); /* restore namespace, remove jrte from rtable */ pstate->p_namespace = sv_namespace; pstate->p_rtable = list_truncate(pstate->p_rtable, sv_rtable_length); 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"); qry->limitCount = transformLimitClause(pstate, limitCount, EXPR_KIND_LIMIT, "LIMIT"); qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasWindowFuncs = pstate->p_hasWindowFuncs; qry->hasAggs = pstate->p_hasAggs; if (pstate->p_hasAggs || qry->groupClause || qry->havingQual) parseCheckAggregates(pstate, qry); foreach(l, lockingClause) { transformLockingClause(pstate, qry, (LockingClause *) lfirst(l), false); } assign_query_collations(pstate, qry); return qry; }
static Node * transformSetOperationTree | ( | ParseState * | pstate, | |
SelectStmt * | stmt, | |||
bool | isTopLevel, | |||
List ** | targetlist | |||
) | [static] |
Definition at line 1485 of file analyze.c.
References addRangeTableEntryForSubquery(), SelectStmt::all, SetOperationStmt::all, Assert, cancel_parser_errposition_callback(), check_stack_depth(), coerce_to_common_type(), SetOperationStmt::colCollations, SetToDefault::collation, SetOperationStmt::colTypes, SetOperationStmt::colTypmods, contain_vars_of_level(), CommonTableExpr::cterecursive, determineRecursiveColTypes(), SortGroupClause::eqop, ereport, errcode(), errmsg(), ERROR, TargetEntry::expr, exprLocation(), exprType(), exprTypmod(), forboth, get_sort_group_operators(), SetOperationStmt::groupClauses, SortGroupClause::hashable, SelectStmt::intoClause, IsA, lappend(), lappend_int(), lappend_oid(), SetOperationStmt::larg, SelectStmt::larg, lfirst, SelectStmt::limitCount, SelectStmt::limitOffset, list_length(), list_make2, locate_var_of_level(), SetToDefault::location, SelectStmt::lockingClause, makeAlias(), makeNode, makeTargetEntry(), NIL, NULL, SortGroupClause::nulls_first, SetOperationStmt::op, SelectStmt::op, ParseState::p_namespace, ParseState::p_parent_cte, ParseState::p_rtable, parse_sub_analyze(), parser_errposition(), SetOperationStmt::rarg, SelectStmt::rarg, TargetEntry::resjunk, rt_fetch, RangeTblRef::rtindex, select_common_collation(), select_common_type(), SETOP_INTERSECT, SETOP_NONE, SETOP_UNION, setup_parser_errposition_callback(), snprintf(), SelectStmt::sortClause, SortGroupClause::sortop, Query::targetList, SortGroupClause::tleSortGroupRef, SetToDefault::typeId, SetToDefault::typeMod, UNKNOWNOID, and SelectStmt::withClause.
Referenced by transformSetOperationStmt().
{ 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), errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT"))); /* * 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]; RangeTblEntry *rte PG_USED_FOR_ASSERTS_ONLY; RangeTblRef *rtr; ListCell *tl; /* * Transform SelectStmt into a Query. * * 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); /* * 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); rte = addRangeTableEntryForSubquery(pstate, selectQuery, makeAlias(selectName, NIL), false, false); /* * Return a RangeTblRef to replace the SelectStmt in the set-op tree. */ rtr = makeNode(RangeTblRef); /* assume new rte is at end */ rtr->rtindex = list_length(pstate->p_rtable); Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable)); 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; context = (stmt->op == SETOP_UNION ? "UNION" : (stmt->op == SETOP_INTERSECT ? "INTERSECT" : "EXCEPT")); op->op = stmt->op; op->all = stmt->all; /* * Recursively transform the left child node. */ op->larg = transformSetOperationTree(pstate, stmt->larg, false, <argetlist); /* * If we are processing a recursive union query, now is the time to * examine the non-recursive term's output columns and mark the * containing CTE as having those result columns. We should do this * only at the topmost setop of the CTE, of course. */ if (isTopLevel && pstate->p_parent_cte && pstate->p_parent_cte->cterecursive) 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); int32 lcoltypmod = exprTypmod(lcolnode); int32 rcoltypmod = exprTypmod(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); /* if same type and same typmod, use typmod; else default */ if (lcoltype == rcoltype && lcoltypmod == rcoltypmod) rescoltypmod = lcoltypmod; else rescoltypmod = -1; /* * Verify the coercions are actually possible. If not, we'd fail * later anyway, but we want to fail now while we have sufficient * context to produce an error cursor position. * * 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; } /* * 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 curCollations 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) { SortGroupClause *grpcl = makeNode(SortGroupClause); Oid sortop; Oid eqop; bool hashable; ParseCallbackState pcbstate; setup_parser_errposition_callback(&pcbstate, pstate, bestlocation); /* determine the eqop and optional sortop */ get_sort_group_operators(rescoltype, false, true, false, &sortop, &eqop, NULL, &hashable); cancel_parser_errposition_callback(&pcbstate); /* we don't have a tlist yet, so can't assign sortgrouprefs */ grpcl->tleSortGroupRef = 0; grpcl->eqop = eqop; grpcl->sortop = sortop; grpcl->nulls_first = false; /* OK with or without sortop */ grpcl->hashable = hashable; op->groupClauses = lappend(op->groupClauses, grpcl); } /* * 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; } }
Query* transformStmt | ( | ParseState * | pstate, | |
Node * | parseTree | |||
) |
Definition at line 215 of file analyze.c.
References Query::canSetTag, Query::commandType, makeNode, nodeTag, SelectStmt::op, Query::querySource, SETOP_NONE, T_CreateTableAsStmt, T_DeclareCursorStmt, T_DeleteStmt, T_ExplainStmt, T_InsertStmt, T_SelectStmt, T_UpdateStmt, transformCreateTableAsStmt(), transformDeclareCursorStmt(), transformDeleteStmt(), transformExplainStmt(), transformInsertStmt(), transformSelectStmt(), transformSetOperationStmt(), transformUpdateStmt(), transformValuesClause(), Query::utilityStmt, and SelectStmt::valuesLists.
Referenced by parse_sub_analyze(), transformCreateTableAsStmt(), transformDeclareCursorStmt(), transformInsertStmt(), transformRuleStmt(), and transformTopLevelStmt().
{ Query *result; switch (nodeTag(parseTree)) { /* * Optimizable statements */ case T_InsertStmt: result = transformInsertStmt(pstate, (InsertStmt *) parseTree); break; case T_DeleteStmt: result = transformDeleteStmt(pstate, (DeleteStmt *) parseTree); break; case T_UpdateStmt: result = transformUpdateStmt(pstate, (UpdateStmt *) parseTree); break; case T_SelectStmt: { SelectStmt *n = (SelectStmt *) parseTree; if (n->valuesLists) result = transformValuesClause(pstate, n); else if (n->op == SETOP_NONE) result = transformSelectStmt(pstate, n); else result = transformSetOperationStmt(pstate, n); } break; /* * 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; 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; }
Query* transformTopLevelStmt | ( | ParseState * | pstate, | |
Node * | parseTree | |||
) |
Definition at line 176 of file analyze.c.
References Assert, CreateTableAsStmt::into, SelectStmt::intoClause, CreateTableAsStmt::is_select_into, IsA, SelectStmt::larg, makeNode, NULL, SelectStmt::op, CreateTableAsStmt::query, CreateTableAsStmt::relkind, SETOP_NONE, and transformStmt().
Referenced by inline_function(), parse_analyze(), parse_analyze_varparams(), pg_analyze_and_rewrite_params(), and transformExplainStmt().
{ 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->relkind = 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); }
static Query * transformUpdateStmt | ( | ParseState * | pstate, | |
UpdateStmt * | stmt | |||
) | [static] |
Definition at line 1877 of file analyze.c.
References ACL_UPDATE, Assert, assign_query_collations(), attnameAttNum(), bms_add_member(), Query::commandType, Query::cteList, elog, ereport, errcode(), errmsg(), ERROR, EXPR_KIND_UPDATE_SOURCE, EXPR_KIND_WHERE, FirstLowInvalidHeapAttributeNumber, UpdateStmt::fromClause, Query::hasModifyingCTE, Query::hasRecursive, Query::hasSubLinks, ResTarget::indirection, RangeVar::inhOpt, interpretInhOption(), InvalidAttrNumber, IsA, Query::jointree, lfirst, list_head(), lnext, ResTarget::location, makeFromExpr(), makeNode, RangeTblEntry::modifiedCols, ResTarget::name, NULL, ParseState::p_hasModifyingCTE, ParseState::p_hasSubLinks, ParseState::p_is_update, ParseState::p_joinlist, ParseState::p_next_resno, ParseState::p_rtable, ParseState::p_target_rangetblentry, ParseState::p_target_relation, parser_errposition(), RelationData::rd_rel, WithClause::recursive, UpdateStmt::relation, RelationGetRelationName, TargetEntry::resjunk, TargetEntry::resname, TargetEntry::resno, Query::resultRelation, UpdateStmt::returningList, Query::returningList, Query::rtable, setTargetTable(), UpdateStmt::targetList, Query::targetList, transformFromClause(), transformReturningList(), transformTargetList(), transformWhereClause(), transformWithClause(), updateTargetListEntry(), UpdateStmt::whereClause, and UpdateStmt::withClause.
Referenced by transformStmt().
{ Query *qry = makeNode(Query); RangeTblEntry *target_rte; Node *qual; ListCell *origTargetList; ListCell *tl; qry->commandType = CMD_UPDATE; pstate->p_is_update = 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->resultRelation = setTargetTable(pstate, stmt->relation, interpretInhOption(stmt->relation->inhOpt), true, ACL_UPDATE); /* * the FROM clause is non-standard SQL syntax. We used to be able to do * this with REPLACE in POSTQUEL so we keep the feature. */ transformFromClause(pstate, stmt->fromClause); qry->targetList = transformTargetList(pstate, stmt->targetList, EXPR_KIND_UPDATE_SOURCE); qual = transformWhereClause(pstate, stmt->whereClause, EXPR_KIND_WHERE, "WHERE"); qry->returningList = transformReturningList(pstate, stmt->returningList); qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; /* * Now we are done with SELECT-like processing, and can get on with * transforming the target list to match the UPDATE target columns. */ /* Prepare to assign non-conflicting resnos to resjunk attributes */ if (pstate->p_next_resno <= pstate->p_target_relation->rd_rel->relnatts) pstate->p_next_resno = pstate->p_target_relation->rd_rel->relnatts + 1; /* Prepare non-junk columns for assignment to target table */ target_rte = pstate->p_target_rangetblentry; origTargetList = list_head(stmt->targetList); foreach(tl, qry->targetList) { 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 (origTargetList == NULL) elog(ERROR, "UPDATE target count mismatch --- internal error"); origTarget = (ResTarget *) lfirst(origTargetList); Assert(IsA(origTarget, ResTarget)); attrno = attnameAttNum(pstate->p_target_relation, origTarget->name, true); if (attrno == InvalidAttrNumber) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" of relation \"%s\" does not exist", origTarget->name, RelationGetRelationName(pstate->p_target_relation)), parser_errposition(pstate, origTarget->location))); updateTargetListEntry(pstate, tle, origTarget->name, attrno, origTarget->indirection, origTarget->location); /* Mark the target column as requiring update permissions */ target_rte->modifiedCols = bms_add_member(target_rte->modifiedCols, attrno - FirstLowInvalidHeapAttributeNumber); origTargetList = lnext(origTargetList); } if (origTargetList != NULL) elog(ERROR, "UPDATE target count mismatch --- internal error"); assign_query_collations(pstate, qry); return qry; }
static Query * transformValuesClause | ( | ParseState * | pstate, | |
SelectStmt * | stmt | |||
) | [static] |
Definition at line 1026 of file analyze.c.
References addRangeTableEntryForValues(), addRTEtoQuery(), Assert, assign_query_collations(), coerce_to_common_type(), Query::commandType, contain_vars_of_level(), Query::cteList, SelectStmt::distinctClause, ereport, errcode(), errmsg(), ERROR, expandRelAttrs(), EXPR_KIND_LIMIT, EXPR_KIND_OFFSET, EXPR_KIND_ORDER_BY, EXPR_KIND_VALUES, exprLocation(), forboth, SelectStmt::fromClause, SelectStmt::groupClause, Query::hasModifyingCTE, Query::hasRecursive, Query::hasSubLinks, SelectStmt::havingClause, SelectStmt::intoClause, IsA, Query::jointree, lappend(), lappend_oid(), lfirst, SelectStmt::limitCount, Query::limitCount, SelectStmt::limitOffset, Query::limitOffset, list_free(), list_length(), list_make1, SelectStmt::lockingClause, makeFromExpr(), makeNode, NIL, NULL, SelectStmt::op, ParseState::p_hasModifyingCTE, ParseState::p_hasSubLinks, ParseState::p_joinlist, ParseState::p_next_resno, ParseState::p_rtable, palloc0(), parser_errposition(), WithClause::recursive, rt_fetch, Query::rtable, select_common_collation(), select_common_type(), SETOP_NONE, SelectStmt::sortClause, Query::sortClause, Query::targetList, SelectStmt::targetList, transformExpressionList(), transformLimitClause(), transformSortClause(), transformWithClause(), SelectStmt::valuesLists, SelectStmt::whereClause, SelectStmt::windowClause, and SelectStmt::withClause.
Referenced by transformStmt().
{ Query *qry = makeNode(Query); List *exprsLists; List *collations; List **colexprs = NULL; int sublist_length = -1; bool lateral = false; RangeTblEntry *rte; int rtindex; 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. This is also a * handy place to reject DEFAULT nodes, which the grammar allows for * simplicity. * * 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) */ sublist = transformExpressionList(pstate, sublist, EXPR_KIND_VALUES); /* * 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)))); } /* Check for DEFAULT and build per-column expression lists */ i = 0; foreach(lc2, sublist) { Node *col = (Node *) lfirst(lc2); if (IsA(col, SetToDefault)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("DEFAULT can only appear in a VALUES list within INSERT"), parser_errposition(pstate, exprLocation(col)))); colexprs[i] = lappend(colexprs[i], col); i++; } /* Release sub-list's cells to save memory */ list_free(sublist); } /* * Now resolve the common types of the columns, and coerce everything to * those types. Then identify the 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. */ collations = NIL; for (i = 0; i < sublist_length; i++) { Oid coltype; 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; } colcoll = select_common_collation(pstate, colexprs[i], true); collations = lappend_oid(collations, colcoll); } /* * Finally, rearrange the coerced expressions into row-organized lists. */ exprsLists = NIL; foreach(lc, colexprs[0]) { Node *col = (Node *) lfirst(lc); List *sublist; sublist = list_make1(col); exprsLists = lappend(exprsLists, sublist); } list_free(colexprs[0]); for (i = 1; i < sublist_length; i++) { forboth(lc, colexprs[i], lc2, exprsLists) { Node *col = (Node *) lfirst(lc); List *sublist = lfirst(lc2); /* sublist pointer in exprsLists won't need adjustment */ (void) lappend(sublist, col); } 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 */ rte = addRangeTableEntryForValues(pstate, exprsLists, collations, NULL, lateral, true); addRTEtoQuery(pstate, rte, true, true, true); /* assume new rte is at end */ rtindex = list_length(pstate->p_rtable); Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); /* * Generate a targetlist as though expanding "*" */ Assert(pstate->p_next_resno == 1); qry->targetList = expandRelAttrs(pstate, rte, rtindex, 0, -1); /* * The grammar allows attaching ORDER BY, LIMIT, and FOR UPDATE to a * VALUES, so cope. */ qry->sortClause = transformSortClause(pstate, stmt->sortClause, &qry->targetList, EXPR_KIND_ORDER_BY, true /* fix unknowns */ , false /* allow SQL92 rules */ ); qry->limitOffset = transformLimitClause(pstate, stmt->limitOffset, EXPR_KIND_OFFSET, "OFFSET"); qry->limitCount = transformLimitClause(pstate, stmt->limitCount, EXPR_KIND_LIMIT, "LIMIT"); if (stmt->lockingClause) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("SELECT FOR UPDATE/SHARE cannot be applied to VALUES"))); qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; assign_query_collations(pstate, qry); return qry; }
Definition at line 49 of file analyze.c.
Referenced by _PG_fini(), _PG_init(), parse_analyze(), parse_analyze_varparams(), and pg_analyze_and_rewrite_params().