I wrote:
>> In the regression test case at hand, the startup costs are all zero so
>> this change wouldn't improve the test case's stability.  So I'm thinking
>> that in addition, it would be a good idea for these functions to break
>> exact compare_path_costs ties in some arbitrary but deterministic way,
>> rather than letting qsort() have the final say on what happens.  If the
>> subplans were all simple relation scans we could order them by planner
>> relid, but I'm not sure what to do if they're not.

> Ah, I have an idea --- let's create a bms_compare() qsort comparator for
> bitmapsets, and use that on the paths' relid sets.  It hardly matters
> what the exact semantics of the comparator are, as long as it behaves
> sanely for equal sets.

Concretely, I propose the attached.  I spent a little bit of extra effort
on the comparator in hopes that it might be useful for something else.

                        regards, tom lane

diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index 733fe3c..edcd19a 100644
*** a/src/backend/nodes/bitmapset.c
--- b/src/backend/nodes/bitmapset.c
*************** bms_equal(const Bitmapset *a, const Bitm
*** 173,178 ****
--- 173,222 ----
  }
  
  /*
+  * bms_compare - qsort-style comparator for bitmapsets
+  *
+  * This guarantees to report values as equal iff bms_equal would say they are
+  * equal.  Otherwise, the highest-numbered bit that is set in one value but
+  * not the other determines the result.  (This rule means that, for example,
+  * {6} is greater than {5}, which seems plausible.)
+  */
+ int
+ bms_compare(const Bitmapset *a, const Bitmapset *b)
+ {
+ 	int			shortlen;
+ 	int			i;
+ 
+ 	/* Handle cases where either input is NULL */
+ 	if (a == NULL)
+ 		return bms_is_empty(b) ? 0 : -1;
+ 	else if (b == NULL)
+ 		return bms_is_empty(a) ? 0 : +1;
+ 	/* Handle cases where one input is longer than the other */
+ 	shortlen = Min(a->nwords, b->nwords);
+ 	for (i = shortlen; i < a->nwords; i++)
+ 	{
+ 		if (a->words[i] != 0)
+ 			return +1;
+ 	}
+ 	for (i = shortlen; i < b->nwords; i++)
+ 	{
+ 		if (b->words[i] != 0)
+ 			return -1;
+ 	}
+ 	/* Process words in common */
+ 	i = shortlen;
+ 	while (--i >= 0)
+ 	{
+ 		bitmapword	aw = a->words[i];
+ 		bitmapword	bw = b->words[i];
+ 
+ 		if (aw != bw)
+ 			return (aw > bw) ? +1 : -1;
+ 	}
+ 	return 0;
+ }
+ 
+ /*
   * bms_make_singleton - build a bitmapset containing a single member
   */
  Bitmapset *
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 7df8761..5c2b996 100644
*** a/src/backend/optimizer/util/pathnode.c
--- b/src/backend/optimizer/util/pathnode.c
*************** create_append_path(RelOptInfo *rel,
*** 1274,1311 ****
  
  /*
   * append_total_cost_compare
!  *	  list_qsort comparator for sorting append child paths by total_cost
   */
  static int
  append_total_cost_compare(const void *a, const void *b)
  {
  	Path	   *path1 = (Path *) lfirst(*(ListCell **) a);
  	Path	   *path2 = (Path *) lfirst(*(ListCell **) b);
  
! 	if (path1->total_cost > path2->total_cost)
! 		return -1;
! 	if (path1->total_cost < path2->total_cost)
! 		return 1;
! 
! 	return 0;
  }
  
  /*
   * append_startup_cost_compare
!  *	  list_qsort comparator for sorting append child paths by startup_cost
   */
  static int
  append_startup_cost_compare(const void *a, const void *b)
  {
  	Path	   *path1 = (Path *) lfirst(*(ListCell **) a);
  	Path	   *path2 = (Path *) lfirst(*(ListCell **) b);
  
! 	if (path1->startup_cost > path2->startup_cost)
! 		return -1;
! 	if (path1->startup_cost < path2->startup_cost)
! 		return 1;
! 
! 	return 0;
  }
  
  /*
--- 1274,1317 ----
  
  /*
   * append_total_cost_compare
!  *	  qsort comparator for sorting append child paths by total_cost descending
!  *
!  * For equal total costs, we fall back to comparing startup costs; if those
!  * are equal too, break ties using bms_compare on the paths' relids.
!  * (This is to avoid getting unpredictable results from qsort.)
   */
  static int
  append_total_cost_compare(const void *a, const void *b)
  {
  	Path	   *path1 = (Path *) lfirst(*(ListCell **) a);
  	Path	   *path2 = (Path *) lfirst(*(ListCell **) b);
+ 	int			cmp;
  
! 	cmp = compare_path_costs(path1, path2, TOTAL_COST);
! 	if (cmp == 0)
! 		cmp = bms_compare(path1->parent->relids, path2->parent->relids);
! 	return -cmp;
  }
  
  /*
   * append_startup_cost_compare
!  *	  qsort comparator for sorting append child paths by startup_cost descending
!  *
!  * For equal startup costs, we fall back to comparing total costs; if those
!  * are equal too, break ties using bms_compare on the paths' relids.
!  * (This is to avoid getting unpredictable results from qsort.)
   */
  static int
  append_startup_cost_compare(const void *a, const void *b)
  {
  	Path	   *path1 = (Path *) lfirst(*(ListCell **) a);
  	Path	   *path2 = (Path *) lfirst(*(ListCell **) b);
+ 	int			cmp;
  
! 	cmp = compare_path_costs(path1, path2, STARTUP_COST);
! 	if (cmp == 0)
! 		cmp = bms_compare(path1->parent->relids, path2->parent->relids);
! 	return -cmp;
  }
  
  /*
diff --git a/src/include/nodes/bitmapset.h b/src/include/nodes/bitmapset.h
index 15397e9..67e8920 100644
*** a/src/include/nodes/bitmapset.h
--- b/src/include/nodes/bitmapset.h
*************** typedef enum
*** 65,70 ****
--- 65,71 ----
  
  extern Bitmapset *bms_copy(const Bitmapset *a);
  extern bool bms_equal(const Bitmapset *a, const Bitmapset *b);
+ extern int	bms_compare(const Bitmapset *a, const Bitmapset *b);
  extern Bitmapset *bms_make_singleton(int x);
  extern void bms_free(Bitmapset *a);
  
diff --git a/src/test/regress/expected/select_parallel.out b/src/test/regress/expected/select_parallel.out
index 7824ca5..5f73be4 100644
*** a/src/test/regress/expected/select_parallel.out
--- b/src/test/regress/expected/select_parallel.out
*************** explain (costs off)
*** 21,32 ****
           Workers Planned: 3
           ->  Partial Aggregate
                 ->  Parallel Append
!                      ->  Parallel Seq Scan on a_star
!                      ->  Parallel Seq Scan on b_star
!                      ->  Parallel Seq Scan on c_star
                       ->  Parallel Seq Scan on d_star
                       ->  Parallel Seq Scan on e_star
!                      ->  Parallel Seq Scan on f_star
  (11 rows)
  
  select round(avg(aa)), sum(aa) from a_star a1;
--- 21,32 ----
           Workers Planned: 3
           ->  Partial Aggregate
                 ->  Parallel Append
!                      ->  Parallel Seq Scan on f_star
                       ->  Parallel Seq Scan on d_star
                       ->  Parallel Seq Scan on e_star
!                      ->  Parallel Seq Scan on c_star
!                      ->  Parallel Seq Scan on b_star
!                      ->  Parallel Seq Scan on a_star
  (11 rows)
  
  select round(avg(aa)), sum(aa) from a_star a1;
*************** explain (costs off)
*** 49,58 ****
                 ->  Parallel Append
                       ->  Seq Scan on d_star
                       ->  Seq Scan on c_star
-                      ->  Parallel Seq Scan on a_star
-                      ->  Parallel Seq Scan on b_star
-                      ->  Parallel Seq Scan on e_star
                       ->  Parallel Seq Scan on f_star
  (11 rows)
  
  select round(avg(aa)), sum(aa) from a_star a2;
--- 49,58 ----
                 ->  Parallel Append
                       ->  Seq Scan on d_star
                       ->  Seq Scan on c_star
                       ->  Parallel Seq Scan on f_star
+                      ->  Parallel Seq Scan on e_star
+                      ->  Parallel Seq Scan on b_star
+                      ->  Parallel Seq Scan on a_star
  (11 rows)
  
  select round(avg(aa)), sum(aa) from a_star a2;
*************** explain (costs off)
*** 75,85 ****
           Workers Planned: 3
           ->  Partial Aggregate
                 ->  Parallel Append
-                      ->  Seq Scan on d_star
                       ->  Seq Scan on f_star
                       ->  Seq Scan on e_star
-                      ->  Seq Scan on b_star
                       ->  Seq Scan on c_star
                       ->  Seq Scan on a_star
  (11 rows)
  
--- 75,85 ----
           Workers Planned: 3
           ->  Partial Aggregate
                 ->  Parallel Append
                       ->  Seq Scan on f_star
+                      ->  Seq Scan on d_star
                       ->  Seq Scan on e_star
                       ->  Seq Scan on c_star
+                      ->  Seq Scan on b_star
                       ->  Seq Scan on a_star
  (11 rows)
  

Reply via email to