+ bms_free(tmpset);
+
+ /*
+ * Assign a param to represent the query output. We only really
+ * care about reserving a parameter ID number.
+ */
+ prm = generate_new_param(root, INTERNALOID, -1);
+ splan->setParam = list_make1_int(prm->paramid);
+
+ /*
+ * Add the subplan and its rtable to the global lists.
+ */
+ root->glob->subplans = lappend(root->glob->subplans, plan);
+ root->glob->subrtables = lappend(root->glob->subrtables,
+ subroot->parse->rtable);
+ splan->plan_id = list_length(root->glob->subplans);
+
+ root->init_plans = lappend(root->init_plans, splan);
+
+ root->cte_plan_ids = lappend_int(root->cte_plan_ids, splan->plan_id);
+
+ /* Lastly, fill in the cost estimates for use later */
+ cost_subplan(root, splan, plan);
+ }
+}
+
+/*
+ * convert_ANY_sublink_to_join: can we convert an ANY SubLink to a join?
+ *
+ * The caller has found an ANY SubLink at the top level of one of the query's
+ * qual clauses, but has not checked the properties of the SubLink further.
+ * Decide whether it is appropriate to process this SubLink in join style.
+ * Return TRUE if so, FALSE if the SubLink cannot be converted.
+ *
+ * The only non-obvious input parameter is available_rels: this is the set
+ * of query rels that can safely be referenced in the sublink expression.
+ * (We must restrict this to avoid changing the semantics when a sublink
+ * is present in an outer join's ON qual.) The conversion must fail if
+ * the converted qual would reference any but these parent-query relids.
+ *
+ * On success, two output parameters are returned:
+ * *new_qual is set to the qual tree that should replace the SubLink in
+ * the parent query's qual tree. The qual clauses are wrapped in a
+ * FlattenedSubLink node to help later processing place them properly.
+ * *fromlist is set to a list of pulled-up jointree item(s) that must be
+ * added at the proper spot in the parent query's jointree.
+ *
+ * Side effects of a successful conversion include adding the SubLink's
+ * subselect to the query's rangetable.
+ */
+bool
+convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink,
+ Relids available_rels,
+ Node **new_qual, List **fromlist)
+{
+ Query *parse = root->parse;
+ Query *subselect = (Query *) sublink->subselect;
+ Relids left_varnos;
+ int rtindex;
+ RangeTblEntry *rte;
+ RangeTblRef *rtr;
+ List *subquery_vars;
+ Expr *quals;
+ FlattenedSubLink *fslink;
+
+ Assert(sublink->subLinkType == ANY_SUBLINK);
+
+ /*
+ * The sub-select must not refer to any Vars of the parent query. (Vars of
+ * higher levels should be okay, though.)
+ */
+ if (contain_vars_of_level((Node *) subselect, 1))
+ return false;
+
+ /*
+ * The test expression must contain some Vars of the current query,
+ * else it's not gonna be a join. (Note that it won't have Vars
+ * referring to the subquery, rather Params.)
+ */
+ left_varnos = pull_varnos(sublink->testexpr);
+ if (bms_is_empty(left_varnos))
+ return false;
+
+ /*
+ * However, it can't refer to anything outside available_rels.
+ */
+ if (!bms_is_subset(left_varnos, available_rels))
+ return false;
+
+ /*
+ * The combining operators and left-hand expressions mustn't be volatile.
+ */
+ if (contain_volatile_functions(sublink->testexpr))
+ return false;
+
+ /*
+ * Okay, pull up the sub-select into upper range table.
+ *
+ * We rely here on the assumption that the outer query has no references
+ * to the inner (necessarily true, other than the Vars that we build
+ * below). Therefore this is a lot easier than what pull_up_subqueries has
+ * to go through.
+ */
+ rte = addRangeTableEntryForSubquery(NULL,
+ subselect,
+ makeAlias("ANY_subquery", NIL),
+ false);
+ parse->rtable = lappend(parse->rtable, rte);
+ rtindex = list_length(parse->rtable);
+
+ /*
+ * Form a RangeTblRef for the pulled-up sub-select. This must be added
+ * to the upper jointree, but it is caller's responsibility to figure
+ * out where.
+ */
+ rtr = makeNode(RangeTblRef);
+ rtr->rtindex = rtindex;
+ *fromlist = list_make1(rtr);
+
+ /*
+ * Build a list of Vars representing the subselect outputs.
+ */
+ subquery_vars = generate_subquery_vars(root,
+ subselect->targetList,
+ rtindex);
+
+ /*
+ * Build the replacement qual expression, replacing Params with these Vars.
+ */
+ quals = (Expr *) convert_testexpr(root,
+ sublink->testexpr,
+ subquery_vars);
+
+ /*
+ * And finally, build the FlattenedSubLink node.
+ *
+ * Note: at this point left_varnos may well contain join relids, since
+ * the testexpr hasn't been run through flatten_join_alias_vars. This
+ * will get fixed when flatten_join_alias_vars is run.
+ */
+ fslink = makeNode(FlattenedSubLink);
+ fslink->jointype = JOIN_SEMI;
+ fslink->lefthand = left_varnos;
+ fslink->righthand = bms_make_singleton(rtindex);
+ fslink->quals = quals;
+
+ *new_qual = (Node *) fslink;
+
+ return true;
+}
+
+/*
+ * convert_EXISTS_sublink_to_join: can we convert an EXISTS SubLink to a join?
+ *
+ * The API of this function is identical to convert_ANY_sublink_to_join's,
+ * except that we also support the case where the caller has found NOT EXISTS,
+ * so we need an additional input parameter "under_not".
+ */
+bool
+convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink,
+ bool under_not,
+ Relids available_rels,
+ Node **new_qual, List **fromlist)
+{
+ Query *parse = root->parse;
+ Query *subselect = (Query *) sublink->subselect;
+ Node *whereClause;
+ int rtoffset;
+ int varno;
+ Relids clause_varnos;
+ Relids left_varnos;
+ Relids right_varnos;
+ Relids subselect_varnos;
+ FlattenedSubLink *fslink;
+
+ Assert(sublink->subLinkType == EXISTS_SUBLINK);
+
+ /*
+ * Copy the subquery so we can modify it safely (see comments in
+ * make_subplan).
+ */
+ subselect = (Query *) copyObject(subselect);
+
+ /*
+ * See if the subquery can be simplified based on the knowledge that
+ * it's being used in EXISTS(). If we aren't able to get rid of its
+ * targetlist, we have to fail, because the pullup operation leaves
+ * us with noplace to evaluate the targetlist.
+ */
+ if (!simplify_EXISTS_query(subselect))
+ return false;
+
+ /*
+ * The subquery must have a nonempty jointree, else we won't have a join.
+ */
+ if (subselect->jointree->fromlist == NIL)
+ return false;
+
+ /*
+ * Separate out the WHERE clause. (We could theoretically also remove
+ * top-level plain JOIN/ON clauses, but it's probably not worth the
+ * trouble.)
+ */
+ whereClause = subselect->jointree->quals;
+ subselect->jointree->quals = NULL;
+
+ /*
+ * The rest of the sub-select must not refer to any Vars of the parent
+ * query. (Vars of higher levels should be okay, though.)
+ */
+ if (contain_vars_of_level((Node *) subselect, 1))
+ return false;