/*
 * Pull up children of a UNION node that are identically-propertied UNIONs.
 *
 * NOTE: we can also pull a UNION ALL up into a UNION, since the distinct
 * output rows will be lost anyway.
 */
static List *
recurse_union_children(Node *setOp, PlannerInfo *root,
					   double tuple_fraction,
					   SetOperationStmt *top_union,
					   List *refnames_tlist)
{
	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));
}
/*
 * plan_set_operations
 *
 *	  Plans the queries for a tree of set operations (UNION/INTERSECT/EXCEPT)
 *
 * This routine only deals with the setOperations tree of the given query.
 * Any top-level ORDER BY requested in root->parse->sortClause will be added
 * when we return to grouping_planner.
 *
 * tuple_fraction is the fraction of tuples we expect will be retrieved.
 * tuple_fraction is interpreted as for grouping_planner(); in particular,
 * zero means "all the tuples will be fetched".  Any LIMIT present at the
 * top level has already been factored into tuple_fraction.
 *
 * *sortClauses is an output argument: it is set to a list of SortClauses
 * representing the result ordering of the topmost set operation.
 */
Plan *
plan_set_operations(PlannerInfo *root, double tuple_fraction,
					List **sortClauses)
{
	Query	   *parse = root->parse;
	SetOperationStmt *topop = (SetOperationStmt *) parse->setOperations;
	Node	   *node;
	Query	   *leftmostQuery;

	Assert(topop && IsA(topop, SetOperationStmt));

	/* check for unsupported stuff */
	Assert(parse->utilityStmt == NULL);
	Assert(parse->jointree->fromlist == NIL);
	Assert(parse->jointree->quals == NULL);
	Assert(parse->groupClause == NIL);
	Assert(parse->havingQual == NULL);
	Assert(parse->distinctClause == NIL);

	/*
	 * Find the leftmost component Query.  We need to use its column names for
	 * all generated tlists (else SELECT INTO won't work right).
	 */
	node = topop->larg;
	while (node && IsA(node, SetOperationStmt))
		node = ((SetOperationStmt *) node)->larg;
	Assert(node && IsA(node, RangeTblRef));
	leftmostQuery = rt_fetch(((RangeTblRef *) node)->rtindex,
							 parse->rtable)->subquery;
	Assert(leftmostQuery != NULL);

	/*
	 * Recurse on setOperations tree to generate plans for set ops. The final
	 * output plan should have just the column types shown as the output from
	 * the top-level node, plus possibly resjunk working columns (we can rely
	 * on upper-level nodes to deal with that).
	 */
	return recurse_set_operations((Node *) topop, root, tuple_fraction,
								  topop->colTypes, true, -1,
								  leftmostQuery->targetList,
								  sortClauses);
}
Exemple #3
0
/*
 * Pull up children of a UNION node that are identically-propertied UNIONs.
 *
 * NOTE: we can also pull a UNION ALL up into a UNION, since the distinct
 * output rows will be lost anyway.
 */
static List *
recurse_union_children(Node *setOp, Query *parse,
                       SetOperationStmt *top_union,
                       List *refnames_tlist)
{
    if (IsA(setOp, SetOperationStmt))
    {
        SetOperationStmt *op = (SetOperationStmt *) setOp;

        if (op->op == top_union->op &&
                (op->all == top_union->all || op->all) &&
                equalo(op->colTypes, top_union->colTypes))
        {
            /* Same UNION, so fold children into parent's subplan list */
            return nconc(recurse_union_children(op->larg, parse,
                                                top_union,
                                                refnames_tlist),
                         recurse_union_children(op->rarg, parse,
                                                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 makeList1(recurse_set_operations(setOp, parse,
                                            top_union->colTypes, false,
                                            -1, refnames_tlist));
}
Exemple #4
0
/*
 * Generate plan for an INTERSECT, INTERSECT ALL, EXCEPT, or EXCEPT ALL node
 */
static Plan *
generate_nonunion_plan(SetOperationStmt *op, Query *parse,
                       List *refnames_tlist)
{
    Plan	   *lplan,
               *rplan,
               *plan;
    List	   *tlist,
               *sortList,
               *planlist;
    SetOpCmd	cmd;

    /* Recurse on children, ensuring their outputs are marked */
    lplan = recurse_set_operations(op->larg, parse,
                                   op->colTypes, false, 0,
                                   refnames_tlist);
    rplan = recurse_set_operations(op->rarg, parse,
                                   op->colTypes, false, 1,
                                   refnames_tlist);
    planlist = makeList2(lplan, rplan);

    /*
     * Generate tlist for Append plan node.
     *
     * The tlist for an Append plan isn't important as far as the Append is
     * concerned, but we must make it look real anyway for the benefit of
     * the next plan level up.	In fact, it has to be real enough that the
     * flag column is shown as a variable not a constant, else setrefs.c
     * will get confused.
     */
    tlist = generate_append_tlist(op->colTypes, true,
                                  planlist, refnames_tlist);

    /*
     * Append the child results together.
     */
    plan = (Plan *) make_append(planlist, false, tlist);

    /*
     * Sort the child results, then add a SetOp plan node to generate the
     * correct output.
     */
    tlist = copyObject(tlist);
    sortList = addAllTargetsToSortList(NULL, NIL, tlist, false);
    plan = (Plan *) make_sort_from_sortclauses(parse, tlist, plan, sortList);
    switch (op->op)
    {
    case SETOP_INTERSECT:
        cmd = op->all ? SETOPCMD_INTERSECT_ALL : SETOPCMD_INTERSECT;
        break;
    case SETOP_EXCEPT:
        cmd = op->all ? SETOPCMD_EXCEPT_ALL : SETOPCMD_EXCEPT;
        break;
    default:
        elog(ERROR, "unrecognized set op: %d",
             (int) op->op);
        cmd = SETOPCMD_INTERSECT;	/* keep compiler quiet */
        break;
    }
    plan = (Plan *) make_setop(cmd, tlist, plan, sortList,
                               length(op->colTypes) + 1);
    return plan;
}