>>>>> "Tom" == Tom Lane <t...@sss.pgh.pa.us> writes:

 >> I've experimented with the attached patch, which detects when this
 >> situation might have occurred and does another pass to try and
 >> build ordered scans without the SAOP condition. However, the
 >> results may not be quite ideal, because at least in some test
 >> queries (not all) the scan with the SAOP included in the
 >> indexquals is being costed higher than the same scan with the SAOP
 >> moved to a Filter, which seems unreasonable.

 Tom> I'm not convinced that that's a-priori unreasonable, since the
 Tom> SAOP will result in multiple index scans under the hood.
 Tom> Conceivably we ought to try the path with and with SAOPs all the
 Tom> time.

OK, here's a patch that always retries on lower SAOP clauses, assuming
that a SAOP in the first column is always applicable - or is even that
assumption too strong?

-- 
Andrew (irc:RhodiumToad)

diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 42dcb11..cfcfbfc 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -50,7 +50,8 @@ typedef enum
 {
 	SAOP_PER_AM,				/* Use ScalarArrayOpExpr if amsearcharray */
 	SAOP_ALLOW,					/* Use ScalarArrayOpExpr for all indexes */
-	SAOP_REQUIRE				/* Require ScalarArrayOpExpr to be used */
+	SAOP_REQUIRE,				/* Require ScalarArrayOpExpr to be used */
+	SAOP_SKIP_LOWER				/* Require lower ScalarArrayOpExpr to be eliminated */
 } SaOpControl;
 
 /* Whether we are looking for plain indexscan, bitmap scan, or either */
@@ -118,7 +119,8 @@ static void get_index_paths(PlannerInfo *root, RelOptInfo *rel,
 static List *build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 				  IndexOptInfo *index, IndexClauseSet *clauses,
 				  bool useful_predicate,
-				  SaOpControl saop_control, ScanTypeControl scantype);
+				  SaOpControl saop_control, ScanTypeControl scantype,
+				  bool *saop_retry);
 static List *build_paths_for_OR(PlannerInfo *root, RelOptInfo *rel,
 				   List *clauses, List *other_clauses);
 static List *generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel,
@@ -734,6 +736,7 @@ get_index_paths(PlannerInfo *root, RelOptInfo *rel,
 {
 	List	   *indexpaths;
 	ListCell   *lc;
+	bool       saop_retry = false;
 
 	/*
 	 * Build simple index paths using the clauses.  Allow ScalarArrayOpExpr
@@ -742,7 +745,23 @@ get_index_paths(PlannerInfo *root, RelOptInfo *rel,
 	indexpaths = build_index_paths(root, rel,
 								   index, clauses,
 								   index->predOK,
-								   SAOP_PER_AM, ST_ANYSCAN);
+								   SAOP_PER_AM, ST_ANYSCAN, &saop_retry);
+
+	/*
+	 * If we allowed any ScalarArrayOpExprs on an index with a useful sort
+	 * ordering, then try again without them, since otherwise we miss important
+	 * paths where the index ordering is relevant.
+	 */
+	if (saop_retry)
+	{
+		indexpaths = list_concat(indexpaths,
+								 build_index_paths(root, rel,
+												   index, clauses,
+												   index->predOK,
+												   SAOP_SKIP_LOWER,
+												   ST_ANYSCAN,
+												   NULL));
+	}
 
 	/*
 	 * Submit all the ones that can form plain IndexScan plans to add_path. (A
@@ -779,7 +798,7 @@ get_index_paths(PlannerInfo *root, RelOptInfo *rel,
 		indexpaths = build_index_paths(root, rel,
 									   index, clauses,
 									   false,
-									   SAOP_REQUIRE, ST_BITMAPSCAN);
+									   SAOP_REQUIRE, ST_BITMAPSCAN, NULL);
 		*bitindexpaths = list_concat(*bitindexpaths, indexpaths);
 	}
 }
@@ -816,12 +835,14 @@ get_index_paths(PlannerInfo *root, RelOptInfo *rel,
  * 'useful_predicate' indicates whether the index has a useful predicate
  * 'saop_control' indicates whether ScalarArrayOpExpr clauses can be used
  * 'scantype' indicates whether we need plain or bitmap scan support
+ * 'saop_retry' indicates whether a SAOP_SKIP_LOWER retry is worthwhile
  */
 static List *
 build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 				  IndexOptInfo *index, IndexClauseSet *clauses,
 				  bool useful_predicate,
-				  SaOpControl saop_control, ScanTypeControl scantype)
+				  SaOpControl saop_control, ScanTypeControl scantype,
+				  bool *saop_retry)
 {
 	List	   *result = NIL;
 	IndexPath  *ipath;
@@ -877,7 +898,9 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 	 * assuming that the scan result is ordered.  (Actually, the result is
 	 * still ordered if there are equality constraints for all earlier
 	 * columns, but it seems too expensive and non-modular for this code to be
-	 * aware of that refinement.)
+	 * aware of that refinement.) But if saop_control is SAOP_SKIP_LOWER, we
+	 * skip exactly these clauses that break sorting, and don't bother
+	 * building any paths otherwise.
 	 *
 	 * We also build a Relids set showing which outer rels are required by the
 	 * selected clauses.  Any lateral_relids are included in that, but not
@@ -901,9 +924,13 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 				/* Ignore if not supported by index */
 				if (saop_control == SAOP_PER_AM && !index->amsearcharray)
 					continue;
-				found_clause = true;
 				if (indexcol > 0)
+				{
 					found_lower_saop_clause = true;
+					if (saop_control == SAOP_SKIP_LOWER)
+						continue;
+				}
+				found_clause = true;
 			}
 			else
 			{
@@ -928,6 +955,29 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
 			return NIL;
 	}
 
+	if (saop_control == SAOP_SKIP_LOWER)
+	{
+		if (!found_lower_saop_clause)
+			return NIL;
+		found_lower_saop_clause = false;
+	}
+	else if (found_lower_saop_clause)
+	{
+		/*
+		 * If we have a lower SAOP clause, we should leave it up to the cost
+		 * estimates to decide whether to use it in the scan or punt it to a
+		 * filter clause, rather than try and second-guess the AM's cost
+		 * estimate mechanism.  In addition, we need to consider the path
+		 * without the SAOP on the basis that it might give us an ordering
+		 * which overcomes any cost advantage of using the SAOP as an index
+		 * qual.  So either way, we flag it up so that the caller can do
+		 * another pass over the same index with SAOP_SKIP_LOWER to catch
+		 * such cases.
+		 */
+		if (saop_retry)
+			*saop_retry = true;
+	}
+
 	/* We do not want the index's rel itself listed in outer_relids */
 	outer_relids = bms_del_member(outer_relids, rel->relid);
 	/* Enforce convention that outer_relids is exactly NULL if empty */
@@ -1137,7 +1187,7 @@ build_paths_for_OR(PlannerInfo *root, RelOptInfo *rel,
 		indexpaths = build_index_paths(root, rel,
 									   index, &clauseset,
 									   useful_predicate,
-									   SAOP_ALLOW, ST_BITMAPSCAN);
+									   SAOP_ALLOW, ST_BITMAPSCAN, NULL);
 		result = list_concat(result, indexpaths);
 	}
 
-- 
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