analyze.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 2,258 行 · 第 1/5 页
C
2,258 行
oldrte->checkForRead = false; newrte->checkForRead = false; addRTEtoQuery(sub_pstate, oldrte, false, true); addRTEtoQuery(sub_pstate, newrte, false, true); /* Transform the rule action statement */ top_subqry = transformStmt(sub_pstate, action, extras_before, extras_after); /* * We cannot support utility-statement actions (eg NOTIFY) * with nonempty rule WHERE conditions, because there's no way * to make the utility action execute conditionally. */ if (top_subqry->commandType == CMD_UTILITY && stmt->whereClause != NULL) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("rules with WHERE conditions may only have SELECT, INSERT, UPDATE, or DELETE actions"))); /* * If the action is INSERT...SELECT, OLD/NEW have been pushed * down into the SELECT, and that's what we need to look at. * (Ugly kluge ... try to fix this when we redesign * querytrees.) */ sub_qry = getInsertSelectQuery(top_subqry, NULL); /* * If the sub_qry is a setop, we cannot attach any * qualifications to it, because the planner won't notice * them. This could perhaps be relaxed someday, but for now, * we may as well reject such a rule immediately. */ if (sub_qry->setOperations != NULL && stmt->whereClause != NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented"))); /* * Validate action's use of OLD/NEW, qual too */ has_old = rangeTableEntry_used((Node *) sub_qry, PRS2_OLD_VARNO, 0) || rangeTableEntry_used(stmt->whereClause, PRS2_OLD_VARNO, 0); has_new = rangeTableEntry_used((Node *) sub_qry, PRS2_NEW_VARNO, 0) || rangeTableEntry_used(stmt->whereClause, PRS2_NEW_VARNO, 0); switch (stmt->event) { case CMD_SELECT: if (has_old) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("ON SELECT rule may not use OLD"))); if (has_new) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("ON SELECT rule may not use NEW"))); break; case CMD_UPDATE: /* both are OK */ break; case CMD_INSERT: if (has_old) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("ON INSERT rule may not use OLD"))); break; case CMD_DELETE: if (has_new) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("ON DELETE rule may not use NEW"))); break; default: elog(ERROR, "unrecognized event type: %d", (int) stmt->event); break; } /* * For efficiency's sake, add OLD to the rule action's * jointree only if it was actually referenced in the * statement or qual. * * For INSERT, NEW is not really a relation (only a reference to * the to-be-inserted tuple) and should never be added to the * jointree. * * For UPDATE, we treat NEW as being another kind of reference to * OLD, because it represents references to *transformed* * tuples of the existing relation. It would be wrong to * enter NEW separately in the jointree, since that would * cause a double join of the updated relation. It's also * wrong to fail to make a jointree entry if only NEW and not * OLD is mentioned. */ if (has_old || (has_new && stmt->event == CMD_UPDATE)) { /* * If sub_qry is a setop, manipulating its jointree will * do no good at all, because the jointree is dummy. (This * should be a can't-happen case because of prior tests.) */ if (sub_qry->setOperations != NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented"))); /* hack so we can use addRTEtoQuery() */ sub_pstate->p_rtable = sub_qry->rtable; sub_pstate->p_joinlist = sub_qry->jointree->fromlist; addRTEtoQuery(sub_pstate, oldrte, true, false); sub_qry->jointree->fromlist = sub_pstate->p_joinlist; } newactions = lappend(newactions, top_subqry); release_pstate_resources(sub_pstate); pfree(sub_pstate); } stmt->actions = newactions; } return qry;}/* * transformSelectStmt - * transforms a Select Statement * * Note: this is also used for DECLARE CURSOR statements. */static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt){ Query *qry = makeNode(Query); Node *qual; qry->commandType = CMD_SELECT; /* make FOR UPDATE clause available to addRangeTableEntry */ pstate->p_forUpdate = stmt->forUpdate; /* process the FROM clause */ transformFromClause(pstate, stmt->fromClause); /* transform targetlist */ qry->targetList = transformTargetList(pstate, stmt->targetList); /* handle any SELECT INTO/CREATE TABLE AS spec */ qry->into = stmt->into; if (stmt->intoColNames) applyColumnNames(qry->targetList, stmt->intoColNames); /* mark column origins */ markTargetListOrigins(pstate, qry->targetList); /* transform WHERE */ qual = transformWhereClause(pstate, stmt->whereClause, "WHERE"); /* * Initial processing of HAVING clause is just like WHERE clause. * Additional work will be done in optimizer/plan/planner.c. */ qry->havingQual = transformWhereClause(pstate, stmt->havingClause, "HAVING"); /* * Transform sorting/grouping stuff. Do ORDER BY first because both * transformGroupClause and transformDistinctClause need the results. */ qry->sortClause = transformSortClause(pstate, stmt->sortClause, qry->targetList, true /* fix unknowns */ ); qry->groupClause = transformGroupClause(pstate, stmt->groupClause, qry->targetList, qry->sortClause); qry->distinctClause = transformDistinctClause(pstate, stmt->distinctClause, qry->targetList, &qry->sortClause); qry->limitOffset = transformLimitClause(pstate, stmt->limitOffset, "OFFSET"); qry->limitCount = transformLimitClause(pstate, stmt->limitCount, "LIMIT"); qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasAggs = pstate->p_hasAggs; if (pstate->p_hasAggs || qry->groupClause) parseCheckAggregates(pstate, qry); if (stmt->forUpdate != NIL) transformForUpdate(qry, stmt->forUpdate); return qry;}/* * transformSetOperationsStmt - * 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; RangeVar *into; List *intoColNames; List *sortClause; Node *limitOffset; Node *limitCount; List *forUpdate; Node *node; List *lefttl, *dtlist, *targetvars, *targetnames, *sv_namespace, *sv_rtable; RangeTblEntry *jrte; RangeTblRef *jrtr; int tllen; qry->commandType = CMD_SELECT; /* * Find leftmost leaf SelectStmt; extract the one-time-only items from * it and from the top-level node. */ leftmostSelect = stmt->larg; while (leftmostSelect && leftmostSelect->op != SETOP_NONE) leftmostSelect = leftmostSelect->larg; Assert(leftmostSelect && IsA(leftmostSelect, SelectStmt) && leftmostSelect->larg == NULL); into = leftmostSelect->into; intoColNames = leftmostSelect->intoColNames; /* clear them to prevent complaints in transformSetOperationTree() */ leftmostSelect->into = NULL; leftmostSelect->intoColNames = NIL; /* * These are not one-time, exactly, but we want to process them here * and not let transformSetOperationTree() see them --- else it'll * just recurse right back here! */ sortClause = stmt->sortClause; limitOffset = stmt->limitOffset; limitCount = stmt->limitCount; forUpdate = stmt->forUpdate; stmt->sortClause = NIL; stmt->limitOffset = NULL; stmt->limitCount = NULL; stmt->forUpdate = NIL; /* We don't support forUpdate with set ops at the moment. */ if (forUpdate) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT"))); /* * Recursively transform the components of the tree. */ sostmt = (SetOperationStmt *) transformSetOperationTree(pstate, stmt); 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 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; lefttl = leftmostQuery->targetList; foreach(dtlist, sostmt->colTypes) { Oid colType = lfirsto(dtlist); Resdom *leftResdom = ((TargetEntry *) lfirst(lefttl))->resdom; char *colName; Resdom *resdom; Expr *expr; Assert(!leftResdom->resjunk); colName = pstrdup(leftResdom->resname); resdom = makeResdom((AttrNumber) pstate->p_next_resno++, colType, -1, colName, false); expr = (Expr *) makeVar(leftmostRTI, leftResdom->resno, colType, -1, 0); qry->targetList = lappend(qry->targetList, makeTargetEntry(resdom, expr)); targetvars = lappend(targetvars, expr); targetnames = lappend(targetnames, makeString(colName)); lefttl = lnext(lefttl); } /* * Handle SELECT INTO/CREATE TABLE AS. * * Any column names from CREATE TABLE AS need to be attached to both the * top level and the leftmost subquery. We do not do this earlier * because we do *not* want the targetnames list to be affected. */ qry->into = into; if (intoColNames) { applyColumnNames(qry->targetList, intoColNames); applyColumnNames(leftmostQuery->targetList, intoColNames); } /* * 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". */ jrte = addRangeTableEntryForJoin(NULL, targetnames, JOIN_INNER, targetvars, NULL, true); jrtr = makeNode(RangeTblRef); jrtr->rtindex = 1; /* only entry in dummy rtable */ sv_rtable = pstate->p_rtable; pstate->p_rtable = makeList1(jrte); sv_namespace = pstate->p_namespace; pstate->p_namespace = makeList1(jrtr); /* * 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 = length(qry->targetList); qry->sortClause = transformSortClause(pstate, sortClause, qry->targetList, false /* no unknowns expected */ ); pstate->p_namespace = sv_namespace; pstate->p_rtable = sv_rtable; if (tllen != length(qry->targetList)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("ORDER BY on a UNION/INTERSECT/EXCEPT result must be on one of the result columns"))); qry->limitOffset = transformLimitClause(pstate, limitOffset, "OFFSET"); qry->limitCount = transformLimitClause(pstate, limitCount, "LIMIT"); qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->hasSubLinks = pstate->p_hasSubLinks; qry->hasAggs = pstate->p_hasAggs; if (pstate->p_hasAggs || qry->groupClause) parseCheckAggregates(pstate, qry); if (forUpdate != NIL) transformForUpdate(qry, forUpdate); return qry;}/* * transformSetOperationTree * Recursively transform leaves and internal nodes of a set-op tree */static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt){ bool isLeaf; Assert(stmt && IsA(stmt, SelectStmt)); /* * Validity-check both leaf and internal SELECTs for disallowed ops. */ if (stmt->into) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("INTO is only allowed on first SELECT of UNION/INTERSECT/EXCEPT"))); /* We don't support forUpdate with set ops at the moment. */ if (stmt->forUpdate) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT"))); /* * If an internal node of a set-op tree has ORDER BY, UPDATE, or LIMIT * clauses attached, w
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?