-pull_up_subqueries(Query *parse, Node *jtnode, bool below_outer_join)
-{
- if (jtnode == NULL)
- return NULL;
- if (IsA(jtnode, RangeTblRef))
- {
- int varno = ((RangeTblRef *) jtnode)->rtindex;
- RangeTblEntry *rte = rt_fetch(varno, parse->rtable);
- Query *subquery = rte->subquery;
-
- /*
- * Is this a subquery RTE, and if so, is the subquery simple
- * enough to pull up? (If not, do nothing at this node.)
- *
- * If we are inside an outer join, only pull up subqueries whose
- * targetlists are nullable --- otherwise substituting their tlist
- * entries for upper Var references would do the wrong thing (the
- * results wouldn't become NULL when they're supposed to). XXX
- * This could be improved by generating pseudo-variables for such
- * expressions; we'd have to figure out how to get the pseudo-
- * variables evaluated at the right place in the modified plan
- * tree. Fix it someday.
- *
- * Note: even if the subquery itself is simple enough, we can't pull
- * it up if there is a reference to its whole tuple result.
- * Perhaps a pseudo-variable is the answer here too.
- */
- if (rte->rtekind == RTE_SUBQUERY && is_simple_subquery(subquery) &&
- (!below_outer_join || has_nullable_targetlist(subquery)) &&
- !contain_whole_tuple_var((Node *) parse, varno, 0))
- {
- int rtoffset;
- List *subtlist;
- List *rt;
-
- /*
- * First, recursively pull up the subquery's subqueries, so
- * that this routine's processing is complete for its jointree
- * and rangetable. NB: if the same subquery is referenced
- * from multiple jointree items (which can't happen normally,
- * but might after rule rewriting), then we will invoke this
- * processing multiple times on that subquery. OK because
- * nothing will happen after the first time. We do have to be
- * careful to copy everything we pull up, however, or risk
- * having chunks of structure multiply linked.
- *
- * Note: 'false' is correct here even if we are within an outer
- * join in the upper query; the lower query starts with a clean
- * slate for outer-join semantics.
- */
- subquery->jointree = (FromExpr *)
- pull_up_subqueries(subquery, (Node *) subquery->jointree,
- false);
-
- /*
- * Now make a modifiable copy of the subquery that we can run
- * OffsetVarNodes and IncrementVarSublevelsUp on.
- */
- subquery = copyObject(subquery);
-
- /*
- * Adjust level-0 varnos in subquery so that we can append its
- * rangetable to upper query's.
- */
- rtoffset = length(parse->rtable);
- OffsetVarNodes((Node *) subquery, rtoffset, 0);
-
- /*
- * Upper-level vars in subquery are now one level closer to their
- * parent than before.
- */
- IncrementVarSublevelsUp((Node *) subquery, -1, 1);
-
- /*
- * Replace all of the top query's references to the subquery's
- * outputs with copies of the adjusted subtlist items, being
- * careful not to replace any of the jointree structure.
- * (This'd be a lot cleaner if we could use
- * query_tree_mutator.)
- */
- subtlist = subquery->targetList;
- parse->targetList = (List *)
- ResolveNew((Node *) parse->targetList,
- varno, 0, subtlist, CMD_SELECT, 0);
- resolvenew_in_jointree((Node *) parse->jointree, varno, subtlist);
- Assert(parse->setOperations == NULL);
- parse->havingQual =
- ResolveNew(parse->havingQual,
- varno, 0, subtlist, CMD_SELECT, 0);
-
- foreach(rt, parse->rtable)
- {
- RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
-
- if (rte->rtekind == RTE_JOIN)
- rte->joinaliasvars = (List *)
- ResolveNew((Node *) rte->joinaliasvars,
- varno, 0, subtlist, CMD_SELECT, 0);
- }
-
- /*
- * Now append the adjusted rtable entries to upper query. (We
- * hold off until after fixing the upper rtable entries; no
- * point in running that code on the subquery ones too.)
- */
- parse->rtable = nconc(parse->rtable, subquery->rtable);
-
- /*
- * Pull up any FOR UPDATE markers, too. (OffsetVarNodes
- * already adjusted the marker values, so just nconc the
- * list.)
- */
- parse->rowMarks = nconc(parse->rowMarks, subquery->rowMarks);
-
- /*
- * Miscellaneous housekeeping.
- */
- parse->hasSubLinks |= subquery->hasSubLinks;
- /* subquery won't be pulled up if it hasAggs, so no work there */
-
- /*
- * Return the adjusted subquery jointree to replace the
- * RangeTblRef entry in my jointree.
- */
- return (Node *) subquery->jointree;
- }
- }
- else if (IsA(jtnode, FromExpr))
- {
- FromExpr *f = (FromExpr *) jtnode;
- List *l;
-
- foreach(l, f->fromlist)
- lfirst(l) = pull_up_subqueries(parse, lfirst(l),
- below_outer_join);
- }
- else if (IsA(jtnode, JoinExpr))
- {
- JoinExpr *j = (JoinExpr *) jtnode;
-
- /* Recurse, being careful to tell myself when inside outer join */
- switch (j->jointype)
- {
- case JOIN_INNER:
- j->larg = pull_up_subqueries(parse, j->larg,
- below_outer_join);
- j->rarg = pull_up_subqueries(parse, j->rarg,
- below_outer_join);
- break;
- case JOIN_LEFT:
- j->larg = pull_up_subqueries(parse, j->larg,
- below_outer_join);
- j->rarg = pull_up_subqueries(parse, j->rarg,
- true);
- break;
- case JOIN_FULL:
- j->larg = pull_up_subqueries(parse, j->larg,
- true);
- j->rarg = pull_up_subqueries(parse, j->rarg,
- true);
- break;
- case JOIN_RIGHT:
- j->larg = pull_up_subqueries(parse, j->larg,
- true);
- j->rarg = pull_up_subqueries(parse, j->rarg,
- below_outer_join);
- break;
- case JOIN_UNION:
-
- /*
- * This is where we fail if upper levels of planner
- * haven't rewritten UNION JOIN as an Append ...
- */
- elog(ERROR, "UNION JOIN is not implemented yet");
- break;
- default:
- elog(ERROR, "pull_up_subqueries: unexpected join type %d",
- j->jointype);
- break;
- }
- }
- else
- elog(ERROR, "pull_up_subqueries: unexpected node type %d",
- nodeTag(jtnode));
- return jtnode;
-}
-
-/*
- * is_simple_subquery
- * Check a subquery in the range table to see if it's simple enough
- * to pull up into the parent query.
- */
-static bool
-is_simple_subquery(Query *subquery)