]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add support for LIMIT and OFFSET in a recursive query. cte-via-queue
authordrh <drh@noemail.net>
Wed, 22 Jan 2014 18:07:04 +0000 (18:07 +0000)
committerdrh <drh@noemail.net>
Wed, 22 Jan 2014 18:07:04 +0000 (18:07 +0000)
FossilOrigin-Name: 1945484e6b9769c1943f750f5b09860417fb190a

manifest
manifest.uuid
src/select.c
test/with1.test

index 896f5cf947857440cdea98d5f3ea9a147faeb262..aa1a8aadb571daa780aa3e82ddca932da75bd36e 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Get\sORDER\sBY\sworking\sfor\srecursive\squeries.
-D 2014-01-22T17:28:35.279
+C Add\ssupport\sfor\sLIMIT\sand\sOFFSET\sin\sa\srecursive\squery.
+D 2014-01-22T18:07:04.473
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 2ef13430cd359f7b361bb863504e227b25cc7f81
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -219,7 +219,7 @@ F src/printf.c 85d07756e45d7496d19439dcae3e6e9e0090f269
 F src/random.c d10c1f85b6709ca97278428fd5db5bbb9c74eece
 F src/resolve.c 7eda9097b29fcf3d2b42fdc17d1de672134e09b6
 F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0
-F src/select.c b5430b99c0339dcfe9d06a4c251548e650e386e0
+F src/select.c f6d84f3a109d3e43d38089da6a4f131a5ce4c6ef
 F src/shell.c 24722d24d4ea8ca93db35e44db7308de786767ca
 F src/sqlite.h.in eed7f7d66a60daaa7b4a597dcd9bad87aad9611b
 F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e
@@ -1092,7 +1092,7 @@ F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c
 F test/win32heap.test ea19770974795cff26e11575e12d422dbd16893c
 F test/win32lock.test 7a6bd73a5dcdee39b5bb93e92395e1773a194361
 F test/win32longpath.test 169c75a3b2e43481f4a62122510210c67b08f26d
-F test/with1.test f4aa699a3712207e02f13945cd2b67d01a9d9f57
+F test/with1.test 97166cc72de5327bbae782aece707c45ee40e41b
 F test/with2.test 2fe78fcd8deef2a0f9cfc49bfc755911d0b3fd64
 F test/withM.test e97f2a8c506ab3ea9eab94e6f6072f6cc924c991
 F test/without_rowid1.test aaa26da19d543cd8d3d2d0e686dfa255556c15c8
@@ -1152,7 +1152,7 @@ F tool/vdbe-compress.tcl 0cf56e9263a152b84da86e75a5c0cdcdb7a47891
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01
 F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff
-P 3eb5f9f8d6ac1ee145cb4119087c516f66fe1456
-R a615c5dcec438a7db4f9b5a3e0983f82
+P 37b343b01841b338954ddfa9b76d92aa50037aec
+R f6fa1b44c6431db29f11962241b14895
 U drh
