]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Do not do an early abort if a lateral subquery is an empty set.
authordrh <>
Sat, 20 Jul 2024 03:57:10 +0000 (03:57 +0000)
committerdrh <>
Sat, 20 Jul 2024 03:57:10 +0000 (03:57 +0000)
FossilOrigin-Name: 9e50d338ebd974b5d1b8baf4c7a400abded28ee997414e896c2e8b9d61d39a94

manifest
manifest.uuid
src/wherecode.c
test/joinL.test

index 55f65b4aaefc5b526d3bbf29ead42f34eb83d6d8..c0bcc98a1c766948090fcd646b59c329e0481f19 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C "LATERAL"\smay\snot\sbe\sa\skeyword,\sas\sthat\swould\scause\sproblems\sfor\slegacy\ndatabases\sthat\shave\stables\snamed\s"lateral".\s\sIt\shas\sto\sbe\sparsed\sas\san\nidentifier.
-D 2024-07-20T03:19:40.623
+C Do\snot\sdo\san\searly\sabort\sif\sa\slateral\ssubquery\sis\san\sempty\sset.
+D 2024-07-20T03:57:10.925
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -842,7 +842,7 @@ F src/wal.h ba252daaa94f889f4b2c17c027e823d9be47ce39da1d3799886bbd51f0490452
 F src/walker.c 7c7ea0115345851c3da4e04e2e239a29983b61fb5b038b94eede6aba462640e2
 F src/where.c e411ef973e520428a3cbbf8a348c7b37bc8f7f639ebdabd64f835861364be3ee
 F src/whereInt.h 002adc3aa2cc10733b9b27958fdbe893987cd989fab25a9853941c1f9b9b0a65
-F src/wherecode.c c9cac0b0b8e809c5e7e79d7796918907fb685ad99be2aaa9737f9787aa47349c
+F src/wherecode.c e6518bac30e75554996c50ffd4d6d01522de0beb54020446fd498dfc2111a193
 F src/whereexpr.c 7d0d34b42b9edfd8e8ca66beb3a6ef63fe211c001af54caf2ccbcd989b783290
 F src/window.c 1e40ffc509bae21e466f6106382d238e91eb73edd4ba10e66ca4fd7af2b96896
 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
@@ -1347,7 +1347,7 @@ F test/joinD.test 2ce62e7353a0702ca5e70008faf319c1d4686aa19fba34275c6d1da0e960be
 F test/joinE.test d5d182f3812771e2c0d97c9dcf5dbe4c41c8e21c82560e59358731c4a3981d6b
 F test/joinF.test 53dd66158806823ea680dd7543b5406af151b5aafa5cd06a7f3231cd94938127
 F test/joinH.test 55f69e64da74d4eca2235237f3acb657aef181e22e45daa228e35bba865e0255
-F test/joinL.test d18dc1f85a8254b260ef5d0e1d3695dca65309f20f04b79e0b1cb754fdef9334
+F test/joinL.test 63ce1df014bb648f99b24def9af6f67f8bec0017a87c5bd707a81d417c6213c2
 F test/journal1.test c7b768041b7f494471531e17abc2f4f5ebf9e5096984f43ed17c4eb80ba34497
 F test/journal2.test 9dac6b4ba0ca79c3b21446bbae993a462c2397c4
 F test/journal3.test 7c3cf23ffc77db06601c1fcfc9743de8441cb77db9d1aa931863d94f5ffa140e
@@ -2196,8 +2196,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 3c045a96bc65713e5544af82b8547cdcdf93fa20f9b7b0851d77f8c090cc650b
-R f090a6ec615c741ecafebf974126b522
+P 8217bddaf8c0697799c7518746996665bbd1f5a327315d18091a6ab3250f1112
+R 5c22f2a95f0481fb172addeee95820dd
 U drh
-Z ff3d32fb657fae9cfdb48424d2f017eb
+Z 86dc0c94580a9c5283fa406f79f2dc76
 # Remove this line to create a well-formed Fossil manifest.
index 5cd623bdcfcb00690f27c85f5047c74f45fb46a2..23a4e092036d11ea26ac3c885b622a108793a7bd 100644 (file)
@@ -1 +1 @@
-8217bddaf8c0697799c7518746996665bbd1f5a327315d18091a6ab3250f1112
+9e50d338ebd974b5d1b8baf4c7a400abded28ee997414e896c2e8b9d61d39a94
index 098af7375ee9f1c66cbeb5c98e8eba4421bc6272..83fce7e3bbb29833680dd3827cd272ab3d1a1b83 100644 (file)
@@ -1418,6 +1418,32 @@ static int whereLoopIsOneRow(WhereLoop *pLoop){
   return 0;
 }
 
