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 + -
显示快捷键?