]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Do not allow floating point rounding errors to cause a window function xInverse(...
authordan <Dan Kennedy>
Mon, 5 Apr 2021 19:05:18 +0000 (19:05 +0000)
committerdan <Dan Kennedy>
Mon, 5 Apr 2021 19:05:18 +0000 (19:05 +0000)
FossilOrigin-Name: 7a19fed4f222bc6c20e13a1367c8235916d21ba5e6f5a31cd26842efe748e744

manifest
manifest.uuid
src/window.c
test/window1.test

index 898ff18fdb89ae686893da050c0bf2e76a9392f6..977f02a23e456939add25f9b85c77f8e34bd0231 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\san\sassert()\sthat\scan\sbe\sfalse\swhen\sdoing\san\sincremental\svacuum\son\sa\ncorrupt\sdatabase\sfile.\s\sdbsqlfuzz\scced0668cfd4da4eb2382cb9dd26c17c64aaff76.
-D 2021-04-05T17:50:38.405
+C Do\snot\sallow\sfloating\spoint\srounding\serrors\sto\scause\sa\swindow\sfunction\sxInverse()\sfunction\sto\sbe\sinvoked\sbefore\sthe\scorresponding\sxStep()\scall.
+D 2021-04-05T19:05:18.685
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -633,7 +633,7 @@ F src/where.c f81b47aeed660808c315290aec02fefe21ffce55e988c603254a2c81d20a0eb5
 F src/whereInt.h 446e5e8018f83358ef917cf32d8e6a86dc8430113d0b17e720f1839d3faa44c4
 F src/wherecode.c 8bdc239eae3d39f9f6ace4299eeb86e1e0c56eed88b60f9c473dc85ae79f4e89
 F src/whereexpr.c 4ba94b8ebbff97c658b212de6ccdb6d3a41847800bdf99e7a2a967c250269f2c
-F src/window.c 547e7a0eee7dca3f246573ca87973831c5804c2127b764835755332dea25fb4b
+F src/window.c 038b6a0bea89932f97553aae51775d4118ac3051bb3edc0e40981b46748ad3f8
 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
 F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627
 F test/affinity3.test eecb0dabee4b7765a8465439d5e99429279ffba23ca74a7eae270a452799f9e7
@@ -1769,7 +1769,7 @@ F test/win32heap.test 10fd891266bd00af68671e702317726375e5407561d859be1aa04696f2
 F test/win32lock.test fbf107c91d8f5512be5a5b87c4c42ab9fdd54972
 F test/win32longpath.test 4baffc3acb2e5188a5e3a895b2b543ed09e62f7c72d713c1feebf76222fe9976
 F test/win32nolock.test ac4f08811a562e45a5755e661f45ca85892bdbbc
-F test/window1.test e52b81fff0c3cb122a1240f336688eb81bea2967a99c4ddb78969adec7aadc2a
+F test/window1.test 1d3527fe4e1ce49005e47f976804aec324a2354b45dd4458465ba1ea6f58bb72
 F test/window2.tcl 492c125fa550cda1dd3555768a2303b3effbeceee215293adf8871efc25f1476
 F test/window2.test e466a88bd626d66edc3d352d7d7e1d5531e0079b549ba44efb029d1fbff9fd3c
 F test/window3.tcl acea6e86a4324a210fd608d06741010ca83ded9fde438341cb978c49928faf03
@@ -1912,7 +1912,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 644bb77f8bb2c3499db7c280440c92fd2649a98ce714ae0d5d690eabfdc04326
-R 50e5cd8c35674ce170fd9e2962032cbf
-U drh
-Z edc55acaa1becb3a682f9cb46af3bd98
+P 15b801016fa204b8e4b7c89e236adb73dd57687959d825664de5097370b24030
+R 9ed7be239ef92bce010fca3c3056debb
+U dan
+Z 77a45f13627348a5a5bf0b83d3d22bff
index 59a65e230a85b7371b17018e190e6e4ab2f268c4..1ec0a3dae8db654c5d5c0ba101608d91e99b99d0 100644 (file)
@@ -1 +1 @@
-15b801016fa204b8e4b7c89e236adb73dd57687959d825664de5097370b24030
\ No newline at end of file
+7a19fed4f222bc6c20e13a1367c8235916d21ba5e6f5a31cd26842efe748e744
\ No newline at end of file
index b19eb339553478c682edc351c2b21f42503f27b7..137288af34a8fe9861caefded58f3d6b3cafe777 100644 (file)
@@ -2048,7 +2048,7 @@ static void windowIfNewPeer(
 **   if( csr1.peerVal - regVal <= csr2.peerVal ) goto lbl;
 **
 ** A special type of arithmetic is used such that if csr1.peerVal is not
-** a numeric type (real or integer), then the result of the addition addition
+** a numeric type (real or integer), then the result of the addition
 ** or subtraction is a a copy of csr1.peerVal.
 */
 static void windowCodeRangeTest(
@@ -2067,8 +2067,13 @@ static void windowCodeRangeTest(
   int regString = ++pParse->nMem;           /* Reg. for constant value '' */
   int arith = OP_Add;                       /* OP_Add or OP_Subtract */
   int addrGe;                               /* Jump destination */
+  int addrDone = sqlite3VdbeMakeLabel(pParse);   /* Address past OP_Ge */
   CollSeq *pColl;
 
+  /* Read the peer-value from each cursor into a register */
+  windowReadPeerValues(p, csr1, reg1);
+  windowReadPeerValues(p, csr2, reg2);
+
   assert( op==OP_Ge || op==OP_Gt || op==OP_Le );
   assert( pOrderBy && pOrderBy->nExpr==1 );
   if( pOrderBy->a[0].sortFlags & KEYINFO_ORDER_DESC ){
@@ -2080,34 +2085,11 @@ static void windowCodeRangeTest(
     arith = OP_Subtract;
   }
 
-  /* Read the peer-value from each cursor into a register */
-  windowReadPeerValues(p, csr1, reg1);
-  windowReadPeerValues(p, csr2, reg2);
-
   VdbeModuleComment((v, "CodeRangeTest: if( R%d %s R%d %s R%d ) goto lbl",
       reg1, (arith==OP_Add ? "+" : "-"), regVal,
       ((op==OP_Ge) ? ">=" : (op==OP_Le) ? "<=" : (op==OP_Gt) ? ">" : "<"), reg2
   ));
 
-  /* Register reg1 currently contains csr1.peerVal (the peer-value from csr1).
-  ** This block adds (or subtracts for DESC) the numeric value in regVal
-  ** from it. Or, if reg1 is not numeric (it is a NULL, a text value or a blob),
-  ** then leave reg1 as it is. In pseudo-code, this is implemented as:
-  **
-  **   if( reg1>='' ) goto addrGe;
-  **   reg1 = reg1 +/- regVal
-  **   addrGe:
-  **
-  ** Since all strings and blobs are greater-than-or-equal-to an empty string,
-  ** the add/subtract is skipped for these, as required. If reg1 is a NULL,
-  ** then the arithmetic is performed, but since adding or subtracting from
-  ** NULL is always NULL anyway, this case is handled as required too.  */
-  sqlite3VdbeAddOp4(v, OP_String8, 0, regString, 0, "", P4_STATIC);
-  addrGe = sqlite3VdbeAddOp3(v, OP_Ge, regString, 0, reg1);
-  VdbeCoverage(v);
-  sqlite3VdbeAddOp3(v, arith, regVal, reg1, reg1);
-  sqlite3VdbeJumpHere(v, addrGe);
-
   /* If the BIGNULL flag is set for the ORDER BY, then it is required to 
   ** consider NULL values to be larger than all other values, instead of 
   ** the usual smaller. The VDBE opcodes OP_Ge and so on do not handle this
@@ -2144,16 +2126,38 @@ static void windowCodeRangeTest(
         break;
       default: assert( op==OP_Lt ); /* no-op */ break;
     }
-    sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+3);
+    sqlite3VdbeAddOp2(v, OP_Goto, 0, addrDone);
 
     /* This block runs if reg1 is not NULL, but reg2 is. */
     sqlite3VdbeJumpHere(v, addr);
     sqlite3VdbeAddOp2(v, OP_IsNull, reg2, lbl); VdbeCoverage(v);
     if( op==OP_Gt || op==OP_Ge ){
-      sqlite3VdbeChangeP2(v, -1, sqlite3VdbeCurrentAddr(v)+1);
+      sqlite3VdbeChangeP2(v, -1, addrDone);
     }
   }
 
+  /* Register reg1 currently contains csr1.peerVal (the peer-value from csr1).
+  ** This block adds (or subtracts for DESC) the numeric value in regVal
+  ** from it. Or, if reg1 is not numeric (it is a NULL, a text value or a blob),
+  ** then leave reg1 as it is. In pseudo-code, this is implemented as:
+  **
+  **   if( reg1>='' ) goto addrGe;
+  **   reg1 = reg1 +/- regVal
+  **   addrGe:
+  **
+  ** Since all strings and blobs are greater-than-or-equal-to an empty string,
+  ** the add/subtract is skipped for these, as required. If reg1 is a NULL,
+  ** then the arithmetic is performed, but since adding or subtracting from
+  ** NULL is always NULL anyway, this case is handled as required too.  */
+  sqlite3VdbeAddOp4(v, OP_String8, 0, regString, 0, "", P4_STATIC);
+  addrGe = sqlite3VdbeAddOp3(v, OP_Ge, regString, 0, reg1);
+  VdbeCoverage(v);
+  if( (op==OP_Ge && arith==OP_Add) || (op==OP_Le && arith==OP_Subtract) ){
+    sqlite3VdbeAddOp3(v, op, reg2, lbl, reg1); VdbeCoverage(v);
+  }
+  sqlite3VdbeAddOp3(v, arith, regVal, reg1, reg1);
+  sqlite3VdbeJumpHere(v, addrGe);
+
   /* Compare registers reg2 and reg1, taking the jump if required. Note that
   ** control skips over this test if the BIGNULL flag is set and either
   ** reg1 or reg2 contain a NULL value.  */
@@ -2161,6 +2165,7 @@ static void windowCodeRangeTest(
   pColl = sqlite3ExprNNCollSeq(pParse, pOrderBy->a[0].pExpr);
   sqlite3VdbeAppendP4(v, (void*)pColl, P4_COLLSEQ);
   sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
+  sqlite3VdbeResolveLabel(v, addrDone);
 
   assert( op==OP_Ge || op==OP_Gt || op==OP_Lt || op==OP_Le );
   testcase(op==OP_Ge); VdbeCoverageIf(v, op==OP_Ge);
index dbaf4388fbff6db37d1f2e95ecf96d14672e67c8..f1e44a2cd6788ce7e2fa40047b4abd177b2e3688 100644 (file)
@@ -2001,4 +2001,48 @@ do_execsql_test 65.4 {
   FROM t1;
 } {{}}
 
+#-------------------------------------------------------------------------
+reset_db
+
+do_execsql_test 66.1 {
+  CREATE TABLE t1(a INTEGER);
+  INSERT INTO t1 VALUES(3578824042033200656);
+  INSERT INTO t1 VALUES(3029012920382354029);
+}
+
+foreach {tn spec} {
+  1 "ORDER BY a RANGE BETWEEN 0.3 PRECEDING AND 10 FOLLOWING"
+  2 "ORDER BY a RANGE BETWEEN 0.3 PRECEDING AND 0.1 PRECEDING"
+  3 "ORDER BY a RANGE BETWEEN 0.3 FOLLOWING AND 10 FOLLOWING"
+  4 "ORDER BY a DESC RANGE BETWEEN 0.3 PRECEDING AND 10 FOLLOWING"
+  5 "ORDER BY a NULLS LAST RANGE BETWEEN 0.3 PRECEDING AND 10 FOLLOWING"
+  6 "ORDER BY a RANGE BETWEEN 1.0 PRECEDING AND 2.0 PRECEDING"
+} {
+  do_execsql_test 66.2.$tn "
+    SELECT total(a) OVER ( $spec ) FROM t1 ORDER BY a
+  " {
+    3.02901292038235e+18 3.5788240420332e+18
+  }
+}
+
+
+do_execsql_test 66.3 {
+  CREATE TABLE t2(a INTEGER);
+  INSERT INTO t2 VALUES(45);
+  INSERT INTO t2 VALUES(30);
+}
+
+foreach {tn spec res} {
+  1 "ORDER BY a RANGE BETWEEN 0.3 PRECEDING AND 10 FOLLOWING"   {30.0 45.0}
+  2 "ORDER BY a RANGE BETWEEN 0.3 PRECEDING AND 0.1 PRECEDING"  {0.0 0.0}
+  3 "ORDER BY a RANGE BETWEEN 0.3 FOLLOWING AND 10 FOLLOWING"   {0.0 0.0}
+  4 "ORDER BY a DESC RANGE BETWEEN 0.3 PRECEDING AND 10 FOLLOWING" {30.0 45.0}
+  5 "ORDER BY a NULLS LAST RANGE BETWEEN 0.3 PRECEDING AND 10 FOLLOWING" {30.0 45.0}
+  6 "ORDER BY a RANGE BETWEEN 1.0 PRECEDING AND 2.0 PRECEDING" {0.0 0.0}
+} {
+  do_execsql_test 66.2.$tn "
+    SELECT total(a) OVER ( $spec ) FROM t2 ORDER BY a
+  " $res
+}
+
 finish_test