]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix the LEFT JOIN strength reduction for IN operators in the WHERE clause.
authordrh <>
Thu, 1 Jun 2023 00:01:20 +0000 (00:01 +0000)
committerdrh <>
Thu, 1 Jun 2023 00:01:20 +0000 (00:01 +0000)
Further simplifications and refinement of the algorithm.

FossilOrigin-Name: 96c72dde79d4069f6c2f81467a35b617633f86f7a7dcafbda991affdaa1f8537

manifest
manifest.uuid
src/expr.c
test/autoindex4.test

index f6d261e0f5d1c7afd7c856c2134b36e49569ab04..1494db44d18058cc390c6aeabbae2d8f50976a37 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sa\scomment\sto\sexplain\swhy\sboth\ssides\sof\san\sAND\smust\sbe\snon-null-row\sin\sorder\nfor\sthe\soverall\sexpression\sto\sbe\snon-null-row.\s\sNo\scode\schanges.
-D 2023-05-31T18:52:46.557
+C Fix\sthe\sLEFT\sJOIN\sstrength\sreduction\sfor\sIN\soperators\sin\sthe\sWHERE\sclause.\nFurther\ssimplifications\sand\srefinement\sof\sthe\salgorithm.
+D 2023-06-01T00:01:20.249
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -587,7 +587,7 @@ F src/date.c cb71f73d5df5303ec6630971b9c8b8c7df466fa3233dbdc643d613806e53e554
 F src/dbpage.c f3eea5f7ec47e09ee7da40f42b25092ecbe961fc59566b8e5f705f34335b2387
 F src/dbstat.c ec92074baa61d883de58c945162d9e666c13cd7cf3a23bc38b4d1c4d0b2c2bef
 F src/delete.c 05e27e3a55dcfeadf2f7ca95a5c5e0928f182c04640ec1954ffa42f3d5c19341
-F src/expr.c 5671567f095756be0b1f84347965bbd0ce8e4d435255b5a3b6ada694a560ee1b
+F src/expr.c 892a0645ed2085ced238ff19eaca6440b2ce78bd6d062fdbf1caa2a578faa690
 F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
 F src/fkey.c 03c134cc8bffe54835f742ddea0b72ebfc8f6b32773d175c71b8afeea6cb5c83
 F src/func.c 03e6b501f3056d0ba398bda17df938b2b566aa0b3ca7e1942a3cd1925d04ec36
@@ -792,7 +792,7 @@ F test/autoinc.test 997d6f185f138229dc4251583a1d04816423dddc2fc034871a01aeb1d728
 F test/autoindex1.test d34caffb0384003ee28eae87679214c029e9be4b332d9649a79e0b94ab70502c
 F test/autoindex2.test 12ef578928102baaa0dc23ad397601a2f4ecb0df
 F test/autoindex3.test dcd6b2f8bed2be67b131e2e671f892e971d934e24fd00988952d0e0a67e24aa7
-F test/autoindex4.test 5df39313526b6f22a26bd119bbd97ca69f28386ab3c671fc10568d921c41eb08
+F test/autoindex4.test 3c2105e9172920e26f950ba3c5823e4972190e022c1e6f260ba476b0af24c593
 F test/autoindex5.test 2ee94f033b87ca0160e08d81034c507aff8e230df2627f0304fa309b2fee19a3
 F test/autovacuum.test 00671369bbf96c6a49989a9425f5b78b94075d6a4b031e5e00000c2c32f365df
 F test/autovacuum2.test 76f7eb4fe6a6bf6d33a196a7141dba98886d2fb53a268d7feca285d5da4759d7
@@ -2072,8 +2072,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P f544a8e47cdd5ad7233887a558489983f4f305a39391ff463c43e2e4157da087
-R cfe6e243041fd222123bb8065a03b511
+P 8396032ce14a75f408f0a75bcb36a6504d5188f20886e275746a2c336a74296f
+R 3b7dd10066b8cc3c88369c73d288e77c
 U drh
-Z abd461353f82d9e52ac72c2c080f514b
+Z 2eb94a55c7e32f21e9851a1c270b5eae
 # Remove this line to create a well-formed Fossil manifest.
index 44346c68217d0e40d65cff168a22ec3a203ad750..acdf525506732a3aaabc67d464c9e2437c466f2c 100644 (file)
@@ -1 +1 @@
-8396032ce14a75f408f0a75bcb36a6504d5188f20886e275746a2c336a74296f
\ No newline at end of file
+96c72dde79d4069f6c2f81467a35b617633f86f7a7dcafbda991affdaa1f8537
\ No newline at end of file
index c7abd161682aa844dd8b4a93bb7831ba379da082..ffe55f94680beb4cb60c74f6a917cadbcd648be3 100644 (file)
@@ -5991,6 +5991,20 @@ int sqlite3ExprImpliesExpr(
   return 0;
 }
 
