Robert Haas <robertmh...@gmail.com> writes:
> Meanwhile, here is an updated patch.

I don't care for that patch too much: it seems a bit brute-force, and I'm
quite worried by the assumption that it's okay to destroy each child's
append_rel_list after processing the child.  That would fail if any of
the Vars/subexpressions in the lists get incorporated into the resulting
child Plan, which does not seem impossible.  (I think in many cases we'd
do a copyObject() when extracting an append_rel_list expression, but this
hardly seems guaranteed.)

I propose instead the attached patch, which operates by identifying which
of the append_rel_list entries actually contain subquery references, and
copying only those; the other ones are just linked into the child's
append_rel_list by reference, which is okay because they won't get
modified.

On my laptop, I get the following timings for your test queries from
unmodified HEAD (--cassert build):

# Q1: 41260.239 ms
# Q2: 45225.768 ms
# Q3: 43066.958 ms
# Q4: 193360.726 ms
# Q5: 40746.503 ms

and these with my patch:

# Q1: 1767.753 ms
# Q2: 3662.131 ms
# Q3: 814.293 ms
# Q4: 64468.914 ms
# Q5: 881.295 ms

which seems to be generally a better result.

> The extraordinarily planning time for query 4 is caused by a
> completely different problem: SearchCatCache eats up huge amounts of
> CPU; its callers are get_attavgwidth and get_typlen.  It's not clear
> to me why doubling the number of relations causes such an enormous
> explosion in calls to those functions; I would expect the number of
> calls to double, but presumably the actual increase is much more.

Actually, Q4 necessarily involves O(N^2) planning time, because for
each of N target relations you're considering a join to an N-member
inheritance tree.  A lot of those ultimately get thrown away by
constraint exclusion, but not before we've expended significant
cycles on them.  I do not think we are going to get much traction
on that --- even if we do something to knock off whatever the leading
term is, there'll still be more O(N^2) behavior right behind it.

                        regards, tom lane

diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 8afde2b7d5069707e346901f819bed888a2333ee..d7fee96ba01272efdf7231d5985ab688912bcf58 100644
*** a/src/backend/optimizer/plan/planner.c
--- b/src/backend/optimizer/plan/planner.c
*************** inheritance_planner(PlannerInfo *root)
*** 834,840 ****
  {
  	Query	   *parse = root->parse;
  	int			parentRTindex = parse->resultRelation;
! 	Bitmapset  *resultRTindexes = NULL;
  	int			nominalRelation = -1;
  	List	   *final_rtable = NIL;
  	int			save_rel_array_size = 0;
--- 834,842 ----
  {
  	Query	   *parse = root->parse;
  	int			parentRTindex = parse->resultRelation;
! 	Bitmapset  *resultRTindexes;
! 	Bitmapset  *subqueryRTindexes;
! 	Bitmapset  *modifiableARIindexes;
  	int			nominalRelation = -1;
  	List	   *final_rtable = NIL;
  	int			save_rel_array_size = 0;
*************** inheritance_planner(PlannerInfo *root)
*** 845,850 ****
--- 847,853 ----
  	List	   *returningLists = NIL;
  	List	   *rowMarks;
  	ListCell   *lc;
+ 	Index		rti;
  
  	Assert(parse->commandType != CMD_INSERT);
  
*************** inheritance_planner(PlannerInfo *root)
*** 867,874 ****
  	 * subqueries during planning, and so we must create copies of them too,
  	 * except where they are target relations, which will each only be used in
  	 * a single plan.
  	 */
! 	resultRTindexes = bms_add_member(resultRTindexes, parentRTindex);
  	foreach(lc, root->append_rel_list)
  	{
  		AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);
--- 870,879 ----
  	 * subqueries during planning, and so we must create copies of them too,
  	 * except where they are target relations, which will each only be used in
  	 * a single plan.
+ 	 *
+ 	 * To begin with, we'll need a bitmapset of the target relation relids.
  	 */
! 	resultRTindexes = bms_make_singleton(parentRTindex);
  	foreach(lc, root->append_rel_list)
  	{
  		AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);
*************** inheritance_planner(PlannerInfo *root)
*** 878,889 ****
  											 appinfo->child_relid);
  	}
  
  	foreach(lc, root->append_rel_list)
  	{
  		AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);
  		PlannerInfo subroot;
  		Plan	   *subplan;
! 		Index		rti;
  
  		/* append_rel_list contains all append rels; ignore others */
  		if (appinfo->parent_relid != parentRTindex)
--- 883,937 ----
  											 appinfo->child_relid);
  	}
  