-Z 4ad026d26a172a85d439a21b0ddb47e4
+Z a802989c6813792648a40d2b5db727ef
index 87e23ab9610fd82b05405f26a3663d44317590ea..adee55d5135bdb9210795181b37475b9a1f2ff9f 100644 (file)
@@ -1 +1 @@
-37b343b01841b338954ddfa9b76d92aa50037aec
\ No newline at end of file
+1945484e6b9769c1943f750f5b09860417fb190a
\ No newline at end of file
index 13e9562ba2ef4056c8ed1f8231bbb09d291db14f..891bbf76046ff7d6f3b04a34a13952c0f98a6e73 100644 (file)
@@ -462,13 +462,13 @@ static void pushOntoSorter(
 */
 static void codeOffset(
   Vdbe *v,          /* Generate code into this VM */
-  Select *p,        /* The SELECT statement being coded */
+  int iOffset,      /* Register holding the offset counter */
   int iContinue     /* Jump here to skip the current record */
 ){
-  if( p->iOffset && iContinue!=0 ){
+  if( iOffset>0 && iContinue!=0 ){
     int addr;
-    sqlite3VdbeAddOp2(v, OP_AddImm, p->iOffset, -1);
-    addr = sqlite3VdbeAddOp1(v, OP_IfNeg, p->iOffset);
+    sqlite3VdbeAddOp2(v, OP_AddImm, iOffset, -1);
+    addr = sqlite3VdbeAddOp1(v, OP_IfNeg, iOffset);
     sqlite3VdbeAddOp2(v, OP_Goto, 0, iContinue);
     VdbeComment((v, "skip OFFSET records"));
     sqlite3VdbeJumpHere(v, addr);
@@ -571,7 +571,7 @@ static void selectInnerLoop(
   assert( pEList!=0 );
   hasDistinct = pDistinct ? pDistinct->eTnctType : WHERE_DISTINCT_NOOP;
   if( pOrderBy==0 && !hasDistinct ){
-    codeOffset(v, p, iContinue);
+    codeOffset(v, p->iOffset, iContinue);
   }
 
   /* Pull the requested columns.
@@ -653,7 +653,7 @@ static void selectInnerLoop(
       }
     }
     if( pOrderBy==0 ){
-      codeOffset(v, p, iContinue);
+      codeOffset(v, p->iOffset, iContinue);
     }
   }
 
@@ -1069,13 +1069,13 @@ static void generateSortTail(
     int ptab2 = pParse->nTab++;
     sqlite3VdbeAddOp3(v, OP_OpenPseudo, ptab2, regSortOut, pOrderBy->nExpr+2);
     addr = 1 + sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak);
-    codeOffset(v, p, addrContinue);
+    codeOffset(v, p->iOffset, addrContinue);
     sqlite3VdbeAddOp2(v, OP_SorterData, iTab, regSortOut);
     sqlite3VdbeAddOp3(v, OP_Column, ptab2, pOrderBy->nExpr+1, regRow);
     sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE);
   }else{
     addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak);
-    codeOffset(v, p, addrContinue);
+    codeOffset(v, p->iOffset, addrContinue);
     sqlite3VdbeAddOp3(v, OP_Column, iTab, pOrderBy->nExpr+1, regRow);
   }
   switch( eDest ){
@@ -1639,8 +1639,13 @@ Vdbe *sqlite3GetVdbe(Parse *pParse){
 **
 ** This routine changes the values of iLimit and iOffset only if
 ** a limit or offset is defined by pLimit and pOffset.  iLimit and
-** iOffset should have been preset to appropriate default values
-** (usually but not always -1) prior to calling this routine.
+** iOffset should have been preset to appropriate default values (zero)
+** prior to calling this routine.
+**
+** The iOffset register (if it exists) is initialized to the value
+** of the OFFSET.  The iLimit register is initialized to LIMIT.  Register
+** iOffset+1 is initialized to LIMIT+OFFSET.
+**
 ** Only if pLimit!=0 or pOffset!=0 do the limit registers get
 ** redefined.  The UNION ALL operator uses this property to force
 ** the reuse of the same limit and offset registers across multiple
@@ -1664,7 +1669,7 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){
   if( p->pLimit ){
     p->iLimit = iLimit = ++pParse->nMem;
     v = sqlite3GetVdbe(pParse);
-    if( NEVER(v==0) ) return;  /* VDBE should have already been allocated */
+    assert( v!=0 );
     if( sqlite3ExprIsInteger(p->pLimit, &n) ){
       sqlite3VdbeAddOp2(v, OP_Integer, n, iLimit);
       VdbeComment((v, "LIMIT counter"));
@@ -1778,21 +1783,21 @@ static void generateWithRecursiveQuery(
   int i;                        /* Loop counter */
   int rc;                       /* Result code */
   ExprList *pOrderBy;           /* The ORDER BY clause */
+  Expr *pLimit, *pOffset;       /* Saved LIMIT and OFFSET */
+  int regLimit, regOffset;      /* Registers used by LIMIT and OFFSET */
 
   /* Obtain authorization to do a recursive query */
   if( sqlite3AuthCheck(pParse, SQLITE_RECURSIVE, 0, 0, 0) ) return;
-  addrBreak = sqlite3VdbeMakeLabel(v);
 
-  /* Check that there is no ORDER BY or LIMIT clause. Neither of these 
-  ** are currently supported on recursive queries.
-  */
-  assert( p->pOffset==0 || p->pLimit );
-  if( /*p->pOrderBy ||*/ p->pLimit ){
-    sqlite3ErrorMsg(pParse, "%s in a recursive query",
-        p->pOrderBy ? "ORDER BY" : "LIMIT"
-    );
-    return;
-  }
+  /* Process the LIMIT and OFFSET clauses, if they exist */
+  addrBreak = sqlite3VdbeMakeLabel(v);
+  computeLimitRegisters(pParse, p, addrBreak);
+  pLimit = p->pLimit;
+  pOffset = p->pOffset;
+  regLimit = p->iLimit;
+  regOffset = p->iOffset;
+  p->pLimit = p->pOffset = 0;
+  p->iLimit = p->iOffset = 0;
 
   /* Locate the cursor number of the Current table */
   for(i=0; ALWAYS(i<pSrc->nSrc); i++){
@@ -1853,8 +1858,10 @@ static void generateWithRecursiveQuery(
 
   /* Output the single row in Current */
   addrCont = sqlite3VdbeMakeLabel(v);
+  codeOffset(v, regOffset, addrCont);
   selectInnerLoop(pParse, p, p->pEList, iCurrent,
       0, 0, pDest, addrCont, addrBreak);
+  if( regLimit ) sqlite3VdbeAddOp3(v, OP_IfZero, regLimit, addrBreak, -1);
   sqlite3VdbeResolveLabel(v, addrCont);
 
   /* Execute the recursive SELECT taking the single row in Current as
@@ -1871,6 +1878,8 @@ static void generateWithRecursiveQuery(
 
 end_of_recursive_query:
   p->pOrderBy = pOrderBy;
+  p->pLimit = pLimit;
+  p->pOffset = pOffset;
   return;
 }
 #endif
@@ -2326,7 +2335,7 @@ static int generateOutputSubroutine(
 
   /* Suppress the first OFFSET entries if there is an OFFSET clause
   */
-  codeOffset(v, p, iContinue);
+  codeOffset(v, p->iOffset, iContinue);
 
   switch( pDest->eDest ){
     /* Store the result as data using a unique key.
index 0895312b0cdaa2c3ec2f07d7548dd2c16fd7b50d..fcd192927701e226af0d2eb0703a6aebe7b76fbc 100644 (file)
@@ -190,11 +190,22 @@ do_execsql_test 5.2.2 {
       )
   SELECT * FROM ancest;
 } {0 0 1 10 2 20 3 30 4 40 4 40 5 50 6 60 7 70 7 70 8 80 8 80 8 80 8 80 9 90 9 90 9 90 9 90}
+do_execsql_test 5.2.3 {
+  WITH RECURSIVE
+    ancest(id, mtime) AS
+      (VALUES(0, 0)
+       UNION ALL
+       SELECT edge.xto, edge.seq FROM edge, ancest
+        WHERE edge.xfrom=ancest.id
+        ORDER BY 2 LIMIT 4 OFFSET 2
+      )
+  SELECT * FROM ancest;
+} {2 20 3 30 4 40 4 40}
 
 do_catchsql_test 5.3 {
-  WITH i(x) AS ( VALUES(1) UNION ALL SELECT x+1 FROM i LIMIT 10 )
-  SELECT x FROM i LIMIT 10;
-} {1 {LIMIT in a recursive query}}
+  WITH i(x) AS ( VALUES(1) UNION ALL SELECT x+1 FROM i LIMIT 5)
+  SELECT x FROM i;
+} {0 {1 2 3 4 5}}
 
 do_execsql_test 5.4 {
   WITH i(x) AS ( VALUES(1) UNION ALL SELECT (x+1)%10 FROM i)