📄 parse_clause.c
字号:
} ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), /* translator: %s is name of a SQL construct, eg ORDER BY */ errmsg("%s position %d is not in select list", clauseText[clause], target_pos))); } /* * Otherwise, we have an expression (this is a Postgres extension not * found in SQL92). Convert the untransformed node to a transformed * expression, and search for a match in the tlist. NOTE: it doesn't * really matter whether there is more than one match. Also, we are * willing to match a resjunk target here, though the above cases must * ignore resjunk targets. */ expr = transformExpr(pstate, node); foreach(tl, *tlist) { TargetEntry *tle = (TargetEntry *) lfirst(tl); if (equal(expr, tle->expr)) return tle; } /* * If no matches, construct a new target entry which is appended to the * end of the target list. This target is given resjunk = TRUE so that it * will not be projected into the final tuple. */ target_result = transformTargetEntry(pstate, node, expr, NULL, true); *tlist = lappend(*tlist, target_result); return target_result;}/* * transformGroupClause - * transform a GROUP BY clause * * GROUP BY items will be added to the targetlist (as resjunk columns) * if not already present, so the targetlist must be passed by reference. */List *transformGroupClause(ParseState *pstate, List *grouplist, List **targetlist, List *sortClause){ List *glist = NIL; ListCell *gl; ListCell *sortItem; sortItem = list_head(sortClause); foreach(gl, grouplist) { TargetEntry *tle; Oid restype; Oid ordering_op; GroupClause *grpcl; tle = findTargetlistEntry(pstate, lfirst(gl), targetlist, GROUP_CLAUSE); /* avoid making duplicate grouplist entries */ if (targetIsInSortList(tle, glist)) continue; /* if tlist item is an UNKNOWN literal, change it to TEXT */ restype = exprType((Node *) tle->expr); if (restype == UNKNOWNOID) { tle->expr = (Expr *) coerce_type(pstate, (Node *) tle->expr, restype, TEXTOID, -1, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST); restype = TEXTOID; } /* * If the GROUP BY clause matches the ORDER BY clause, we want to * adopt the ordering operators from the latter rather than using the * default ops. This allows "GROUP BY foo ORDER BY foo DESC" to be * done with only one sort step. Note we are assuming that any * user-supplied ordering operator will bring equal values together, * which is all that GROUP BY needs. */ if (sortItem && ((SortClause *) lfirst(sortItem))->tleSortGroupRef == tle->ressortgroupref) { ordering_op = ((SortClause *) lfirst(sortItem))->sortop; sortItem = lnext(sortItem); } else { ordering_op = ordering_oper_opid(restype); sortItem = NULL; /* disregard ORDER BY once match fails */ } grpcl = makeNode(GroupClause); grpcl->tleSortGroupRef = assignSortGroupRef(tle, *targetlist); grpcl->sortop = ordering_op; glist = lappend(glist, grpcl); } return glist;}/* * transformSortClause - * transform an ORDER BY clause * * ORDER BY items will be added to the targetlist (as resjunk columns) * if not already present, so the targetlist must be passed by reference. */List *transformSortClause(ParseState *pstate, List *orderlist, List **targetlist, bool resolveUnknown){ List *sortlist = NIL; ListCell *olitem; foreach(olitem, orderlist) { SortBy *sortby = lfirst(olitem); TargetEntry *tle; tle = findTargetlistEntry(pstate, sortby->node, targetlist, ORDER_CLAUSE); sortlist = addTargetToSortList(pstate, tle, sortlist, *targetlist, sortby->sortby_kind, sortby->useOp, resolveUnknown); } return sortlist;}/* * transformDistinctClause - * transform a DISTINCT or DISTINCT ON clause * * Since we may need to add items to the query's sortClause list, that list * is passed by reference. Likewise for the targetlist. */List *transformDistinctClause(ParseState *pstate, List *distinctlist, List **targetlist, List **sortClause){ List *result = NIL; ListCell *slitem; ListCell *dlitem; /* No work if there was no DISTINCT clause */ if (distinctlist == NIL) return NIL; if (linitial(distinctlist) == NULL) { /* We had SELECT DISTINCT */ /* * All non-resjunk elements from target list that are not already in * the sort list should be added to it. (We don't really care what * order the DISTINCT fields are checked in, so we can leave the * user's ORDER BY spec alone, and just add additional sort keys to it * to ensure that all targetlist items get sorted.) */ *sortClause = addAllTargetsToSortList(pstate, *sortClause, *targetlist, true); /* * Now, DISTINCT list consists of all non-resjunk sortlist items. * Actually, all the sortlist items had better be non-resjunk! * Otherwise, user wrote SELECT DISTINCT with an ORDER BY item that * does not appear anywhere in the SELECT targetlist, and we can't * implement that with only one sorting pass... */ foreach(slitem, *sortClause) { SortClause *scl = (SortClause *) lfirst(slitem); TargetEntry *tle = get_sortgroupclause_tle(scl, *targetlist); if (tle->resjunk) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("for SELECT DISTINCT, ORDER BY expressions must appear in select list"))); else result = lappend(result, copyObject(scl)); } } else { /* We had SELECT DISTINCT ON (expr, ...) */ /* * If the user writes both DISTINCT ON and ORDER BY, then the two * expression lists must match (until one or the other runs out). * Otherwise the ORDER BY requires a different sort order than the * DISTINCT does, and we can't implement that with only one sort pass * (and if we do two passes, the results will be rather * unpredictable). However, it's OK to have more DISTINCT ON * expressions than ORDER BY expressions; we can just add the extra * DISTINCT values to the sort list, much as we did above for ordinary * DISTINCT fields. * * Actually, it'd be OK for the common prefixes of the two lists to * match in any order, but implementing that check seems like more * trouble than it's worth. */ ListCell *nextsortlist = list_head(*sortClause); foreach(dlitem, distinctlist) { TargetEntry *tle; tle = findTargetlistEntry(pstate, lfirst(dlitem), targetlist, DISTINCT_ON_CLAUSE); if (nextsortlist != NULL) { SortClause *scl = (SortClause *) lfirst(nextsortlist); if (tle->ressortgroupref != scl->tleSortGroupRef) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("SELECT DISTINCT ON expressions must match initial ORDER BY expressions"))); result = lappend(result, copyObject(scl)); nextsortlist = lnext(nextsortlist); } else { *sortClause = addTargetToSortList(pstate, tle, *sortClause, *targetlist, SORTBY_ASC, NIL, true); /* * Probably, the tle should always have been added at the end * of the sort list ... but search to be safe. */ foreach(slitem, *sortClause) { SortClause *scl = (SortClause *) lfirst(slitem); if (tle->ressortgroupref == scl->tleSortGroupRef) { result = lappend(result, copyObject(scl)); break; } } if (slitem == NULL) /* should not happen */ elog(ERROR, "failed to add DISTINCT ON clause to target list"); } } } return result;}/* * addAllTargetsToSortList * Make sure all non-resjunk targets in the targetlist are in the * ORDER BY list, adding the not-yet-sorted ones to the end of the list. * This is typically used to help implement SELECT DISTINCT. * * See addTargetToSortList for info about pstate and resolveUnknown inputs. * * Returns the updated ORDER BY list. */List *addAllTargetsToSortList(ParseState *pstate, List *sortlist, List *targetlist, bool resolveUnknown){ ListCell *l; foreach(l, targetlist) { TargetEntry *tle = (TargetEntry *) lfirst(l); if (!tle->resjunk) sortlist = addTargetToSortList(pstate, tle, sortlist, targetlist, SORTBY_ASC, NIL, resolveUnknown); } return sortlist;}/* * addTargetToSortList * If the given targetlist entry isn't already in the ORDER BY list, * add it to the end of the list, using the sortop with given name * or the default sort operator if opname == NIL. * * If resolveUnknown is TRUE, convert TLEs of type UNKNOWN to TEXT. If not, * do nothing (which implies the search for a sort operator will fail). * pstate should be provided if resolveUnknown is TRUE, but can be NULL * otherwise. * * Returns the updated ORDER BY list. */List *addTargetToSortList(ParseState *pstate, TargetEntry *tle, List *sortlist, List *targetlist, int sortby_kind, List *sortby_opname, bool resolveUnknown){ /* avoid making duplicate sortlist entries */ if (!targetIsInSortList(tle, sortlist)) { SortClause *sortcl = makeNode(SortClause); Oid restype = exprType((Node *) tle->expr); /* if tlist item is an UNKNOWN literal, change it to TEXT */ if (restype == UNKNOWNOID && resolveUnknown) { tle->expr = (Expr *) coerce_type(pstate, (Node *) tle->expr, restype, TEXTOID, -1, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST); restype = TEXTOID; } sortcl->tleSortGroupRef = assignSortGroupRef(tle, targetlist); switch (sortby_kind) { case SORTBY_ASC: sortcl->sortop = ordering_oper_opid(restype); break; case SORTBY_DESC: sortcl->sortop = reverse_ordering_oper_opid(restype); break; case SORTBY_USING: Assert(sortby_opname != NIL); sortcl->sortop = compatible_oper_opid(sortby_opname, restype, restype, false); break; default: elog(ERROR, "unrecognized sortby_kind: %d", sortby_kind); break; } sortlist = lappend(sortlist, sortcl); } return sortlist;}/* * assignSortGroupRef * Assign the targetentry an unused ressortgroupref, if it doesn't * already have one. Return the assigned or pre-existing refnumber. * * 'tlist' is the targetlist containing (or to contain) the given targetentry. */IndexassignSortGroupRef(TargetEntry *tle, List *tlist){ Index maxRef; ListCell *l; if (tle->ressortgroupref) /* already has one? */ return tle->ressortgroupref; /* easiest way to pick an unused refnumber: max used + 1 */ maxRef = 0; foreach(l, tlist) { Index ref = ((TargetEntry *) lfirst(l))->ressortgroupref; if (ref > maxRef) maxRef = ref; } tle->ressortgroupref = maxRef + 1; return tle->ressortgroupref;}/* * targetIsInSortList * Is the given target item already in the sortlist? * * Works for both SortClause and GroupClause lists. Note that the main * reason we need this routine (and not just a quick test for nonzeroness * of ressortgroupref) is that a TLE might be in only one of the lists. */booltargetIsInSortList(TargetEntry *tle, List *sortList){ Index ref = tle->ressortgroupref; ListCell *l; /* no need to scan list if tle has no marker */ if (ref == 0) return false; foreach(l, sortList) { SortClause *scl = (SortClause *) lfirst(l); if (scl->tleSortGroupRef == ref) return true; } return false;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -