prepunion.c
来自「PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统」· C语言 代码 · 共 1,246 行 · 第 1/3 页
C
1,246 行
List *child_sortclauses; if (IsA(setOp, SetOperationStmt)) { SetOperationStmt *op = (SetOperationStmt *) setOp; if (op->op == top_union->op && (op->all == top_union->all || op->all) && equal(op->colTypes, top_union->colTypes)) { /* Same UNION, so fold children into parent's subplan list */ return list_concat(recurse_union_children(op->larg, root, tuple_fraction, top_union, refnames_tlist), recurse_union_children(op->rarg, root, tuple_fraction, top_union, refnames_tlist)); } } /* * Not same, so plan this child separately. * * Note we disallow any resjunk columns in child results. This is * necessary since the Append node that implements the union won't do any * projection, and upper levels will get confused if some of our output * tuples have junk and some don't. This case only arises when we have an * EXCEPT or INTERSECT as child, else there won't be resjunk anyway. */ return list_make1(recurse_set_operations(setOp, root, tuple_fraction, top_union->colTypes, false, -1, refnames_tlist, &child_sortclauses));}/* * Generate targetlist for a set-operation plan node * * colTypes: column datatypes for non-junk columns * flag: -1 if no flag column needed, 0 or 1 to create a const flag column * varno: varno to use in generated Vars * hack_constants: true to copy up constants (see comments in code) * input_tlist: targetlist of this node's input node * refnames_tlist: targetlist to take column names from */static List *generate_setop_tlist(List *colTypes, int flag, Index varno, bool hack_constants, List *input_tlist, List *refnames_tlist){ List *tlist = NIL; int resno = 1; ListCell *i, *j, *k; TargetEntry *tle; Node *expr; j = list_head(input_tlist); k = list_head(refnames_tlist); foreach(i, colTypes) { Oid colType = lfirst_oid(i); TargetEntry *inputtle = (TargetEntry *) lfirst(j); TargetEntry *reftle = (TargetEntry *) lfirst(k); Assert(inputtle->resno == resno); Assert(reftle->resno == resno); Assert(!inputtle->resjunk); Assert(!reftle->resjunk); /* * Generate columns referencing input columns and having appropriate * data types and column names. Insert datatype coercions where * necessary. * * HACK: constants in the input's targetlist are copied up as-is * rather than being referenced as subquery outputs. This is mainly * to ensure that when we try to coerce them to the output column's * datatype, the right things happen for UNKNOWN constants. But do * this only at the first level of subquery-scan plans; we don't want * phony constants appearing in the output tlists of upper-level * nodes! */ if (hack_constants && inputtle->expr && IsA(inputtle->expr, Const)) expr = (Node *) inputtle->expr; else expr = (Node *) makeVar(varno, inputtle->resno, exprType((Node *) inputtle->expr), exprTypmod((Node *) inputtle->expr), 0); if (exprType(expr) != colType) { expr = coerce_to_common_type(NULL, /* no UNKNOWNs here */ expr, colType, "UNION/INTERSECT/EXCEPT"); } tle = makeTargetEntry((Expr *) expr, (AttrNumber) resno++, pstrdup(reftle->resname), false); tlist = lappend(tlist, tle); j = lnext(j); k = lnext(k); } if (flag >= 0) { /* Add a resjunk flag column */ /* flag value is the given constant */ expr = (Node *) makeConst(INT4OID, sizeof(int4), Int32GetDatum(flag), false, true); tle = makeTargetEntry((Expr *) expr, (AttrNumber) resno++, pstrdup("flag"), true); tlist = lappend(tlist, tle); } return tlist;}/* * Generate targetlist for a set-operation Append node * * colTypes: column datatypes for non-junk columns * flag: true to create a flag column copied up from subplans * input_plans: list of sub-plans of the Append * refnames_tlist: targetlist to take column names from * * The entries in the Append's targetlist should always be simple Vars; * we just have to make sure they have the right datatypes and typmods. * The Vars are always generated with varno 0. */static List *generate_append_tlist(List *colTypes, bool flag, List *input_plans, List *refnames_tlist){ List *tlist = NIL; int resno = 1; ListCell *curColType; ListCell *ref_tl_item; int colindex; TargetEntry *tle; Node *expr; ListCell *planl; int32 *colTypmods; /* * First extract typmods to use. * * If the inputs all agree on type and typmod of a particular column, use * that typmod; else use -1. */ colTypmods = (int32 *) palloc(list_length(colTypes) * sizeof(int32)); foreach(planl, input_plans) { Plan *subplan = (Plan *) lfirst(planl); ListCell *subtlist; curColType = list_head(colTypes); colindex = 0; foreach(subtlist, subplan->targetlist) { TargetEntry *subtle = (TargetEntry *) lfirst(subtlist); if (subtle->resjunk) continue; Assert(curColType != NULL); if (exprType((Node *) subtle->expr) == lfirst_oid(curColType)) { /* If first subplan, copy the typmod; else compare */ int32 subtypmod = exprTypmod((Node *) subtle->expr); if (planl == list_head(input_plans)) colTypmods[colindex] = subtypmod; else if (subtypmod != colTypmods[colindex]) colTypmods[colindex] = -1; } else { /* types disagree, so force typmod to -1 */ colTypmods[colindex] = -1; } curColType = lnext(curColType); colindex++; } Assert(curColType == NULL); } /* * Now we can build the tlist for the Append. */ colindex = 0; forboth(curColType, colTypes, ref_tl_item, refnames_tlist) { Oid colType = lfirst_oid(curColType); int32 colTypmod = colTypmods[colindex++]; TargetEntry *reftle = (TargetEntry *) lfirst(ref_tl_item); Assert(reftle->resno == resno); Assert(!reftle->resjunk); expr = (Node *) makeVar(0, resno, colType, colTypmod, 0); tle = makeTargetEntry((Expr *) expr, (AttrNumber) resno++, pstrdup(reftle->resname), false); tlist = lappend(tlist, tle); } if (flag) { /* Add a resjunk flag column */ /* flag value is shown as copied up from subplan */ expr = (Node *) makeVar(0, resno, INT4OID, -1, 0); tle = makeTargetEntry((Expr *) expr, (AttrNumber) resno++, pstrdup("flag"), true); tlist = lappend(tlist, tle); } pfree(colTypmods); return tlist;}/* * Does tlist have same datatypes as requested colTypes? * * Resjunk columns are ignored if junkOK is true; otherwise presence of * a resjunk column will always cause a 'false' result. */static booltlist_same_datatypes(List *tlist, List *colTypes, bool junkOK){ ListCell *l; ListCell *curColType = list_head(colTypes); foreach(l, tlist) { TargetEntry *tle = (TargetEntry *) lfirst(l); if (tle->resjunk) { if (!junkOK) return false; } else { if (curColType == NULL) return false; if (exprType((Node *) tle->expr) != lfirst_oid(curColType)) return false; curColType = lnext(curColType); } } if (curColType != NULL) return false; return true;}/* * find_all_inheritors - * Returns a list of relation OIDs including the given rel plus * all relations that inherit from it, directly or indirectly. */List *find_all_inheritors(Oid parentrel){ List *rels_list; ListCell *l; /* * We build a list starting with the given rel and adding all direct and * indirect children. We can use a single list as both the record of * already-found rels and the agenda of rels yet to be scanned for more * children. This is a bit tricky but works because the foreach() macro * doesn't fetch the next list element until the bottom of the loop. */ rels_list = list_make1_oid(parentrel); foreach(l, rels_list) { Oid currentrel = lfirst_oid(l); List *currentchildren; /* Get the direct children of this rel */ currentchildren = find_inheritance_children(currentrel); /* * Add to the queue only those children not already seen. This avoids * making duplicate entries in case of multiple inheritance paths from * the same parent. (It'll also keep us from getting into an infinite * loop, though theoretically there can't be any cycles in the * inheritance graph anyway.) */ rels_list = list_concat_unique_oid(rels_list, currentchildren); } return rels_list;}/* * expand_inherited_rtentry * Check whether a rangetable entry represents an inheritance set. * If so, add entries for all the child tables to the query's * rangetable, and return an integer list of RT indexes for the * whole inheritance set (parent and children). * If not, return NIL. * * Note that the original RTE is considered to represent the whole * inheritance set. The first member of the returned list is an RTE * for the same table, but with inh = false, to represent the parent table * in its role as a simple member of the set. The original RT index is * never a member of the returned list. * * A childless table is never considered to be an inheritance set; therefore * the result will never be a one-element list. It'll be either empty * or have two or more elements. * * Note: there are cases in which this routine will be invoked multiple * times on the same RTE. We will generate a separate set of child RTEs * for each invocation. This is somewhat wasteful but seems not worth * trying to avoid. */List *expand_inherited_rtentry(PlannerInfo *root, Index rti){ Query *parse = root->parse; RangeTblEntry *rte = rt_fetch(rti, parse->rtable); Oid parentOID; List *inhOIDs; List *inhRTIs; ListCell *l; /* Does RT entry allow inheritance? */ if (!rte->inh) return NIL; Assert(rte->rtekind == RTE_RELATION); /* Fast path for common case of childless table */ parentOID = rte->relid; if (!has_subclass(parentOID)) { /* Clear flag to save repeated tests if called again */ rte->inh = false; return NIL; } /* Scan for all members of inheritance set */ inhOIDs = find_all_inheritors(parentOID); /* * Check that there's at least one descendant, else treat as no-child * case. This could happen despite above has_subclass() check, if table * once had a child but no longer does. */ if (list_length(inhOIDs) < 2) { /* Clear flag to save repeated tests if called again */ rte->inh = false; return NIL; } /* OK, it's an inheritance set; expand it */ inhRTIs = NIL; foreach(l, inhOIDs) { Oid childOID = lfirst_oid(l); RangeTblEntry *childrte; Index childRTindex; /* * It is possible that the parent table has children that are temp * tables of other backends. We cannot safely access such tables * (because of buffering issues), and the best thing to do seems to be * to silently ignore them. */ if (childOID != parentOID && isOtherTempNamespace(get_rel_namespace(childOID))) continue; /* * Build an RTE for the child, and attach to query's rangetable list. * We copy most fields of the parent's RTE, but replace relation OID, * and set inh = false. */ childrte = copyObject(rte); childrte->relid = childOID; childrte->inh = false; parse->rtable = lappend(parse->rtable, childrte); childRTindex = list_length(parse->rtable); inhRTIs = lappend_int(inhRTIs, childRTindex); }
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?