+ 	/*
+ 	 * Now, generate a bitmapset of the relids of the subquery RTEs, including
+ 	 * security-barrier RTEs that will become subqueries, as just explained.
+ 	 */
+ 	subqueryRTindexes = NULL;
+ 	rti = 1;
+ 	foreach(lc, parse->rtable)
+ 	{
+ 		RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
+ 
+ 		if (rte->rtekind == RTE_SUBQUERY ||
+ 			(rte->securityQuals != NIL &&
+ 			 !bms_is_member(rti, resultRTindexes)))
+ 			subqueryRTindexes = bms_add_member(subqueryRTindexes, rti);
+ 		rti++;
+ 	}
+ 
+ 	/*
+ 	 * Next, we want to identify which AppendRelInfo items contain references
+ 	 * to any of the aforesaid subquery RTEs.  These items will need to be
+ 	 * copied and modified to adjust their subquery references; whereas the
+ 	 * other ones need not be touched.  It's worth being tense over this
+ 	 * because we can usually avoid processing most of the AppendRelInfo
+ 	 * items, thereby saving O(N^2) space and time when the target is a large
+ 	 * inheritance tree.  We can identify AppendRelInfo items by their
+ 	 * child_relid, since that should be unique within the list.
+ 	 */
+ 	modifiableARIindexes = NULL;
+ 	foreach(lc, root->append_rel_list)
+ 	{
+ 		AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);
+ 
+ 		if (bms_is_member(appinfo->parent_relid, subqueryRTindexes) ||
+ 			bms_is_member(appinfo->child_relid, subqueryRTindexes) ||
+ 			bms_overlap(pull_varnos((Node *) appinfo->translated_vars),
+ 						subqueryRTindexes))
+ 			modifiableARIindexes = bms_add_member(modifiableARIindexes,
+ 												  appinfo->child_relid);
+ 	}
+ 
+ 	/*
+ 	 * And now we can get on with generating a plan for each child table.
+ 	 */
  	foreach(lc, root->append_rel_list)
  	{
  		AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(lc);
  		PlannerInfo subroot;
  		Plan	   *subplan;
! 		ListCell   *lc2;
  
  		/* append_rel_list contains all append rels; ignore others */
  		if (appinfo->parent_relid != parentRTindex)
*************** inheritance_planner(PlannerInfo *root)
*** 917,925 ****
  		/*
  		 * The append_rel_list likewise might contain references to subquery
  		 * RTEs (if any subqueries were flattenable UNION ALLs).  So prepare
! 		 * to apply ChangeVarNodes to that, too.
  		 */
! 		subroot.append_rel_list = (List *) copyObject(root->append_rel_list);
  
  		/*
  		 * Add placeholders to the child Query's rangetable list to fill the
--- 965,985 ----
  		/*
  		 * The append_rel_list likewise might contain references to subquery
  		 * RTEs (if any subqueries were flattenable UNION ALLs).  So prepare
! 		 * to apply ChangeVarNodes to that, too.  As explained above, we only
! 		 * want to copy items that actually contain such references; the
! 		 * rest can just get linked into the subroot's append_rel_list.
  		 */
! 		subroot.append_rel_list = NIL;
! 		foreach(lc2, root->append_rel_list)
! 		{
! 			AppendRelInfo *appinfo2 = (AppendRelInfo *) lfirst(lc2);
! 
! 			if (bms_is_member(appinfo2->child_relid, modifiableARIindexes))
! 				appinfo2 = (AppendRelInfo *) copyObject(appinfo2);
! 
! 			subroot.append_rel_list = lappend(subroot.append_rel_list,
! 											  appinfo2);
! 		}
  
  		/*
  		 * Add placeholders to the child Query's rangetable list to fill the
*************** inheritance_planner(PlannerInfo *root)
*** 933,945 ****
  
  		/*
  		 * If this isn't the first child Query, generate duplicates of all
! 		 * subquery RTEs, and adjust Var numbering to reference the
! 		 * duplicates. To simplify the loop logic, we scan the original rtable
! 		 * not the copy just made by adjust_appendrel_attrs; that should be OK
! 		 * since subquery RTEs couldn't contain any references to the target
! 		 * rel.
  		 */
! 		if (final_rtable != NIL)
  		{
  			ListCell   *lr;
  
--- 993,1005 ----
  
  		/*
  		 * If this isn't the first child Query, generate duplicates of all
! 		 * subquery (or subquery-to-be) RTEs, and adjust Var numbering to
! 		 * reference the duplicates.  To simplify the loop logic, we scan the
! 		 * original rtable not the copy just made by adjust_appendrel_attrs;
! 		 * that should be OK since subquery RTEs couldn't contain any
! 		 * references to the target rel.
  		 */
! 		if (final_rtable != NIL && !bms_is_empty(subqueryRTindexes))
  		{
  			ListCell   *lr;
  
*************** inheritance_planner(PlannerInfo *root)
*** 948,961 ****
  			{
  				RangeTblEntry *rte = (RangeTblEntry *) lfirst(lr);
  
! 				/*
! 				 * Copy subquery RTEs and RTEs with security barrier quals
! 				 * that will be turned into subqueries, except those that are
! 				 * target relations.
! 				 */
! 				if (rte->rtekind == RTE_SUBQUERY ||
! 					(rte->securityQuals != NIL &&
! 					 !bms_is_member(rti, resultRTindexes)))
  				{
  					Index		newrti;
  
--- 1008,1014 ----
  			{
  				RangeTblEntry *rte = (RangeTblEntry *) lfirst(lr);
  
! 				if (bms_is_member(rti, subqueryRTindexes))
  				{
  					Index		newrti;
  
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to