+/*
+** Find an appropriate label for iLevel loop to jump to if it the table
+** for that loop is empty.
+**
+** For a simple query, we might as well jump to the break-address of the
+** outermost loop, halting the query, since if one of the joined tables
+** is empty, the result set will be empty.  But that does not work if
+** there are outer joins.  Nor does it work if the empty table is a
+** correlated subquery (with the LATERAL keyword).
+*/
+static SQLITE_NOINLINE int haltAddress(
+  WhereInfo *pWInfo,
+  int iLevel,
+  SrcItem *pTabItem
+){
+  if( pTabItem->fg.isLateral==0 ){
+    while( 1 /*exit-by-break*/ ){
+      if( pWInfo->a[iLevel].iLeftJoin ) break;
+      if( pWInfo->a[iLevel].pRJ ) break;
+      if( iLevel==0 ) break;
+      iLevel--;
+    };
+  }
+  return pWInfo->a[iLevel].addrBrk;
+}
+
 /*
 ** Generate code for the start of the iLevel-th loop in the WHERE clause
 ** implementation described by pWInfo.
@@ -1440,7 +1466,6 @@ Bitmask sqlite3WhereCodeOneLoopStart(
   sqlite3 *db;                    /* Database connection */
   SrcItem *pTabItem;              /* FROM clause term being coded */
   int addrBrk;                    /* Jump here to break out of the loop */
-  int addrHalt;                   /* addrBrk for the outermost loop */
   int addrCont;                   /* Jump here to continue with next cycle */
   int iRowidReg = 0;        /* Rowid is stored in this register, if not zero */
   int iReleaseReg = 0;      /* Temp register to free before returning */
@@ -1499,14 +1524,6 @@ Bitmask sqlite3WhereCodeOneLoopStart(
     VdbeComment((v, "init LEFT JOIN match flag"));
   }
 
-  /* Compute a safe address to jump to if we discover that the table for
-  ** this loop is empty and can never contribute content. */
-  for(j=iLevel; j>0; j--){
-    if( pWInfo->a[j].iLeftJoin ) break;
-    if( pWInfo->a[j].pRJ ) break;
-  }
-  addrHalt = pWInfo->a[j].addrBrk;
-
   /* Special case of a FROM clause subquery implemented as a co-routine */
   if( pTabItem->fg.viaCoroutine ){
     int regYield = pTabItem->regReturn;
@@ -1738,7 +1755,8 @@ Bitmask sqlite3WhereCodeOneLoopStart(
       VdbeCoverageIf(v, pX->op==TK_GE);
       sqlite3ReleaseTempReg(pParse, rTemp);
     }else{
-      sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iCur, addrHalt);
+      sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iCur, 
+                        haltAddress(pWInfo, iLevel, pTabItem));
       VdbeCoverageIf(v, bRev==0);
       VdbeCoverageIf(v, bRev!=0);
     }
@@ -2533,7 +2551,8 @@ Bitmask sqlite3WhereCodeOneLoopStart(
       codeCursorHint(pTabItem, pWInfo, pLevel, 0);
       pLevel->op = aStep[bRev];
       pLevel->p1 = iCur;
-      pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrHalt);
+      pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, 
+                               haltAddress(pWInfo, iLevel, pTabItem));
       VdbeCoverageIf(v, bRev==0);
       VdbeCoverageIf(v, bRev!=0);
       pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP;
index 508f2634c8df27226697fd1db9ae352e1f0c1a2f..7006bdfbce534ccd1ce2b55af68b567031fdf682 100644 (file)
@@ -141,7 +141,7 @@ do_execsql_test 2.1 {
   SELECT user_id, first_order_time, next_order_time, id FROM
     LATERAL (SELECT user_id, min(created_at) AS first_order_time
        FROM orders GROUP BY user_id) AS o1
-    LEFT JOIN LATERAL
+    JOIN LATERAL
     (SELECT id, created_at AS next_order_time
      FROM orders
      WHERE user_id = o1.user_id AND created_at > o1.first_order_time
@@ -149,7 +149,6 @@ do_execsql_test 2.1 {
     ON true;
 } {
   1   2024-07-20T01:35:03  2024-07-20T01:58:10 4
-  2   2024-07-20T01:35:07  NULL                NULL
   3   2024-07-20T01:35:10  2024-07-20T01:58:17 5
 }