Merge the 3.53.2 patch release into the reuse-schema-3.53 branch
FossilOrigin-Name: 2524c2f07a67ad792b1ff70fb702cedae7689a55d1ee3556d527363a9eec8c07
This commit is contained in:
+11
-1
@@ -302,6 +302,12 @@
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
** Assume any b-tree layer with more levels than this is corrupt.
|
||||
*/
|
||||
#define FTS3_MAX_BTREE_HEIGHT 48
|
||||
|
||||
typedef struct Fts3HashWrapper Fts3HashWrapper;
|
||||
struct Fts3HashWrapper {
|
||||
Fts3Hash hash; /* Hash table */
|
||||
@@ -2018,7 +2024,11 @@ static int fts3SelectLeaf(
|
||||
assert( piLeaf || piLeaf2 );
|
||||
|
||||
fts3GetVarint32(zNode, &iHeight);
|
||||
rc = fts3ScanInteriorNode(zTerm, nTerm, zNode, nNode, piLeaf, piLeaf2);
|
||||
if( iHeight>FTS3_MAX_BTREE_HEIGHT ){
|
||||
rc = FTS_CORRUPT_VTAB;
|
||||
}else{
|
||||
rc = fts3ScanInteriorNode(zTerm, nTerm, zNode, nNode, piLeaf, piLeaf2);
|
||||
}
|
||||
assert_fts3_nc( !piLeaf2 || !piLeaf || rc!=SQLITE_OK || (*piLeaf<=*piLeaf2) );
|
||||
|
||||
if( rc==SQLITE_OK && iHeight>1 ){
|
||||
|
||||
+1
-1
@@ -341,7 +341,7 @@ static int fts3auxNextMethod(sqlite3_vtab_cursor *pCursor){
|
||||
/* State 3. The integer just read is a column number. */
|
||||
default: assert( eState==3 );
|
||||
iCol = (int)v;
|
||||
if( iCol<1 || iCol>0x3fffffff ){
|
||||
if( iCol<1 || iCol>(pFts3->nColumn+1) ){
|
||||
rc = SQLITE_CORRUPT_VTAB;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3556,8 +3556,7 @@ static void fts5PoslistFilterCallback(
|
||||
|
||||
do {
|
||||
while( i<nChunk && pChunk[i]!=0x01 ){
|
||||
while( pChunk[i] & 0x80 ) i++;
|
||||
i++;
|
||||
fts5IndexSkipVarint(pChunk, i);
|
||||
}
|
||||
if( pCtx->eState ){
|
||||
fts5BufferSafeAppendBlob(pCtx->pBuf, &pChunk[iStart], i-iStart);
|
||||
@@ -5301,6 +5300,11 @@ static void fts5DoSecureDelete(
|
||||
}else{
|
||||
iStart = fts5GetU16(&aPg[0]);
|
||||
}
|
||||
if( iStart>nPg ){
|
||||
FTS5_CORRUPT_IDX(p);
|
||||
sqlite3_free(aIdx);
|
||||
return;
|
||||
}
|
||||
|
||||
iSOP = iStart + fts5GetVarint(&aPg[iStart], &iDelta);
|
||||
assert_nc( iSOP<=pSeg->iLeafOffset );
|
||||
|
||||
@@ -1998,6 +1998,142 @@ do_catchsql_test 12.1 {
|
||||
SELECT rowid FROM ft('a:aaa')
|
||||
} {0 1}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_test 13.0 {
|
||||
sqlite3 db {}
|
||||
db deserialize [decode_hexdb {
|
||||
.open --hexdb
|
||||
| size 24576 pagesize 4096 filename vuln_001.db
|
||||
| page 1 offset 0
|
||||
| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3.
|
||||
| 16: 10 00 01 01 00 40 20 20 00 00 00 03 00 00 00 06 .....@ ........
|
||||
| 32: 00 00 00 00 00 00 00 00 00 00 00 06 00 00 00 04 ................
|
||||
| 48: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 ................
|
||||
| 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 ................
|
||||
| 96: 00 2e 76 89 0d 00 00 00 06 0e 09 00 0f ca 0f 77 ..v............w
|
||||
| 112: 0f 0f 0e b7 0e 5e 0e 09 00 00 00 00 00 00 00 00 .....^..........
|
||||
| 3584: 00 00 00 00 00 00 00 00 00 53 06 06 17 1d 1d 01 .........S......
|
||||
| 3600: 7b 74 61 62 6c 65 74 5f 63 6f 6e 66 69 67 74 5f .tablet_configt_
|
||||
| 3616: 63 6f 6e 66 69 67 06 43 52 45 41 54 45 20 54 41 config.CREATE TA
|
||||
| 3632: 42 4c 45 20 27 74 5f 63 6f 6e 66 69 67 27 28 6b BLE 't_config'(k
|
||||
| 3648: 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 76 29 PRIMARY KEY, v)
|
||||
| 3664: 20 57 49 54 48 4f 55 54 20 52 4f 57 49 44 57 05 WITHOUT ROWIDW.
|
||||
| 3680: 06 17 1f 1f 01 7f 74 61 62 6c 65 74 5f 64 6f 63 ......tablet_doc
|
||||
| 3696: 73 69 7a 65 74 5f 64 6f 63 73 69 7a 65 05 43 52 sizet_docsize.CR
|
||||
| 3712: 45 41 54 45 20 54 41 42 4c 45 20 27 74 5f 64 6f EATE TABLE 't_do
|
||||
| 3728: 63 73 69 7a 65 27 28 69 64 20 49 4e 54 45 47 45 csize'(id INTEGE
|
||||
| 3744: 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 73 R PRIMARY KEY, s
|
||||
| 3760: 7a 20 42 4c 4f 42 29 56 04 06 17 1f 1f 01 7d 74 z BLOB)V.......t
|
||||
| 3776: 61 62 6c 65 74 5f 63 6f 6e 74 65 6e 74 74 5f 63 ablet_contentt_c
|
||||
| 3792: 6f 6e 74 65 6e 74 04 43 52 45 41 54 45 20 54 41 ontent.CREATE TA
|
||||
| 3808: 42 4c 45 20 27 74 5f 63 6f 6e 74 65 6e 74 27 28 BLE 't_content'(
|
||||
| 3824: 69 64 20 49 4e 54 45 47 45 52 20 50 52 49 4d 41 id INTEGER PRIMA
|
||||
| 3840: 52 59 20 4b 45 59 2c 20 63 30 2c 20 63 31 29 66 RY KEY, c0, c1)f
|
||||
| 3856: 03 07 17 17 17 01 81 2b 74 61 62 6c 65 74 5f 69 .......+tablet_i
|
||||
| 3872: 64 78 74 5f 69 64 78 03 43 52 45 41 54 45 20 54 dxt_idx.CREATE T
|
||||
| 3888: 41 42 4c 45 20 27 74 5f 69 64 78 27 28 73 65 67 ABLE 't_idx'(seg
|
||||
| 3904: 69 64 2c 20 74 65 72 6d 2c 20 70 67 6e 6f 2c 20 id, term, pgno,
|
||||
| 3920: 50 52 49 4d 41 52 59 20 4b 45 59 28 73 65 67 69 PRIMARY KEY(segi
|
||||
| 3936: 64 2c 20 74 65 72 6d 29 29 20 57 49 54 48 4f 55 d, term)) WITHOU
|
||||
| 3952: 54 20 52 4f 57 49 44 51 02 06 17 19 19 01 7f 74 T ROWIDQ.......t
|
||||
| 3968: 61 62 6c 65 74 5f 64 61 74 61 74 5f 64 61 74 61 ablet_datat_data
|
||||
| 3984: 02 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 74 .CREATE TABLE 't
|
||||
| 4000: 5f 64 61 74 61 27 28 69 64 20 49 4e 54 45 47 45 _data'(id INTEGE
|
||||
| 4016: 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 62 R PRIMARY KEY, b
|
||||
| 4032: 6c 6f 63 6b 20 42 4c 4f 42 29 34 01 06 17 0f 0f lock BLOB)4.....
|
||||
| 4048: 08 5b 74 61 62 6c 65 74 74 43 52 45 41 54 45 20 .[tablettCREATE
|
||||
| 4064: 56 49 52 54 55 41 4c 20 54 41 42 4c 45 20 74 20 VIRTUAL TABLE t
|
||||
| 4080: 55 53 49 4e 47 20 66 74 73 35 28 61 2c 20 62 29 USING fts5(a, b)
|
||||
| page 2 offset 4096
|
||||
| 0: 0d 00 00 00 04 0b da 00 0f e7 0f ef 0f ce 0b da ................
|
||||
| 3024: 00 00 00 00 00 00 00 00 00 00 87 6c 84 80 80 80 ...........l....
|
||||
| 3040: 80 02 04 00 8f 5c 00 00 03 e8 80 80 80 80 80 80 ................
|
||||
| 3056: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3072: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3088: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3104: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3120: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3136: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3152: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3168: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3184: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3200: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3216: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3232: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3248: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3264: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3280: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3296: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3312: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3328: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3344: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3360: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3376: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3392: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3408: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3424: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3440: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3456: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3472: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3488: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3504: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3520: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3536: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3552: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3568: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3584: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3600: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3616: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3632: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3648: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3664: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3680: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3696: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3712: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3728: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3744: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3760: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3776: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3792: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3808: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3824: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3840: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3856: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3872: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3888: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3904: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3920: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3936: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3952: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3968: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 3984: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 4000: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 4016: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 ................
|
||||
| 4032: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 12 84 ................
|
||||
| 4048: 80 80 80 80 01 03 00 2a 00 0b 00 0e 06 30 68 65 .......*.....0he
|
||||
| 4064: 6c 6c 6f 01 04 04 04 06 01 03 00 12 01 02 02 0f llo.............
|
||||
| 4080: 0a 03 00 24 00 00 00 00 01 01 01 00 01 01 01 02 ...$............
|
||||
| page 3 offset 8192
|
||||
| 0: 0a 00 00 00 01 0f fa 00 0f fa 00 00 00 00 00 00 ................
|
||||
| 4080: 00 00 00 00 00 00 00 00 00 00 05 04 09 0c 01 02 ................
|
||||
| page 4 offset 12288
|
||||
| 0: 0d 00 00 00 01 0f e8 00 0f e8 00 00 00 00 00 00 ................
|
||||
| 4064: 00 00 00 00 00 00 00 00 16 01 04 00 23 1b 68 65 ............#.he
|
||||
| 4080: 6c 6c 6f 20 77 6f 72 6c 64 66 6f 6f 20 62 61 72 llo worldfoo bar
|
||||
| page 5 offset 16384
|
||||
| 0: 0d 00 00 00 01 0f f9 00 0f f9 00 00 00 00 00 00 ................
|
||||
| 4080: 00 00 00 00 00 00 00 00 00 05 01 03 00 10 02 02 ................
|
||||
| page 6 offset 20480
|
||||
| 0: 0a 00 00 00 01 0f f4 00 0f f4 00 00 00 00 00 00 ................
|
||||
| 4080: 00 00 00 00 0b 03 1b 01 76 65 72 73 69 6f 6e 04 ........version.
|
||||
| end vuln_001.db
|
||||
}]} {}
|
||||
|
||||
do_catchsql_test 13.1 {
|
||||
SELECT * FROM t('a:hello')
|
||||
} {0 {{hello world} {foo bar}}}
|
||||
|
||||
sqlite3_fts5_may_be_corrupt 0
|
||||
finish_test
|
||||
|
||||
|
||||
@@ -125,4 +125,40 @@ do_catchsql_test 2.3 {
|
||||
DELETE FROM t1 WHERE rowid = 1
|
||||
} {/.*fts5: corrupt.*/}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
PRAGMA page_size=4096;
|
||||
PRAGMA journal_mode=DELETE;
|
||||
CREATE VIRTUAL TABLE t USING fts5(x, detail=none);
|
||||
|
||||
WITH s(i) AS (
|
||||
VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<5000
|
||||
)
|
||||
INSERT INTO t(rowid, x) SELECT i, 'vulnerabilitytest' FROM s;
|
||||
|
||||
INSERT INTO t(t) VALUES('optimize');
|
||||
INSERT INTO t(t, rank) VALUES('secure-delete', 1);
|
||||
} {delete}
|
||||
|
||||
do_test 3.1 {
|
||||
db eval { SELECT rowid AS rowid, block FROM t_data ORDER BY rowid } {
|
||||
if {$rowid>=10 && [string length $block]>=4} {
|
||||
binary scan $block Su first_rowid_off
|
||||
set pgno [expr ($rowid & 0x7FFFFFFF)]
|
||||
if {$pgno>=2 && $first_rowid_off>0} break
|
||||
}
|
||||
}
|
||||
|
||||
set bad [binary format a*a* "\xFF\xFF" [string range $block 2 end]]
|
||||
db eval {
|
||||
UPDATE t_data SET block = $bad WHERE rowid=$rowid
|
||||
}
|
||||
} {}
|
||||
|
||||
do_catchsql_test 3.2 {
|
||||
DELETE FROM t WHERE rowid=4500;
|
||||
} {1 {fts5: corruption in table "t"}}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -60,6 +60,10 @@ static void compressFunc(
|
||||
nIn = sqlite3_value_bytes(argv[0]);
|
||||
nOut = 13 + nIn + (nIn+999)/1000;
|
||||
pOut = sqlite3_malloc64( nOut+5 );
|
||||
if( pOut==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
return;
|
||||
}
|
||||
for(i=4; i>=0; i--){
|
||||
x[i] = (nIn >> (7*(4-i)))&0x7f;
|
||||
}
|
||||
@@ -99,6 +103,10 @@ static void uncompressFunc(
|
||||
if( (pIn[i]&0x80)!=0 ){ i++; break; }
|
||||
}
|
||||
pOut = sqlite3_malloc64( nOut+1 );
|
||||
if( pOut==0 ){
|
||||
sqlite3_result_error_nomem(context);
|
||||
return;
|
||||
}
|
||||
rc = uncompress(pOut, &nOut, &pIn[i], nIn-i);
|
||||
if( rc==Z_OK ){
|
||||
sqlite3_result_blob(context, pOut, nOut, sqlite3_free);
|
||||
|
||||
+37
-37
@@ -340,45 +340,45 @@ static void percentSort(double *a, unsigned int n){
|
||||
int iGt; /* Entries at or after a[iGt] are greater than rPivot */
|
||||
int i; /* Loop counter */
|
||||
double rPivot; /* The pivot value */
|
||||
|
||||
assert( n>=2 );
|
||||
if( a[0]>a[n-1] ){
|
||||
SWAP_DOUBLE(a[0],a[n-1])
|
||||
}
|
||||
if( n==2 ) return;
|
||||
iGt = n-1;
|
||||
i = n/2;
|
||||
if( a[0]>a[i] ){
|
||||
SWAP_DOUBLE(a[0],a[i])
|
||||
}else if( a[i]>a[iGt] ){
|
||||
SWAP_DOUBLE(a[i],a[iGt])
|
||||
}
|
||||
if( n==3 ) return;
|
||||
rPivot = a[i];
|
||||
iLt = i = 1;
|
||||
do{
|
||||
if( a[i]<rPivot ){
|
||||
if( i>iLt ) SWAP_DOUBLE(a[i],a[iLt])
|
||||
iLt++;
|
||||
i++;
|
||||
}else if( a[i]>rPivot ){
|
||||
do{
|
||||
iGt--;
|
||||
}while( iGt>i && a[iGt]>rPivot );
|
||||
SWAP_DOUBLE(a[i],a[iGt])
|
||||
}else{
|
||||
i++;
|
||||
|
||||
while( n>=2 ){
|
||||
if( a[0]>a[n-1] ){
|
||||
SWAP_DOUBLE(a[0],a[n-1])
|
||||
}
|
||||
if( n==2 ) return;
|
||||
iGt = n-1;
|
||||
i = n/2;
|
||||
if( a[0]>a[i] ){
|
||||
SWAP_DOUBLE(a[0],a[i])
|
||||
}else if( a[i]>a[iGt] ){
|
||||
SWAP_DOUBLE(a[i],a[iGt])
|
||||
}
|
||||
if( n==3 ) return;
|
||||
rPivot = a[i];
|
||||
iLt = i = 1;
|
||||
do{
|
||||
if( a[i]<rPivot ){
|
||||
if( i>iLt ) SWAP_DOUBLE(a[i],a[iLt])
|
||||
iLt++;
|
||||
i++;
|
||||
}else if( a[i]>rPivot ){
|
||||
do{
|
||||
iGt--;
|
||||
}while( iGt>i && a[iGt]>rPivot );
|
||||
SWAP_DOUBLE(a[i],a[iGt])
|
||||
}else{
|
||||
i++;
|
||||
}
|
||||
}while( i<iGt );
|
||||
if( iLt>n/2 ){
|
||||
if( n-iGt>=2 ) percentSort(a+iGt, n-iGt);
|
||||
n = iLt;
|
||||
}else{
|
||||
if( iLt>=2 ) percentSort(a, iLt);
|
||||
a += iGt;
|
||||
n -= iGt;
|
||||
}
|
||||
}while( i<iGt );
|
||||
if( iLt>=2 ) percentSort(a, iLt);
|
||||
if( n-iGt>=2 ) percentSort(a+iGt, n-iGt);
|
||||
|
||||
/* Uncomment for testing */
|
||||
#if 0
|
||||
for(i=0; i<n-1; i++){
|
||||
assert( a[i]<=a[i+1] );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -296,7 +296,7 @@ static void prefixLengthFunc(
|
||||
|
||||
nByte = (nL > nR ? nL : nR);
|
||||
for(i=0; i<nByte; i++){
|
||||
if( zL[i]!=zR[i] ) break;
|
||||
if( zL[i]==0 || zL[i]!=zR[i] ) break;
|
||||
if( (zL[i] & 0xC0)!=0x80 ) nRet++;
|
||||
}
|
||||
|
||||
|
||||
@@ -488,6 +488,7 @@ static void zipfileResetCursor(ZipfileCsr *pCsr){
|
||||
pNext = p->pNext;
|
||||
zipfileEntryFree(p);
|
||||
}
|
||||
pCsr->pFreeEntry = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+1
-1
@@ -1402,7 +1402,7 @@ static int nodeRowidIndex(
|
||||
){
|
||||
int ii;
|
||||
int nCell = NCELL(pNode);
|
||||
assert( nCell<200 );
|
||||
assert( nCell<65536 && nCell>=0 );
|
||||
for(ii=0; ii<nCell; ii++){
|
||||
if( nodeGetRowid(pRtree, pNode, ii)==iRowid ){
|
||||
*piIndex = ii;
|
||||
|
||||
@@ -18,6 +18,7 @@ if {![info exists testdir]} {
|
||||
}
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !rtree { finish_test ; return }
|
||||
set testprefix rtreeA
|
||||
|
||||
proc create_t1 {} {
|
||||
db close
|
||||
@@ -258,5 +259,33 @@ do_test rtreeA-7.120 {
|
||||
sqlite3_extended_errcode db
|
||||
} {SQLITE_CORRUPT_VTAB}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
set N 300
|
||||
set rec [binary format SS 0 $N]
|
||||
for {set ii 1} {$ii<=$N} {incr ii} {
|
||||
set cell [binary format Wff $ii $ii [expr $ii+1]]
|
||||
set rec [binary format a*a* $rec $cell]
|
||||
}
|
||||
|
||||
do_execsql_test 8.1 {
|
||||
CREATE VIRTUAL TABLE rt USING rtree(id, x1, x2);
|
||||
INSERT INTO rt VALUES(1, 0.0, 1.0);
|
||||
UPDATE rt_node SET data = $rec WHERE nodeno = 1;
|
||||
DELETE FROM rt_rowid;
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<300
|
||||
)
|
||||
INSERT INTO rt_rowid SELECT i, 1 FROM s;
|
||||
DELETE FROM rt_parent;
|
||||
}
|
||||
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
|
||||
do_execsql_test 8.2 {
|
||||
SELECT count(*) FROM rt WHERE id = 1
|
||||
} {1}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -82,6 +82,9 @@ do_test 2.2.1 {
|
||||
# It is not possible to apply the changeset generated by the following
|
||||
# SQL, as none of the three updated rows may be updated as part of the
|
||||
# first pass.
|
||||
#
|
||||
# UPDATE 19/05/2026 - it is now possible to apply such an update.
|
||||
#
|
||||
do_then_apply_sql -ignorenoop {
|
||||
UPDATE t1 SET b=0 WHERE a=1;
|
||||
UPDATE t1 SET b=1 WHERE a=2;
|
||||
@@ -89,7 +92,7 @@ do_test 2.2.1 {
|
||||
UPDATE t1 SET b=3 WHERE a=1;
|
||||
}
|
||||
db2 eval { SELECT a, b FROM t1 }
|
||||
} {1 1 2 2 3 3}
|
||||
} {1 3 2 1 3 2}
|
||||
do_test 2.2.2 { db eval { SELECT a, b FROM t1 } } {1 3 2 1 3 2}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
Executable
+298
@@ -0,0 +1,298 @@
|
||||
# 2026 May 18
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library.
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source [file join [file dirname [info script]] session_common.tcl]
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !session {finish_test; return}
|
||||
|
||||
set testprefix sessionconflict2
|
||||
|
||||
forcedelete test.db2
|
||||
sqlite3 db2 test.db2
|
||||
|
||||
do_test 1.0 {
|
||||
do_common_sql {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b, c UNIQUE);
|
||||
INSERT INTO t1 VALUES(1, 1, 1);
|
||||
INSERT INTO t1 VALUES(2, 2, 2);
|
||||
INSERT INTO t1 VALUES(3, 3, 3);
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test 1.1 {
|
||||
do_then_apply_sql {
|
||||
UPDATE t1 SET c=NULL WHERE a=1;
|
||||
UPDATE t1 SET c=1 WHERE a=3;
|
||||
UPDATE t1 SET c=3 WHERE a=1;
|
||||
}
|
||||
} {}
|
||||
|
||||
do_execsql_test -db db 1.2 {
|
||||
SELECT rowid, * FROM t1
|
||||
} {
|
||||
1 1 1 3
|
||||
2 2 2 2
|
||||
3 3 3 1
|
||||
}
|
||||
|
||||
do_execsql_test -db db2 1.3 {
|
||||
SELECT rowid, * FROM t1
|
||||
} {
|
||||
1 1 1 3
|
||||
2 2 2 2
|
||||
3 3 3 1
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
reset_db
|
||||
db2 close
|
||||
forcedelete test.db2
|
||||
sqlite3 db2 test.db2
|
||||
|
||||
do_test 2.0 {
|
||||
do_common_sql {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b, c UNIQUE) WITHOUT ROWID;
|
||||
INSERT INTO t1 VALUES(1, 1, 1);
|
||||
INSERT INTO t1 VALUES(2, 2, 2);
|
||||
INSERT INTO t1 VALUES(3, 3, 3);
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test 2.1 {
|
||||
do_then_apply_sql {
|
||||
UPDATE t1 SET c=NULL WHERE a=1;
|
||||
UPDATE t1 SET c=1 WHERE a=3;
|
||||
UPDATE t1 SET c=3 WHERE a=1;
|
||||
}
|
||||
} {}
|
||||
|
||||
do_execsql_test -db db 2.2 {
|
||||
SELECT * FROM t1
|
||||
} {
|
||||
1 1 3
|
||||
2 2 2
|
||||
3 3 1
|
||||
}
|
||||
|
||||
do_execsql_test -db db2 2.3 {
|
||||
SELECT * FROM t1
|
||||
} {
|
||||
1 1 3
|
||||
2 2 2
|
||||
3 3 1
|
||||
}
|
||||
|
||||
#--------------------------------------------------------------------------
|
||||
reset_db
|
||||
db2 close
|
||||
forcedelete test.db2
|
||||
sqlite3 db2 test.db2
|
||||
|
||||
do_test 3.0 {
|
||||
do_common_sql {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c UNIQUE);
|
||||
INSERT INTO t1 VALUES(1, 1, 1);
|
||||
INSERT INTO t1 VALUES(2, 2, 2);
|
||||
INSERT INTO t1 VALUES(3, 3, 3);
|
||||
}
|
||||
} {}
|
||||
|
||||
do_test 3.1 {
|
||||
do_then_apply_sql {
|
||||
UPDATE t1 SET c=NULL WHERE a=1;
|
||||
UPDATE t1 SET c=1 WHERE a=3;
|
||||
UPDATE t1 SET c=3 WHERE a=1;
|
||||
}
|
||||
} {}
|
||||
|
||||
do_execsql_test -db db 3.2 {
|
||||
SELECT rowid, * FROM t1
|
||||
} {
|
||||
1 1 1 3
|
||||
2 2 2 2
|
||||
3 3 3 1
|
||||
}
|
||||
|
||||
do_execsql_test -db db2 3.3 {
|
||||
SELECT rowid, * FROM t1
|
||||
} {
|
||||
1 1 1 3
|
||||
2 2 2 2
|
||||
3 3 3 1
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
db2 close
|
||||
reset_db
|
||||
forcedelete test.db2
|
||||
sqlite3 db2 test.db2
|
||||
|
||||
set ::conflict_list [list]
|
||||
proc xConflict {args} {
|
||||
lappend ::conflict_list $args
|
||||
return "OMIT"
|
||||
}
|
||||
|
||||
proc do_conflict_test {tn bNoUpdateLoop script clist} {
|
||||
|
||||
uplevel [list do_test $tn.1 [subst -nocommands {
|
||||
sqlite3session S db "main"
|
||||
S attach *
|
||||
eval {$script}
|
||||
set ::changeset [S changeset]
|
||||
S delete
|
||||
}] {}]
|
||||
|
||||
if {$bNoUpdateLoop} {
|
||||
uplevel [list do_test $tn.2.no {
|
||||
set ::conflict_list [list]
|
||||
sqlite3changeset_apply_v2 -noupdateloop db2 $::changeset xConflict
|
||||
set ::conflict_list
|
||||
} [list {*}$clist]]
|
||||
} else {
|
||||
uplevel [list do_test $tn.2 {
|
||||
set ::conflict_list [list]
|
||||
sqlite3changeset_apply_v2 db2 $::changeset xConflict
|
||||
set ::conflict_list
|
||||
} [list {*}$clist]]
|
||||
}
|
||||
}
|
||||
|
||||
do_test 4.0 {
|
||||
do_common_sql {
|
||||
CREATE TABLE t1(a INT PRIMARY KEY, b, c UNIQUE, d UNIQUE);
|
||||
WITH s(i) AS (
|
||||
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<10
|
||||
)
|
||||
INSERT INTO t1 SELECT i, i, i, i FROM s;
|
||||
}
|
||||
} {}
|
||||
|
||||
proc swap {tbl pkcol valcol pk1 pk2} {
|
||||
set val1 [db one "SELECT $valcol FROM $tbl WHERE $pkcol = \$pk1"]
|
||||
set val2 [db one "SELECT $valcol FROM $tbl WHERE $pkcol = \$pk2"]
|
||||
|
||||
db eval "
|
||||
UPDATE $tbl SET $valcol = NULL WHERE $pkcol IN (\$pk1, \$pk2);
|
||||
UPDATE $tbl SET $valcol = \$val2 WHERE $pkcol = \$pk1;
|
||||
UPDATE $tbl SET $valcol = \$val1 WHERE $pkcol = \$pk2;
|
||||
"
|
||||
}
|
||||
|
||||
do_conflict_test 4.1.1 0 {
|
||||
swap t1 a c 4 5
|
||||
swap t1 a c 2 3
|
||||
swap t1 a c 8 1
|
||||
} {
|
||||
}
|
||||
|
||||
do_execsql_test -db db 4.1.2 {
|
||||
SELECT a, c FROM t1
|
||||
} {
|
||||
1 8 2 3 3 2 4 5 5 4 6 6 7 7 8 1 9 9 10 10
|
||||
}
|
||||
do_execsql_test -db db2 4.1.3 {
|
||||
SELECT a, c FROM t1
|
||||
} {
|
||||
1 8 2 3 3 2 4 5 5 4 6 6 7 7 8 1 9 9 10 10
|
||||
}
|
||||
|
||||
do_conflict_test 4.2.1 0 {
|
||||
swap t1 a d 10 9
|
||||
swap t1 a d 8 7
|
||||
swap t1 a d 7 6
|
||||
swap t1 a d 5 4
|
||||
swap t1 a d 4 8
|
||||
} {
|
||||
}
|
||||
|
||||
do_execsql_test -db db 4.2.2 {
|
||||
SELECT a, d FROM t1
|
||||
} {
|
||||
1 1 2 2 3 3 4 7 5 4 6 8 7 6 8 5 9 10 10 9
|
||||
}
|
||||
do_execsql_test -db db2 4.2.3 {
|
||||
SELECT a, d FROM t1
|
||||
} {
|
||||
1 1 2 2 3 3 4 7 5 4 6 8 7 6 8 5 9 10 10 9
|
||||
}
|
||||
|
||||
do_execsql_test -db db2 4.3 {
|
||||
INSERT INTO t1(a, b, c, d) VALUES(11, 11, 11, 11);
|
||||
}
|
||||
|
||||
do_conflict_test 4.3.1 0 {
|
||||
swap t1 a c 3 6
|
||||
db eval { UPDATE t1 SET d=11 WHERE a=10; }
|
||||
swap t1 a d 4 8
|
||||
} {
|
||||
{UPDATE t1 CONSTRAINT {i 10 {} {} {} {} i 9} {{} {} {} {} {} {} i 11}}
|
||||
}
|
||||
|
||||
do_conflict_test 4.3.2 0 {
|
||||
swap t1 a c 1 2
|
||||
swap t1 a c 3 4
|
||||
db eval { UPDATE t1 SET c=11 WHERE a=10; }
|
||||
swap t1 a d 5 6
|
||||
swap t1 a d 4 5
|
||||
} {
|
||||
{UPDATE t1 CONSTRAINT {i 10 {} {} i 10 {} {}} {{} {} {} {} i 11 {} {}}}
|
||||
}
|
||||
|
||||
do_conflict_test 4.3.3 0 {
|
||||
swap t1 a c 1 2
|
||||
swap t1 a c 2 3
|
||||
swap t1 a c 3 4
|
||||
swap t1 a c 4 1
|
||||
} {
|
||||
}
|
||||
db2 close
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
forcedelete test.db2
|
||||
sqlite3 db2 test.db2
|
||||
|
||||
do_test 5.0 {
|
||||
do_common_sql {
|
||||
CREATE TABLE t1(a INT PRIMARY KEY, b UNIQUE);
|
||||
INSERT INTO t1 VALUES('one', 'one');
|
||||
INSERT INTO t1 VALUES('two', 'two');
|
||||
INSERT INTO t1 VALUES('three', 'three');
|
||||
INSERT INTO t1 VALUES('four', 'four');
|
||||
INSERT INTO t1 VALUES('five', 'five');
|
||||
}
|
||||
} {}
|
||||
|
||||
do_conflict_test 5.1 1 {
|
||||
} {
|
||||
}
|
||||
|
||||
do_conflict_test 5.2 1 {
|
||||
swap t1 a b one two
|
||||
} {
|
||||
{UPDATE t1 CONSTRAINT {t two t two} {{} {} t one}}
|
||||
{UPDATE t1 CONSTRAINT {t one t one} {{} {} t two}}
|
||||
}
|
||||
|
||||
do_conflict_test 5.2 0 {
|
||||
swap t1 a b four five
|
||||
} {
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -94,4 +94,45 @@ do_faultsim_test 2 -faults oom-t* -prep {
|
||||
catch { S delete }
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b UNIQUE);
|
||||
INSERT INTO t1 VALUES(1, 'one');
|
||||
INSERT INTO t1 VALUES(2, 'two');
|
||||
INSERT INTO t1 VALUES(3, 'three');
|
||||
INSERT INTO t1 VALUES(4, 'four');
|
||||
INSERT INTO t1 VALUES(5, 'five');
|
||||
}
|
||||
faultsim_save_and_close
|
||||
faultsim_restore_and_reopen
|
||||
|
||||
set C [changeset_from_sql {
|
||||
UPDATE t1 SET b=NULL WHERE a IN (1, 3, 5);
|
||||
UPDATE t1 SET b='three' WHERE a=1;
|
||||
UPDATE t1 SET b='five' WHERE a=3;
|
||||
UPDATE t1 SET b='one' WHERE a=5;
|
||||
}]
|
||||
|
||||
do_execsql_test 3.1 {
|
||||
SELECT * FROM t1
|
||||
} {
|
||||
1 three 2 two 3 five 4 four 5 one
|
||||
}
|
||||
|
||||
proc xConflict {args} {
|
||||
lappend ::conflict_list $args
|
||||
return "OMIT"
|
||||
}
|
||||
|
||||
do_faultsim_test 3 -faults oom* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
db eval {SELECT * FROM sqlite_schema}
|
||||
} -body {
|
||||
sqlite3changeset_apply_v2 db $::C xConflict
|
||||
set {} {}
|
||||
} -test {
|
||||
faultsim_test_result {0 {}} {1 SQLITE_NOMEM}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
+359
-31
@@ -1545,6 +1545,16 @@ static int sessionPrepareDfltStmt(
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Finalize statement pStmt. If (*pRc) is SQLITE_OK when this function is
|
||||
** called, set it to the results of the sqlite3_finalize() call. Or, if
|
||||
** it is already set to an error code, leave it as is.
|
||||
*/
|
||||
static void sessionFinalizeStmt(sqlite3_stmt *pStmt, int *pRc){
|
||||
int rc = sqlite3_finalize(pStmt);
|
||||
if( *pRc==SQLITE_OK ) *pRc = rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Table pTab has one or more existing change-records with old.* records
|
||||
** with fewer than pTab->nCol columns. This function updates all such
|
||||
@@ -1567,9 +1577,8 @@ static int sessionUpdateChanges(sqlite3_session *pSession, SessionTable *pTab){
|
||||
}
|
||||
}
|
||||
|
||||
sessionFinalizeStmt(pStmt, &rc);
|
||||
pSession->rc = rc;
|
||||
rc = sqlite3_finalize(pStmt);
|
||||
if( pSession->rc==SQLITE_OK ) pSession->rc = rc;
|
||||
return pSession->rc;
|
||||
}
|
||||
|
||||
@@ -2895,11 +2904,11 @@ static int sessionSelectStmt(
|
||||
);
|
||||
sessionAppendStr(&cols, "tbl, ?2, stat", &rc);
|
||||
}else{
|
||||
#if 0
|
||||
#if 0
|
||||
if( bRowid ){
|
||||
sessionAppendStr(&cols, SESSIONS_ROWID, &rc);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
for(i=0; i<nCol; i++){
|
||||
if( cols.nBuf ) sessionAppendStr(&cols, ", ", &rc);
|
||||
sessionAppendIdent(&cols, azCol[i], &rc);
|
||||
@@ -4355,6 +4364,7 @@ struct SessionApplyCtx {
|
||||
u8 bRebaseStarted; /* If table header is already in rebase */
|
||||
u8 bRebase; /* True to collect rebase information */
|
||||
u8 bIgnoreNoop; /* True to ignore no-op conflicts */
|
||||
u8 bNoUpdateLoop; /* No update-loop processing */
|
||||
int bRowid;
|
||||
char *zErr; /* Error message, if any */
|
||||
};
|
||||
@@ -4928,7 +4938,7 @@ static int sessionConflictHandler(
|
||||
u8 *aBlob = &pIter->in.aData[pIter->in.iCurrent];
|
||||
int nBlob = pIter->in.iNext - pIter->in.iCurrent;
|
||||
sessionAppendBlob(&p->constraints, aBlob, nBlob, &rc);
|
||||
return SQLITE_OK;
|
||||
return rc;
|
||||
}else if( p->bIgnoreNoop==0 || op!=SQLITE_DELETE
|
||||
|| eType==SQLITE_CHANGESET_CONFLICT
|
||||
){
|
||||
@@ -5176,7 +5186,264 @@ static int sessionApplyOneWithRetry(
|
||||
}
|
||||
|
||||
/*
|
||||
** Retry the changes accumulated in the pApply->constraints buffer.
|
||||
** Create an iterator to iterate through the retry buffer pRetry.
|
||||
*/
|
||||
static int sessionRetryIterInit(
|
||||
SessionBuffer *pRetry, /* Buffer to iterate through */
|
||||
int bPatchset, /* True for patchset, false for changeset */
|
||||
const char *zTab, /* Table name */
|
||||
SessionApplyCtx *pApply, /* Session apply context */
|
||||
sqlite3_changeset_iter **ppIter /* OUT: New iterator */
|
||||
){
|
||||
sqlite3_changeset_iter *pRet = 0;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
rc = sessionChangesetStart(
|
||||
&pRet, 0, 0, pRetry->nBuf, pRetry->aBuf, pApply->bInvertConstraints, 1
|
||||
);
|
||||
if( rc==SQLITE_OK ){
|
||||
size_t nByte = 2*pApply->nCol*sizeof(sqlite3_value*);
|
||||
pRet->bPatchset = bPatchset;
|
||||
pRet->zTab = (char*)zTab;
|
||||
pRet->nCol = pApply->nCol;
|
||||
pRet->abPK = pApply->abPK;
|
||||
sessionBufferGrow(&pRet->tblhdr, nByte, &rc);
|
||||
pRet->apValue = (sqlite3_value**)pRet->tblhdr.aBuf;
|
||||
if( rc==SQLITE_OK ){
|
||||
memset(pRet->apValue, 0, nByte);
|
||||
}else{
|
||||
sqlite3changeset_finalize(pRet);
|
||||
pRet = 0;
|
||||
}
|
||||
}
|
||||
|
||||
*ppIter = pRet;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Attempt to apply all the changes in retry buffer pRetry to the database.
|
||||
** Except, if parameter iSkip is greater than or equal to 0, skip change
|
||||
** iSkip.
|
||||
*/
|
||||
static int sessionApplyRetryBuffer(
|
||||
SessionBuffer *pRetry, /* Buffer to apply changes from */
|
||||
int iSkip, /* If >=0, index of change to omit */
|
||||
sqlite3 *db, /* Database handle */
|
||||
int bPatchset, /* True for patchset, false for changeset */
|
||||
const char *zTab, /* Name of table to write to */
|
||||
SessionApplyCtx *pApply, /* Apply context */
|
||||
int(*xConflict)(void*, int, sqlite3_changeset_iter*),
|
||||
void *pCtx /* First argument passed to xConflict */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
int rc2 = SQLITE_OK;
|
||||
int ii = 0;
|
||||
sqlite3_changeset_iter *pIter = 0;
|
||||
|
||||
assert( pApply->constraints.nBuf==0 );
|
||||
|
||||
rc = sessionRetryIterInit(pRetry, bPatchset, zTab, pApply, &pIter);
|
||||
|
||||
for(ii=0; rc==SQLITE_OK && SQLITE_ROW==sqlite3changeset_next(pIter); ii++){
|
||||
if( ii!=iSkip ){
|
||||
rc = sessionApplyOneWithRetry(db, pIter, pApply, xConflict, pCtx);
|
||||
}
|
||||
}
|
||||
|
||||
rc2 = sqlite3changeset_finalize(pIter);
|
||||
if( rc==SQLITE_OK ) rc = rc2;
|
||||
assert( pApply->bDeferConstraints || pApply->constraints.nBuf==0 );
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Check if table zTab in the "main" database of db is a WITHOUT ROWID
|
||||
** table.
|
||||
**
|
||||
** If no error occurs, return SQLITE_OK and set output variable (*pbWR) to
|
||||
** true if zTab is a WITHOUT ROWID table, or false otherwise. Or, if an
|
||||
** error does occur, return an SQLite error code. The final value of (*pbWR)
|
||||
** is undefined in this case.
|
||||
*/
|
||||
static int sessionTableIsWithoutRowid(sqlite3 *db, const char *zTab, int *pbWR){
|
||||
sqlite3_stmt *pList = 0;
|
||||
char *zSql = 0;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
zSql = sqlite3_mprintf("PRAGMA table_list = %Q", zTab);
|
||||
if( zSql==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
rc = sqlite3_prepare_v2(db, zSql, -1, &pList, 0);
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_step(pList);
|
||||
*pbWR = sqlite3_column_int(pList, 4);
|
||||
rc = sqlite3_finalize(pList);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Iterator pUp points to an UPDATE change. This function deletes the
|
||||
** affected row from the database and creates an INSERT statement that
|
||||
** may be used to reinsert the row as it is after the UPDATE change
|
||||
** has been applied.
|
||||
**
|
||||
** If successful, SQLITE_OK is returned and output variable (*ppInsert)
|
||||
** is left pointing to a prepared INSERT statement. It is the responsibility
|
||||
** of the caller to eventually free this statement using sqlite3_finalize().
|
||||
** Or, if an error occurs, an SQLite error code is returned and (*ppInsert)
|
||||
** set to NULL. pApply->zErr may be set to an error message in this case.
|
||||
*/
|
||||
static int sessionUpdateToDeleteInsert(
|
||||
sqlite3 *db, /* Database to write to */
|
||||
const char *zTab, /* Table name */
|
||||
SessionApplyCtx *pApply, /* Apply context */
|
||||
sqlite3_changeset_iter *pUp, /* Iterator pointing to UPDATE change */
|
||||
sqlite3_stmt **ppInsert /* OUT: INSERT statement */
|
||||
){
|
||||
sqlite3_stmt *pRet = 0; /* The INSERT statement */
|
||||
sqlite3_stmt *pSelect = 0; /* SELECT to read current values of row */
|
||||
int rc = SQLITE_OK;
|
||||
int bWR = 0;
|
||||
|
||||
rc = sessionTableIsWithoutRowid(db, zTab, &bWR);
|
||||
if( rc==SQLITE_OK ){
|
||||
char *zSelect = 0;
|
||||
char *zInsert = 0;
|
||||
SessionBuffer cols = {0, 0, 0};
|
||||
SessionBuffer insbind = {0, 0, 0};
|
||||
SessionBuffer pkcols = {0, 0, 0};
|
||||
SessionBuffer selbind = {0, 0, 0};
|
||||
|
||||
const char *zComma = "";
|
||||
const char *zComma2 = "";
|
||||
int ii;
|
||||
for(ii=0; ii<pApply->nCol; ii++){
|
||||
sessionAppendStr(&cols, zComma, &rc);
|
||||
sessionAppendIdent(&cols, pApply->azCol[ii], &rc);
|
||||
sessionAppendStr(&insbind, zComma, &rc);
|
||||
sessionAppendStr(&insbind, "?", &rc);
|
||||
zComma = ", ";
|
||||
|
||||
if( pApply->abPK[ii] ){
|
||||
sessionAppendStr(&pkcols, zComma2, &rc);
|
||||
sessionAppendIdent(&pkcols, pApply->azCol[ii], &rc);
|
||||
sessionAppendStr(&selbind, zComma2, &rc);
|
||||
sessionAppendPrintf(&selbind, &rc, "?%d", ii+1);
|
||||
zComma2 = ", ";
|
||||
}
|
||||
}
|
||||
if( bWR==0 ){
|
||||
sessionAppendStr(&cols, zComma, &rc);
|
||||
sessionAppendStr(&cols, SESSIONS_ROWID, &rc);
|
||||
sessionAppendStr(&insbind, zComma, &rc);
|
||||
sessionAppendStr(&insbind, "?", &rc);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
zSelect = sqlite3_mprintf("SELECT %s FROM %Q WHERE (%s) IS (%s)",
|
||||
cols.aBuf, zTab, pkcols.aBuf, selbind.aBuf
|
||||
);
|
||||
if( zSelect==0 ) rc = SQLITE_NOMEM;
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
zInsert = sqlite3_mprintf("INSERT INTO %Q(%s) VALUES(%s)",
|
||||
zTab, cols.aBuf, insbind.aBuf
|
||||
);
|
||||
if( zInsert==0 ) rc = SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sessionPrepare(db, &pSelect, &pApply->zErr, zSelect);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sessionPrepare(db, &pRet, &pApply->zErr, zInsert);
|
||||
}
|
||||
|
||||
sqlite3_free(zSelect);
|
||||
sqlite3_free(zInsert);
|
||||
sqlite3_free(cols.aBuf);
|
||||
sqlite3_free(insbind.aBuf);
|
||||
sqlite3_free(pkcols.aBuf);
|
||||
sqlite3_free(selbind.aBuf);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sessionBindRow(
|
||||
pUp, sqlite3changeset_old, pApply->nCol, pApply->abPK, pSelect
|
||||
);
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && sqlite3_step(pSelect)==SQLITE_ROW ){
|
||||
int iCol;
|
||||
for(iCol=0; iCol<pApply->nCol; iCol++){
|
||||
sqlite3_value *pVal = pUp->apValue[iCol+pApply->nCol];
|
||||
if( pVal==0 ){
|
||||
pVal = sqlite3_column_value(pSelect, iCol);
|
||||
}
|
||||
rc = sqlite3_bind_value(pRet, iCol+1, pVal);
|
||||
}
|
||||
if( bWR==0 ){
|
||||
sqlite3_bind_int64(pRet, iCol+1, sqlite3_column_int64(pSelect, iCol));
|
||||
}
|
||||
}
|
||||
sessionFinalizeStmt(pSelect, &rc);
|
||||
|
||||
/* Delete the row from the database. */
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sessionBindRow(
|
||||
pUp, sqlite3changeset_old, pApply->nCol, pApply->abPK, pApply->pDelete
|
||||
);
|
||||
sqlite3_bind_int(pApply->pDelete, pApply->nCol+1, 1);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_step(pApply->pDelete);
|
||||
rc = sqlite3_reset(pApply->pDelete);
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3_finalize(pRet);
|
||||
pRet = 0;
|
||||
}
|
||||
|
||||
*ppInsert = pRet;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Retry the changes accumulated in the pApply->constraints buffer. The
|
||||
** pApply->constraints buffer contains all changes to table zTab that
|
||||
** could not be applied due to SQLITE_CONSTRAINT errors. This function
|
||||
** attempts to apply them as follows:
|
||||
**
|
||||
** 1) It runs through the buffer and attempts to retry each change,
|
||||
** removing any that are successfully applied from the buffer. This
|
||||
** is repeated until no further progress can be made.
|
||||
**
|
||||
** 2) For each UPDATE change in the buffer, try the following in a
|
||||
** savepoint transaction:
|
||||
**
|
||||
** a) DELETE the affected row,
|
||||
** b) Attempt step (1) with remaining changes,
|
||||
** c) Attempt to INSERT a row equivalent to the one that would be
|
||||
** created by applying this UPDATE change.
|
||||
**
|
||||
** If the INSERT in (c) succeeds, the savepoint is committed and all
|
||||
** successfully applied changes are removed from the buffer. Step (2)
|
||||
** is then repeated.
|
||||
**
|
||||
** 3) Once step (2) has been attempted for each UPDATE in the change,
|
||||
** a final attempt is made to apply each remaining change. This time,
|
||||
** if an SQLITE_CONSTRAINT error is encountered, the conflict handler
|
||||
** is invoked and the user has to decide whether to omit the change
|
||||
** or rollback the entire _apply() operation.
|
||||
*/
|
||||
static int sessionRetryConstraints(
|
||||
sqlite3 *db,
|
||||
@@ -5187,41 +5454,101 @@ static int sessionRetryConstraints(
|
||||
void *pCtx /* First argument passed to xConflict */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
int iUpdate = 0;
|
||||
|
||||
/* Step (1) */
|
||||
while( pApply->constraints.nBuf ){
|
||||
sqlite3_changeset_iter *pIter2 = 0;
|
||||
SessionBuffer cons = pApply->constraints;
|
||||
memset(&pApply->constraints, 0, sizeof(SessionBuffer));
|
||||
|
||||
rc = sessionChangesetStart(
|
||||
&pIter2, 0, 0, cons.nBuf, cons.aBuf, pApply->bInvertConstraints, 1
|
||||
rc = sessionApplyRetryBuffer(
|
||||
&cons, -1, db, bPatchset, zTab, pApply, xConflict, pCtx
|
||||
);
|
||||
if( rc==SQLITE_OK ){
|
||||
size_t nByte = 2*pApply->nCol*sizeof(sqlite3_value*);
|
||||
int rc2;
|
||||
pIter2->bPatchset = bPatchset;
|
||||
pIter2->zTab = (char*)zTab;
|
||||
pIter2->nCol = pApply->nCol;
|
||||
pIter2->abPK = pApply->abPK;
|
||||
sessionBufferGrow(&pIter2->tblhdr, nByte, &rc);
|
||||
pIter2->apValue = (sqlite3_value**)pIter2->tblhdr.aBuf;
|
||||
if( rc==SQLITE_OK ) memset(pIter2->apValue, 0, nByte);
|
||||
|
||||
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3changeset_next(pIter2) ){
|
||||
rc = sessionApplyOneWithRetry(db, pIter2, pApply, xConflict, pCtx);
|
||||
}
|
||||
|
||||
rc2 = sqlite3changeset_finalize(pIter2);
|
||||
if( rc==SQLITE_OK ) rc = rc2;
|
||||
}
|
||||
assert( pApply->bDeferConstraints || pApply->constraints.nBuf==0 );
|
||||
|
||||
sqlite3_free(cons.aBuf);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
if( pApply->constraints.nBuf>=cons.nBuf ){
|
||||
/* No progress was made on the last round. */
|
||||
pApply->bDeferConstraints = 0;
|
||||
|
||||
/* If no progress has been made this round, break out of the loop. */
|
||||
if( pApply->constraints.nBuf>=cons.nBuf ) break;
|
||||
}
|
||||
|
||||
/* Step (2) */
|
||||
while( rc==SQLITE_OK && pApply->constraints.nBuf && !pApply->bNoUpdateLoop ){
|
||||
SessionBuffer cons = {0, 0, 0};
|
||||
sqlite3_changeset_iter *pUp = 0;
|
||||
sqlite3_stmt *pInsert = 0;
|
||||
int iSkip = 0;
|
||||
|
||||
rc = sessionRetryIterInit(
|
||||
&pApply->constraints, bPatchset, zTab, pApply, &pUp
|
||||
);
|
||||
if( rc==SQLITE_OK ){
|
||||
int iThis = -1;
|
||||
while( SQLITE_ROW==sqlite3changeset_next(pUp) ){
|
||||
if( pUp->op==SQLITE_UPDATE ) iThis++;
|
||||
if( iThis==iUpdate ) break;
|
||||
iSkip++;
|
||||
}
|
||||
if( iThis==iUpdate ){
|
||||
rc = sqlite3_exec(db, "SAVEPOINT update_op", 0, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sessionUpdateToDeleteInsert(db, zTab, pApply, pUp, &pInsert);
|
||||
}
|
||||
}
|
||||
sqlite3changeset_finalize(pUp);
|
||||
if( iThis!=iUpdate ) break;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
cons = pApply->constraints;
|
||||
|
||||
while( rc==SQLITE_OK && pApply->constraints.nBuf>0 ){
|
||||
SessionBuffer app = pApply->constraints;
|
||||
memset(&pApply->constraints, 0, sizeof(SessionBuffer));
|
||||
rc = sessionApplyRetryBuffer(
|
||||
&app, iSkip, db, bPatchset, zTab, pApply, xConflict, pCtx
|
||||
);
|
||||
if( app.aBuf!=cons.aBuf ){
|
||||
sqlite3_free(app.aBuf);
|
||||
}
|
||||
if( pApply->constraints.nBuf>=app.nBuf ){
|
||||
break;
|
||||
}
|
||||
iSkip = -1;
|
||||
}
|
||||
}
|
||||
|
||||
iUpdate++;
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_step(pInsert);
|
||||
rc = sqlite3_finalize(pInsert);
|
||||
if( rc==SQLITE_CONSTRAINT ){
|
||||
rc = sqlite3_exec(db, "ROLLBACK TO update_op", 0, 0, 0);
|
||||
sqlite3_free(pApply->constraints.aBuf);
|
||||
pApply->constraints = cons;
|
||||
memset(&cons, 0, sizeof(cons));
|
||||
}else if( rc==SQLITE_OK ){
|
||||
iUpdate = 0;
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_exec(db, "RELEASE update_op", 0, 0, 0);
|
||||
}
|
||||
}else{
|
||||
sqlite3_finalize(pInsert);
|
||||
}
|
||||
|
||||
sqlite3_free(cons.aBuf);
|
||||
}
|
||||
|
||||
/* Step (3) */
|
||||
if( rc==SQLITE_OK && pApply->constraints.nBuf ){
|
||||
SessionBuffer cons = pApply->constraints;
|
||||
memset(&pApply->constraints, 0, sizeof(SessionBuffer));
|
||||
pApply->bDeferConstraints = 0;
|
||||
rc = sessionApplyRetryBuffer(
|
||||
&cons, -1, db, bPatchset, zTab, pApply, xConflict, pCtx
|
||||
);
|
||||
sqlite3_free(cons.aBuf);
|
||||
}
|
||||
|
||||
return rc;
|
||||
@@ -5275,6 +5602,7 @@ static int sessionChangesetApply(
|
||||
sApply.bRebase = (ppRebase && pnRebase);
|
||||
sApply.bInvertConstraints = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
|
||||
sApply.bIgnoreNoop = !!(flags & SQLITE_CHANGESETAPPLY_IGNORENOOP);
|
||||
sApply.bNoUpdateLoop = !!(flags & SQLITE_CHANGESETAPPLY_NOUPDATELOOP);
|
||||
if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){
|
||||
rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0);
|
||||
}
|
||||
|
||||
@@ -1366,11 +1366,23 @@ int sqlite3changeset_apply_v3(
|
||||
** database behave as if they were declared with "ON UPDATE NO ACTION ON
|
||||
** DELETE NO ACTION", even if they are actually CASCADE, RESTRICT, SET NULL
|
||||
** or SET DEFAULT.
|
||||
**
|
||||
** <dt>SQLITE_CHANGESETAPPLY_NOUPDATELOOP <dd>
|
||||
** Sometimes, a changeset contains two or more update statements such that
|
||||
** although after applying all updates the database will contain no
|
||||
** constraint violations, no single update can be applied before the others.
|
||||
** The simplest example of this is a pair of UPDATEs that have "swapped"
|
||||
** two column values with a UNIQUE constraint.
|
||||
** <p>
|
||||
** Usually, sqlite3changeset_apply() and similar functions work hard to try
|
||||
** to find a way to apply such a changeset. However, if this flag is set,
|
||||
** then all such updates are considered CONSTRAINT conflicts.
|
||||
*/
|
||||
#define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001
|
||||
#define SQLITE_CHANGESETAPPLY_INVERT 0x0002
|
||||
#define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004
|
||||
#define SQLITE_CHANGESETAPPLY_FKNOACTION 0x0008
|
||||
#define SQLITE_CHANGESETAPPLY_NOUPDATELOOP 0x0010
|
||||
|
||||
/*
|
||||
** CAPI3REF: Constants Passed To The Conflict Handler
|
||||
|
||||
@@ -914,6 +914,9 @@ static int SQLITE_TCLAPI testSqlite3changesetApply(
|
||||
}
|
||||
else if( n>2 && n<=11 && 0==sqlite3_strnicmp("-ignorenoop", z1, n) ){
|
||||
flags |= SQLITE_CHANGESETAPPLY_IGNORENOOP;
|
||||
}
|
||||
else if( n>3 && n<=13 && 0==sqlite3_strnicmp("-noupdateloop", z1, n) ){
|
||||
flags |= SQLITE_CHANGESETAPPLY_NOUPDATELOOP;
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
C Merge\sthe\slatest\sbranch-3.53\sfixes\sinto\sthe\sreuse-schema-3.53\sbranch.
|
||||
D 2026-06-02T17:22:20.657
|
||||
C Merge\sthe\s3.53.2\spatch\srelease\sinto\sthe\sreuse-schema-3.53\sbranch
|
||||
D 2026-06-03T19:28:11.526
|
||||
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
@@ -80,10 +80,10 @@ F ext/fts3/README.content b9078d0843a094d86af0d48dffbff13c906702b4c3558012e67b9c
|
||||
F ext/fts3/README.syntax b72477722e9b4fe43f8403227d790a1c94221bfad15c27863a4b36d1052e892b
|
||||
F ext/fts3/README.tokenizers b92bdeb8b46503f0dd301d364efc5ef59ef9fa8e2758b8e742f39fa93a2e422d
|
||||
F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
|
||||
F ext/fts3/fts3.c 6cc7bbc307f27e7b6ee2e1d5ff63ffff4df3b42529dfe00eb34ddded417961b3
|
||||
F ext/fts3/fts3.c 1716994c40715223431d98e5132c40a3c1a00c011c5bde2270bad1bd06be3ccd
|
||||
F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
|
||||
F ext/fts3/fts3Int.h 277f32f304e82f4397fc2a74793c0a95318b7abb9670b519e4805a00946cbd9b
|
||||
F ext/fts3/fts3_aux.c c105f6502df588f49a383eb22aed953844fb0e31265361a0cc8dd73037b37e39
|
||||
F ext/fts3/fts3_aux.c bbb614abcc995bf748cba4717e9fd8de67898e33c80f60379f6feb2358189b4b
|
||||
F ext/fts3/fts3_expr.c 907dd64deb881c0586d5ecbabc96cfebdbe2ff9e7a052d379fd3b606edfa21ea
|
||||
F ext/fts3/fts3_hash.c d9dba473741445789330c7513d4f65737c92df23c3212784312931641814672a
|
||||
F ext/fts3/fts3_hash.h 39cf6874dc239d6b4e30479b1975fe5b22a3caaf
|
||||
@@ -114,7 +114,7 @@ F ext/fts5/fts5_buffer.c dcc3f0352339fe79c9d8abbc1c2009bc3469206467880bf43558447
|
||||
F ext/fts5/fts5_config.c bfba970fe1e4eed18ee57c8d51458e226db9a960ddf775c5e50e3d76603a667e
|
||||
F ext/fts5/fts5_expr.c 71d48e8cf0358deace4949276647d317ff7665db6db09f40b81e2e7fe6664c7c
|
||||
F ext/fts5/fts5_hash.c d5871df92ce3fa210a650cf419ee916b87c29977e86084d06612edf772bff6f5
|
||||
F ext/fts5/fts5_index.c 87faf9a9ca65ec7be0886e13e9f7571d0d2416ab88e77a06d57bbaf3734bd51f
|
||||
F ext/fts5/fts5_index.c 1627d9c184dec6be913647ba1e31a906e825b8460c9575e1c367ee27fe847f0c
|
||||
F ext/fts5/fts5_main.c b0fed47b3b4420ba6810373480a75bc28a9c0b7d16478d19a396436fb3ff17d7
|
||||
F ext/fts5/fts5_storage.c 19bc7c4cbe1e6a2dd9849ef7d84b5ca1fcbf194cefc3e386b901e00e08bf05c2
|
||||
F ext/fts5/fts5_tcl.c 2be6cc14f9448f720fd4418339cd202961a0801ea9424cb3d9de946f8f5a051c
|
||||
@@ -165,9 +165,9 @@ F ext/fts5/test/fts5corrupt.test 237fce1c3261bb3a5bec333b0f0dbf5b105ec32627ef14c
|
||||
F ext/fts5/test/fts5corrupt2.test 4a03a158c2cb617c9f76d26b35c1ef2534124bc0bbddcea38dfd5b170ebea27b
|
||||
F ext/fts5/test/fts5corrupt3.test 121a8a7622dfe1be1bc55cbe70eddd6a3416f76a837dc8c06a11a32e781595a4
|
||||
F ext/fts5/test/fts5corrupt4.test dc08d19f5b8943e95a7778a7d8da592042504faf18dd93f68f7d7a0d7d7dd733
|
||||
F ext/fts5/test/fts5corrupt5.test bdf6c04a1c9176507c8c0e66842b78b3fbcafccde20a41bb22a1b19896784b54
|
||||
F ext/fts5/test/fts5corrupt5.test b9085599389721b38f080f501660c931cd608f8ecbc93c23644344f74ef7aa21
|
||||
F ext/fts5/test/fts5corrupt6.test 2d72db743db7b5d9c9a6d0cfef24d799ed1aa5e8192b66c40e871a37ed9eed06
|
||||
F ext/fts5/test/fts5corrupt7.test 814aab492d7a09abb5bfdd81cc66fc206d7f3868f9a3bae91876e02efc466fb3
|
||||
F ext/fts5/test/fts5corrupt7.test 9664c15360e8b649ad76f457a0bbf5a7271b8eff1a8ee141ea039bc63240c934
|
||||
F ext/fts5/test/fts5corrupt8.test 0b10750caf8aa23fa1c379ca4caf6130d41454505e4d5315590f4061eedcbe44
|
||||
F ext/fts5/test/fts5corrupt9.test 4253b9b59f33effac8b67da72ec34309c738aca2d5e8e2656bfbbd6a489a1dfe
|
||||
F ext/fts5/test/fts5corruptA.test 7b31551444569420903d34ae50a55a1227d16969264f0b50de2dc812bc0b3414
|
||||
@@ -370,7 +370,7 @@ F ext/misc/btreeinfo.c 13bc9e9f1c13cde370d0e4a6a2683e9f1926a4cead7fb72c71871b11a
|
||||
F ext/misc/cksumvfs.c 9d7d0cf1a8893ac5d48922bfe9f3f217b4a61a6265f559263a02bb2001259913
|
||||
F ext/misc/closure.c c983987a8d7846c3e52b1885ed3e20af7d4ca52a81a8f94ec6d1cd68f93acc86
|
||||
F ext/misc/completion.c 3f5db28e88c3313103b2dd86d910a2944fd500c46754e473493968ce81e994a4
|
||||
F ext/misc/compress.c 8191118b9b73e7796c961790db62d35d9b0fb724b045e005a5713dc9e0795565
|
||||
F ext/misc/compress.c 5cc142aa82d1589a31c384657d0418c0eb0871348a2201e5dca32d24a0dd6654
|
||||
F ext/misc/csv.c 5e9d4dd749e762c144104c0f01db5bf4458735b19081ebe481a64e589a66687a
|
||||
F ext/misc/dbdump.c 678f1b9ae2317b4473f65d03132a2482c3f4b08920799ed80feedd2941a06680
|
||||
F ext/misc/decimal.c 23698283d9365ce66d54b5bb97c01e69b4aa7ac804f226f9117a0d42efd15a65
|
||||
@@ -387,8 +387,8 @@ F ext/misc/nextchar.c 7877914c2a80c2f181dd04c3dbef550dfb54c93495dc03da2403b5dd58
|
||||
F ext/misc/noop.c f1a21cc9b7a4e667e5c8458d80ba680b8bd4315a003f256006046879f679c5a0
|
||||
F ext/misc/normalize.c fbb144a861809686ff2b5b6eee8bb2e1207f9bf13ce7376e5273c700a1eafbd5
|
||||
F ext/misc/pcachetrace.c f4227ce03fb16aa8d6f321b72dd051097419d7a028a9853af048bee7645cb405
|
||||
F ext/misc/percentile.c 72e05a21db20a2fa85264b99515941f00ae698824c9db82d7edfbb16cea8ec80
|
||||
F ext/misc/prefixes.c efc261429fd8ab43cedc0d38b94d86d87437af5550651bceb618bd7e84e25125
|
||||
F ext/misc/percentile.c b6b9c740f0dc42838efd776eec0f68a93bc5ac86524c71337a70950860bf6c4d
|
||||
F ext/misc/prefixes.c e7d3e7a39174db412c5f77c266fa221c2dc3cb2621a9422835a607ad08805eb2
|
||||
F ext/misc/qpvtab.c 470a5fffba005c8e1994209e59c1848122351e19522de71beb68d666c4fa39a5
|
||||
F ext/misc/randomjson.c ef835fc64289e76ac4873b85fe12f9463a036168d7683cf2b773e36e6262c4ed
|
||||
F ext/misc/regexp.c 9dada9e9aa91f0cc23e35429e5d1111f110cc201b4c8dcc49aa6d2fc4b2a865d
|
||||
@@ -418,7 +418,7 @@ F ext/misc/vtablog.c 6c0c11c4822ab6c1a205718ea7c6d1bb561d96b27104b9c1fe84d01aa62
|
||||
F ext/misc/vtshim.c f5ab480d1e33fa46a0b138359bedc9979e32798d72348e04bbe6093f9ae95c7b
|
||||
F ext/misc/wholenumber.c aa5e6d786fe8d79bc100ea0e852249c026a91ae65a5c1bcb2b869cd1a7cdd6d5
|
||||
F ext/misc/windirent.h 02211ce51f3034c675f2dbf4d228194d51b3ee05734678bad5106fff6292e60c
|
||||
F ext/misc/zipfile.c 38211d2b23e1fcb57af2d7c597458112e6c3d6130bf97dc4c224816d9f8218fa
|
||||
F ext/misc/zipfile.c 58d535e6f177709c3f3607e19aa8e1b4c3c57c1f2c78bd4cdfac15e0b2f53e5a
|
||||
F ext/misc/zorder.c bddff2e1b9661a90c95c2a9a9c7ecd8908afab5763256294dd12d609d4664eee
|
||||
F ext/qrf/README.md 9e644615d7d7b77ef7e9db798765679e50c5ed12eda48bce21c9ef9eb4715e9d
|
||||
F ext/qrf/dev-notes.md e68a6d91ce4c7eb296ef2daadc2bb79c95c317ad15b9fafe40850c67b29c2430
|
||||
@@ -492,7 +492,7 @@ F ext/recover/sqlite3recover.h 011c799f02deb70ab685916f6f538e6bb32c4e0025e79bfd0
|
||||
F ext/recover/test_recover.c 3d0fb1df7823f5bc22a0b93955034d16a2dfa2eb1e443e9a0123a77f120599a3
|
||||
F ext/rtree/README 734aa36238bcd2dee91db5dba107d5fcbdb02396612811377a8ad50f1272b1c1
|
||||
F ext/rtree/geopoly.c 77bad24162b6ae84a2fd6de3f1f8ad0b255a4ceb572f50f8771d1ac9d4d986d8
|
||||
F ext/rtree/rtree.c a53d91522db6fa01bdaaa646285a5402a43334a319a8ca6bcb7362ec07731b76
|
||||
F ext/rtree/rtree.c e350bb1c7adefab45cd4ce77e11393c7f3a9ee21c27345a684b6e1c04fae1a12
|
||||
F ext/rtree/rtree.h 4a690463901cb5e6127cf05eb8e642f127012fd5003830dbc974eca5802d9412
|
||||
F ext/rtree/rtree1.test e0608db762b2aadca0ecb6f97396cf66244490adc3ba88f2a292b27be3e1da3e
|
||||
F ext/rtree/rtree2.test 9d9deddbb16fd0c30c36e6b4fdc3ee3132d765567f0f9432ee71e1303d32603d
|
||||
@@ -503,7 +503,7 @@ F ext/rtree/rtree6.test 2f5ffc69670395c1a84fad7924e2d49e82a25460c5293fb1e54e1aa9
|
||||
F ext/rtree/rtree7.test c8fb2e555b128dd0f0bdb520c61380014f497f8a23c40f2e820acc9f9e4fdce5
|
||||
F ext/rtree/rtree8.test 4da84c7f328bbdca15052fa13da6e8b8d426433347bf75fc85574c2f5a411a02
|
||||
F ext/rtree/rtree9.test fd3c9384ef8aabbc127b3878764070398f136eebc551cd20484b570f2cc1956a
|
||||
F ext/rtree/rtreeA.test 14e67fccc5b41efbad7ea99d21d11aaa66d2067da7d5b296ee86e4de64391d82
|
||||
F ext/rtree/rtreeA.test c90dd34792aeb7e15909530b5e0e91a8ad69f4d4b7bb52c14c4ef5c60050cace
|
||||
F ext/rtree/rtreeB.test ab93136c45cf25af78d22665c2a6d75068eef6bf3a710356e4ba8d5f37bed364
|
||||
F ext/rtree/rtreeC.test 2978b194d09b13e106bdb0e1c5b408b9d42eb338c1082bf43c87ef43bd626147
|
||||
F ext/rtree/rtreeD.test fe46aa7f012e137bd58294409b16c0d43976c3bb92c8f710481e577c4a1100dc
|
||||
@@ -545,7 +545,7 @@ F ext/session/sessionC.test de98b5e173fd86c79af0d0541534398d2ea75dc0d5d74a00103e
|
||||
F ext/session/sessionD.test 470ff917dc849e2eb78142ade63aaabd729d773833cff0ff01bca0eda68a21ce
|
||||
F ext/session/sessionE.test b2010949c9d7415306f64e3c2072ddabc4b8250c98478d3c0c4d064bce83111d
|
||||
F ext/session/sessionF.test d37ed800881e742c208df443537bf29aa49fd56eac520d0f0c6df3e6320f3401
|
||||
F ext/session/sessionG.test 3efe388282d641b65485b5462e67851002cd91a282dc95b685d085eb8efdad0a
|
||||
F ext/session/sessionG.test 64c2b69531aebdb36d5977a5f832d77e4c8bda0c746a6c630adf23660bb1c7c2
|
||||
F ext/session/sessionH.test 71bbff6b1abb2c4ac62b84dee53273c37e0b21e5fde3aed80929403e091ef859
|
||||
F ext/session/sessionI.test 11e7b6729fc942982a5104a40132f70a2e964d64d60dc5809b8206465af74822
|
||||
F ext/session/session_common.tcl a31f537a929a695a852d241c9434f2847cadf329856401921139fbb03a5a7697
|
||||
@@ -558,10 +558,11 @@ F ext/session/sessionblob.test 87faf667870b72f08e91969abd9f52a383ab7b514506ee194
|
||||
F ext/session/sessionchange.test 6618cb1c1338a4b6df173b6ac42d09623fb71269962abf23ebb7617fe9f45a50
|
||||
F ext/session/sessionchange2.test 6b5b7e3d1cc4ede43817f7fb68e3771aac4aa6500adb21a458b3e5a9fd841f83
|
||||
F ext/session/sessionconflict.test 19e4a53795c4c930bfec49e809311e09b2a9e202d9446e56d7a8b139046a0c07 x
|
||||
F ext/session/sessionconflict2.test d7f4caf59360dbca8a4698b9a3a322adf6f547f810ad41d131cacb730e02dd5e x
|
||||
F ext/session/sessiondiff.test e89f7aedcdd89e5ebac3a455224eb553a171e9586fc3e1e6a7b3388d2648ba8d
|
||||
F ext/session/sessionfault.test c2b43d01213b389a3f518e90775fca2120812ba51e50444c4066962263e45c11
|
||||
F ext/session/sessionfault2.test b0d6a7c1d7398a7e800d84657404909c7d385965ea8576dc79ed344c46fbf41c
|
||||
F ext/session/sessionfault3.test 9397819ec25b0960c5bc03c78613f9cb5cacc970f83e817aec1775c2a839a787
|
||||
F ext/session/sessionfault3.test aea5331fa6dbe5ca4e19826605e624c0e1767545411479f27c5ef82b41046925
|
||||
F ext/session/sessioninvert.test 7ccb7609a2c11e4e13e606df439bf3d484ba8e455d0bd3aa8d4828a940e1a242 x
|
||||
F ext/session/sessionmem.test f2a735db84a3e9e19f571033b725b0b2daf847f3f28b1da55a0c1a4e74f1de09
|
||||
F ext/session/sessionnoact.test 2cf060c12a7a23e663f0ec796561e58638c5c10a846653d37be886414b06ddc9
|
||||
@@ -572,9 +573,9 @@ F ext/session/sessionrowid.test 85187c2f1b38861a5844868126f69f9ec62223a03449a98a
|
||||
F ext/session/sessionsize.test 8fcf4685993c3dbaa46a24183940ab9f5aa9ed0d23e5fb63bfffbdb56134b795
|
||||
F ext/session/sessionstat1.test 5e718d5888c0c49bbb33a7a4f816366db85f59f6a4f97544a806421b85dc2dec
|
||||
F ext/session/sessionwor.test 6fd9a2256442cebde5b2284936ae9e0d54bde692d0f5fd009ecef8511f4cf3fc
|
||||
F ext/session/sqlite3session.c e36c91f273e4d2ce11c9e3aaba160038c9703cda1feeb79a96bb00f3de1a6d5e
|
||||
F ext/session/sqlite3session.h 063e7bf7be2fff874456f452a224b5b3013b25682d108933b0351c93a1279b9c
|
||||
F ext/session/test_session.c 9435a0d2c67b6c693bbf943657eeb83198efe06f796de80a6fd563013fa20bcc
|
||||
F ext/session/sqlite3session.c 1010718d9d88eeb1efd1bf3d6bcbc85e598e9d5bb094c2d202515622d2233481
|
||||
F ext/session/sqlite3session.h ca7c4422c1514a95056cc8d333217df6b1829d39058126b1de85d10cd62d7a9c
|
||||
F ext/session/test_session.c 95fbf8fc721fdb1a0d00268f930d6763c5299ed766b317f9dd3cf9ca5262e337
|
||||
F ext/wasm/EXPORTED_FUNCTIONS.fiddle 7fb73f7150ab79d83bb45a67d257553c905c78cd3d693101699243f36c5ae6c3
|
||||
F ext/wasm/GNUmakefile 68c750f173106d9d63f12c1edf1256c6f4bad9894b155da5db64322f4912de4b
|
||||
F ext/wasm/README-dist.txt f01081a850ce38a56706af6b481e3a7878e24e42b314cfcd4b129f0f8427066a
|
||||
@@ -691,7 +692,7 @@ F src/delete.c 1f2268d6fe3c78fc1bf794ba65d7026498b78e2342ffaf85825dedae546e6fde
|
||||
F src/expr.c 8a458928fe502526d4ce90fa4e084a6e1a4dceb6da747381973b5b0086b1492c
|
||||
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
|
||||
F src/fkey.c 931f74cec1dc8038a0217ef340c91ce147dd1bbed08dc40c47ee0ec6edfffb08
|
||||
F src/func.c 76e790b7b32895195e4c474259952d6a0ae10b53c0e35f4a2a2c4bc79eb29067
|
||||
F src/func.c 6f6abe78831fba92ebb5c3d347273f37656ae357f5902e1fb48aa297d57fbcc6
|
||||
F src/global.c a19e4b1ca1335f560e9560e590fc13081e21f670643367f99cb9e8f9dc7d615b
|
||||
F src/hash.c 03c8c0f4be9e8bcb6de65aa26d34a61d48a9430747084a69f9469fbb00ea52ca
|
||||
F src/hash.h 46b92795a95bfefb210f52f0c316e9d7cdbcdd7e7fcfb0d8be796d3a5767cddf
|
||||
@@ -823,7 +824,7 @@ F src/walker.c d5006d6b005e4ea7302ad390957a8d41ed83faa177e412f89bc5600a7462a014
|
||||
F src/where.c 6329f6f9c1e965f5927bf0da8cff552c73a1228a3344b1d5abdd3b713dc0f285
|
||||
F src/whereInt.h 8d94cb116c9e06205c3d5ac87af065fc044f8cf08bfdccd94b6ea1c1308e65da
|
||||
F src/wherecode.c bc39ccbe3648f01157038b16cc55bdbff128590972b7185521b5526dc2815765
|
||||
F src/whereexpr.c e167672449982d8b79bf3a2463324affc03399979689f735ac3a99ce87e14e2a
|
||||
F src/whereexpr.c 61b1c9d98d39f6bcd79fcb4ebb4c9478effb259fefac4c7d3e78e4957f97f75a
|
||||
F src/window.c c0a38cd32473e8e8e7bc435039f914a36ca42465506dc491c65870c01ddac9fb
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/affinity2.test 4d7a34d328e58ca2a2d78fd76c27614a41ca7ddf4312ded9c68c04f430b3b47d
|
||||
@@ -1169,7 +1170,7 @@ F test/fts3corrupt3.test 0d5b69a0998b4adf868cc301fc78f3d0707745f1d984ce044c205cd
|
||||
F test/fts3corrupt4.test c7f414fe29b97a478d15c90382c4ae077a2bbd2283bf8c63bf66dadaaed3edb8
|
||||
F test/fts3corrupt5.test 0549f85ec4bd22e992f645f13c59b99d652f2f5e643dac75568bfd23a6db7ed5
|
||||
F test/fts3corrupt6.test f417c910254f32c0bc9ead7affa991a1d5aec35b3b32a183ffb05eea78289525
|
||||
F test/fts3corrupt7.test 1da31776e24bb91d3c028e663456b61280b121a74496ccf2fef3fe33790ad2b0
|
||||
F test/fts3corrupt7.test 93622a4336b161a733accbd66311d93749660243cdda268fd647c21e1e680770
|
||||
F test/fts3cov.test 1e5ecea0e4c1394cea97adcfb9fd3d2d5998fd563dacf465f413e6c7fa5cffb3
|
||||
F test/fts3d.test 2bd8c97bcb9975f2334147173b4872505b6a41359a4f9068960a36afe07a679f
|
||||
F test/fts3defer.test f4c20e4c7153d20a98ee49ee5f3faef624fefc9a067f8d8d629db380c4d9f1de
|
||||
@@ -1508,7 +1509,7 @@ F test/parser1.test 131f4733472252d53d8ed681115257866f55740ab697fa05900d76604934
|
||||
F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
|
||||
F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
|
||||
F test/pendingrace.test e99efc5ab3584da3dfc8cd6a0ec4e5a42214820574f5ea24ee93f1d84655f463
|
||||
F test/percentile.test eaee1ff3e35d5fe933ac98d927c9a7d2f4f1a1a19ee22e7d45e97a6d9ee32077
|
||||
F test/percentile.test fd78896fa882fa4fbf693640097859721f3629926c2ccf804af5bcb7001fd35b
|
||||
F test/permutations.test e6de4f5777f7785737ac3d1d964b8656e5477a134665b2fe8a91884ab9b685b3
|
||||
F test/pg_common.tcl 3b27542224db1e713ae387459b5d117c836a5f6e328846922993b6d2b7640d9f
|
||||
F test/pragma.test 7d07b7bb76e273215d6a20c4f83c3062cc28976c737ccb70a686025801e86c8f
|
||||
@@ -1518,7 +1519,7 @@ F test/pragma4.test 396ef9bff1fb966d41721545ad4b12bfc26aae315f5fe51d9b917828d49e
|
||||
F test/pragma5.test 7b33fc43e2e41abf17f35fb73f71b49671a380ea92a6c94b6ce530a25f8d9102
|
||||
F test/pragma6.test c5ec577ba087954b4dfa619a3cbe97b155b60a0af487527abe89b10fc17e6512
|
||||
F test/pragmafault.test 275edaf3161771d37de60e5c2b412627ac94cef11739236bec12ed1258b240f8
|
||||
F test/prefixes.test f2c4f30d5d439ca203ec69d72f5e1cc408916104e5310423542faef8003cf013
|
||||
F test/prefixes.test fe52bb0c2fee10fb9b918a095d443ebf31dd48e5e2c7a1b21feba594fe7ff2d0
|
||||
F test/printf.test bcb093ef5cbd17e2d94d93d62045ee61ed0f465c1ca123f284774e474e73a9ea
|
||||
F test/printf2.test 3f55c1871a5a65507416076f6eb97e738d5210aeda7595a74ee895f2224cce60
|
||||
F test/progress.test ebab27f670bd0d4eb9d20d49cef96e68141d92fb
|
||||
@@ -2115,7 +2116,7 @@ F test/writecrash.test 13520af28f376bfc8c0bcd130efc1fff20bb165198e8b94cf153f1f75
|
||||
F test/zeroblob.test 7b74cefc7b281dfa2b07cd237987fbe94b4a2037a7771e9e83f2d5f608b1d99e
|
||||
F test/zeroblobfault.test 861d8191a0d944dfebb3cb4d2c5b4e46a5a119eaec5a63dd996c2389f8063441
|
||||
F test/zerodamage.test 9c41628db7e8d9e8a0181e59ea5f189df311a9f6ce99cc376dc461f66db6f8dc
|
||||
F test/zipfile.test 3aa05e7311f01679d81a2b49ddb7189b433dfda17580d7e8f9e904e87ee99440
|
||||
F test/zipfile.test 7b5c2f43ce9f7c882237e74822a331d94f4f83891e7a64c503519b434d06a5bf
|
||||
F test/zipfile2.test 21afaffcf4f7769df38bf16e4a9c4dfa6ba1b0f5b695f844ec61fafb92db0db7
|
||||
F test/zipfilefault.test 44d4d7a7f7cca7521d569d7f71026b241d65a6b1757aa409c1a168827edbbc2c
|
||||
F tool/GetFile.cs 47852aa0d806fe47ed1ac5138bdce7f000fe87aaa7f28107d0cb1e26682aeb44
|
||||
@@ -2212,8 +2213,8 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee
|
||||
F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2
|
||||
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
|
||||
F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
|
||||
P 123aa0530c4458a623ef5bc150a54299a87ac9b55e046ddb95748adbdfa7f6c1 46e74947f155199f5ce2440e8d5f19849d0daeb1d9d47381b0f84388901eab4c
|
||||
R a23e75becddc1d3563b5650ecb0cef2d
|
||||
P 158991b8ff7824594b21a2f94df0b958e786d0fe0ebc3c0ddaf723ce6df7b088 d6e03d8c777cfa2d35e3b60d8ec3e0187f3e9f99d8e2ee9cac695fd6fcdf1a24
|
||||
R 6b6ec335d2b439d7f29a44dcb30a24f8
|
||||
U drh
|
||||
Z f24fe033a873e9f7edd626fa3f371fdc
|
||||
Z 88aea04cdc6ea7e848d4d2e658f42e61
|
||||
# Remove this line to create a well-formed Fossil manifest.
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
158991b8ff7824594b21a2f94df0b958e786d0fe0ebc3c0ddaf723ce6df7b088
|
||||
2524c2f07a67ad792b1ff70fb702cedae7689a55d1ee3556d527363a9eec8c07
|
||||
|
||||
+39
-40
@@ -2956,47 +2956,46 @@ static void percentSort(double *a, unsigned int n){
|
||||
int iGt; /* Entries at or after a[iGt] are greater than rPivot */
|
||||
int i; /* Loop counter */
|
||||
double rPivot; /* The pivot value */
|
||||
|
||||
assert( n>=2 );
|
||||
if( a[0]>a[n-1] ){
|
||||
SWAP_DOUBLE(a[0],a[n-1])
|
||||
}
|
||||
if( n==2 ) return;
|
||||
iGt = n-1;
|
||||
i = n/2;
|
||||
if( a[0]>a[i] ){
|
||||
SWAP_DOUBLE(a[0],a[i])
|
||||
}else if( a[i]>a[iGt] ){
|
||||
SWAP_DOUBLE(a[i],a[iGt])
|
||||
}
|
||||
if( n==3 ) return;
|
||||
rPivot = a[i];
|
||||
iLt = i = 1;
|
||||
do{
|
||||
if( a[i]<rPivot ){
|
||||
if( i>iLt ) SWAP_DOUBLE(a[i],a[iLt])
|
||||
iLt++;
|
||||
i++;
|
||||
}else if( a[i]>rPivot ){
|
||||
do{
|
||||
iGt--;
|
||||
}while( iGt>i && a[iGt]>rPivot );
|
||||
SWAP_DOUBLE(a[i],a[iGt])
|
||||
}else{
|
||||
i++;
|
||||
}
|
||||
}while( i<iGt );
|
||||
if( iLt>=2 ) percentSort(a, iLt);
|
||||
if( n-iGt>=2 ) percentSort(a+iGt, n-iGt);
|
||||
|
||||
/* Uncomment for testing */
|
||||
#if 0
|
||||
for(i=0; i<n-1; i++){
|
||||
assert( a[i]<=a[i+1] );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
while( n>=2 ){
|
||||
if( a[0]>a[n-1] ){
|
||||
SWAP_DOUBLE(a[0],a[n-1])
|
||||
}
|
||||
if( n==2 ) return;
|
||||
iGt = n-1;
|
||||
i = n/2;
|
||||
if( a[0]>a[i] ){
|
||||
SWAP_DOUBLE(a[0],a[i])
|
||||
}else if( a[i]>a[iGt] ){
|
||||
SWAP_DOUBLE(a[i],a[iGt])
|
||||
}
|
||||
if( n==3 ) return;
|
||||
rPivot = a[i];
|
||||
iLt = i = 1;
|
||||
do{
|
||||
if( a[i]<rPivot ){
|
||||
if( i>iLt ) SWAP_DOUBLE(a[i],a[iLt])
|
||||
iLt++;
|
||||
i++;
|
||||
}else if( a[i]>rPivot ){
|
||||
do{
|
||||
iGt--;
|
||||
}while( iGt>i && a[iGt]>rPivot );
|
||||
SWAP_DOUBLE(a[i],a[iGt])
|
||||
}else{
|
||||
i++;
|
||||
}
|
||||
}while( i<iGt );
|
||||
if( iLt>n/2 ){
|
||||
if( n-iGt>=2 ) percentSort(a+iGt, n-iGt);
|
||||
n = iLt;
|
||||
}else{
|
||||
if( iLt>=2 ) percentSort(a, iLt);
|
||||
a += iGt;
|
||||
n -= iGt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** The "inverse" function for percentile(Y,P) is called to remove a
|
||||
|
||||
@@ -963,7 +963,6 @@ static void exprAnalyzeOrTerm(
|
||||
*/
|
||||
static int termIsEquivalence(Parse *pParse, Expr *pExpr, SrcList *pSrc){
|
||||
char aff1, aff2;
|
||||
CollSeq *pColl;
|
||||
if( !OptimizationEnabled(pParse->db, SQLITE_Transitive) ) return 0; /* (1) */
|
||||
if( pExpr->op!=TK_EQ && pExpr->op!=TK_IS ) return 0; /* (2) */
|
||||
if( ExprHasProperty(pExpr, EP_OuterON|EP_Collate) ) return 0; /* (3) */
|
||||
@@ -981,7 +980,6 @@ static int termIsEquivalence(Parse *pParse, Expr *pExpr, SrcList *pSrc){
|
||||
){
|
||||
return 0; /* (5) */
|
||||
}
|
||||
pColl = sqlite3ExprCompareCollSeq(pParse, pExpr);
|
||||
if( !sqlite3ExprCollSeqMatch(pParse, pExpr->pLeft, pExpr->pRight) ){
|
||||
return 0; /* (6) */
|
||||
}
|
||||
|
||||
@@ -277,4 +277,72 @@ do_catchsql_test 2.1 {
|
||||
SELECT 0 FROM t1 WHERE t1 MATCH 'rtree NEAR rtree"json1 enable"';
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
set DEPTH 40000
|
||||
|
||||
proc fts3_put_varint {v} {
|
||||
set l [list]
|
||||
while {$v >= 0x80} {
|
||||
lappend l [expr ($v & 0x7F) | 0x80]
|
||||
set v [expr $v >> 7]
|
||||
}
|
||||
lappend l [expr $v & 0x7F]
|
||||
|
||||
binary format cu* $l
|
||||
}
|
||||
|
||||
proc make_interior_node {height child_blockid} {
|
||||
binary format a*a* [fts3_put_varint $height] [fts3_put_varint $child_blockid]
|
||||
}
|
||||
db func make_interior_node make_interior_node
|
||||
|
||||
do_execsql_test 3.0 {
|
||||
CREATE VIRTUAL TABLE fts USING fts3(content TEXT);
|
||||
INSERT INTO fts(content) VALUES ('hello world');
|
||||
DELETE FROM fts_segdir;
|
||||
INSERT INTO fts_segdir
|
||||
(level, idx, start_block, leaves_end_block, end_block, root) VALUES
|
||||
(0, 0, 100, 100 + $DEPTH, 100 + $DEPTH, make_interior_node($DEPTH+1, 100));
|
||||
}
|
||||
|
||||
do_test 3.1 {
|
||||
execsql BEGIN
|
||||
for {set ii 0} {$ii<$DEPTH} {incr ii} {
|
||||
execsql {
|
||||
INSERT INTO fts_segments(blockid, block)
|
||||
VALUES($ii+100, make_interior_node($DEPTH-$ii, 101+$ii))
|
||||
}
|
||||
}
|
||||
execsql COMMIT
|
||||
} {}
|
||||
|
||||
do_catchsql_test 3.2 {
|
||||
SELECT * FROM fts WHERE fts MATCH 'x';
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
|
||||
do_execsql_test 4.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts4(content);
|
||||
DELETE FROM t1_segments;
|
||||
DELETE FROM t1_segdir;
|
||||
}
|
||||
|
||||
do_execsql_test 4.2 {
|
||||
INSERT INTO t1_segdir
|
||||
(level, idx, start_block, leaves_end_block, end_block, root)
|
||||
VALUES(0, 0, 0, 0, 0, X'000568656C6C6F08010201FFFFFF7F00');
|
||||
}
|
||||
|
||||
do_execsql_test 4.3 {
|
||||
CREATE VIRTUAL TABLE t1_terms USING fts4aux(t1);
|
||||
}
|
||||
|
||||
do_catchsql_test 4.4 {
|
||||
SELECT * FROM t1_terms;
|
||||
} {1 {database disk image is malformed}}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -592,4 +592,15 @@ do_execsql_test percentile-6.0 {
|
||||
SELECT median(iif(n%2,0.1,1.0)) FROM c;
|
||||
} 0.55
|
||||
|
||||
# Bug 2026-06-03T07:26:42Z
|
||||
# Use tail recursion in percentSort() to prevent stack overflow
|
||||
#
|
||||
do_execsql_test percentile-7.0 {
|
||||
WITH RECURSIVE t1(n,x) AS (
|
||||
VALUES(1,1.0)
|
||||
UNION ALL
|
||||
SELECT n+1, if(n%2,n*2.0,999998.0-2.0*n) FROM t1 WHERE n<1000000
|
||||
) SELECT median(x) FROM t1;
|
||||
} 499998.0
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -92,5 +92,9 @@ do_execsql_test 3.2 {
|
||||
SELECT prefix_length('abc',null);
|
||||
} 0
|
||||
|
||||
# Bug 2026-06-03T07:42:00Z
|
||||
do_execsql_test 3.3 {
|
||||
SELECT prefix_length(zeroblob(15000),zeroblob(5000));
|
||||
} 0
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -930,4 +930,15 @@ if {[catch {db one {SELECT length( zeroblob(1200000000) )}}]==0} {
|
||||
} {1 {out of memory}}
|
||||
}
|
||||
|
||||
# Bug 2026-06-03T10:58:51Z
|
||||
do_catchsql_test 25.0 {
|
||||
WITH t(v) AS (
|
||||
SELECT unhex(
|
||||
'504B03041400000000000000000000000000000000000000000001000000
|
||||
61504B01021E031400000000000000000000000000000000000000000001
|
||||
000000000000000000000000000000000061504B05060000000001000100
|
||||
2F0000001F0000000000',char(10,13,32))
|
||||
UNION ALL SELECT 'x'
|
||||
) SELECT z.name FROM t, zipfile(t.v) AS z;
|
||||
} {1 {cannot open file: x}}
|
||||
finish_test
|
||||
|
||||
Reference in New Issue
Block a user