Hi list,

This patch enables a simple optimization in
eval_const_expressions_mutator. If we know that one argument to
DistinctExpr is NULL then we can optimize it to a NullTest, which can
be an indexable expression.

For example the query:
EXPLAIN (costs off) SELECT * FROM foo WHERE j IS NOT DISTINCT FROM NULL;

Old behavior:
Seq Scan on foo
  Filter: (NOT (j IS DISTINCT FROM NULL::integer))

New behavior:
Index Scan using foo_j_idx on foo
  Index Cond: (j IS NULL)

Regards,
Marti
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index cd3da46..d9568ca 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -2436,7 +2436,7 @@ eval_const_expressions_mutator(Node *node,
 				ListCell   *arg;
 				bool		has_null_input = false;
 				bool		all_null_input = true;
-				bool		has_nonconst_input = false;
+				Expr	   *nonconst_expr = NULL;
 				Expr	   *simple;
 				DistinctExpr *newexpr;
 
@@ -2463,11 +2463,11 @@ eval_const_expressions_mutator(Node *node,
 						all_null_input &= ((Const *) lfirst(arg))->constisnull;
 					}
 					else
-						has_nonconst_input = true;
+						nonconst_expr = lfirst(arg);
 				}
 
 				/* all constants? then can optimize this out */
-				if (!has_nonconst_input)
+				if (nonconst_expr == NULL)
 				{
 					/* all nulls? then not distinct */
 					if (all_null_input)
@@ -2512,6 +2512,24 @@ eval_const_expressions_mutator(Node *node,
 						return (Node *) csimple;
 					}
 				}
+				else if (has_null_input)
+				{
+					/*
+					 * We can optimize: (expr) IS DISTINCT FROM NULL
+					 * into: (expr) IS NOT NULL
+					 */
+					NullTest   *newntest;
+
+					newntest = makeNode(NullTest);
+					newntest->nulltesttype = IS_NOT_NULL;
+					newntest->arg = (Expr *) nonconst_expr;
+
+					/* make_row_distinct_op already flattens row comparisons */
+					Assert(! type_is_rowtype(exprType((Node *) nonconst_expr)));
+					newntest->argisrow = false;
+
+					return (Node *) newntest;
+				}
 
 				/*
 				 * The expression cannot be simplified any further, so build
diff --git a/src/test/regress/expected/select_distinct.out b/src/test/regress/expected/select_distinct.out
index 38107a0..e8ddc49 100644
--- a/src/test/regress/expected/select_distinct.out
+++ b/src/test/regress/expected/select_distinct.out
@@ -195,6 +195,13 @@ SELECT null IS DISTINCT FROM null as "no";
  f
 (1 row)
 
+EXPLAIN (costs off) SELECT * FROM disttable WHERE f1 IS DISTINCT FROM NULL;
+         QUERY PLAN         
+----------------------------
+ Seq Scan on disttable
+   Filter: (f1 IS NOT NULL)
+(2 rows)
+
 -- negated form
 SELECT 1 IS NOT DISTINCT FROM 2 as "no";
  no 
@@ -220,3 +227,10 @@ SELECT null IS NOT DISTINCT FROM null as "yes";
  t
 (1 row)
 
+EXPLAIN (costs off) SELECT * FROM disttable WHERE f1 IS NOT DISTINCT FROM NULL;
+       QUERY PLAN       
+------------------------
+ Seq Scan on disttable
+   Filter: (f1 IS NULL)
+(2 rows)
+
diff --git a/src/test/regress/sql/select_distinct.sql b/src/test/regress/sql/select_distinct.sql
index 328ba51..9767eed 100644
--- a/src/test/regress/sql/select_distinct.sql
+++ b/src/test/regress/sql/select_distinct.sql
@@ -56,9 +56,11 @@ SELECT 1 IS DISTINCT FROM 2 as "yes";
 SELECT 2 IS DISTINCT FROM 2 as "no";
 SELECT 2 IS DISTINCT FROM null as "yes";
 SELECT null IS DISTINCT FROM null as "no";
+EXPLAIN (costs off) SELECT * FROM disttable WHERE f1 IS DISTINCT FROM NULL;
 
 -- negated form
 SELECT 1 IS NOT DISTINCT FROM 2 as "no";
 SELECT 2 IS NOT DISTINCT FROM 2 as "yes";
 SELECT 2 IS NOT DISTINCT FROM null as "no";
 SELECT null IS NOT DISTINCT FROM null as "yes";
+EXPLAIN (costs off) SELECT * FROM disttable WHERE f1 IS NOT DISTINCT FROM NULL;
-- 
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