📄 parse_clause.c
字号:
/* * We require user to supply an alias for a subselect, per SQL92. To relax * this, we'd have to be prepared to gin up a unique alias for an * unlabeled subselect. */ if (r->alias == NULL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("subquery in FROM must have an alias"))); /* * Analyze and transform the subquery. */ parsetrees = parse_sub_analyze(r->subquery, pstate); /* * Check that we got something reasonable. Most of these conditions are * probably impossible given restrictions of the grammar, but check 'em * anyway. */ if (list_length(parsetrees) != 1) elog(ERROR, "unexpected parse analysis result for subquery in FROM"); query = (Query *) linitial(parsetrees); if (query == NULL || !IsA(query, Query)) elog(ERROR, "unexpected parse analysis result for subquery in FROM"); if (query->commandType != CMD_SELECT) elog(ERROR, "expected SELECT query from subquery in FROM"); if (query->resultRelation != 0 || query->into != NULL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("subquery in FROM may not have SELECT INTO"))); /* * The subquery cannot make use of any variables from FROM items created * earlier in the current query. Per SQL92, the scope of a FROM item does * not include other FROM items. Formerly we hacked the namespace so that * the other variables weren't even visible, but it seems more useful to * leave them visible and give a specific error message. * * XXX this will need further work to support SQL99's LATERAL() feature, * wherein such references would indeed be legal. * * We can skip groveling through the subquery if there's not anything * visible in the current query. Also note that outer references are OK. */ if (pstate->p_relnamespace || pstate->p_varnamespace) { if (contain_vars_of_level((Node *) query, 1)) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("subquery in FROM may not refer to other relations of same query level"))); } /* * OK, build an RTE for the subquery. */ rte = addRangeTableEntryForSubquery(pstate, query, r->alias, true); return rte;}/* * transformRangeFunction --- transform a function call appearing in FROM */static RangeTblEntry *transformRangeFunction(ParseState *pstate, RangeFunction *r){ Node *funcexpr; char *funcname; RangeTblEntry *rte; /* * Get function name for possible use as alias. We use the same * transformation rules as for a SELECT output expression. For a FuncCall * node, the result will be the function name, but it is possible for the * grammar to hand back other node types. */ funcname = FigureColname(r->funccallnode); /* * Transform the raw expression. */ funcexpr = transformExpr(pstate, r->funccallnode); /* * The function parameters cannot make use of any variables from other * FROM items. (Compare to transformRangeSubselect(); the coding is * different though because we didn't parse as a sub-select with its own * level of namespace.) * * XXX this will need further work to support SQL99's LATERAL() feature, * wherein such references would indeed be legal. */ if (pstate->p_relnamespace || pstate->p_varnamespace) { if (contain_vars_of_level(funcexpr, 0)) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("function expression in FROM may not refer to other relations of same query level"))); } /* * Disallow aggregate functions in the expression. (No reason to postpone * this check until parseCheckAggregates.) */ if (pstate->p_hasAggs) { if (checkExprHasAggs(funcexpr)) ereport(ERROR, (errcode(ERRCODE_GROUPING_ERROR), errmsg("cannot use aggregate function in function expression in FROM"))); } /* * If a coldeflist is supplied, ensure it defines a legal set of names (no * duplicates) and datatypes (no pseudo-types, for instance). */ if (r->coldeflist) { TupleDesc tupdesc; tupdesc = BuildDescForRelation(r->coldeflist); CheckAttributeNamesTypes(tupdesc, RELKIND_COMPOSITE_TYPE); } /* * OK, build an RTE for the function. */ rte = addRangeTableEntryForFunction(pstate, funcname, funcexpr, r, true); return rte;}/* * transformFromClauseItem - * Transform a FROM-clause item, adding any required entries to the * range table list being built in the ParseState, and return the * transformed item ready to include in the joinlist and namespaces. * This routine can recurse to handle SQL92 JOIN expressions. * * The function return value is the node to add to the jointree (a * RangeTblRef or JoinExpr). Additional output parameters are: * * *top_rte: receives the RTE corresponding to the jointree item. * (We could extract this from the function return node, but it saves cycles * to pass it back separately.) * * *top_rti: receives the rangetable index of top_rte. (Ditto.) * * *relnamespace: receives a List of the RTEs exposed as relation names * by this item. * * *containedRels: receives a bitmap set of the rangetable indexes * of all the base and join relations represented in this jointree item. * This is needed for checking JOIN/ON conditions in higher levels. * * We do not need to pass back an explicit varnamespace value, because * in all cases the varnamespace contribution is exactly top_rte. */static Node *transformFromClauseItem(ParseState *pstate, Node *n, RangeTblEntry **top_rte, int *top_rti, List **relnamespace, Relids *containedRels){ if (IsA(n, RangeVar)) { /* Plain relation reference */ RangeTblRef *rtr; RangeTblEntry *rte; int rtindex; rte = transformTableEntry(pstate, (RangeVar *) n); /* assume new rte is at end */ rtindex = list_length(pstate->p_rtable); Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); *top_rte = rte; *top_rti = rtindex; *relnamespace = list_make1(rte); *containedRels = bms_make_singleton(rtindex); rtr = makeNode(RangeTblRef); rtr->rtindex = rtindex; return (Node *) rtr; } else if (IsA(n, RangeSubselect)) { /* sub-SELECT is like a plain relation */ RangeTblRef *rtr; RangeTblEntry *rte; int rtindex; rte = transformRangeSubselect(pstate, (RangeSubselect *) n); /* assume new rte is at end */ rtindex = list_length(pstate->p_rtable); Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); *top_rte = rte; *top_rti = rtindex; *relnamespace = list_make1(rte); *containedRels = bms_make_singleton(rtindex); rtr = makeNode(RangeTblRef); rtr->rtindex = rtindex; return (Node *) rtr; } else if (IsA(n, RangeFunction)) { /* function is like a plain relation */ RangeTblRef *rtr; RangeTblEntry *rte; int rtindex; rte = transformRangeFunction(pstate, (RangeFunction *) n); /* assume new rte is at end */ rtindex = list_length(pstate->p_rtable); Assert(rte == rt_fetch(rtindex, pstate->p_rtable)); *top_rte = rte; *top_rti = rtindex; *relnamespace = list_make1(rte); *containedRels = bms_make_singleton(rtindex); rtr = makeNode(RangeTblRef); rtr->rtindex = rtindex; return (Node *) rtr; } else if (IsA(n, JoinExpr)) { /* A newfangled join expression */ JoinExpr *j = (JoinExpr *) n; RangeTblEntry *l_rte; RangeTblEntry *r_rte; int l_rtindex; int r_rtindex; Relids l_containedRels, r_containedRels, my_containedRels; List *l_relnamespace, *r_relnamespace, *my_relnamespace, *l_colnames, *r_colnames, *res_colnames, *l_colvars, *r_colvars, *res_colvars; RangeTblEntry *rte; /* * Recursively process the left and right subtrees */ j->larg = transformFromClauseItem(pstate, j->larg, &l_rte, &l_rtindex, &l_relnamespace, &l_containedRels); j->rarg = transformFromClauseItem(pstate, j->rarg, &r_rte, &r_rtindex, &r_relnamespace, &r_containedRels); /* * Check for conflicting refnames in left and right subtrees. Must do * this because higher levels will assume I hand back a self- * consistent namespace subtree. */ checkNameSpaceConflicts(pstate, l_relnamespace, r_relnamespace); /* * Generate combined relation membership info for possible use by * transformJoinOnClause below. */ my_relnamespace = list_concat(l_relnamespace, r_relnamespace); my_containedRels = bms_join(l_containedRels, r_containedRels); pfree(r_relnamespace); /* free unneeded list header */ /* * Extract column name and var lists from both subtrees * * Note: expandRTE returns new lists, safe for me to modify */ expandRTE(l_rte, l_rtindex, 0, false, &l_colnames, &l_colvars); expandRTE(r_rte, r_rtindex, 0, false, &r_colnames, &r_colvars); /* * Natural join does not explicitly specify columns; must generate * columns to join. Need to run through the list of columns from each * table or join result and match up the column names. Use the first * table, and check every column in the second table for a match. * (We'll check that the matches were unique later on.) The result of * this step is a list of column names just like an explicitly-written * USING list. */ if (j->isNatural) { List *rlist = NIL; ListCell *lx, *rx; Assert(j->using == NIL); /* shouldn't have USING() too */ foreach(lx, l_colnames) { char *l_colname = strVal(lfirst(lx)); Value *m_name = NULL; foreach(rx, r_colnames) { char *r_colname = strVal(lfirst(rx)); if (strcmp(l_colname, r_colname) == 0) { m_name = makeString(l_colname); break; } } /* matched a right column? then keep as join column... */ if (m_name != NULL) rlist = lappend(rlist, m_name); } j->using = rlist; } /* * Now transform the join qualifications, if any. */ res_colnames = NIL; res_colvars = NIL; if (j->using) { /* * JOIN/USING (or NATURAL JOIN, as transformed above). Transform * the list into an explicit ON-condition, and generate a list of * merged result columns. */ List *ucols = j->using; List *l_usingvars = NIL; List *r_usingvars = NIL; ListCell *ucol; Assert(j->quals == NULL); /* shouldn't have ON() too */ foreach(ucol, ucols) { char *u_colname = strVal(lfirst(ucol)); ListCell *col; int ndx; int l_index = -1; int r_index = -1; Var *l_colvar, *r_colvar; /* Check for USING(foo,foo) */ foreach(col, res_colnames) { char *res_colname = strVal(lfirst(col)); if (strcmp(res_colname, u_colname) == 0) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_COLUMN), errmsg("column name \"%s\" appears more than once in USING clause", u_colname))); } /* Find it in left input */ ndx = 0; foreach(col, l_colnames) { char *l_colname = strVal(lfirst(col)); if (strcmp(l_colname, u_colname) == 0) { if (l_index >= 0) ereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_COLUMN), errmsg("common column name \"%s\" appears more than once in left table", u_colname))); l_index = ndx; } ndx++; } if (l_index < 0) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" specified in USING clause does not exist in left table", u_colname))); /* Find it in right input */ ndx = 0; foreach(col, r_colnames) { char *r_colname = strVal(lfirst(col)); if (strcmp(r_colname, u_colname) == 0) { if (r_index >= 0) ereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_COLUMN), errmsg("common column name \"%s\" appears more than once in right table", u_colname))); r_index = ndx; } ndx++; } if (r_index < 0) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" specified in USING clause does not exist in right table", u_colname))); l_colvar = list_nth(l_colvars, l_index); l_usingvars = lappend(l_usingvars, l_colvar);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -