parse_clause.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 1,591 行 · 第 1/3 页
C
1,591 行
*/ rtr = makeNode(RangeTblRef); /* assume new rte is at end */ rtr->rtindex = length(pstate->p_rtable); Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable)); return rtr;}/* * 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 namespace. * This routine can recurse to handle SQL92 JOIN expressions. * * Aside from the primary return value (the transformed joinlist item) * this routine also returns an integer list of the rangetable indexes * of all the base and join relations represented in the joinlist item. * This list is needed for checking JOIN/ON conditions in higher levels. */static Node *transformFromClauseItem(ParseState *pstate, Node *n, List **containedRels){ if (IsA(n, RangeVar)) { /* Plain relation reference */ RangeTblRef *rtr; rtr = transformTableEntry(pstate, (RangeVar *) n); *containedRels = makeListi1(rtr->rtindex); return (Node *) rtr; } else if (IsA(n, RangeSubselect)) { /* sub-SELECT is like a plain relation */ RangeTblRef *rtr; rtr = transformRangeSubselect(pstate, (RangeSubselect *) n); *containedRels = makeListi1(rtr->rtindex); return (Node *) rtr; } else if (IsA(n, RangeFunction)) { /* function is like a plain relation */ RangeTblRef *rtr; rtr = transformRangeFunction(pstate, (RangeFunction *) n); *containedRels = makeListi1(rtr->rtindex); return (Node *) rtr; } else if (IsA(n, JoinExpr)) { /* A newfangled join expression */ JoinExpr *j = (JoinExpr *) n; List *my_containedRels, *l_containedRels, *r_containedRels, *l_colnames, *r_colnames, *res_colnames, *l_colvars, *r_colvars, *res_colvars; Index leftrti, rightrti; RangeTblEntry *rte; /* * Recursively process the left and right subtrees */ j->larg = transformFromClauseItem(pstate, j->larg, &l_containedRels); j->rarg = transformFromClauseItem(pstate, j->rarg, &r_containedRels); /* * Generate combined list of relation indexes for possible use by * transformJoinOnClause below. */ my_containedRels = nconc(l_containedRels, 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, j->larg, j->rarg); /* * Extract column name and var lists from both subtrees * * Note: expandRTE returns new lists, safe for me to modify */ if (IsA(j->larg, RangeTblRef)) leftrti = ((RangeTblRef *) j->larg)->rtindex; else if (IsA(j->larg, JoinExpr)) leftrti = ((JoinExpr *) j->larg)->rtindex; else { elog(ERROR, "unrecognized node type: %d", (int) nodeTag(j->larg)); leftrti = 0; /* keep compiler quiet */ } rte = rt_fetch(leftrti, pstate->p_rtable); expandRTE(pstate, rte, &l_colnames, &l_colvars); if (IsA(j->rarg, RangeTblRef)) rightrti = ((RangeTblRef *) j->rarg)->rtindex; else if (IsA(j->rarg, JoinExpr)) rightrti = ((JoinExpr *) j->rarg)->rtindex; else { elog(ERROR, "unrecognized node type: %d", (int) nodeTag(j->rarg)); rightrti = 0; /* keep compiler quiet */ } rte = rt_fetch(rightrti, pstate->p_rtable); expandRTE(pstate, rte, &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; List *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; List *ucol; Assert(j->quals == NULL); /* shouldn't have ON() too */ foreach(ucol, ucols) { char *u_colname = strVal(lfirst(ucol)); List *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 = nth(l_index, l_colvars); l_usingvars = lappend(l_usingvars, l_colvar); r_colvar = nth(r_index, r_colvars); r_usingvars = lappend(r_usingvars, r_colvar); res_colnames = lappend(res_colnames, lfirst(ucol)); res_colvars = lappend(res_colvars, buildMergedJoinVar(pstate, j->jointype, l_colvar, r_colvar)); } j->quals = transformJoinUsingClause(pstate, l_usingvars, r_usingvars); } else if (j->quals) { /* User-written ON-condition; transform it */ j->quals = transformJoinOnClause(pstate, j, my_containedRels); } else { /* CROSS JOIN: no quals */ } /* Add remaining columns from each side to the output columns */ extractRemainingColumns(res_colnames, l_colnames, l_colvars, &l_colnames, &l_colvars); extractRemainingColumns(res_colnames, r_colnames, r_colvars, &r_colnames, &r_colvars); res_colnames = nconc(res_colnames, l_colnames); res_colvars = nconc(res_colvars, l_colvars); res_colnames = nconc(res_colnames, r_colnames); res_colvars = nconc(res_colvars, r_colvars); /* * Check alias (AS clause), if any. */ if (j->alias) { if (j->alias->colnames != NIL) { if (length(j->alias->colnames) > length(res_colnames)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("column alias list for \"%s\" has too many entries", j->alias->aliasname))); } } /* * Now build an RTE for the result of the join */ rte = addRangeTableEntryForJoin(pstate, res_colnames, j->jointype, res_colvars, j->alias, true); /* assume new rte is at end */ j->rtindex = length(pstate->p_rtable); Assert(rte == rt_fetch(j->rtindex, pstate->p_rtable)); /* * Include join RTE in returned containedRels list */ *containedRels = lconsi(j->rtindex, my_containedRels); return (Node *) j; } else elog(ERROR, "unrecognized node type: %d", (int) nodeTag(n)); return NULL; /* can't get here, keep compiler quiet */}/* * buildMergedJoinVar - * generate a suitable replacement expression for a merged join column */static Node *buildMergedJoinVar(ParseState *pstate, JoinType jointype, Var *l_colvar, Var *r_colvar){ Oid outcoltype; int32 outcoltypmod; Node *l_node, *r_node, *res_node; /* * Choose output type if input types are dissimilar. */ outcoltype = l_colvar->vartype; outcoltypmod = l_colvar->vartypmod; if (outcoltype != r_colvar->vartype) { outcoltype = select_common_type(makeListo2(l_colvar->vartype, r_colvar->vartype), "JOIN/USING"); outcoltypmod = -1; /* ie, unknown */ } else if (outcoltypmod != r_colvar->vartypmod) { /* same type, but not same typmod */ outcoltypmod = -1; /* ie, unknown */ } /* * Insert coercion functions if needed. Note that a difference in * typmod can only happen if input has typmod but outcoltypmod is -1. * In that case we insert a RelabelType to clearly mark that result's * typmod is not same as input. */ if (l_colvar->vartype != outcoltype) l_node = coerce_type(pstate, (Node *) l_colvar, l_colvar->vartype, outcoltype, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST); else if (l_colvar->vartypmod != outcoltypmod) l_node = (Node *) makeRelabelType((Expr *) l_colvar, outcoltype, outcoltypmod, COERCE_IMPLICIT_CAST); else l_node = (Node *) l_colvar; if (r_colvar->vartype != outcoltype) r_node = coerce_type(pstate, (Node *) r_colvar, r_colvar->vartype, outcoltype, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST); else if (r_colvar->vartypmod != outcoltypmod) r_node = (Node *) makeRelabelType((Expr *) r_colvar, outcoltype, outcoltypmod, COERCE_IMPLICIT_CAST); else r_node = (Node *) r_colvar; /* * Choose what to emit */ switch (jointype) { case JOIN_INNER: /* * We can use either var; prefer non-coerced one if available. */ if (IsA(l_node, Var)) res_node = l_node; else if (IsA(r_node, Var)) res_node = r_node; else res_node = l_node; break; case JOIN_LEFT: /* Always use left var */ res_node = l_node; break; case JOIN_RIGHT: /* Always use right var */ res_node = r_node; break; case JOIN_FULL: { /* * Here we must build a COALESCE expression to ensure that * the join output is non-null if either input is. */ CoalesceExpr *c = makeNode(CoalesceExpr); c->coalescetype = outcoltype; c->args = makeList2(l_node, r_node); res_node = (Node *) c; break; } default: elog(ERROR, "unrecognized join type: %d", (int) jointype); res_node = NULL; /* keep compiler quiet */ break; } return res_node;}/* * transformWhereClause - * Transform the qualification and make sure it is of type boolean. * Used for WHERE and allied clauses. * * constructName does not affect the semantics, but is used in error messages */Node *transformWhereClause(ParseState *pstate, Node *clause, const char *constructName){ Node *qual; if (clause == NULL) return NULL; qual = transformExpr(pstate, clause); qual = coerce_to_boolean(pstate, qual, constructName); return qual;}/* * transformLimitClause - * Transform the expression and make sure it is of type integer. * Used for LIMIT and allied clauses. * * constructName does not affect the semantics, but is used in error messages */Node *transformLimitClause(ParseState *pstate, Node *clause, const char *constructName){ Node *qual; if (clause == NULL) return NULL; qual = transformExpr(pstate, clause); qual = coerce_to_integer(pstate, qual, constructName); /* * LIMIT can't refer to any vars or aggregates of the current query; * we don't allow subselects either (though that case would at least * be sensible) */ if (contain_vars_of_level(qual, 0)) { ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), /* translator: %s is name of a SQL construct, eg LIMIT */ errmsg("argument of %s must not contain variables", constructName))); } if (checkExprHasAggs(qual)) { ereport(ERROR, (errcode(ERRCODE_GROUPING_ERROR), /* translator: %s is name of a SQL construct, eg LIMIT */ errmsg("argument of %s must not contain aggregates", constructName))); } if (contain_subplans(qual)) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), /* translator: %s is name of a SQL construct, eg LIMIT */ errmsg("argument of %s must not contain subqueries", constructName))); } return qual;}/* * findTargetlistEntry - * Returns the targetlist entry matching the given (untransformed) node. * If no matching entry exists, one is created and appended to the target * list as a "resjunk" node. * * node the ORDER BY, GROUP BY, or DISTINCT ON expression to be matched * tlist the existing target list (NB: this will never be NIL, which is a * good thing since we'd be unable to append to it if it were...) * clause identifies clause type being processed. */static TargetEntry *findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause){ TargetEntry *target_result = NULL; List *tl; Node *expr; /*----------
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?