+/* This is a helper functino to impliesNotNullRow().  In this routine,
+** set pWalker->eCode to one only if *both* of the input expressions
+** separately have the implies-not-null-row property.
+*/
+static void bothImplyNotNullRow(Walker *pWalker, Expr *pE1, Expr *pE2){
+  if( pWalker->eCode==0 ){
+    sqlite3WalkExpr(pWalker, pE1);
+    if( pWalker->eCode ){
+      pWalker->eCode = 0;
+      sqlite3WalkExpr(pWalker, pE2);
+    }
+  }
+}
+
 /*
 ** This is the Expr node callback for sqlite3ExprImpliesNonNullRow().
 ** If the expression node requires that the table at pWalker->iCur
@@ -6030,7 +6044,7 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
 
     case TK_OR:
     case TK_AND:
-      /* Both sides of an AND or OR must separately imply non-NULL row.
+      /* Both sides of an AND or OR must separately imply non-null-row.
       ** Consider these cases:
       **    1.  NOT (x AND y)
       **    2.  x OR y
@@ -6039,22 +6053,37 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
       */
       testcase( pExpr->op==TK_OR );
       testcase( pExpr->op==TK_AND );
-      if( pWalker->eCode==0 ){
-        sqlite3WalkExpr(pWalker, pExpr->pLeft);
-        if( pWalker->eCode ){
-          pWalker->eCode = 0;
-          sqlite3WalkExpr(pWalker, pExpr->pRight);
-        }
-      }
+      bothImplyNotNullRow(pWalker, pExpr->pLeft, pExpr->pRight);
       return WRC_Prune;
 
     case TK_CASE:
+      /* In "CASE x WHEN y THEN ..." the overall expression is non-null-row
+      ** if either x or y is non-null-row.  If the neither x nor y is
+      ** non-null-row, assume the whole expression is not, to be safe. */
+      assert( ExprUseXList(pExpr) );
+      assert( pExpr->x.pList->nExpr>0 );
+      sqlite3WalkExpr(pWalker, pExpr->pLeft);
+      sqlite3WalkExpr(pWalker, pExpr->x.pList->a[0].pExpr);
+      return WRC_Prune;
+        
     case TK_IN:
+      /* Beware of "x NOT IN ()" and "x NOT IN (SELECT 1 WHERE false)",
+      ** both of which can be true.  But apart from these cases, if
+      ** the left-hand side of the IN is NULL then the IN itself will be
+      ** NULL. */
+      if( ExprUseXList(pExpr) && pExpr->x.pList->nExpr>0 ){
+        sqlite3WalkExpr(pWalker, pExpr->pLeft);
+      }
+      return WRC_Prune;
+
     case TK_BETWEEN:
-      testcase( pExpr->op==TK_CASE );
-      testcase( pExpr->op==TK_IN );
-      testcase( pExpr->op==TK_BETWEEN );
+      /* In "x NOT BETWEEN y AND z" either x must be non-null-row or else
+      ** both y and z must be non-null row */
+      assert( ExprUseXList(pExpr) );
+      assert( pExpr->x.pList->nExpr==2 );
       sqlite3WalkExpr(pWalker, pExpr->pLeft);
+      bothImplyNotNullRow(pWalker, pExpr->x.pList->a[0].pExpr,
+                                   pExpr->x.pList->a[1].pExpr);
       return WRC_Prune;
 
     /* Virtual tables are allowed to use constraints like x=NULL.  So
index d9ab783e4226eddb45dea75ae471e1c89d6d2a8d..6af99f5e15622ce63b9da7d1bbb996ed85d1733f 100644 (file)
@@ -141,7 +141,7 @@ foreach {id data1 data2 jointype onclause whereclause answer} {
   {coalesce(y,4)==4}
   {3 4 3 4}
 
-  5
+  5.1
   VALUES(1,2),(3,4),(NULL,4)
   VALUES(1,2),(3,4)
   {LEFT JOIN}
@@ -149,6 +149,22 @@ foreach {id data1 data2 jointype onclause whereclause answer} {
   {y=4 OR y IS NULL}
   {3 4 3 4 {} 4 {} {}}
 
+  5.2
+  VALUES(1,2),(3,4),(NULL,4)
+  VALUES(1,2),(3,4)
+  {LEFT JOIN}
+  a=x
+  {y NOT IN ()}
+  {1 2 1 2 3 4 3 4 {} 4 {} {}}
+
+  5.3
+  VALUES(1,2),(3,4),(NULL,4)
+  VALUES(1,2),(3,4)
+  {LEFT JOIN}
+  a=x
+  {y NOT IN (SELECT 1 WHERE false)}
+  {1 2 1 2 3 4 3 4 {} 4 {} {}}
+
   6
   VALUES(1,2),(3,4)
   VALUES(1,2),(3,4),(NULL,4)
@@ -193,6 +209,12 @@ foreach {id data1 data2 jointype onclause whereclause answer} {
     db eval {PRAGMA automatic_index=OFF;}
     db eval $sql
   } $answer
+  do_test autoindex4-4.$id.3 {
+    db eval {PRAGMA automatic_index=ON;}
+    optimization_control db all 0
+    db eval $sql
+  } $answer
+  optimization_control db all 1
 }