analyze.c
来自「PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统」· C语言 代码 · 共 2,222 行 · 第 1/5 页
C
2,222 行
stmt->actions = newactions; } /* Close relation, but keep the exclusive lock */ heap_close(rel, NoLock); 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/FOR SHARE info available to addRangeTableEntry */ pstate->p_locking_clause = stmt->lockingClause; /* 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); qry->intoHasOids = interpretOidsOption(stmt->intoHasOids); /* 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. */ 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 || qry->havingQual) parseCheckAggregates(pstate, qry); if (stmt->lockingClause) transformLockingClause(qry, stmt->lockingClause); 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; LockingClause *lockingClause; Node *node; ListCell *left_tlist, *dtlist; List *targetvars, *targetnames, *sv_relnamespace, *sv_varnamespace, *sv_rtable; RangeTblEntry *jrte; 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; lockingClause = stmt->lockingClause; stmt->sortClause = NIL; stmt->limitOffset = NULL; stmt->limitCount = NULL; stmt->lockingClause = 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"))); /* * 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; left_tlist = list_head(leftmostQuery->targetList); foreach(dtlist, sostmt->colTypes) { Oid colType = lfirst_oid(dtlist); TargetEntry *lefttle = (TargetEntry *) lfirst(left_tlist); char *colName; TargetEntry *tle; Expr *expr; Assert(!lefttle->resjunk); colName = pstrdup(lefttle->resname); expr = (Expr *) makeVar(leftmostRTI, lefttle->resno, colType, -1, 0); tle = makeTargetEntry(expr, (AttrNumber) pstate->p_next_resno++, colName, false); qry->targetList = lappend(qry->targetList, tle); targetvars = lappend(targetvars, expr); targetnames = lappend(targetnames, makeString(colName)); left_tlist = lnext(left_tlist); } /* * 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 varnamespace 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, false); sv_rtable = pstate->p_rtable; pstate->p_rtable = list_make1(jrte); sv_relnamespace = pstate->p_relnamespace; pstate->p_relnamespace = NIL; /* no qualified names allowed */ sv_varnamespace = pstate->p_varnamespace; pstate->p_varnamespace = list_make1(jrte); /* * 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, false /* no unknowns expected */ ); pstate->p_rtable = sv_rtable; pstate->p_relnamespace = sv_relnamespace; pstate->p_varnamespace = sv_varnamespace; if (tllen != list_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 || qry->havingQual) parseCheckAggregates(pstate, qry); if (lockingClause) transformLockingClause(qry, lockingClause); 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 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, UPDATE, or LIMIT * 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) isLeaf = true; else isLeaf = false; } if (isLeaf) { /* Process leaf SELECT */ List *selectList; Query *selectQuery; char selectName[32]; RangeTblEntry *rte; RangeTblRef *rtr; /* * 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. */ selectList = parse_sub_analyze((Node *) stmt, pstate); Assert(list_length(selectList) == 1); selectQuery = (Query *) linitial(selectList); Assert(IsA(selectQuery, Query)); /* * 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_relnamespace || pstate->p_varnamespace) { if (contain_vars_of_level((Node *) selectQuery, 1)) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("UNION/INTERSECT/EXCEPT member statement may not refer to other relations of same query level"))); } /* * 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); /* * 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 *lcoltypes; List *rcoltypes; ListCell *l; ListCell *r; 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 child nodes. */ op->larg = transformSetOperationTree(pstate, stmt->larg); op->rarg = transformSetOperationTree(pstate, stmt->rarg); /* * Verify that the two children have the same number of non-junk * columns, and determine the types of the merged output columns. */ lcoltypes = getSetColTypes(pstate, op->larg); rcoltypes = getSetColTypes(pstate, op->rarg); if (list_length(lcoltypes) != list_length(rcoltypes)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("each %s query must have the same number of columns", context))); op->colTypes = NIL;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?