Compare commits
109 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2343c7eb3f | |||
| 6890d3e458 | |||
| 5f7b95f73b | |||
| 2fa517bf2a | |||
| 2e52532217 | |||
| 8c5e8feb35 | |||
| 31a1349515 | |||
| 5db59b33c4 | |||
| 0e6f67b754 | |||
| 03626e3812 | |||
| 1650fcb1b5 | |||
| f6c69221e3 | |||
| f62154d4ac | |||
| 14494fa712 | |||
| 35497fccbe | |||
| fcbd6cdb41 | |||
| a4613e1f4c | |||
| baf5dec700 | |||
| 610e17bd91 | |||
| 0caad09585 | |||
| d59bcbdee7 | |||
| 78a9e92ecf | |||
| 9dc7ad1d7e | |||
| 1379521f4a | |||
| 6abc7a6b37 | |||
| 8e74e7ba4c | |||
| 18814dfb7e | |||
| cb9a364390 | |||
| e7eeeb99f7 | |||
| d26cc54183 | |||
| 51b15c3871 | |||
| f64ece143c | |||
| 6034d47618 | |||
| 27c8467734 | |||
| 2d2e4f3895 | |||
| cc04dacb85 | |||
| 50133dea28 | |||
| 9ec82ff2ba | |||
| 7aee83b935 | |||
| 6ee610bfc0 | |||
| cdf360a0d5 | |||
| 42e28f12a3 | |||
| 945b094632 | |||
| b7dab70a92 | |||
| fae58d51ce | |||
| d08406477a | |||
| 83ec2761fa | |||
| 8bb9fd3b45 | |||
| d930b5cb06 | |||
| 2b15f6e11b | |||
| 87f500ce43 | |||
| d447dced96 | |||
| 7271d7a19c | |||
| d47e1ccb57 | |||
| 2a86c1962c | |||
| 585ce1923c | |||
| 7888d14caa | |||
| 666d34c791 | |||
| 210b0d0eb3 | |||
| d1b2566b07 | |||
| bace324082 | |||
| 6aabff38e9 | |||
| 65c4c0b02d | |||
| 1b9fd2651b | |||
| 38305ab55f | |||
| 35f30d3398 | |||
| eacb4412b1 | |||
| 7f11afaba1 | |||
| 451e76d5b5 | |||
| 3b2936fada | |||
| 184d902db1 | |||
| 36cae856ee | |||
| cd64553015 | |||
| dfd0de82f1 | |||
| befcd8ad84 | |||
| 508286701a | |||
| 4df65fc20f | |||
| 8674e49214 | |||
| 2f31d02049 | |||
| aac853bae8 | |||
| d3e73d6c1c | |||
| 7059138a08 | |||
| 8a9e83583b | |||
| 25fd2e247b | |||
| c711f53f30 | |||
| de48a3f98a | |||
| da060052e3 | |||
| ce31643fd9 | |||
| ef2f5925d8 | |||
| be7a0cee4e | |||
| 7b20a15d13 | |||
| ae051a8970 | |||
| 70d90a2c37 | |||
| b701c9a6c3 | |||
| 24be549329 | |||
| ea8f0a155e | |||
| ba47020b63 | |||
| e206ea7f48 | |||
| 2c6fec21dc | |||
| 372f942f17 | |||
| 785d8ed0d4 | |||
| f91c1318f4 | |||
| f112f0b3de | |||
| 8e36ddd37e | |||
| 1d21bac8aa | |||
| d9bcb32ebb | |||
| e4a8b8769e | |||
| 7441df72be | |||
| ab61cf7d93 |
+7
-2
@@ -1177,8 +1177,13 @@ LogEst$(TEXE): $(TOP)/tool/logest.c sqlite3.h
|
||||
wordcount$(TEXE): $(TOP)/test/wordcount.c sqlite3.lo
|
||||
$(LTLINK) -o $@ $(TOP)/test/wordcount.c sqlite3.lo $(TLIBS)
|
||||
|
||||
speedtest1$(TEXE): $(TOP)/test/speedtest1.c sqlite3.lo
|
||||
$(LTLINK) -o $@ $(TOP)/test/speedtest1.c sqlite3.lo $(TLIBS)
|
||||
speedtest1$(TEXE): $(TOP)/test/speedtest1.c sqlite3.c
|
||||
$(LTLINK) $(ST_OPT) -o $@ $(TOP)/test/speedtest1.c sqlite3.c $(TLIBS)
|
||||
|
||||
KV_OPT += -DSQLITE_DIRECT_OVERFLOW_READ
|
||||
|
||||
kvtest$(TEXE): $(TOP)/test/kvtest.c sqlite3.c
|
||||
$(LTLINK) $(KV_OPT) -o $@ $(TOP)/test/kvtest.c sqlite3.c $(TLIBS)
|
||||
|
||||
rbu$(EXE): $(TOP)/ext/rbu/rbu.c $(TOP)/ext/rbu/sqlite3rbu.c sqlite3.lo
|
||||
$(LTLINK) -I. -o $@ $(TOP)/ext/rbu/rbu.c sqlite3.lo $(TLIBS)
|
||||
|
||||
@@ -24,6 +24,13 @@ USE_AMALGAMATION = 1
|
||||
USE_FULLWARN = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to enable treating warnings as errors (-WX, etc) when
|
||||
# compiling.
|
||||
#
|
||||
!IFNDEF USE_FATAL_WARN
|
||||
USE_FATAL_WARN = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to enable full runtime error checks (-RTC1, etc). This
|
||||
# has no effect if (any) optimizations are enabled.
|
||||
#
|
||||
@@ -493,6 +500,12 @@ TCC = $(CC) -nologo -W4 -DINCLUDE_MSVC_H=1 $(CCOPTS) $(TCCOPTS)
|
||||
TCC = $(CC) -nologo -W3 $(CCOPTS) $(TCCOPTS)
|
||||
!ENDIF
|
||||
|
||||
# Check if warnings should be treated as errors when compiling.
|
||||
#
|
||||
!IF $(USE_FATAL_WARN)!=0
|
||||
TCC = $(TCC) -WX
|
||||
!ENDIF
|
||||
|
||||
TCC = $(TCC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) -I$(TOP)\src -fp:precise
|
||||
RCC = $(RC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) -I$(TOP)\src $(RCOPTS) $(RCCOPTS)
|
||||
|
||||
|
||||
@@ -24,6 +24,13 @@ TOP = .
|
||||
USE_FULLWARN = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to enable treating warnings as errors (-WX, etc) when
|
||||
# compiling.
|
||||
#
|
||||
!IFNDEF USE_FATAL_WARN
|
||||
USE_FATAL_WARN = 0
|
||||
!ENDIF
|
||||
|
||||
# Set this non-0 to enable full runtime error checks (-RTC1, etc). This
|
||||
# has no effect if (any) optimizations are enabled.
|
||||
#
|
||||
@@ -454,6 +461,12 @@ TCC = $(CC) -nologo -W4 -DINCLUDE_MSVC_H=1 $(CCOPTS) $(TCCOPTS)
|
||||
TCC = $(CC) -nologo -W3 $(CCOPTS) $(TCCOPTS)
|
||||
!ENDIF
|
||||
|
||||
# Check if warnings should be treated as errors when compiling.
|
||||
#
|
||||
!IF $(USE_FATAL_WARN)!=0
|
||||
TCC = $(TCC) -WX
|
||||
!ENDIF
|
||||
|
||||
TCC = $(TCC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) -fp:precise
|
||||
RCC = $(RC) -DSQLITE_OS_WIN=1 -I. -I$(TOP) $(RCOPTS) $(RCCOPTS)
|
||||
|
||||
|
||||
+4
-4
@@ -1363,9 +1363,9 @@ static int fts3InitVtab(
|
||||
p->pTokenizer = pTokenizer;
|
||||
p->nMaxPendingData = FTS3_MAX_PENDING_DATA;
|
||||
p->bHasDocsize = (isFts4 && bNoDocsize==0);
|
||||
p->bHasStat = isFts4;
|
||||
p->bFts4 = isFts4;
|
||||
p->bDescIdx = bDescIdx;
|
||||
p->bHasStat = (u8)isFts4;
|
||||
p->bFts4 = (u8)isFts4;
|
||||
p->bDescIdx = (u8)bDescIdx;
|
||||
p->nAutoincrmerge = 0xff; /* 0xff means setting unknown */
|
||||
p->zContentTbl = zContent;
|
||||
p->zLanguageid = zLanguageid;
|
||||
@@ -3422,7 +3422,7 @@ static int fts3SetHasStat(Fts3Table *p){
|
||||
if( rc==SQLITE_OK ){
|
||||
int bHasStat = (sqlite3_step(pStmt)==SQLITE_ROW);
|
||||
rc = sqlite3_finalize(pStmt);
|
||||
if( rc==SQLITE_OK ) p->bHasStat = bHasStat;
|
||||
if( rc==SQLITE_OK ) p->bHasStat = (u8)bHasStat;
|
||||
}
|
||||
sqlite3_free(zSql);
|
||||
}else{
|
||||
|
||||
@@ -1601,7 +1601,7 @@ Fts5ExprPhrase *sqlite3Fts5ParseTerm(
|
||||
|
||||
rc = fts5ParseStringFromToken(pToken, &z);
|
||||
if( rc==SQLITE_OK ){
|
||||
int flags = FTS5_TOKENIZE_QUERY | (bPrefix ? FTS5_TOKENIZE_QUERY : 0);
|
||||
int flags = FTS5_TOKENIZE_QUERY | (bPrefix ? FTS5_TOKENIZE_PREFIX : 0);
|
||||
int n;
|
||||
sqlite3Fts5Dequote(z);
|
||||
n = (int)strlen(z);
|
||||
|
||||
@@ -152,7 +152,7 @@ foreach {tn expr res} {
|
||||
1 {abc} {"abc"}
|
||||
2 {one} {"one"|"i"|"1"}
|
||||
3 {3} {"3"|"iii"|"three"}
|
||||
4 {3*} {"3"|"iii"|"three" *}
|
||||
4 {3*} {"3" *}
|
||||
} {
|
||||
do_execsql_test 4.1.$tn {
|
||||
SELECT fts5_expr($expr, 'tokenize=tclnum')
|
||||
|
||||
@@ -262,5 +262,44 @@ do_execsql_test 8.3 {
|
||||
brown dog fox jump lazi over quick the
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that the FTS5_TOKENIZE_PREFIX flag is passed to the tokenizer
|
||||
# implementation.
|
||||
#
|
||||
reset_db
|
||||
proc tcl_create {args} { return "tcl_tokenize" }
|
||||
sqlite3_fts5_create_tokenizer db tcl tcl_create
|
||||
set ::flags [list]
|
||||
proc tcl_tokenize {tflags text} {
|
||||
lappend ::flags $tflags
|
||||
foreach {w iStart iEnd} [fts5_tokenize_split $text] {
|
||||
sqlite3_fts5_token $w $iStart $iEnd
|
||||
}
|
||||
}
|
||||
|
||||
do_execsql_test 9.1.1 {
|
||||
CREATE VIRTUAL TABLE t1 USING fts5(a, tokenize=tcl);
|
||||
INSERT INTO t1 VALUES('abc');
|
||||
INSERT INTO t1 VALUES('xyz');
|
||||
} {}
|
||||
do_test 9.1.2 { set ::flags } {document document}
|
||||
|
||||
set ::flags [list]
|
||||
do_execsql_test 9.2.1 { SELECT * FROM t1('abc'); } {abc}
|
||||
do_test 9.2.2 { set ::flags } {query}
|
||||
|
||||
set ::flags [list]
|
||||
do_execsql_test 9.3.1 { SELECT * FROM t1('ab*'); } {abc}
|
||||
do_test 9.3.2 { set ::flags } {prefixquery}
|
||||
|
||||
set ::flags [list]
|
||||
do_execsql_test 9.4.1 { SELECT * FROM t1('"abc xyz" *'); } {}
|
||||
do_test 9.4.2 { set ::flags } {prefixquery}
|
||||
|
||||
set ::flags [list]
|
||||
do_execsql_test 9.5.1 { SELECT * FROM t1('"abc xyz*"'); } {}
|
||||
do_test 9.5.2 { set ::flags } {query}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
+21
-23
@@ -493,38 +493,36 @@ static void icuLoadCollation(
|
||||
** Register the ICU extension functions with database db.
|
||||
*/
|
||||
int sqlite3IcuInit(sqlite3 *db){
|
||||
struct IcuScalar {
|
||||
static const struct IcuScalar {
|
||||
const char *zName; /* Function name */
|
||||
int nArg; /* Number of arguments */
|
||||
int enc; /* Optimal text encoding */
|
||||
void *pContext; /* sqlite3_user_data() context */
|
||||
unsigned char nArg; /* Number of arguments */
|
||||
unsigned short enc; /* Optimal text encoding */
|
||||
unsigned char iContext; /* sqlite3_user_data() context */
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
|
||||
} scalars[] = {
|
||||
{"regexp", 2, SQLITE_ANY|SQLITE_DETERMINISTIC, 0, icuRegexpFunc},
|
||||
|
||||
{"lower", 1, SQLITE_UTF16|SQLITE_DETERMINISTIC, 0, icuCaseFunc16},
|
||||
{"lower", 2, SQLITE_UTF16|SQLITE_DETERMINISTIC, 0, icuCaseFunc16},
|
||||
{"upper", 1, SQLITE_UTF16|SQLITE_DETERMINISTIC, (void*)1, icuCaseFunc16},
|
||||
{"upper", 2, SQLITE_UTF16|SQLITE_DETERMINISTIC, (void*)1, icuCaseFunc16},
|
||||
|
||||
{"lower", 1, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuCaseFunc16},
|
||||
{"lower", 2, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuCaseFunc16},
|
||||
{"upper", 1, SQLITE_UTF8|SQLITE_DETERMINISTIC, (void*)1, icuCaseFunc16},
|
||||
{"upper", 2, SQLITE_UTF8|SQLITE_DETERMINISTIC, (void*)1, icuCaseFunc16},
|
||||
|
||||
{"like", 2, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuLikeFunc},
|
||||
{"like", 3, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuLikeFunc},
|
||||
|
||||
{"icu_load_collation", 2, SQLITE_UTF8, (void*)db, icuLoadCollation},
|
||||
{"icu_load_collation", 2, SQLITE_UTF8, 1, icuLoadCollation},
|
||||
{"regexp", 2, SQLITE_ANY|SQLITE_DETERMINISTIC, 0, icuRegexpFunc},
|
||||
{"lower", 1, SQLITE_UTF16|SQLITE_DETERMINISTIC, 0, icuCaseFunc16},
|
||||
{"lower", 2, SQLITE_UTF16|SQLITE_DETERMINISTIC, 0, icuCaseFunc16},
|
||||
{"upper", 1, SQLITE_UTF16|SQLITE_DETERMINISTIC, 1, icuCaseFunc16},
|
||||
{"upper", 2, SQLITE_UTF16|SQLITE_DETERMINISTIC, 1, icuCaseFunc16},
|
||||
{"lower", 1, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuCaseFunc16},
|
||||
{"lower", 2, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuCaseFunc16},
|
||||
{"upper", 1, SQLITE_UTF8|SQLITE_DETERMINISTIC, 1, icuCaseFunc16},
|
||||
{"upper", 2, SQLITE_UTF8|SQLITE_DETERMINISTIC, 1, icuCaseFunc16},
|
||||
{"like", 2, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuLikeFunc},
|
||||
{"like", 3, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, icuLikeFunc},
|
||||
};
|
||||
|
||||
int rc = SQLITE_OK;
|
||||
int i;
|
||||
|
||||
|
||||
for(i=0; rc==SQLITE_OK && i<(int)(sizeof(scalars)/sizeof(scalars[0])); i++){
|
||||
struct IcuScalar *p = &scalars[i];
|
||||
const struct IcuScalar *p = &scalars[i];
|
||||
rc = sqlite3_create_function(
|
||||
db, p->zName, p->nArg, p->enc, p->pContext, p->xFunc, 0, 0
|
||||
db, p->zName, p->nArg, p->enc,
|
||||
p->iContext ? (void*)db : (void*)0,
|
||||
p->xFunc, 0, 0
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@
|
||||
** how JSONB might improve on that.)
|
||||
*/
|
||||
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_JSON1)
|
||||
#if !defined(_SQLITEINT_H_)
|
||||
#if !defined(SQLITEINT_H)
|
||||
#include "sqlite3ext.h"
|
||||
#endif
|
||||
SQLITE_EXTENSION_INIT1
|
||||
|
||||
+399
@@ -0,0 +1,399 @@
|
||||
/*
|
||||
** 2017-01-27
|
||||
**
|
||||
** 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 SQLite extension implements a functions that compute SHA1 hashes.
|
||||
** Two SQL functions are implemented:
|
||||
**
|
||||
** sha1(X)
|
||||
** sha1_query(Y)
|
||||
**
|
||||
** The sha1(X) function computes the SHA1 hash of the input X, or NULL if
|
||||
** X is NULL.
|
||||
**
|
||||
** The sha1_query(Y) function evalutes all queries in the SQL statements of Y
|
||||
** and returns a hash of their results.
|
||||
*/
|
||||
#include "sqlite3ext.h"
|
||||
SQLITE_EXTENSION_INIT1
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/******************************************************************************
|
||||
** The Hash Engine
|
||||
*/
|
||||
/* Context for the SHA1 hash */
|
||||
typedef struct SHA1Context SHA1Context;
|
||||
struct SHA1Context {
|
||||
unsigned int state[5];
|
||||
unsigned int count[2];
|
||||
unsigned char buffer[64];
|
||||
};
|
||||
|
||||
|
||||
#if __GNUC__ && (defined(__i386__) || defined(__x86_64__))
|
||||
/*
|
||||
* GCC by itself only generates left rotates. Use right rotates if
|
||||
* possible to be kinder to dinky implementations with iterative rotate
|
||||
* instructions.
|
||||
*/
|
||||
#define SHA_ROT(op, x, k) \
|
||||
({ unsigned int y; asm(op " %1,%0" : "=r" (y) : "I" (k), "0" (x)); y; })
|
||||
#define rol(x,k) SHA_ROT("roll", x, k)
|
||||
#define ror(x,k) SHA_ROT("rorl", x, k)
|
||||
|
||||
#else
|
||||
/* Generic C equivalent */
|
||||
#define SHA_ROT(x,l,r) ((x) << (l) | (x) >> (r))
|
||||
#define rol(x,k) SHA_ROT(x,k,32-(k))
|
||||
#define ror(x,k) SHA_ROT(x,32-(k),k)
|
||||
#endif
|
||||
|
||||
|
||||
#define blk0le(i) (block[i] = (ror(block[i],8)&0xFF00FF00) \
|
||||
|(rol(block[i],8)&0x00FF00FF))
|
||||
#define blk0be(i) block[i]
|
||||
#define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15] \
|
||||
^block[(i+2)&15]^block[i&15],1))
|
||||
|
||||
/*
|
||||
* (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1
|
||||
*
|
||||
* Rl0() for little-endian and Rb0() for big-endian. Endianness is
|
||||
* determined at run-time.
|
||||
*/
|
||||
#define Rl0(v,w,x,y,z,i) \
|
||||
z+=((w&(x^y))^y)+blk0le(i)+0x5A827999+rol(v,5);w=ror(w,2);
|
||||
#define Rb0(v,w,x,y,z,i) \
|
||||
z+=((w&(x^y))^y)+blk0be(i)+0x5A827999+rol(v,5);w=ror(w,2);
|
||||
#define R1(v,w,x,y,z,i) \
|
||||
z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=ror(w,2);
|
||||
#define R2(v,w,x,y,z,i) \
|
||||
z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=ror(w,2);
|
||||
#define R3(v,w,x,y,z,i) \
|
||||
z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=ror(w,2);
|
||||
#define R4(v,w,x,y,z,i) \
|
||||
z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=ror(w,2);
|
||||
|
||||
/*
|
||||
* Hash a single 512-bit block. This is the core of the algorithm.
|
||||
*/
|
||||
#define a qq[0]
|
||||
#define b qq[1]
|
||||
#define c qq[2]
|
||||
#define d qq[3]
|
||||
#define e qq[4]
|
||||
|
||||
void SHA1Transform(unsigned int state[5], const unsigned char buffer[64]){
|
||||
unsigned int qq[5]; /* a, b, c, d, e; */
|
||||
static int one = 1;
|
||||
unsigned int block[16];
|
||||
memcpy(block, buffer, 64);
|
||||
memcpy(qq,state,5*sizeof(unsigned int));
|
||||
|
||||
/* Copy p->state[] to working vars */
|
||||
/*
|
||||
a = state[0];
|
||||
b = state[1];
|
||||
c = state[2];
|
||||
d = state[3];
|
||||
e = state[4];
|
||||
*/
|
||||
|
||||
/* 4 rounds of 20 operations each. Loop unrolled. */
|
||||
if( 1 == *(unsigned char*)&one ){
|
||||
Rl0(a,b,c,d,e, 0); Rl0(e,a,b,c,d, 1); Rl0(d,e,a,b,c, 2); Rl0(c,d,e,a,b, 3);
|
||||
Rl0(b,c,d,e,a, 4); Rl0(a,b,c,d,e, 5); Rl0(e,a,b,c,d, 6); Rl0(d,e,a,b,c, 7);
|
||||
Rl0(c,d,e,a,b, 8); Rl0(b,c,d,e,a, 9); Rl0(a,b,c,d,e,10); Rl0(e,a,b,c,d,11);
|
||||
Rl0(d,e,a,b,c,12); Rl0(c,d,e,a,b,13); Rl0(b,c,d,e,a,14); Rl0(a,b,c,d,e,15);
|
||||
}else{
|
||||
Rb0(a,b,c,d,e, 0); Rb0(e,a,b,c,d, 1); Rb0(d,e,a,b,c, 2); Rb0(c,d,e,a,b, 3);
|
||||
Rb0(b,c,d,e,a, 4); Rb0(a,b,c,d,e, 5); Rb0(e,a,b,c,d, 6); Rb0(d,e,a,b,c, 7);
|
||||
Rb0(c,d,e,a,b, 8); Rb0(b,c,d,e,a, 9); Rb0(a,b,c,d,e,10); Rb0(e,a,b,c,d,11);
|
||||
Rb0(d,e,a,b,c,12); Rb0(c,d,e,a,b,13); Rb0(b,c,d,e,a,14); Rb0(a,b,c,d,e,15);
|
||||
}
|
||||
R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
|
||||
R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
|
||||
R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
|
||||
R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
|
||||
R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
|
||||
R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
|
||||
R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
|
||||
R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
|
||||
R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
|
||||
R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
|
||||
R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
|
||||
R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
|
||||
R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
|
||||
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
|
||||
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
|
||||
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
|
||||
|
||||
/* Add the working vars back into context.state[] */
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
state[4] += e;
|
||||
}
|
||||
|
||||
|
||||
/* Initialize a SHA1 context */
|
||||
static void hash_init(SHA1Context *p){
|
||||
/* SHA1 initialization constants */
|
||||
p->state[0] = 0x67452301;
|
||||
p->state[1] = 0xEFCDAB89;
|
||||
p->state[2] = 0x98BADCFE;
|
||||
p->state[3] = 0x10325476;
|
||||
p->state[4] = 0xC3D2E1F0;
|
||||
p->count[0] = p->count[1] = 0;
|
||||
}
|
||||
|
||||
/* Add new content to the SHA1 hash */
|
||||
static void hash_step(
|
||||
SHA1Context *p, /* Add content to this context */
|
||||
const unsigned char *data, /* Data to be added */
|
||||
unsigned int len /* Number of bytes in data */
|
||||
){
|
||||
unsigned int i, j;
|
||||
|
||||
j = p->count[0];
|
||||
if( (p->count[0] += len << 3) < j ){
|
||||
p->count[1] += (len>>29)+1;
|
||||
}
|
||||
j = (j >> 3) & 63;
|
||||
if( (j + len) > 63 ){
|
||||
(void)memcpy(&p->buffer[j], data, (i = 64-j));
|
||||
SHA1Transform(p->state, p->buffer);
|
||||
for(; i + 63 < len; i += 64){
|
||||
SHA1Transform(p->state, &data[i]);
|
||||
}
|
||||
j = 0;
|
||||
}else{
|
||||
i = 0;
|
||||
}
|
||||
(void)memcpy(&p->buffer[j], &data[i], len - i);
|
||||
}
|
||||
|
||||
/* Compute a string using sqlite3_vsnprintf() and hash it */
|
||||
static void hash_step_vformat(
|
||||
SHA1Context *p, /* Add content to this context */
|
||||
const char *zFormat,
|
||||
...
|
||||
){
|
||||
va_list ap;
|
||||
int n;
|
||||
char zBuf[50];
|
||||
va_start(ap, zFormat);
|
||||
sqlite3_vsnprintf(sizeof(zBuf),zBuf,zFormat,ap);
|
||||
va_end(ap);
|
||||
n = (int)strlen(zBuf);
|
||||
hash_step(p, (unsigned char*)zBuf, n);
|
||||
}
|
||||
|
||||
|
||||
/* Add padding and compute the message digest. Render the
|
||||
** message digest as lower-case hexadecimal and put it into
|
||||
** zOut[]. zOut[] must be at least 41 bytes long. */
|
||||
static void hash_finish(
|
||||
SHA1Context *p, /* The SHA1 context to finish and render */
|
||||
char *zOut /* Store hexadecimal hash here */
|
||||
){
|
||||
unsigned int i;
|
||||
unsigned char finalcount[8];
|
||||
unsigned char digest[20];
|
||||
static const char zEncode[] = "0123456789abcdef";
|
||||
|
||||
for (i = 0; i < 8; i++){
|
||||
finalcount[i] = (unsigned char)((p->count[(i >= 4 ? 0 : 1)]
|
||||
>> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
|
||||
}
|
||||
hash_step(p, (const unsigned char *)"\200", 1);
|
||||
while ((p->count[0] & 504) != 448){
|
||||
hash_step(p, (const unsigned char *)"\0", 1);
|
||||
}
|
||||
hash_step(p, finalcount, 8); /* Should cause a SHA1Transform() */
|
||||
for (i = 0; i < 20; i++){
|
||||
digest[i] = (unsigned char)((p->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
|
||||
}
|
||||
for(i=0; i<20; i++){
|
||||
zOut[i*2] = zEncode[(digest[i]>>4)&0xf];
|
||||
zOut[i*2+1] = zEncode[digest[i] & 0xf];
|
||||
}
|
||||
zOut[i*2]= 0;
|
||||
}
|
||||
/* End of the hashing logic
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
** Implementation of the sha1(X) function.
|
||||
**
|
||||
** Return a lower-case hexadecimal rendering of the SHA1 hash of the
|
||||
** argument X. If X is a BLOB, it is hashed as is. For all other
|
||||
** types of input, X is converted into a UTF-8 string and the string
|
||||
** is hash without the trailing 0x00 terminator. The hash of a NULL
|
||||
** value is NULL.
|
||||
*/
|
||||
static void sha1Func(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
SHA1Context cx;
|
||||
int eType = sqlite3_value_type(argv[0]);
|
||||
int nByte = sqlite3_value_bytes(argv[0]);
|
||||
char zOut[44];
|
||||
|
||||
if( eType==SQLITE_NULL ) return;
|
||||
hash_init(&cx);
|
||||
if( eType==SQLITE_BLOB ){
|
||||
hash_step(&cx, sqlite3_value_blob(argv[0]), nByte);
|
||||
}else{
|
||||
hash_step(&cx, sqlite3_value_text(argv[0]), nByte);
|
||||
}
|
||||
hash_finish(&cx, zOut);
|
||||
sqlite3_result_text(context, zOut, 40, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the sha1_query(SQL) function.
|
||||
**
|
||||
** This function compiles and runs the SQL statement(s) given in the
|
||||
** argument. The results are hashed using SHA1 and that hash is returned.
|
||||
**
|
||||
** The original SQL text is included as part of the hash.
|
||||
**
|
||||
** The hash is not just a concatenation of the outputs. Each query
|
||||
** is delimited and each row and value within the query is delimited,
|
||||
** with all values being marked with their datatypes.
|
||||
*/
|
||||
static void sha1QueryFunc(
|
||||
sqlite3_context *context,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
sqlite3 *db = sqlite3_context_db_handle(context);
|
||||
const char *zSql = (const char*)sqlite3_value_text(argv[0]);
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
int nCol; /* Number of columns in the result set */
|
||||
int i; /* Loop counter */
|
||||
int rc;
|
||||
int n;
|
||||
const char *z;
|
||||
SHA1Context cx;
|
||||
char zOut[44];
|
||||
|
||||
if( zSql==0 ) return;
|
||||
hash_init(&cx);
|
||||
while( zSql[0] ){
|
||||
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zSql);
|
||||
if( rc ){
|
||||
char *zMsg = sqlite3_mprintf("error SQL statement [%s]: %s",
|
||||
zSql, sqlite3_errmsg(db));
|
||||
sqlite3_finalize(pStmt);
|
||||
sqlite3_result_error(context, zMsg, -1);
|
||||
sqlite3_free(zMsg);
|
||||
return;
|
||||
}
|
||||
if( !sqlite3_stmt_readonly(pStmt) ){
|
||||
char *zMsg = sqlite3_mprintf("non-query: [%s]", sqlite3_sql(pStmt));
|
||||
sqlite3_finalize(pStmt);
|
||||
sqlite3_result_error(context, zMsg, -1);
|
||||
sqlite3_free(zMsg);
|
||||
return;
|
||||
}
|
||||
nCol = sqlite3_column_count(pStmt);
|
||||
z = sqlite3_sql(pStmt);
|
||||
n = (int)strlen(z);
|
||||
hash_step_vformat(&cx,"S%d:",n);
|
||||
hash_step(&cx,(unsigned char*)z,n);
|
||||
|
||||
/* Compute a hash over the result of the query */
|
||||
while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
||||
hash_step(&cx,(const unsigned char*)"R",1);
|
||||
for(i=0; i<nCol; i++){
|
||||
switch( sqlite3_column_type(pStmt,i) ){
|
||||
case SQLITE_NULL: {
|
||||
hash_step(&cx, (const unsigned char*)"N",1);
|
||||
break;
|
||||
}
|
||||
case SQLITE_INTEGER: {
|
||||
sqlite3_uint64 u;
|
||||
int j;
|
||||
unsigned char x[9];
|
||||
sqlite3_int64 v = sqlite3_column_int64(pStmt,i);
|
||||
memcpy(&u, &v, 8);
|
||||
for(j=8; j>=1; j--){
|
||||
x[j] = u & 0xff;
|
||||
u >>= 8;
|
||||
}
|
||||
x[0] = 'I';
|
||||
hash_step(&cx, x, 9);
|
||||
break;
|
||||
}
|
||||
case SQLITE_FLOAT: {
|
||||
sqlite3_uint64 u;
|
||||
int j;
|
||||
unsigned char x[8];
|
||||
double r = sqlite3_column_double(pStmt,i);
|
||||
memcpy(&u, &r, 8);
|
||||
for(j=8; j>=1; j--){
|
||||
x[j] = u & 0xff;
|
||||
u >>= 8;
|
||||
}
|
||||
x[0] = 'F';
|
||||
hash_step(&cx,x,9);
|
||||
break;
|
||||
}
|
||||
case SQLITE_TEXT: {
|
||||
int n = sqlite3_column_bytes(pStmt, i);
|
||||
const unsigned char *z = sqlite3_column_text(pStmt, i);
|
||||
hash_step_vformat(&cx,"T%d:",n);
|
||||
hash_step(&cx, z, n);
|
||||
break;
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
int n = sqlite3_column_bytes(pStmt, i);
|
||||
const unsigned char *z = sqlite3_column_blob(pStmt, i);
|
||||
hash_step_vformat(&cx,"B%d:",n);
|
||||
hash_step(&cx, z, n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
}
|
||||
hash_finish(&cx, zOut);
|
||||
sqlite3_result_text(context, zOut, 40, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
int sqlite3_sha_init(
|
||||
sqlite3 *db,
|
||||
char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
SQLITE_EXTENSION_INIT2(pApi);
|
||||
(void)pzErrMsg; /* Unused parameter */
|
||||
rc = sqlite3_create_function(db, "sha1", 1, SQLITE_UTF8, 0,
|
||||
sha1Func, 0, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3_create_function(db, "sha1_query", 1, SQLITE_UTF8, 0,
|
||||
sha1QueryFunc, 0, 0);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -95,6 +95,7 @@ static int vtshimCreate(
|
||||
if( rc ){
|
||||
sqlite3_free(pNew);
|
||||
*ppVtab = 0;
|
||||
return rc;
|
||||
}
|
||||
pNew->pAux = pAux;
|
||||
pNew->ppPrev = &pAux->pAllVtab;
|
||||
@@ -133,6 +134,7 @@ static int vtshimConnect(
|
||||
if( rc ){
|
||||
sqlite3_free(pNew);
|
||||
*ppVtab = 0;
|
||||
return rc;
|
||||
}
|
||||
pNew->pAux = pAux;
|
||||
pNew->ppPrev = &pAux->pAllVtab;
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
# 2014 October 22
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
if {![info exists testdir]} {
|
||||
set testdir [file join [file dirname [info script]] .. .. test]
|
||||
}
|
||||
source $testdir/tester.tcl
|
||||
source $testdir/malloc_common.tcl
|
||||
set ::testprefix rbufault4
|
||||
|
||||
for {set tn 1} {1} {incr tn} {
|
||||
reset_db
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
|
||||
CREATE INDEX i1b ON t1(b);
|
||||
CREATE INDEX i1c ON t1(c);
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
INSERT INTO t1 VALUES(4, 5, 6);
|
||||
}
|
||||
|
||||
forcedelete test.db2
|
||||
sqlite3rbu_vacuum rbu test.db test.db2
|
||||
for {set i 0} {$i < $tn} {incr i} { rbu step }
|
||||
set rc [rbu close]
|
||||
if {$rc!="SQLITE_OK"} {
|
||||
if {$rc!="SQLITE_DONE"} {error $rc}
|
||||
break
|
||||
}
|
||||
faultsim_save
|
||||
|
||||
do_faultsim_test $tn -faults oom-t* -prep {
|
||||
faultsim_restore
|
||||
} -body {
|
||||
sqlite3rbu_vacuum rbu test.db test.db2
|
||||
while 1 {
|
||||
set rc [rbu step]
|
||||
if {$rc=="SQLITE_DONE"} break
|
||||
if {$rc!="SQLITE_OK"} { error $rc }
|
||||
}
|
||||
} -test {
|
||||
catch {rbu close}
|
||||
faultsim_test_result {0 {}} {1 SQLITE_NOMEM} {1 SQLITE_IOERR_NOMEM}
|
||||
|
||||
sqlite3rbu_vacuum rbu test.db test.db2
|
||||
while {[rbu step]=="SQLITE_OK"} {}
|
||||
set trc [rbu close]
|
||||
if {$trc!="SQLITE_DONE"} { error "Got $trc instead of SQLITE_DONE!" }
|
||||
|
||||
set rc [db one {PRAGMA integrity_check}]
|
||||
if {$rc!="ok"} { error "Got $rc instead of ok!" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
# 2017 January 13
|
||||
#
|
||||
# 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 contains tests for resumption of RBU operations in the
|
||||
# case where the previous RBU process crashed.
|
||||
#
|
||||
|
||||
source [file join [file dirname [info script]] rbu_common.tcl]
|
||||
set ::testprefix rburesume
|
||||
|
||||
forcedelete test.db-shm test.db-oal
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b, c);
|
||||
CREATE INDEX t1a ON t1(a);
|
||||
CREATE INDEX t1b ON t1(b);
|
||||
CREATE INDEX t1c ON t1(c);
|
||||
WITH s(i) AS (
|
||||
VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<50
|
||||
)
|
||||
INSERT INTO t1 SELECT randomblob(50), randomblob(75), randomblob(100) FROM s;
|
||||
}
|
||||
db_save_and_close
|
||||
|
||||
do_test 1.1 {
|
||||
list [file exists test.db] \
|
||||
[file exists test.db-wal] \
|
||||
[file exists test.db-shm] \
|
||||
[file exists test.db-oal]
|
||||
} {1 0 0 0}
|
||||
|
||||
# Each iteration of the following loop:
|
||||
#
|
||||
# 1. Restores the db to the state it was in following test case 1.0
|
||||
# 2. Opens an RBU vacuum and steps it $n times.
|
||||
# 3. Closes the RBU vacuum handled opened in (2).
|
||||
# 4. Opens a second RBU vacuum handle, resumes and completes the vacuum op.
|
||||
#
|
||||
# The loop runs until $n is large enough that step (2) vacuums the entire
|
||||
# database.
|
||||
#
|
||||
for {set n 1} {$n < 5000} {incr n} {
|
||||
db_restore
|
||||
forcedelete state.db
|
||||
sqlite3rbu_vacuum rbu test.db state.db
|
||||
for {set i 0} {$i<$n} {incr i} {
|
||||
set rc [rbu step]
|
||||
if {$rc == "SQLITE_DONE"} break
|
||||
}
|
||||
rbu close
|
||||
if {$rc == "SQLITE_DONE"} break
|
||||
|
||||
do_test 1.2.$n.1 {
|
||||
sqlite3rbu_vacuum rbu test.db state.db
|
||||
while {[rbu step]=="SQLITE_OK"} {}
|
||||
rbu close
|
||||
} {SQLITE_DONE}
|
||||
|
||||
do_test 1.2.$n.2 {
|
||||
sqlite3 db2 test.db
|
||||
db2 eval {
|
||||
SELECT count(*) FROM t1;
|
||||
PRAGMA integrity_check;
|
||||
}
|
||||
} {50 ok}
|
||||
db2 close
|
||||
}
|
||||
|
||||
# Each iteration of this loop:
|
||||
#
|
||||
# 1. Restores the db to the state it was in following test case 1.0
|
||||
# 2. Opens an RBU vacuum and steps it $n times.
|
||||
# 3. Takes a copy of all database files and the state db.
|
||||
# 4. Opens a second RBU vacuum handle on the copy, resumes and completes the
|
||||
# vacuum op.
|
||||
#
|
||||
# The loop runs until $n is large enough that step (2) vacuums the entire
|
||||
# database.
|
||||
#
|
||||
for {set n 1} {$n < 5000} {incr n} {
|
||||
db_restore
|
||||
forcedelete state.db state.db-shm state.db-oal state.db-wal
|
||||
sqlite3rbu_vacuum rbu test.db state.db
|
||||
for {set i 0} {$i<$n} {incr i} {
|
||||
set rc [rbu step]
|
||||
if {$rc == "SQLITE_DONE"} break
|
||||
}
|
||||
if {$rc == "SQLITE_DONE"} {
|
||||
rbu close
|
||||
break
|
||||
}
|
||||
|
||||
foreach f {test.db test.db-oal test.db-wal test.db-shm test.db-vacuum} {
|
||||
set f2 [string map [list test.db test.db2] $f]
|
||||
if {[file exists $f]} {
|
||||
forcecopy $f $f2
|
||||
} else {
|
||||
forcedelete $f2
|
||||
}
|
||||
}
|
||||
forcecopy state.db state.db2
|
||||
rbu close
|
||||
|
||||
do_test 1.3.$n.1 {
|
||||
sqlite3rbu_vacuum rbu test.db2 state.db2
|
||||
while {[rbu step]=="SQLITE_OK"} {}
|
||||
rbu close
|
||||
} {SQLITE_DONE}
|
||||
|
||||
do_test 1.3.$n.2 {
|
||||
sqlite3 db2 test.db2
|
||||
db2 eval {
|
||||
SELECT count(*) FROM t1;
|
||||
PRAGMA integrity_check;
|
||||
}
|
||||
} {50 ok}
|
||||
db2 close
|
||||
}
|
||||
|
||||
# Each iteration of this loop:
|
||||
#
|
||||
# 1. Restores the db to the state it was in following test case 1.0
|
||||
# 2. Opens an RBU vacuum and steps it 10 times. Then closes it.
|
||||
# 2. Opens an RBU vacuum and steps it $n times.
|
||||
# 3. Takes a copy of all database files and the state db.
|
||||
# 4. Opens a second RBU vacuum handle on the copy, resumes and completes the
|
||||
# vacuum op.
|
||||
#
|
||||
# The loop runs until $n is large enough that step (3) vacuums the entire
|
||||
# database.
|
||||
#
|
||||
for {set n 1} {$n < 5000} {incr n} {
|
||||
db_restore
|
||||
forcedelete state.db state.db-shm state.db-oal state.db-wal
|
||||
|
||||
sqlite3rbu_vacuum rbu test.db state.db
|
||||
for {set i 0} {$i<10} {incr i} {
|
||||
rbu step
|
||||
}
|
||||
rbu close
|
||||
|
||||
sqlite3rbu_vacuum rbu test.db state.db
|
||||
for {set i 0} {$i<$n} {incr i} {
|
||||
set rc [rbu step]
|
||||
if {$rc == "SQLITE_DONE"} break
|
||||
}
|
||||
if {$rc == "SQLITE_DONE"} {
|
||||
rbu close
|
||||
break
|
||||
}
|
||||
|
||||
foreach f {test.db test.db-oal test.db-wal test.db-shm test.db-vacuum} {
|
||||
set f2 [string map [list test.db test.db2] $f]
|
||||
if {[file exists $f]} {
|
||||
forcecopy $f $f2
|
||||
} else {
|
||||
forcedelete $f2
|
||||
}
|
||||
}
|
||||
forcecopy state.db state.db2
|
||||
rbu close
|
||||
|
||||
do_test 1.4.$n.1 {
|
||||
sqlite3rbu_vacuum rbu test.db2 state.db2
|
||||
while {[rbu step]=="SQLITE_OK"} {}
|
||||
rbu close
|
||||
} {SQLITE_DONE}
|
||||
|
||||
do_test 1.4.$n.2 {
|
||||
sqlite3 db2 test.db2
|
||||
db2 eval {
|
||||
SELECT count(*) FROM t1;
|
||||
PRAGMA integrity_check;
|
||||
}
|
||||
} {50 ok}
|
||||
db2 close
|
||||
}
|
||||
|
||||
forcedelete rbu.db
|
||||
do_test 2.0 {
|
||||
sqlite3 db2 rbu.db
|
||||
db2 eval {
|
||||
CREATE TABLE data_t1(a, b, c, rbu_control);
|
||||
WITH s(i) AS (
|
||||
VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<10
|
||||
)
|
||||
INSERT INTO data_t1
|
||||
SELECT randomblob(50), randomblob(75), randomblob(100), 0 FROM s;
|
||||
}
|
||||
db2 close
|
||||
} {}
|
||||
|
||||
# Each iteration of this loop:
|
||||
#
|
||||
# 1. Restores the db to the state it was in following test case 1.0
|
||||
# 2. Opens an RBU handle to apply the RBU update created in test case 2.0.
|
||||
# 3. Steps the RBU handle $n times.
|
||||
# 4. Takes a copy of all database files and the state db.
|
||||
# 5. Opens a second RBU handle on the copy, resumes and completes the
|
||||
# RBU op. Checks it worked as expected.
|
||||
#
|
||||
# The loop runs until $n is large enough that step (3) applies the entire
|
||||
# update.
|
||||
#
|
||||
for {set n 1} {$n < 5000} {incr n} {
|
||||
db_restore
|
||||
forcedelete state.db state.db-shm state.db-oal state.db-wal
|
||||
sqlite3rbu rbu test.db rbu.db state.db
|
||||
|
||||
for {set i 0} {$i<$n} {incr i} {
|
||||
set rc [rbu step]
|
||||
if {$rc == "SQLITE_DONE"} break
|
||||
}
|
||||
if {$rc == "SQLITE_DONE"} {
|
||||
rbu close
|
||||
break
|
||||
}
|
||||
|
||||
foreach f {test.db test.db-oal test.db-wal test.db-shm test.db-vacuum} {
|
||||
set f2 [string map [list test.db test.db2] $f]
|
||||
if {[file exists $f]} {
|
||||
forcecopy $f $f2
|
||||
} else {
|
||||
forcedelete $f2
|
||||
}
|
||||
}
|
||||
forcecopy state.db state.db2
|
||||
rbu close
|
||||
|
||||
do_test 2.$n.1 {
|
||||
sqlite3rbu rbu test.db2 rbu.db state.db2
|
||||
while {[rbu step]=="SQLITE_OK"} {}
|
||||
rbu close
|
||||
} {SQLITE_DONE}
|
||||
|
||||
do_test 2.$n.2 {
|
||||
sqlite3 db2 test.db2
|
||||
db2 eval {
|
||||
SELECT count(*) FROM t1;
|
||||
PRAGMA integrity_check;
|
||||
}
|
||||
} {60 ok}
|
||||
db2 close
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -199,6 +199,37 @@ if {$::tcl_platform(platform)=="unix"} {
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test the outcome of some other connection running a checkpoint while
|
||||
# the incremental checkpoint is suspended.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 6.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
|
||||
CREATE INDEX i1b ON t1(b);
|
||||
CREATE INDEX i1c ON t1(c);
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
INSERT INTO t1 VALUES(4, 5, 6);
|
||||
}
|
||||
forcedelete test.db2
|
||||
|
||||
do_test 6.1 {
|
||||
sqlite3rbu_vacuum rbu test.db test.db2
|
||||
while {[rbu state]!="checkpoint"} { rbu step }
|
||||
rbu close
|
||||
} {SQLITE_OK}
|
||||
|
||||
do_execsql_test 6.2 {
|
||||
SELECT 1 FROM sqlite_master LIMIT 1;
|
||||
PRAGMA wal_checkpoint;
|
||||
} {1 0 4 4}
|
||||
|
||||
do_test 6.3 {
|
||||
sqlite3rbu_vacuum rbu test.db test.db2
|
||||
while {[rbu step]!="SQLITE_DONE"} { rbu step }
|
||||
rbu close
|
||||
execsql { PRAGMA integrity_check }
|
||||
} {ok}
|
||||
|
||||
finish_test
|
||||
|
||||
|
||||
+31
-8
@@ -2333,7 +2333,7 @@ static RbuState *rbuLoadState(sqlite3rbu *p){
|
||||
** Open the database handle and attach the RBU database as "rbu". If an
|
||||
** error occurs, leave an error code and message in the RBU handle.
|
||||
*/
|
||||
static void rbuOpenDatabase(sqlite3rbu *p){
|
||||
static void rbuOpenDatabase(sqlite3rbu *p, int *pbRetry){
|
||||
assert( p->rc || (p->dbMain==0 && p->dbRbu==0) );
|
||||
assert( p->rc || rbuIsVacuum(p) || p->zTarget!=0 );
|
||||
|
||||
@@ -2408,7 +2408,7 @@ static void rbuOpenDatabase(sqlite3rbu *p){
|
||||
}else{
|
||||
RbuState *pState = rbuLoadState(p);
|
||||
if( pState ){
|
||||
bOpen = (pState->eStage>RBU_STAGE_MOVE);
|
||||
bOpen = (pState->eStage>=RBU_STAGE_MOVE);
|
||||
rbuFreeState(pState);
|
||||
}
|
||||
}
|
||||
@@ -2420,6 +2420,15 @@ static void rbuOpenDatabase(sqlite3rbu *p){
|
||||
if( !rbuIsVacuum(p) ){
|
||||
p->dbMain = rbuOpenDbhandle(p, p->zTarget, 1);
|
||||
}else if( p->pRbuFd->pWalFd ){
|
||||
if( pbRetry ){
|
||||
p->pRbuFd->bNolock = 0;
|
||||
sqlite3_close(p->dbRbu);
|
||||
sqlite3_close(p->dbMain);
|
||||
p->dbMain = 0;
|
||||
p->dbRbu = 0;
|
||||
*pbRetry = 1;
|
||||
return;
|
||||
}
|
||||
p->rc = SQLITE_ERROR;
|
||||
p->zErrmsg = sqlite3_mprintf("cannot vacuum wal mode database");
|
||||
}else{
|
||||
@@ -2600,16 +2609,18 @@ static void rbuSetupCheckpoint(sqlite3rbu *p, RbuState *pState){
|
||||
if( rc2!=SQLITE_INTERNAL ) p->rc = rc2;
|
||||
}
|
||||
|
||||
if( p->rc==SQLITE_OK ){
|
||||
if( p->rc==SQLITE_OK && p->nFrame>0 ){
|
||||
p->eStage = RBU_STAGE_CKPT;
|
||||
p->nStep = (pState ? pState->nRow : 0);
|
||||
p->aBuf = rbuMalloc(p, p->pgsz);
|
||||
p->iWalCksum = rbuShmChecksum(p);
|
||||
}
|
||||
|
||||
if( p->rc==SQLITE_OK && pState && pState->iWalCksum!=p->iWalCksum ){
|
||||
p->rc = SQLITE_DONE;
|
||||
p->eStage = RBU_STAGE_DONE;
|
||||
if( p->rc==SQLITE_OK ){
|
||||
if( p->nFrame==0 || (pState && pState->iWalCksum!=p->iWalCksum) ){
|
||||
p->rc = SQLITE_DONE;
|
||||
p->eStage = RBU_STAGE_DONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2782,7 +2793,7 @@ static void rbuMoveOalFile(sqlite3rbu *p){
|
||||
#endif
|
||||
|
||||
if( p->rc==SQLITE_OK ){
|
||||
rbuOpenDatabase(p);
|
||||
rbuOpenDatabase(p, 0);
|
||||
rbuSetupCheckpoint(p, 0);
|
||||
}
|
||||
}
|
||||
@@ -3493,6 +3504,7 @@ static sqlite3rbu *openRbuHandle(
|
||||
/* Open the target, RBU and state databases */
|
||||
if( p->rc==SQLITE_OK ){
|
||||
char *pCsr = (char*)&p[1];
|
||||
int bRetry = 0;
|
||||
if( zTarget ){
|
||||
p->zTarget = pCsr;
|
||||
memcpy(p->zTarget, zTarget, nTarget+1);
|
||||
@@ -3504,7 +3516,18 @@ static sqlite3rbu *openRbuHandle(
|
||||
if( zState ){
|
||||
p->zState = rbuMPrintf(p, "%s", zState);
|
||||
}
|
||||
rbuOpenDatabase(p);
|
||||
|
||||
/* If the first attempt to open the database file fails and the bRetry
|
||||
** flag it set, this means that the db was not opened because it seemed
|
||||
** to be a wal-mode db. But, this may have happened due to an earlier
|
||||
** RBU vacuum operation leaving an old wal file in the directory.
|
||||
** If this is the case, it will have been checkpointed and deleted
|
||||
** when the handle was closed and a second attempt to open the
|
||||
** database may succeed. */
|
||||
rbuOpenDatabase(p, &bRetry);
|
||||
if( bRetry ){
|
||||
rbuOpenDatabase(p, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if( p->rc==SQLITE_OK ){
|
||||
|
||||
+214
-39
@@ -116,6 +116,7 @@ struct Rtree {
|
||||
sqlite3 *db; /* Host database connection */
|
||||
int iNodeSize; /* Size in bytes of each node in the node table */
|
||||
u8 nDim; /* Number of dimensions */
|
||||
u8 nDim2; /* Twice the number of dimensions */
|
||||
u8 eCoordType; /* RTREE_COORD_REAL32 or RTREE_COORD_INT32 */
|
||||
u8 nBytesPerCell; /* Bytes consumed per cell */
|
||||
int iDepth; /* Current depth of the r-tree structure */
|
||||
@@ -362,6 +363,65 @@ struct RtreeMatchArg {
|
||||
# define MIN(x,y) ((x) > (y) ? (y) : (x))
|
||||
#endif
|
||||
|
||||
/* What version of GCC is being used. 0 means GCC is not being used */
|
||||
#ifndef GCC_VERSION
|
||||
#ifdef __GNUC__
|
||||
# define GCC_VERSION (__GNUC__*1000000+__GNUC_MINOR__*1000+__GNUC_PATCHLEVEL__)
|
||||
#else
|
||||
# define GCC_VERSION 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* What version of CLANG is being used. 0 means CLANG is not being used */
|
||||
#ifndef CLANG_VERSION
|
||||
#if defined(__clang__) && !defined(_WIN32)
|
||||
# define CLANG_VERSION \
|
||||
(__clang_major__*1000000+__clang_minor__*1000+__clang_patchlevel__)
|
||||
#else
|
||||
# define CLANG_VERSION 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* The testcase() macro should already be defined in the amalgamation. If
|
||||
** it is not, make it a no-op.
|
||||
*/
|
||||
#ifndef SQLITE_AMALGMATION
|
||||
# define testcase(X)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Macros to determine whether the machine is big or little endian,
|
||||
** and whether or not that determination is run-time or compile-time.
|
||||
**
|
||||
** For best performance, an attempt is made to guess at the byte-order
|
||||
** using C-preprocessor macros. If that is unsuccessful, or if
|
||||
** -DSQLITE_RUNTIME_BYTEORDER=1 is set, then byte-order is determined
|
||||
** at run-time.
|
||||
*/
|
||||
#ifndef SQLITE_BYTEORDER
|
||||
#if (defined(i386) || defined(__i386__) || defined(_M_IX86) || \
|
||||
defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \
|
||||
defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \
|
||||
defined(__arm__)) && !defined(SQLITE_RUNTIME_BYTEORDER)
|
||||
# define SQLITE_BYTEORDER 1234
|
||||
#endif
|
||||
#if (defined(sparc) || defined(__ppc__)) \
|
||||
&& !defined(SQLITE_RUNTIME_BYTEORDER)
|
||||
# define SQLITE_BYTEORDER 4321
|
||||
#endif
|
||||
# define SQLITE_BYTEORDER 0 /* 0 means "unknown at compile-time" */
|
||||
#endif
|
||||
|
||||
|
||||
/* What version of MSVC is being used. 0 means MSVC is not being used */
|
||||
#ifndef MSVC_VERSION
|
||||
#if defined(_MSC_VER)
|
||||
# define MSVC_VERSION _MSC_VER
|
||||
#else
|
||||
# define MSVC_VERSION 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Functions to deserialize a 16 bit integer, 32 bit real number and
|
||||
** 64 bit integer. The deserialized value is returned.
|
||||
@@ -370,14 +430,43 @@ static int readInt16(u8 *p){
|
||||
return (p[0]<<8) + p[1];
|
||||
}
|
||||
static void readCoord(u8 *p, RtreeCoord *pCoord){
|
||||
assert( ((((char*)p) - (char*)0)&3)==0 ); /* p is always 4-byte aligned */
|
||||
#if SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300
|
||||
pCoord->u = _byteswap_ulong(*(u32*)p);
|
||||
#elif SQLITE_BYTEORDER==1234 && (GCC_VERSION>=4003000 || CLANG_VERSION>=3000000)
|
||||
pCoord->u = __builtin_bswap32(*(u32*)p);
|
||||
#elif SQLITE_BYTEORDER==1234
|
||||
pCoord->u = ((pCoord->u>>24)&0xff)|((pCoord->u>>8)&0xff00)|
|
||||
((pCoord->u&0xff)<<24)|((pCoord->u&0xff00)<<8);
|
||||
#elif SQLITE_BYTEORDER==4321
|
||||
pCoord->u = *(u32*)p;
|
||||
#else
|
||||
pCoord->u = (
|
||||
(((u32)p[0]) << 24) +
|
||||
(((u32)p[1]) << 16) +
|
||||
(((u32)p[2]) << 8) +
|
||||
(((u32)p[3]) << 0)
|
||||
);
|
||||
#endif
|
||||
}
|
||||
static i64 readInt64(u8 *p){
|
||||
#if SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300
|
||||
u64 x;
|
||||
testcase( ((((char*)p) - (char*)0)&7)!=0 ); /* not always 8-byte aligned */
|
||||
memcpy(&x, p, 8);
|
||||
return (i64)_byteswap_uint64(x);
|
||||
#elif SQLITE_BYTEORDER==1234 && (GCC_VERSION>=4003000 || CLANG_VERSION>=3000000)
|
||||
u64 x;
|
||||
testcase( ((((char*)p) - (char*)0)&7)!=0 ); /* not always 8-byte aligned */
|
||||
memcpy(&x, p, 8);
|
||||
return (i64)__builtin_bswap64(x);
|
||||
#elif SQLITE_BYTEORDER==4321
|
||||
i64 x;
|
||||
testcase( ((((char*)p) - (char*)0)&7)!=0 ); /* not always 8-byte aligned */
|
||||
memcpy(&x, p, 8);
|
||||
return x;
|
||||
#else
|
||||
testcase( ((((char*)p) - (char*)0)&7)!=0 ); /* not always 8-byte aligned */
|
||||
return (
|
||||
(((i64)p[0]) << 56) +
|
||||
(((i64)p[1]) << 48) +
|
||||
@@ -388,6 +477,7 @@ static i64 readInt64(u8 *p){
|
||||
(((i64)p[6]) << 8) +
|
||||
(((i64)p[7]) << 0)
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -402,16 +492,38 @@ static int writeInt16(u8 *p, int i){
|
||||
}
|
||||
static int writeCoord(u8 *p, RtreeCoord *pCoord){
|
||||
u32 i;
|
||||
assert( ((((char*)p) - (char*)0)&3)==0 ); /* p is always 4-byte aligned */
|
||||
assert( sizeof(RtreeCoord)==4 );
|
||||
assert( sizeof(u32)==4 );
|
||||
#if SQLITE_BYTEORDER==1234 && (GCC_VERSION>=4003000 || CLANG_VERSION>=3000000)
|
||||
i = __builtin_bswap32(pCoord->u);
|
||||
memcpy(p, &i, 4);
|
||||
#elif SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300
|
||||
i = _byteswap_ulong(pCoord->u);
|
||||
memcpy(p, &i, 4);
|
||||
#elif SQLITE_BYTEORDER==4321
|
||||
i = pCoord->u;
|
||||
memcpy(p, &i, 4);
|
||||
#else
|
||||
i = pCoord->u;
|
||||
p[0] = (i>>24)&0xFF;
|
||||
p[1] = (i>>16)&0xFF;
|
||||
p[2] = (i>> 8)&0xFF;
|
||||
p[3] = (i>> 0)&0xFF;
|
||||
#endif
|
||||
return 4;
|
||||
}
|
||||
static int writeInt64(u8 *p, i64 i){
|
||||
testcase( ((((char*)p) - (char*)0)&7)!=0 ); /* Not always 8-byte aligned */
|
||||
#if SQLITE_BYTEORDER==1234 && (GCC_VERSION>=4003000 || CLANG_VERSION>=3000000)
|
||||
i = (i64)__builtin_bswap64((u64)i);
|
||||
memcpy(p, &i, 8);
|
||||
#elif SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300
|
||||
i = (i64)_byteswap_uint64((u64)i);
|
||||
memcpy(p, &i, 8);
|
||||
#elif SQLITE_BYTEORDER==4321
|
||||
memcpy(p, &i, 8);
|
||||
#else
|
||||
p[0] = (i>>56)&0xFF;
|
||||
p[1] = (i>>48)&0xFF;
|
||||
p[2] = (i>>40)&0xFF;
|
||||
@@ -420,6 +532,7 @@ static int writeInt64(u8 *p, i64 i){
|
||||
p[5] = (i>>16)&0xFF;
|
||||
p[6] = (i>> 8)&0xFF;
|
||||
p[7] = (i>> 0)&0xFF;
|
||||
#endif
|
||||
return 8;
|
||||
}
|
||||
|
||||
@@ -602,7 +715,7 @@ static void nodeOverwriteCell(
|
||||
int ii;
|
||||
u8 *p = &pNode->zData[4 + pRtree->nBytesPerCell*iCell];
|
||||
p += writeInt64(p, pCell->iRowid);
|
||||
for(ii=0; ii<(pRtree->nDim*2); ii++){
|
||||
for(ii=0; ii<pRtree->nDim2; ii++){
|
||||
p += writeCoord(p, &pCell->aCoord[ii]);
|
||||
}
|
||||
pNode->isDirty = 1;
|
||||
@@ -736,13 +849,16 @@ static void nodeGetCell(
|
||||
){
|
||||
u8 *pData;
|
||||
RtreeCoord *pCoord;
|
||||
int ii;
|
||||
int ii = 0;
|
||||
pCell->iRowid = nodeGetRowid(pRtree, pNode, iCell);
|
||||
pData = pNode->zData + (12 + pRtree->nBytesPerCell*iCell);
|
||||
pCoord = pCell->aCoord;
|
||||
for(ii=0; ii<pRtree->nDim*2; ii++){
|
||||
readCoord(&pData[ii*4], &pCoord[ii]);
|
||||
}
|
||||
do{
|
||||
readCoord(pData, &pCoord[ii]);
|
||||
readCoord(pData+4, &pCoord[ii+1]);
|
||||
pData += 8;
|
||||
ii += 2;
|
||||
}while( ii<pRtree->nDim2 );
|
||||
}
|
||||
|
||||
|
||||
@@ -911,15 +1027,22 @@ static int rtreeEof(sqlite3_vtab_cursor *cur){
|
||||
** false. a[] is the four bytes of the on-disk record to be decoded.
|
||||
** Store the results in "r".
|
||||
**
|
||||
** There are three versions of this macro, one each for little-endian and
|
||||
** big-endian processors and a third generic implementation. The endian-
|
||||
** specific implementations are much faster and are preferred if the
|
||||
** processor endianness is known at compile-time. The SQLITE_BYTEORDER
|
||||
** macro is part of sqliteInt.h and hence the endian-specific
|
||||
** implementation will only be used if this module is compiled as part
|
||||
** of the amalgamation.
|
||||
** There are five versions of this macro. The last one is generic. The
|
||||
** other four are various architectures-specific optimizations.
|
||||
*/
|
||||
#if defined(SQLITE_BYTEORDER) && SQLITE_BYTEORDER==1234
|
||||
#if SQLITE_BYTEORDER==1234 && MSVC_VERSION>=1300
|
||||
#define RTREE_DECODE_COORD(eInt, a, r) { \
|
||||
RtreeCoord c; /* Coordinate decoded */ \
|
||||
c.u = _byteswap_ulong(*(u32*)a); \
|
||||
r = eInt ? (sqlite3_rtree_dbl)c.i : (sqlite3_rtree_dbl)c.f; \
|
||||
}
|
||||
#elif SQLITE_BYTEORDER==1234 && (GCC_VERSION>=4003000 || CLANG_VERSION>=3000000)
|
||||
#define RTREE_DECODE_COORD(eInt, a, r) { \
|
||||
RtreeCoord c; /* Coordinate decoded */ \
|
||||
c.u = __builtin_bswap32(*(u32*)a); \
|
||||
r = eInt ? (sqlite3_rtree_dbl)c.i : (sqlite3_rtree_dbl)c.f; \
|
||||
}
|
||||
#elif SQLITE_BYTEORDER==1234
|
||||
#define RTREE_DECODE_COORD(eInt, a, r) { \
|
||||
RtreeCoord c; /* Coordinate decoded */ \
|
||||
memcpy(&c.u,a,4); \
|
||||
@@ -927,7 +1050,7 @@ static int rtreeEof(sqlite3_vtab_cursor *cur){
|
||||
((c.u&0xff)<<24)|((c.u&0xff00)<<8); \
|
||||
r = eInt ? (sqlite3_rtree_dbl)c.i : (sqlite3_rtree_dbl)c.f; \
|
||||
}
|
||||
#elif defined(SQLITE_BYTEORDER) && SQLITE_BYTEORDER==4321
|
||||
#elif SQLITE_BYTEORDER==4321
|
||||
#define RTREE_DECODE_COORD(eInt, a, r) { \
|
||||
RtreeCoord c; /* Coordinate decoded */ \
|
||||
memcpy(&c.u,a,4); \
|
||||
@@ -958,6 +1081,7 @@ static int rtreeCallbackConstraint(
|
||||
sqlite3_rtree_query_info *pInfo = pConstraint->pInfo; /* Callback info */
|
||||
int nCoord = pInfo->nCoord; /* No. of coordinates */
|
||||
int rc; /* Callback return code */
|
||||
RtreeCoord c; /* Translator union */
|
||||
sqlite3_rtree_dbl aCoord[RTREE_MAX_DIMENSIONS*2]; /* Decoded coordinates */
|
||||
|
||||
assert( pConstraint->op==RTREE_MATCH || pConstraint->op==RTREE_QUERY );
|
||||
@@ -967,8 +1091,35 @@ static int rtreeCallbackConstraint(
|
||||
pInfo->iRowid = readInt64(pCellData);
|
||||
}
|
||||
pCellData += 8;
|
||||
for(i=0; i<nCoord; i++, pCellData += 4){
|
||||
RTREE_DECODE_COORD(eInt, pCellData, aCoord[i]);
|
||||
#ifndef SQLITE_RTREE_INT_ONLY
|
||||
if( eInt==0 ){
|
||||
switch( nCoord ){
|
||||
case 10: readCoord(pCellData+36, &c); aCoord[9] = c.f;
|
||||
readCoord(pCellData+32, &c); aCoord[8] = c.f;
|
||||
case 8: readCoord(pCellData+28, &c); aCoord[7] = c.f;
|
||||
readCoord(pCellData+24, &c); aCoord[6] = c.f;
|
||||
case 6: readCoord(pCellData+20, &c); aCoord[5] = c.f;
|
||||
readCoord(pCellData+16, &c); aCoord[4] = c.f;
|
||||
case 4: readCoord(pCellData+12, &c); aCoord[3] = c.f;
|
||||
readCoord(pCellData+8, &c); aCoord[2] = c.f;
|
||||
default: readCoord(pCellData+4, &c); aCoord[1] = c.f;
|
||||
readCoord(pCellData, &c); aCoord[0] = c.f;
|
||||
}
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
switch( nCoord ){
|
||||
case 10: readCoord(pCellData+36, &c); aCoord[9] = c.i;
|
||||
readCoord(pCellData+32, &c); aCoord[8] = c.i;
|
||||
case 8: readCoord(pCellData+28, &c); aCoord[7] = c.i;
|
||||
readCoord(pCellData+24, &c); aCoord[6] = c.i;
|
||||
case 6: readCoord(pCellData+20, &c); aCoord[5] = c.i;
|
||||
readCoord(pCellData+16, &c); aCoord[4] = c.i;
|
||||
case 4: readCoord(pCellData+12, &c); aCoord[3] = c.i;
|
||||
readCoord(pCellData+8, &c); aCoord[2] = c.i;
|
||||
default: readCoord(pCellData+4, &c); aCoord[1] = c.i;
|
||||
readCoord(pCellData, &c); aCoord[0] = c.i;
|
||||
}
|
||||
}
|
||||
if( pConstraint->op==RTREE_MATCH ){
|
||||
rc = pConstraint->u.xGeom((sqlite3_rtree_geometry*)pInfo,
|
||||
@@ -1009,6 +1160,7 @@ static void rtreeNonleafConstraint(
|
||||
|
||||
assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE
|
||||
|| p->op==RTREE_GT || p->op==RTREE_EQ );
|
||||
assert( ((((char*)pCellData) - (char*)0)&3)==0 ); /* 4-byte aligned */
|
||||
switch( p->op ){
|
||||
case RTREE_LE:
|
||||
case RTREE_LT:
|
||||
@@ -1049,6 +1201,7 @@ static void rtreeLeafConstraint(
|
||||
assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE
|
||||
|| p->op==RTREE_GT || p->op==RTREE_EQ );
|
||||
pCellData += 8 + p->iCoord*4;
|
||||
assert( ((((char*)pCellData) - (char*)0)&3)==0 ); /* 4-byte aligned */
|
||||
RTREE_DECODE_COORD(eInt, pCellData, xN);
|
||||
switch( p->op ){
|
||||
case RTREE_LE: if( xN <= p->u.rValue ) return; break;
|
||||
@@ -1365,7 +1518,7 @@ static int rtreeStepToLeaf(RtreeCursor *pCur){
|
||||
if( rScore<RTREE_ZERO ) rScore = RTREE_ZERO;
|
||||
p = rtreeSearchPointNew(pCur, rScore, x.iLevel);
|
||||
if( p==0 ) return SQLITE_NOMEM;
|
||||
p->eWithin = eWithin;
|
||||
p->eWithin = (u8)eWithin;
|
||||
p->id = x.id;
|
||||
p->iCell = x.iCell;
|
||||
RTREE_QUEUE_TRACE(pCur, "PUSH-S:");
|
||||
@@ -1553,7 +1706,7 @@ static int rtreeFilter(
|
||||
p->id = iNode;
|
||||
p->eWithin = PARTLY_WITHIN;
|
||||
rc = nodeRowidIndex(pRtree, pLeaf, iRowid, &iCell);
|
||||
p->iCell = iCell;
|
||||
p->iCell = (u8)iCell;
|
||||
RTREE_QUEUE_TRACE(pCsr, "PUSH-F1:");
|
||||
}else{
|
||||
pCsr->atEOF = 1;
|
||||
@@ -1586,7 +1739,7 @@ static int rtreeFilter(
|
||||
if( rc!=SQLITE_OK ){
|
||||
break;
|
||||
}
|
||||
p->pInfo->nCoord = pRtree->nDim*2;
|
||||
p->pInfo->nCoord = pRtree->nDim2;
|
||||
p->pInfo->anQueue = pCsr->anQueue;
|
||||
p->pInfo->mxLevel = pRtree->iDepth + 1;
|
||||
}else{
|
||||
@@ -1601,7 +1754,7 @@ static int rtreeFilter(
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
RtreeSearchPoint *pNew;
|
||||
pNew = rtreeSearchPointNew(pCsr, RTREE_ZERO, pRtree->iDepth+1);
|
||||
pNew = rtreeSearchPointNew(pCsr, RTREE_ZERO, (u8)(pRtree->iDepth+1));
|
||||
if( pNew==0 ) return SQLITE_NOMEM;
|
||||
pNew->id = 1;
|
||||
pNew->iCell = 0;
|
||||
@@ -1729,7 +1882,7 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
break;
|
||||
}
|
||||
zIdxStr[iIdx++] = op;
|
||||
zIdxStr[iIdx++] = p->iColumn - 1 + '0';
|
||||
zIdxStr[iIdx++] = (char)(p->iColumn - 1 + '0');
|
||||
pIdxInfo->aConstraintUsage[ii].argvIndex = (iIdx/2);
|
||||
pIdxInfo->aConstraintUsage[ii].omit = 1;
|
||||
}
|
||||
@@ -1753,9 +1906,26 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
*/
|
||||
static RtreeDValue cellArea(Rtree *pRtree, RtreeCell *p){
|
||||
RtreeDValue area = (RtreeDValue)1;
|
||||
int ii;
|
||||
for(ii=0; ii<(pRtree->nDim*2); ii+=2){
|
||||
area = (area * (DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii])));
|
||||
assert( pRtree->nDim>=1 && pRtree->nDim<=5 );
|
||||
#ifndef SQLITE_RTREE_INT_ONLY
|
||||
if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
|
||||
switch( pRtree->nDim ){
|
||||
case 5: area = p->aCoord[9].f - p->aCoord[8].f;
|
||||
case 4: area *= p->aCoord[7].f - p->aCoord[6].f;
|
||||
case 3: area *= p->aCoord[5].f - p->aCoord[4].f;
|
||||
case 2: area *= p->aCoord[3].f - p->aCoord[2].f;
|
||||
default: area *= p->aCoord[1].f - p->aCoord[0].f;
|
||||
}
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
switch( pRtree->nDim ){
|
||||
case 5: area = p->aCoord[9].i - p->aCoord[8].i;
|
||||
case 4: area *= p->aCoord[7].i - p->aCoord[6].i;
|
||||
case 3: area *= p->aCoord[5].i - p->aCoord[4].i;
|
||||
case 2: area *= p->aCoord[3].i - p->aCoord[2].i;
|
||||
default: area *= p->aCoord[1].i - p->aCoord[0].i;
|
||||
}
|
||||
}
|
||||
return area;
|
||||
}
|
||||
@@ -1765,9 +1935,10 @@ static RtreeDValue cellArea(Rtree *pRtree, RtreeCell *p){
|
||||
** of the objects size in each dimension.
|
||||
*/
|
||||
static RtreeDValue cellMargin(Rtree *pRtree, RtreeCell *p){
|
||||
RtreeDValue margin = (RtreeDValue)0;
|
||||
RtreeDValue margin;
|
||||
int ii;
|
||||
for(ii=0; ii<(pRtree->nDim*2); ii+=2){
|
||||
margin = DCOORD(p->aCoord[1]) - DCOORD(p->aCoord[0]);
|
||||
for(ii=2; ii<pRtree->nDim2; ii+=2){
|
||||
margin += (DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii]));
|
||||
}
|
||||
return margin;
|
||||
@@ -1777,17 +1948,19 @@ static RtreeDValue cellMargin(Rtree *pRtree, RtreeCell *p){
|
||||
** Store the union of cells p1 and p2 in p1.
|
||||
*/
|
||||
static void cellUnion(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){
|
||||
int ii;
|
||||
int ii = 0;
|
||||
if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
|
||||
for(ii=0; ii<(pRtree->nDim*2); ii+=2){
|
||||
do{
|
||||
p1->aCoord[ii].f = MIN(p1->aCoord[ii].f, p2->aCoord[ii].f);
|
||||
p1->aCoord[ii+1].f = MAX(p1->aCoord[ii+1].f, p2->aCoord[ii+1].f);
|
||||
}
|
||||
ii += 2;
|
||||
}while( ii<pRtree->nDim2 );
|
||||
}else{
|
||||
for(ii=0; ii<(pRtree->nDim*2); ii+=2){
|
||||
do{
|
||||
p1->aCoord[ii].i = MIN(p1->aCoord[ii].i, p2->aCoord[ii].i);
|
||||
p1->aCoord[ii+1].i = MAX(p1->aCoord[ii+1].i, p2->aCoord[ii+1].i);
|
||||
}
|
||||
ii += 2;
|
||||
}while( ii<pRtree->nDim2 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1798,7 +1971,7 @@ static void cellUnion(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){
|
||||
static int cellContains(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){
|
||||
int ii;
|
||||
int isInt = (pRtree->eCoordType==RTREE_COORD_INT32);
|
||||
for(ii=0; ii<(pRtree->nDim*2); ii+=2){
|
||||
for(ii=0; ii<pRtree->nDim2; ii+=2){
|
||||
RtreeCoord *a1 = &p1->aCoord[ii];
|
||||
RtreeCoord *a2 = &p2->aCoord[ii];
|
||||
if( (!isInt && (a2[0].f<a1[0].f || a2[1].f>a1[1].f))
|
||||
@@ -1833,7 +2006,7 @@ static RtreeDValue cellOverlap(
|
||||
for(ii=0; ii<nCell; ii++){
|
||||
int jj;
|
||||
RtreeDValue o = (RtreeDValue)1;
|
||||
for(jj=0; jj<(pRtree->nDim*2); jj+=2){
|
||||
for(jj=0; jj<pRtree->nDim2; jj+=2){
|
||||
RtreeDValue x1, x2;
|
||||
x1 = MAX(DCOORD(p->aCoord[jj]), DCOORD(aCell[ii].aCoord[jj]));
|
||||
x2 = MIN(DCOORD(p->aCoord[jj+1]), DCOORD(aCell[ii].aCoord[jj+1]));
|
||||
@@ -2889,7 +3062,7 @@ static int rtreeUpdate(
|
||||
** This problem was discovered after years of use, so we silently ignore
|
||||
** these kinds of misdeclared tables to avoid breaking any legacy.
|
||||
*/
|
||||
assert( nData<=(pRtree->nDim*2 + 3) );
|
||||
assert( nData<=(pRtree->nDim2 + 3) );
|
||||
|
||||
#ifndef SQLITE_RTREE_INT_ONLY
|
||||
if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
|
||||
@@ -3266,9 +3439,10 @@ static int rtreeInit(
|
||||
pRtree->base.pModule = &rtreeModule;
|
||||
pRtree->zDb = (char *)&pRtree[1];
|
||||
pRtree->zName = &pRtree->zDb[nDb+1];
|
||||
pRtree->nDim = (argc-4)/2;
|
||||
pRtree->nBytesPerCell = 8 + pRtree->nDim*4*2;
|
||||
pRtree->eCoordType = eCoordType;
|
||||
pRtree->nDim = (u8)((argc-4)/2);
|
||||
pRtree->nDim2 = argc - 4;
|
||||
pRtree->nBytesPerCell = 8 + pRtree->nDim2*4;
|
||||
pRtree->eCoordType = (u8)eCoordType;
|
||||
memcpy(pRtree->zDb, argv[1], nDb);
|
||||
memcpy(pRtree->zName, argv[2], nName);
|
||||
|
||||
@@ -3341,7 +3515,8 @@ static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){
|
||||
UNUSED_PARAMETER(nArg);
|
||||
memset(&node, 0, sizeof(RtreeNode));
|
||||
memset(&tree, 0, sizeof(Rtree));
|
||||
tree.nDim = sqlite3_value_int(apArg[0]);
|
||||
tree.nDim = (u8)sqlite3_value_int(apArg[0]);
|
||||
tree.nDim2 = tree.nDim*2;
|
||||
tree.nBytesPerCell = 8 + 8 * tree.nDim;
|
||||
node.zData = (u8 *)sqlite3_value_blob(apArg[1]);
|
||||
|
||||
@@ -3354,7 +3529,7 @@ static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){
|
||||
nodeGetCell(&tree, &node, ii, &cell);
|
||||
sqlite3_snprintf(512-nCell,&zCell[nCell],"%lld", cell.iRowid);
|
||||
nCell = (int)strlen(zCell);
|
||||
for(jj=0; jj<tree.nDim*2; jj++){
|
||||
for(jj=0; jj<tree.nDim2; jj++){
|
||||
#ifndef SQLITE_RTREE_INT_ONLY
|
||||
sqlite3_snprintf(512-nCell,&zCell[nCell], " %g",
|
||||
(double)cell.aCoord[jj].f);
|
||||
|
||||
+147
-111
@@ -20,46 +20,61 @@ ifcapable !session {finish_test; return}
|
||||
|
||||
set testprefix session1
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(x PRIMARY KEY, y);
|
||||
# Run all tests in this file twice. Once with "WITHOUT ROWID", and once
|
||||
# with regular rowid tables.
|
||||
#
|
||||
foreach {tn trailing} {
|
||||
1 ""
|
||||
2 " WITHOUT ROWID "
|
||||
} {
|
||||
eval [string map [list %WR% $trailing] {
|
||||
|
||||
db close
|
||||
forcedelete test.db test.db2
|
||||
reset_db
|
||||
|
||||
do_execsql_test $tn.1.0 {
|
||||
CREATE TABLE t1(x PRIMARY KEY, y) %WR%;
|
||||
INSERT INTO t1 VALUES('abc', 'def');
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test creating, attaching tables to and deleting session objects.
|
||||
#
|
||||
do_test 1.1 { sqlite3session S db main } {S}
|
||||
do_test 1.2 { S delete } {}
|
||||
do_test 1.3 { sqlite3session S db main } {S}
|
||||
do_test 1.4 { S attach t1 } {}
|
||||
do_test 1.5 { S delete } {}
|
||||
do_test 1.6 { sqlite3session S db main } {S}
|
||||
do_test 1.7 { S attach t1 ; S attach t2 ; S attach t3 } {}
|
||||
do_test 1.8 { S attach t1 ; S attach t2 ; S attach t3 } {}
|
||||
do_test 1.9 { S delete } {}
|
||||
do_test 1.10 {
|
||||
do_test $tn.1.1 { sqlite3session S db main } {S}
|
||||
do_test $tn.1.2 { S delete } {}
|
||||
do_test $tn.1.3 { sqlite3session S db main } {S}
|
||||
do_test $tn.1.4 { S attach t1 } {}
|
||||
do_test $tn.1.5 { S delete } {}
|
||||
do_test $tn.1.6 { sqlite3session S db main } {S}
|
||||
do_test $tn.1.7 { S attach t1 ; S attach t2 ; S attach t3 } {}
|
||||
do_test $tn.1.8 { S attach t1 ; S attach t2 ; S attach t3 } {}
|
||||
do_test $tn.1.9 { S delete } {}
|
||||
do_test $tn.1.10 {
|
||||
sqlite3session S db main
|
||||
S attach t1
|
||||
execsql { INSERT INTO t1 VALUES('ghi', 'jkl') }
|
||||
} {}
|
||||
do_test 1.11 { S delete } {}
|
||||
do_test 1.12 {
|
||||
sqlite3session S db main
|
||||
S attach t1
|
||||
execsql { INSERT INTO t1 VALUES('mno', 'pqr') }
|
||||
execsql { UPDATE t1 SET x = 111 WHERE rowid = 1 }
|
||||
execsql { DELETE FROM t1 WHERE rowid = 2 }
|
||||
} {}
|
||||
do_test 1.13 {
|
||||
S changeset
|
||||
S delete
|
||||
} {}
|
||||
do_test $tn.1.11 { S delete } {}
|
||||
if {$tn==1} {
|
||||
do_test $tn.1.12 {
|
||||
sqlite3session S db main
|
||||
S attach t1
|
||||
execsql { INSERT INTO t1 VALUES('mno', 'pqr') }
|
||||
execsql { UPDATE t1 SET x = 111 WHERE rowid = 1 }
|
||||
execsql { DELETE FROM t1 WHERE rowid = 2 }
|
||||
} {}
|
||||
do_test $tn.1.13 {
|
||||
S changeset
|
||||
S delete
|
||||
} {}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Simple changeset tests. Also test the sqlite3changeset_invert()
|
||||
# function.
|
||||
#
|
||||
do_test 2.1.1 {
|
||||
do_test $tn.2.1.1 {
|
||||
execsql { DELETE FROM t1 }
|
||||
sqlite3session S db main
|
||||
S attach t1
|
||||
@@ -67,36 +82,36 @@ do_test 2.1.1 {
|
||||
execsql { INSERT INTO t1 VALUES(2, 'Ayutthaya') }
|
||||
execsql { INSERT INTO t1 VALUES(3, 'Thonburi') }
|
||||
} {}
|
||||
do_changeset_test 2.1.2 S {
|
||||
do_changeset_test $tn.2.1.2 S {
|
||||
{INSERT t1 0 X. {} {i 1 t Sukhothai}}
|
||||
{INSERT t1 0 X. {} {i 2 t Ayutthaya}}
|
||||
{INSERT t1 0 X. {} {i 3 t Thonburi}}
|
||||
}
|
||||
do_changeset_invert_test 2.1.3 S {
|
||||
do_changeset_invert_test $tn.2.1.3 S {
|
||||
{DELETE t1 0 X. {i 1 t Sukhothai} {}}
|
||||
{DELETE t1 0 X. {i 2 t Ayutthaya} {}}
|
||||
{DELETE t1 0 X. {i 3 t Thonburi} {}}
|
||||
}
|
||||
do_test 2.1.4 { S delete } {}
|
||||
do_test $tn.2.1.4 { S delete } {}
|
||||
|
||||
do_test 2.2.1 {
|
||||
do_test $tn.2.2.1 {
|
||||
sqlite3session S db main
|
||||
S attach t1
|
||||
execsql { DELETE FROM t1 WHERE 1 }
|
||||
} {}
|
||||
do_changeset_test 2.2.2 S {
|
||||
do_changeset_test $tn.2.2.2 S {
|
||||
{DELETE t1 0 X. {i 1 t Sukhothai} {}}
|
||||
{DELETE t1 0 X. {i 2 t Ayutthaya} {}}
|
||||
{DELETE t1 0 X. {i 3 t Thonburi} {}}
|
||||
}
|
||||
do_changeset_invert_test 2.2.3 S {
|
||||
do_changeset_invert_test $tn.2.2.3 S {
|
||||
{INSERT t1 0 X. {} {i 1 t Sukhothai}}
|
||||
{INSERT t1 0 X. {} {i 2 t Ayutthaya}}
|
||||
{INSERT t1 0 X. {} {i 3 t Thonburi}}
|
||||
}
|
||||
do_test 2.2.4 { S delete } {}
|
||||
do_test $tn.2.2.4 { S delete } {}
|
||||
|
||||
do_test 2.3.1 {
|
||||
do_test $tn.2.3.1 {
|
||||
execsql { DELETE FROM t1 }
|
||||
sqlite3session S db main
|
||||
execsql { INSERT INTO t1 VALUES(1, 'Sukhothai') }
|
||||
@@ -110,7 +125,7 @@ do_test 2.3.1 {
|
||||
}
|
||||
} {}
|
||||
|
||||
do_changeset_test 2.3.2 S {
|
||||
do_changeset_test $tn.2.3.2 S {
|
||||
{INSERT t1 0 X. {} {i 10 t Sukhothai}}
|
||||
{DELETE t1 0 X. {i 1 t Sukhothai} {}}
|
||||
{UPDATE t1 0 X. {i 2 t Ayutthaya} {{} {} t Surin}}
|
||||
@@ -118,24 +133,24 @@ do_changeset_test 2.3.2 S {
|
||||
{INSERT t1 0 X. {} {i 20 t Thapae}}
|
||||
}
|
||||
|
||||
do_changeset_invert_test 2.3.3 S {
|
||||
do_changeset_invert_test $tn.2.3.3 S {
|
||||
{DELETE t1 0 X. {i 10 t Sukhothai} {}}
|
||||
{INSERT t1 0 X. {} {i 1 t Sukhothai}}
|
||||
{UPDATE t1 0 X. {i 2 t Surin} {{} {} t Ayutthaya}}
|
||||
{INSERT t1 0 X. {} {i 3 t Thonburi}}
|
||||
{DELETE t1 0 X. {i 20 t Thapae} {}}
|
||||
}
|
||||
do_test 2.3.4 { S delete } {}
|
||||
do_test $tn.2.3.4 { S delete } {}
|
||||
|
||||
do_test 2.4.1 {
|
||||
do_test $tn.2.4.1 {
|
||||
sqlite3session S db main
|
||||
S attach t1
|
||||
execsql { INSERT INTO t1 VALUES(100, 'Bangkok') }
|
||||
execsql { DELETE FROM t1 WHERE x = 100 }
|
||||
} {}
|
||||
do_changeset_test 2.4.2 S {}
|
||||
do_changeset_invert_test 2.4.3 S {}
|
||||
do_test 2.4.4 { S delete } {}
|
||||
do_changeset_test $tn.2.4.2 S {}
|
||||
do_changeset_invert_test $tn.2.4.3 S {}
|
||||
do_test $tn.2.4.4 { S delete } {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test the application of simple changesets. These tests also test that
|
||||
@@ -189,16 +204,16 @@ proc do_db2_test {testname sql {result {}}} {
|
||||
|
||||
# Test INSERT changesets.
|
||||
#
|
||||
do_test 3.1.0 {
|
||||
execsql { CREATE TABLE t1(a PRIMARY KEY, b NOT NULL) } db2
|
||||
do_test $tn.3.1.0 {
|
||||
execsql { CREATE TABLE t1(a PRIMARY KEY, b NOT NULL) %WR% } db2
|
||||
execsql {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b);
|
||||
CREATE TABLE t1(a PRIMARY KEY, b) %WR%;
|
||||
INSERT INTO t1 VALUES(1, 'one');
|
||||
INSERT INTO t1 VALUES(2, 'two');
|
||||
} db
|
||||
} {}
|
||||
do_db2_test 3.1.1 "INSERT INTO t1 VALUES(6, 'VI')"
|
||||
do_conflict_test 3.1.2 -tables t1 -sql {
|
||||
do_db2_test $tn.3.1.1 "INSERT INTO t1 VALUES(6, 'VI')"
|
||||
do_conflict_test $tn.3.1.2 -tables t1 -sql {
|
||||
INSERT INTO t1 VALUES(3, 'three');
|
||||
INSERT INTO t1 VALUES(4, 'four');
|
||||
INSERT INTO t1 VALUES(5, 'five');
|
||||
@@ -210,34 +225,34 @@ do_conflict_test 3.1.2 -tables t1 -sql {
|
||||
{INSERT t1 CONSTRAINT {i 8 n {}}}
|
||||
}
|
||||
|
||||
do_db2_test 3.1.3 "SELECT * FROM t1" {
|
||||
6 VI 3 three 4 four 5 five 7 seven
|
||||
do_db2_test $tn.3.1.3 "SELECT * FROM t1 ORDER BY a" {
|
||||
3 three 4 four 5 five 6 VI 7 seven
|
||||
}
|
||||
do_execsql_test 3.1.4 "SELECT * FROM t1" {
|
||||
do_execsql_test $tn.3.1.4 "SELECT * FROM t1" {
|
||||
1 one 2 two 3 three 4 four 5 five 6 six 7 seven 8 {}
|
||||
}
|
||||
|
||||
# Test DELETE changesets.
|
||||
#
|
||||
do_execsql_test 3.2.1 {
|
||||
do_execsql_test $tn.3.2.1 {
|
||||
PRAGMA foreign_keys = on;
|
||||
CREATE TABLE t2(a PRIMARY KEY, b);
|
||||
CREATE TABLE t2(a PRIMARY KEY, b)%WR%;
|
||||
CREATE TABLE t3(c, d REFERENCES t2);
|
||||
INSERT INTO t2 VALUES(1, 'one');
|
||||
INSERT INTO t2 VALUES(2, 'two');
|
||||
INSERT INTO t2 VALUES(3, 'three');
|
||||
INSERT INTO t2 VALUES(4, 'four');
|
||||
}
|
||||
do_db2_test 3.2.2 {
|
||||
do_db2_test $tn.3.2.2 {
|
||||
PRAGMA foreign_keys = on;
|
||||
CREATE TABLE t2(a PRIMARY KEY, b);
|
||||
CREATE TABLE t2(a PRIMARY KEY, b)%WR%;
|
||||
CREATE TABLE t3(c, d REFERENCES t2);
|
||||
INSERT INTO t2 VALUES(1, 'one');
|
||||
INSERT INTO t2 VALUES(2, 'two');
|
||||
INSERT INTO t2 VALUES(4, 'five');
|
||||
INSERT INTO t3 VALUES('i', 1);
|
||||
}
|
||||
do_conflict_test 3.2.3 -tables t2 -sql {
|
||||
do_conflict_test $tn.3.2.3 -tables t2 -sql {
|
||||
DELETE FROM t2 WHERE a = 1;
|
||||
DELETE FROM t2 WHERE a = 2;
|
||||
DELETE FROM t2 WHERE a = 3;
|
||||
@@ -247,26 +262,26 @@ do_conflict_test 3.2.3 -tables t2 -sql {
|
||||
{DELETE t2 DATA {i 4 t four} {i 4 t five}}
|
||||
{FOREIGN_KEY 1}
|
||||
}
|
||||
do_execsql_test 3.2.4 "SELECT * FROM t2" {}
|
||||
do_db2_test 3.2.5 "SELECT * FROM t2" {4 five}
|
||||
do_execsql_test $tn.3.2.4 "SELECT * FROM t2" {}
|
||||
do_db2_test $tn.3.2.5 "SELECT * FROM t2" {4 five}
|
||||
|
||||
# Test UPDATE changesets.
|
||||
#
|
||||
do_execsql_test 3.3.1 {
|
||||
CREATE TABLE t4(a, b, c, PRIMARY KEY(b, c));
|
||||
do_execsql_test $tn.3.3.1 {
|
||||
CREATE TABLE t4(a, b, c, PRIMARY KEY(b, c))%WR%;
|
||||
INSERT INTO t4 VALUES(1, 2, 3);
|
||||
INSERT INTO t4 VALUES(4, 5, 6);
|
||||
INSERT INTO t4 VALUES(7, 8, 9);
|
||||
INSERT INTO t4 VALUES(10, 11, 12);
|
||||
}
|
||||
do_db2_test 3.3.2 {
|
||||
CREATE TABLE t4(a NOT NULL, b, c, PRIMARY KEY(b, c));
|
||||
do_db2_test $tn.3.3.2 {
|
||||
CREATE TABLE t4(a NOT NULL, b, c, PRIMARY KEY(b, c))%WR%;
|
||||
INSERT INTO t4 VALUES(0, 2, 3);
|
||||
INSERT INTO t4 VALUES(4, 5, 7);
|
||||
INSERT INTO t4 VALUES(7, 8, 9);
|
||||
INSERT INTO t4 VALUES(10, 11, 12);
|
||||
}
|
||||
do_conflict_test 3.3.3 -tables t4 -sql {
|
||||
do_conflict_test $tn.3.3.3 -tables t4 -sql {
|
||||
UPDATE t4 SET a = -1 WHERE b = 2;
|
||||
UPDATE t4 SET a = -1 WHERE b = 5;
|
||||
UPDATE t4 SET a = NULL WHERE c = 9;
|
||||
@@ -276,8 +291,8 @@ do_conflict_test 3.3.3 -tables t4 -sql {
|
||||
{UPDATE t4 NOTFOUND {i 4 i 5 i 6} {i -1 {} {} {} {}}}
|
||||
{UPDATE t4 CONSTRAINT {i 7 i 8 i 9} {n {} {} {} {} {}}}
|
||||
}
|
||||
do_db2_test 3.3.4 { SELECT * FROM t4 } {0 2 3 4 5 7 7 8 9 x 11 12}
|
||||
do_execsql_test 3.3.5 { SELECT * FROM t4 } {-1 2 3 -1 5 6 {} 8 9 x 11 12}
|
||||
do_db2_test $tn.3.3.4 { SELECT * FROM t4 } {0 2 3 4 5 7 7 8 9 x 11 12}
|
||||
do_execsql_test $tn.3.3.5 { SELECT * FROM t4 } {-1 2 3 -1 5 6 {} 8 9 x 11 12}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# This next block of tests verifies that values returned by the conflict
|
||||
@@ -297,16 +312,16 @@ proc xConflict {args} {
|
||||
return $::conflict_return
|
||||
}
|
||||
|
||||
foreach {tn conflict_return after} {
|
||||
foreach {tn2 conflict_return after} {
|
||||
1 OMIT {1 2 value1 4 5 7 10 x x}
|
||||
2 REPLACE {1 2 value1 4 5 value2 10 8 9}
|
||||
} {
|
||||
test_reset
|
||||
|
||||
do_test 4.$tn.1 {
|
||||
do_test $tn.4.$tn2.1 {
|
||||
foreach db {db db2} {
|
||||
execsql {
|
||||
CREATE TABLE t1(a, b, c, PRIMARY KEY(a));
|
||||
CREATE TABLE t1(a, b, c, PRIMARY KEY(a))%WR%;
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
INSERT INTO t1 VALUES(4, 5, 6);
|
||||
INSERT INTO t1 VALUES(7, 8, 9);
|
||||
@@ -318,7 +333,7 @@ foreach {tn conflict_return after} {
|
||||
} db2
|
||||
} {}
|
||||
|
||||
do_conflict_test 4.$tn.2 -tables t1 -sql {
|
||||
do_conflict_test $tn.4.$tn2.2 -tables t1 -sql {
|
||||
UPDATE t1 SET c = 'value1' WHERE a = 1; -- no conflict
|
||||
UPDATE t1 SET c = 'value2' WHERE a = 4; -- DATA conflict
|
||||
UPDATE t1 SET a = 10 WHERE a = 7; -- CONFLICT conflict
|
||||
@@ -327,19 +342,19 @@ foreach {tn conflict_return after} {
|
||||
{UPDATE t1 DATA {i 4 {} {} i 6} {{} {} {} {} t value2} {i 4 i 5 i 7}}
|
||||
}
|
||||
|
||||
do_db2_test 4.$tn.3 "SELECT * FROM t1 ORDER BY a" $after
|
||||
do_db2_test $tn.4.$tn2.3 "SELECT * FROM t1 ORDER BY a" $after
|
||||
}
|
||||
|
||||
foreach {tn conflict_return} {
|
||||
foreach {tn2 conflict_return} {
|
||||
1 OMIT
|
||||
2 REPLACE
|
||||
} {
|
||||
test_reset
|
||||
|
||||
do_test 5.$tn.1 {
|
||||
do_test $tn.5.$tn2.1 {
|
||||
# Create an identical schema in both databases.
|
||||
set schema {
|
||||
CREATE TABLE "'foolish name'"(x, y, z, PRIMARY KEY(x, y));
|
||||
CREATE TABLE "'foolish name'"(x, y, z, PRIMARY KEY(x, y))%WR%;
|
||||
}
|
||||
execsql $schema db
|
||||
execsql $schema db2
|
||||
@@ -354,7 +369,7 @@ foreach {tn conflict_return} {
|
||||
|
||||
} {}
|
||||
|
||||
do_conflict_test 5.$tn.2 -tables {{'foolish name'}} -sql {
|
||||
do_conflict_test $tn.5.$tn2.2 -tables {{'foolish name'}} -sql {
|
||||
INSERT INTO "'foolish name'" VALUES('one', 'two', 2);
|
||||
} -conflicts {
|
||||
{INSERT {'foolish name'} CONFLICT {t one t two i 2} {t one t two t i}}
|
||||
@@ -362,14 +377,14 @@ foreach {tn conflict_return} {
|
||||
|
||||
set res(REPLACE) {one one ii one two 2 two two ii}
|
||||
set res(OMIT) {one one ii one two i two two ii}
|
||||
do_db2_test 5.$tn.3 {
|
||||
do_db2_test $tn.5.$tn2.3 {
|
||||
SELECT * FROM "'foolish name'" ORDER BY x, y
|
||||
} $res($conflict_return)
|
||||
|
||||
|
||||
do_test 5.$tn.1 {
|
||||
do_test $tn.5.$tn2.1 {
|
||||
set schema {
|
||||
CREATE TABLE d1("z""z" PRIMARY KEY, y);
|
||||
CREATE TABLE d1("z""z" PRIMARY KEY, y)%WR%;
|
||||
INSERT INTO d1 VALUES(1, 'one');
|
||||
INSERT INTO d1 VALUES(2, 'two');
|
||||
}
|
||||
@@ -382,7 +397,7 @@ foreach {tn conflict_return} {
|
||||
|
||||
} {}
|
||||
|
||||
do_conflict_test 5.$tn.2 -tables d1 -sql {
|
||||
do_conflict_test $tn.5.$tn2.2 -tables d1 -sql {
|
||||
DELETE FROM d1 WHERE "z""z" = 2;
|
||||
} -conflicts {
|
||||
{DELETE d1 DATA {i 2 t two} {i 2 t TWO}}
|
||||
@@ -390,7 +405,7 @@ foreach {tn conflict_return} {
|
||||
|
||||
set res(REPLACE) {1 one}
|
||||
set res(OMIT) {1 one 2 TWO}
|
||||
do_db2_test 5.$tn.3 "SELECT * FROM d1" $res($conflict_return)
|
||||
do_db2_test $tn.5.$tn2.3 "SELECT * FROM d1" $res($conflict_return)
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
@@ -398,10 +413,10 @@ foreach {tn conflict_return} {
|
||||
#
|
||||
test_reset
|
||||
set schema {
|
||||
CREATE TABLE t1(a COLLATE nocase PRIMARY KEY, b);
|
||||
CREATE TABLE t2(a, b PRIMARY KEY);
|
||||
CREATE TABLE t1(a COLLATE nocase PRIMARY KEY, b)%WR%;
|
||||
CREATE TABLE t2(a, b PRIMARY KEY)%WR%;
|
||||
}
|
||||
do_test 6.0 {
|
||||
do_test $tn.6.0 {
|
||||
execsql $schema db
|
||||
execsql $schema db2
|
||||
execsql {
|
||||
@@ -411,7 +426,7 @@ do_test 6.0 {
|
||||
} {}
|
||||
|
||||
set conflict_return ""
|
||||
do_conflict_test 6.1 -tables {t1 t2} -sql {
|
||||
do_conflict_test $tn.6.1 -tables {t1 t2} -sql {
|
||||
INSERT INTO t1 VALUES('1', '2');
|
||||
INSERT INTO t1 VALUES('A', 'B');
|
||||
INSERT INTO t2 VALUES('A', 'B');
|
||||
@@ -419,8 +434,8 @@ do_conflict_test 6.1 -tables {t1 t2} -sql {
|
||||
{INSERT t1 CONFLICT {t A t B} {t a t b}}
|
||||
}
|
||||
|
||||
do_db2_test 6.2 "SELECT * FROM t1" {a b 1 2}
|
||||
do_db2_test 6.3 "SELECT * FROM t2" {a b A B}
|
||||
do_db2_test $tn.6.2 "SELECT * FROM t1 ORDER BY a" {1 2 a b}
|
||||
do_db2_test $tn.6.3 "SELECT * FROM t2 ORDER BY a" {A B a b}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that session objects are not confused by changes to table in
|
||||
@@ -429,10 +444,10 @@ do_db2_test 6.3 "SELECT * FROM t2" {a b A B}
|
||||
catch { db2 close }
|
||||
drop_all_tables
|
||||
forcedelete test.db2
|
||||
do_iterator_test 7.1 * {
|
||||
do_iterator_test $tn.7.1 * {
|
||||
ATTACH 'test.db2' AS aux;
|
||||
CREATE TABLE main.t1(x PRIMARY KEY, y);
|
||||
CREATE TABLE aux.t1(x PRIMARY KEY, y);
|
||||
CREATE TABLE main.t1(x PRIMARY KEY, y)%WR%;
|
||||
CREATE TABLE aux.t1(x PRIMARY KEY, y)%WR%;
|
||||
|
||||
INSERT INTO main.t1 VALUES('one', 1);
|
||||
INSERT INTO main.t1 VALUES('two', 2);
|
||||
@@ -446,10 +461,10 @@ do_iterator_test 7.1 * {
|
||||
#-------------------------------------------------------------------------
|
||||
# Test the sqlite3session_isempty() function.
|
||||
#
|
||||
do_test 8.1 {
|
||||
do_test $tn.8.1 {
|
||||
execsql {
|
||||
CREATE TABLE t5(x PRIMARY KEY, y);
|
||||
CREATE TABLE t6(x PRIMARY KEY, y);
|
||||
CREATE TABLE t5(x PRIMARY KEY, y)%WR%;
|
||||
CREATE TABLE t6(x PRIMARY KEY, y)%WR%;
|
||||
INSERT INTO t5 VALUES('a', 'b');
|
||||
INSERT INTO t6 VALUES('a', 'b');
|
||||
}
|
||||
@@ -458,20 +473,20 @@ do_test 8.1 {
|
||||
|
||||
S isempty
|
||||
} {1}
|
||||
do_test 8.2 {
|
||||
do_test $tn.8.2 {
|
||||
execsql { DELETE FROM t5 }
|
||||
S isempty
|
||||
} {0}
|
||||
do_test 8.3 {
|
||||
do_test $tn.8.3 {
|
||||
S delete
|
||||
sqlite3session S db main
|
||||
S attach t5
|
||||
execsql { DELETE FROM t5 }
|
||||
S isempty
|
||||
} {1}
|
||||
do_test 8.4 { S delete } {}
|
||||
do_test $tn.8.4 { S delete } {}
|
||||
|
||||
do_test 8.5 {
|
||||
do_test $tn.8.5 {
|
||||
sqlite3session S db main
|
||||
S attach t5
|
||||
S attach t6
|
||||
@@ -479,7 +494,7 @@ do_test 8.5 {
|
||||
S isempty
|
||||
} {0}
|
||||
|
||||
do_test 8.6 {
|
||||
do_test $tn.8.6 {
|
||||
S delete
|
||||
sqlite3session S db main
|
||||
S attach t5
|
||||
@@ -487,20 +502,20 @@ do_test 8.6 {
|
||||
execsql { INSERT INTO t6 VALUES(1, 2) }
|
||||
S isempty
|
||||
} {0}
|
||||
do_test 8.7 { S delete } {}
|
||||
do_test $tn.8.7 { S delete } {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 9.1 {
|
||||
CREATE TABLE t7(a, b, c, d, e PRIMARY KEY, f, g);
|
||||
do_execsql_test $tn.9.1 {
|
||||
CREATE TABLE t7(a, b, c, d, e PRIMARY KEY, f, g)%WR%;
|
||||
INSERT INTO t7 VALUES(1, 1, 1, 1, 1, 1, 1);
|
||||
}
|
||||
do_test 9.2 {
|
||||
do_test $tn.9.2 {
|
||||
sqlite3session S db main
|
||||
S attach *
|
||||
execsql { UPDATE t7 SET b=2, d=2 }
|
||||
} {}
|
||||
do_changeset_test 9.2 S {{UPDATE t7 0 ....X.. {{} {} i 1 {} {} i 1 i 1 {} {} {} {}} {{} {} i 2 {} {} i 2 {} {} {} {} {} {}}}}
|
||||
do_changeset_test $tn.9.2 S {{UPDATE t7 0 ....X.. {{} {} i 1 {} {} i 1 i 1 {} {} {} {}} {{} {} i 2 {} {} i 2 {} {} {} {} {} {}}}}
|
||||
S delete
|
||||
catch { db2 close }
|
||||
|
||||
@@ -509,9 +524,9 @@ catch { db2 close }
|
||||
#
|
||||
reset_db
|
||||
set tblname [string repeat tblname123 100]
|
||||
do_test 10.1.1 {
|
||||
do_test $tn.10.1.1 {
|
||||
execsql "
|
||||
CREATE TABLE $tblname (a PRIMARY KEY, b);
|
||||
CREATE TABLE $tblname (a PRIMARY KEY, b)%WR%;
|
||||
INSERT INTO $tblname VALUES('xyz', 'def');
|
||||
"
|
||||
sqlite3session S db main
|
||||
@@ -522,18 +537,18 @@ do_test 10.1.1 {
|
||||
"
|
||||
} {}
|
||||
breakpoint
|
||||
do_changeset_test 10.1.2 S "
|
||||
do_changeset_test $tn.10.1.2 S "
|
||||
{INSERT $tblname 0 X. {} {t uvw t abc}}
|
||||
{DELETE $tblname 0 X. {t xyz t def} {}}
|
||||
"
|
||||
do_test 10.1.4 { S delete } {}
|
||||
do_test $tn.10.1.4 { S delete } {}
|
||||
|
||||
#---------------------------------------------------------------
|
||||
reset_db
|
||||
do_execsql_test 11.1 {
|
||||
do_execsql_test $tn.11.1 {
|
||||
CREATE TABLE t1(a, b);
|
||||
}
|
||||
do_test 11.2 {
|
||||
do_test $tn.11.2 {
|
||||
sqlite3session S db main
|
||||
S attach t1
|
||||
execsql {
|
||||
@@ -550,9 +565,9 @@ S delete
|
||||
#
|
||||
reset_db
|
||||
set tblname [string repeat tblname123 100]
|
||||
do_test 10.1.1 {
|
||||
do_test $tn.10.1.1 {
|
||||
execsql "
|
||||
CREATE TABLE $tblname (a PRIMARY KEY, b);
|
||||
CREATE TABLE $tblname (a PRIMARY KEY, b)%WR%;
|
||||
INSERT INTO $tblname VALUES('xyz', 'def');
|
||||
"
|
||||
sqlite3session S db main
|
||||
@@ -563,23 +578,44 @@ do_test 10.1.1 {
|
||||
"
|
||||
} {}
|
||||
breakpoint
|
||||
do_changeset_test 10.1.2 S "
|
||||
do_changeset_test $tn.10.1.2 S "
|
||||
{INSERT $tblname 0 X. {} {t uvw t abc}}
|
||||
{DELETE $tblname 0 X. {t xyz t def} {}}
|
||||
"
|
||||
do_test 10.1.4 { S delete } {}
|
||||
do_test $tn.10.1.4 { S delete } {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test the effect of updating a column from 0.0 to 0.0.
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 11.1 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b REAL);
|
||||
do_execsql_test $tn.11.1 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b REAL)%WR%;
|
||||
INSERT INTO t1 VALUES(1, 0.0);
|
||||
}
|
||||
do_iterator_test 11.2 * {
|
||||
do_iterator_test $tn.11.2 * {
|
||||
UPDATE t1 SET b = 0.0;
|
||||
} {
|
||||
}
|
||||
|
||||
reset_db
|
||||
do_execsql_test $tn.12.1 {
|
||||
CREATE TABLE t1(r INTEGER PRIMARY KEY, a, b)%WR%;
|
||||
CREATE INDEX i1 ON t1(a);
|
||||
INSERT INTO t1 VALUES(1, 1, 1);
|
||||
INSERT INTO t1 VALUES(2, 1, 2);
|
||||
INSERT INTO t1 VALUES(3, 1, 3);
|
||||
}
|
||||
|
||||
do_iterator_test $tn.12.2 * {
|
||||
UPDATE t1 SET b='one' WHERE a=1;
|
||||
} {
|
||||
{UPDATE t1 0 X.. {i 1 {} {} i 1} {{} {} {} {} t one}}
|
||||
{UPDATE t1 0 X.. {i 2 {} {} i 2} {{} {} {} {} t one}}
|
||||
{UPDATE t1 0 X.. {i 3 {} {} i 3} {{} {} {} {} t one}}
|
||||
}
|
||||
|
||||
}]
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -0,0 +1,360 @@
|
||||
/*
|
||||
** 2017 January 31
|
||||
**
|
||||
** 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 contains the source code for a standalone program used to
|
||||
** test the performance of the sessions module. Compile and run:
|
||||
**
|
||||
** ./session_speed_test -help
|
||||
**
|
||||
** for details.
|
||||
*/
|
||||
|
||||
#include "sqlite3.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*************************************************************************
|
||||
** Start of generic command line parser.
|
||||
*/
|
||||
#define CMDLINE_BARE 0
|
||||
#define CMDLINE_INTEGER 1
|
||||
#define CMDLINE_STRING 2
|
||||
#define CMDLINE_BOOLEAN 3
|
||||
|
||||
typedef struct CmdLineOption CmdLineOption;
|
||||
struct CmdLineOption {
|
||||
const char *zText; /* Name of command line option */
|
||||
const char *zHelp; /* Help text for option */
|
||||
int eType; /* One of the CMDLINE_* values */
|
||||
int iOff; /* Offset of output variable */
|
||||
};
|
||||
|
||||
#define CMDLINE_INT32(x,y,z) {x, y, CMDLINE_INTEGER, z}
|
||||
#define CMDLINE_BOOL(x,y,z) {x, y, CMDLINE_BOOLEAN, z}
|
||||
#define CMDLINE_TEXT(x,y,z) {x, y, CMDLINE_STRING, z}
|
||||
#define CMDLINE_NONE(x,y,z) {x, y, CMDLINE_BARE, z}
|
||||
|
||||
static void option_requires_argument_error(CmdLineOption *pOpt){
|
||||
fprintf(stderr, "Option requires a%s argument: %s\n",
|
||||
pOpt->eType==CMDLINE_INTEGER ? "n integer" :
|
||||
pOpt->eType==CMDLINE_STRING ? " string" : " boolean",
|
||||
pOpt->zText
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void ambiguous_option_error(const char *zArg){
|
||||
fprintf(stderr, "Option is ambiguous: %s\n", zArg);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void unknown_option_error(
|
||||
const char *zArg,
|
||||
CmdLineOption *aOpt,
|
||||
const char *zHelp
|
||||
){
|
||||
int i;
|
||||
fprintf(stderr, "Unknown option: %s\n", zArg);
|
||||
fprintf(stderr, "\nOptions are:\n");
|
||||
fprintf(stderr, " % -30sEcho command line options\n", "-cmdline:verbose");
|
||||
for(i=0; aOpt[i].zText; i++){
|
||||
int eType = aOpt[i].eType;
|
||||
char *zOpt = sqlite3_mprintf("%s %s", aOpt[i].zText,
|
||||
eType==CMDLINE_BARE ? "" :
|
||||
eType==CMDLINE_INTEGER ? "N" :
|
||||
eType==CMDLINE_BOOLEAN ? "BOOLEAN" : "TEXT"
|
||||
);
|
||||
fprintf(stderr, " % -30s%s\n", zOpt, aOpt[i].zHelp);
|
||||
sqlite3_free(zOpt);
|
||||
}
|
||||
if( zHelp ){
|
||||
fprintf(stderr, "\n%s\n", zHelp);
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static int get_integer_option(CmdLineOption *pOpt, const char *zArg){
|
||||
int i = 0;
|
||||
int iRet = 0;
|
||||
int bSign = 1;
|
||||
if( zArg[0]=='-' ){
|
||||
bSign = -1;
|
||||
i = 1;
|
||||
}
|
||||
while( zArg[i] ){
|
||||
if( zArg[i]<'0' || zArg[i]>'9' ) option_requires_argument_error(pOpt);
|
||||
iRet = iRet*10 + (zArg[i] - '0');
|
||||
i++;
|
||||
}
|
||||
return (iRet*bSign);
|
||||
}
|
||||
|
||||
static int get_boolean_option(CmdLineOption *pOpt, const char *zArg){
|
||||
if( 0==sqlite3_stricmp(zArg, "true") ) return 1;
|
||||
if( 0==sqlite3_stricmp(zArg, "1") ) return 1;
|
||||
if( 0==sqlite3_stricmp(zArg, "0") ) return 0;
|
||||
if( 0==sqlite3_stricmp(zArg, "false") ) return 0;
|
||||
option_requires_argument_error(pOpt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void parse_command_line(
|
||||
int argc,
|
||||
char **argv,
|
||||
int iStart,
|
||||
CmdLineOption *aOpt,
|
||||
void *pStruct,
|
||||
const char *zHelp
|
||||
){
|
||||
char *pOut = (char*)pStruct;
|
||||
int bVerbose = 0;
|
||||
int iArg;
|
||||
|
||||
for(iArg=iStart; iArg<argc; iArg++){
|
||||
const char *zArg = argv[iArg];
|
||||
int nArg = strlen(zArg);
|
||||
int nMatch = 0;
|
||||
int iOpt;
|
||||
|
||||
for(iOpt=0; aOpt[iOpt].zText; iOpt++){
|
||||
CmdLineOption *pOpt = &aOpt[iOpt];
|
||||
if( 0==sqlite3_strnicmp(pOpt->zText, zArg, nArg) ){
|
||||
if( nMatch ){
|
||||
ambiguous_option_error(zArg);
|
||||
}
|
||||
nMatch++;
|
||||
if( pOpt->eType==CMDLINE_BARE ){
|
||||
*(int*)(&pOut[pOpt->iOff]) = 1;
|
||||
}else{
|
||||
iArg++;
|
||||
if( iArg==argc ){
|
||||
option_requires_argument_error(pOpt);
|
||||
}
|
||||
switch( pOpt->eType ){
|
||||
case CMDLINE_INTEGER:
|
||||
*(int*)(&pOut[pOpt->iOff]) = get_integer_option(pOpt, argv[iArg]);
|
||||
break;
|
||||
case CMDLINE_STRING:
|
||||
*(const char**)(&pOut[pOpt->iOff]) = argv[iArg];
|
||||
break;
|
||||
case CMDLINE_BOOLEAN:
|
||||
*(int*)(&pOut[pOpt->iOff]) = get_boolean_option(pOpt, argv[iArg]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( nMatch==0 && 0==sqlite3_strnicmp("-cmdline:verbose", zArg, nArg) ){
|
||||
bVerbose = 1;
|
||||
nMatch = 1;
|
||||
}
|
||||
|
||||
if( nMatch==0 ){
|
||||
unknown_option_error(zArg, aOpt, zHelp);
|
||||
}
|
||||
}
|
||||
|
||||
if( bVerbose ){
|
||||
int iOpt;
|
||||
fprintf(stdout, "Options are: ");
|
||||
for(iOpt=0; aOpt[iOpt].zText; iOpt++){
|
||||
CmdLineOption *pOpt = &aOpt[iOpt];
|
||||
if( pOpt->eType!=CMDLINE_BARE || *(int*)(&pOut[pOpt->iOff]) ){
|
||||
fprintf(stdout, "%s ", pOpt->zText);
|
||||
}
|
||||
switch( pOpt->eType ){
|
||||
case CMDLINE_INTEGER:
|
||||
fprintf(stdout, "%d ", *(int*)(&pOut[pOpt->iOff]));
|
||||
break;
|
||||
case CMDLINE_BOOLEAN:
|
||||
fprintf(stdout, "%d ", *(int*)(&pOut[pOpt->iOff]));
|
||||
break;
|
||||
case CMDLINE_STRING:
|
||||
fprintf(stdout, "%s ", *(const char**)(&pOut[pOpt->iOff]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
fprintf(stdout, "\n");
|
||||
}
|
||||
}
|
||||
/*
|
||||
** End of generic command line parser.
|
||||
*************************************************************************/
|
||||
|
||||
static void abort_due_to_error(int rc){
|
||||
fprintf(stderr, "Error: %d\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
static void execsql(sqlite3 *db, const char *zSql){
|
||||
int rc = sqlite3_exec(db, zSql, 0, 0, 0);
|
||||
if( rc!=SQLITE_OK ) abort_due_to_error(rc);
|
||||
}
|
||||
|
||||
static int xConflict(void *pCtx, int eConflict, sqlite3_changeset_iter *p){
|
||||
return SQLITE_CHANGESET_ABORT;
|
||||
}
|
||||
|
||||
static void run_test(
|
||||
sqlite3 *db,
|
||||
sqlite3 *db2,
|
||||
int nRow,
|
||||
const char *zSql
|
||||
){
|
||||
sqlite3_session *pSession = 0;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
int rc;
|
||||
int i;
|
||||
int nChangeset;
|
||||
void *pChangeset;
|
||||
|
||||
/* Attach a session object to database db */
|
||||
rc = sqlite3session_create(db, "main", &pSession);
|
||||
if( rc!=SQLITE_OK ) abort_due_to_error(rc);
|
||||
|
||||
/* Configure the session to capture changes on all tables */
|
||||
rc = sqlite3session_attach(pSession, 0);
|
||||
if( rc!=SQLITE_OK ) abort_due_to_error(rc);
|
||||
|
||||
/* Prepare the SQL statement */
|
||||
rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
|
||||
if( rc!=SQLITE_OK ) abort_due_to_error(rc);
|
||||
|
||||
/* Open a transaction */
|
||||
execsql(db, "BEGIN");
|
||||
|
||||
/* Execute the SQL statement nRow times */
|
||||
for(i=0; i<nRow; i++){
|
||||
sqlite3_bind_int(pStmt, 1, i);
|
||||
sqlite3_step(pStmt);
|
||||
rc = sqlite3_reset(pStmt);
|
||||
if( rc!=SQLITE_OK ) abort_due_to_error(rc);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
|
||||
/* Extract a changeset from the sessions object */
|
||||
rc = sqlite3session_changeset(pSession, &nChangeset, &pChangeset);
|
||||
if( rc!=SQLITE_OK ) abort_due_to_error(rc);
|
||||
execsql(db, "COMMIT");
|
||||
|
||||
/* Apply the changeset to the second db */
|
||||
rc = sqlite3changeset_apply(db2, nChangeset, pChangeset, 0, xConflict, 0);
|
||||
if( rc!=SQLITE_OK ) abort_due_to_error(rc);
|
||||
|
||||
/* Cleanup */
|
||||
sqlite3_free(pChangeset);
|
||||
sqlite3session_delete(pSession);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv){
|
||||
struct Options {
|
||||
int nRow;
|
||||
int bWithoutRowid;
|
||||
int bInteger;
|
||||
int bAll;
|
||||
const char *zDb;
|
||||
};
|
||||
struct Options o = { 2500, 0, 0, 0, "session_speed_test.db" };
|
||||
|
||||
CmdLineOption aOpt[] = {
|
||||
CMDLINE_INT32( "-rows", "number of rows in test",
|
||||
offsetof(struct Options, nRow) ),
|
||||
CMDLINE_BOOL("-without-rowid", "use WITHOUT ROWID tables",
|
||||
offsetof(struct Options, bWithoutRowid) ),
|
||||
CMDLINE_BOOL("-integer", "use integer data (instead of text/blobs)",
|
||||
offsetof(struct Options, bInteger) ),
|
||||
CMDLINE_NONE("-all", "Run all 4 combos of -without-rowid and -integer",
|
||||
offsetof(struct Options, bAll) ),
|
||||
CMDLINE_TEXT("-database", "prefix for database files to use",
|
||||
offsetof(struct Options, zDb) ),
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
const char *azCreate[] = {
|
||||
"CREATE TABLE t1(a PRIMARY KEY, b, c, d)",
|
||||
"CREATE TABLE t1(a PRIMARY KEY, b, c, d) WITHOUT ROWID",
|
||||
};
|
||||
|
||||
const char *azInsert[] = {
|
||||
"INSERT INTO t1 VALUES("
|
||||
"printf('%.8d',?), randomblob(50), randomblob(50), randomblob(50))",
|
||||
"INSERT INTO t1 VALUES(?, random(), random(), random())"
|
||||
};
|
||||
|
||||
const char *azUpdate[] = {
|
||||
"UPDATE t1 SET d = randomblob(50) WHERE a = printf('%.8d',?)",
|
||||
"UPDATE t1 SET d = random() WHERE a = ?"
|
||||
};
|
||||
|
||||
const char *azDelete[] = {
|
||||
"DELETE FROM t1 WHERE a = printf('%.8d',?)",
|
||||
"DELETE FROM t1 WHERE a = ?"
|
||||
};
|
||||
|
||||
int rc;
|
||||
sqlite3 *db;
|
||||
sqlite3 *db2;
|
||||
char *zDb2;
|
||||
int bWithoutRowid;
|
||||
int bInteger;
|
||||
|
||||
parse_command_line(argc, argv, 1, aOpt, (void*)&o,
|
||||
"This program creates two new, empty, databases each containing a single\n"
|
||||
"table. It then does the following:\n\n"
|
||||
" 1. Inserts -rows rows into the first database\n"
|
||||
" 2. Updates each row in the first db\n"
|
||||
" 3. Delete each row from the first db\n\n"
|
||||
"The modifications made by each step are captured in a changeset and\n"
|
||||
"applied to the second database.\n"
|
||||
);
|
||||
zDb2 = sqlite3_mprintf("%s2", o.zDb);
|
||||
|
||||
for(bWithoutRowid=0; bWithoutRowid<2; bWithoutRowid++){
|
||||
for(bInteger=0; bInteger<2; bInteger++){
|
||||
if( o.bAll || (o.bWithoutRowid==bWithoutRowid && o.bInteger==bInteger) ){
|
||||
fprintf(stdout, "Testing %s data with %s table\n",
|
||||
bInteger ? "integer" : "blob/text",
|
||||
bWithoutRowid ? "WITHOUT ROWID" : "rowid"
|
||||
);
|
||||
|
||||
/* Open new database handles on two empty databases */
|
||||
unlink(o.zDb);
|
||||
rc = sqlite3_open(o.zDb, &db);
|
||||
if( rc!=SQLITE_OK ) abort_due_to_error(rc);
|
||||
unlink(zDb2);
|
||||
rc = sqlite3_open(zDb2, &db2);
|
||||
if( rc!=SQLITE_OK ) abort_due_to_error(rc);
|
||||
|
||||
/* Create the schema in both databases. */
|
||||
execsql(db, azCreate[o.bWithoutRowid]);
|
||||
execsql(db2, azCreate[o.bWithoutRowid]);
|
||||
|
||||
/* Run the three tests */
|
||||
run_test(db, db2, o.nRow, azInsert[o.bInteger]);
|
||||
run_test(db, db2, o.nRow, azUpdate[o.bInteger]);
|
||||
run_test(db, db2, o.nRow, azDelete[o.bInteger]);
|
||||
|
||||
/* Close the db handles */
|
||||
sqlite3_close(db);
|
||||
sqlite3_close(db2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
# 2017 Jan 31
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# The focus of this file is testing the session module. Specifically,
|
||||
# testing support for WITHOUT ROWID tables.
|
||||
#
|
||||
|
||||
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 sessionwor
|
||||
|
||||
proc test_reset {} {
|
||||
catch { db close }
|
||||
catch { db2 close }
|
||||
forcedelete test.db test.db2
|
||||
sqlite3 db test.db
|
||||
sqlite3 db2 test.db2
|
||||
}
|
||||
|
||||
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b) WITHOUT ROWID;
|
||||
}
|
||||
|
||||
do_iterator_test 1.1 t1 {
|
||||
INSERT INTO t1 VALUES('one', 'two');
|
||||
} {
|
||||
{INSERT t1 0 X. {} {t one t two}}
|
||||
}
|
||||
|
||||
do_iterator_test 1.2 t1 {
|
||||
UPDATE t1 SET b='three'
|
||||
} {
|
||||
{UPDATE t1 0 X. {t one t two} {{} {} t three}}
|
||||
}
|
||||
|
||||
do_iterator_test 1.3 t1 {
|
||||
DELETE FROM t1;
|
||||
} {
|
||||
{DELETE t1 0 X. {t one t three} {}}
|
||||
}
|
||||
|
||||
finish_test
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
** directory as this file for additional information.
|
||||
*/
|
||||
#ifdef SQLITE_USER_AUTHENTICATION
|
||||
#ifndef _SQLITEINT_H_
|
||||
#ifndef SQLITEINT_H
|
||||
# include "sqliteInt.h"
|
||||
#endif
|
||||
|
||||
|
||||
@@ -478,6 +478,8 @@ SHELL_OPT += -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
|
||||
FUZZERSHELL_OPT = -DSQLITE_ENABLE_JSON1
|
||||
FUZZCHECK_OPT = -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_MEMSYS5
|
||||
DBFUZZ_OPT =
|
||||
KV_OPT = -DSQLITE_THREADSAFE=0 -DSQLITE_DIRECT_OVERFLOW_READ
|
||||
ST_OPT = -DSQLITE_THREADSAFE=0
|
||||
|
||||
# This is the default Makefile target. The objects listed here
|
||||
# are what get build when you type just "make" with no arguments.
|
||||
@@ -892,8 +894,11 @@ wordcount$(EXE): $(TOP)/test/wordcount.c sqlite3.c
|
||||
$(TCC) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -o wordcount$(EXE) \
|
||||
$(TOP)/test/wordcount.c sqlite3.c
|
||||
|
||||
speedtest1$(EXE): $(TOP)/test/speedtest1.c sqlite3.o
|
||||
$(TCC) -I. $(OTAFLAGS) -o speedtest1$(EXE) $(TOP)/test/speedtest1.c sqlite3.o $(THREADLIB)
|
||||
speedtest1$(EXE): $(TOP)/test/speedtest1.c sqlite3.c
|
||||
$(TCCX) -I. $(ST_OPT) -o speedtest1$(EXE) $(TOP)/test/speedtest1.c sqlite3.c $(THREADLIB)
|
||||
|
||||
kvtest$(EXE): $(TOP)/test/kvtest.c sqlite3.c
|
||||
$(TCCX) -I. $(KV+OPT) -o kvtest$(EXE) $(TOP)/test/kvtest.c sqlite3.c $(THREADLIB)
|
||||
|
||||
rbu$(EXE): $(TOP)/ext/rbu/rbu.c $(TOP)/ext/rbu/sqlite3rbu.c sqlite3.o
|
||||
$(TCC) -I. -o rbu$(EXE) $(TOP)/ext/rbu/rbu.c sqlite3.o \
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
C Modify\sthe\sOP_RowData\sopcode\sso\sthat\swhen\sP3!=0\sit\sis\sallowed\sto\shold\san\nephemeral\scopy\sof\sthe\scontent.\s\sThis\savoids\sunnecessary\smemcpy()\soperations\nin\sthe\sxfer-optimization\sand\sVACUUM.
|
||||
D 2017-01-09T15:44:25.721
|
||||
F Makefile.in 41bd4cad981487345c4a84081074bcdb876e4b2e
|
||||
C This\sis\san\sexperimental\spatch\sthat\sensures\sthat\sall\scursors\shave\stheir\sposition\nsaved\sprior\sto\sstarting\sa\sROLLBACK\sTO.
|
||||
D 2017-02-02T00:46:55.000
|
||||
F Makefile.in 5f415e7867296d678fed2e6779aea10c1318b4bc
|
||||
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
|
||||
F Makefile.msc b8ca53350ae545e3562403d5da2a69cec79308da
|
||||
F Makefile.msc ba953c8921fc7e18333f61898007206de7e23964
|
||||
F README.md 8ecc12493ff9f820cdea6520a9016001cb2e59b7
|
||||
F VERSION cddd8d88dc8202afa0ebc96da61fc4acbd1e96a5
|
||||
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
|
||||
@@ -11,7 +11,7 @@ F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90
|
||||
F art/sqlite370.jpg d512473dae7e378a67e28ff96a34da7cb331def2
|
||||
F autoconf/INSTALL 83e4a25da9fd053c7b3665eaaaf7919707915903
|
||||
F autoconf/Makefile.am 1a47d071e3d5435f8f7ebff7eb6703848bbd65d4
|
||||
F autoconf/Makefile.msc b6d27f735911fd09a589d3c966936c47a5850c17
|
||||
F autoconf/Makefile.msc 3f29e0fc2a78e113250d79a5da375c1d8d8c06b0
|
||||
F autoconf/README.first 6c4f34fe115ff55d4e8dbfa3cecf04a0188292f7
|
||||
F autoconf/README.txt 4f04b0819303aabaa35fff5f7b257fb0c1ef95f1
|
||||
F autoconf/configure.ac cacf2616abf6e4a569bde2ef365c143caeec40bc
|
||||
@@ -70,7 +70,7 @@ F ext/fts3/README.content fdc666a70d5257a64fee209f97cf89e0e6e32b51
|
||||
F ext/fts3/README.syntax a19711dc5458c20734b8e485e75fb1981ec2427a
|
||||
F ext/fts3/README.tokenizers e0a8b81383ea60d0334d274fadf305ea14a8c314
|
||||
F ext/fts3/README.txt 8c18f41574404623b76917b9da66fcb0ab38328d
|
||||
F ext/fts3/fts3.c e028eb13432f108d2e22cded019fc980700e4e00
|
||||
F ext/fts3/fts3.c 5a8cafedffd101e9946f8909ecf8d34aaa383b4d
|
||||
F ext/fts3/fts3.h 3a10a0af180d502cecc50df77b1b22df142817fe
|
||||
F ext/fts3/fts3Int.h 89d0bd4595a0de384dac78e94b803de12586e8dd
|
||||
F ext/fts3/fts3_aux.c 9edc3655fcb287f0467d0a4b886a01c6185fe9f1
|
||||
@@ -102,7 +102,7 @@ F ext/fts5/fts5Int.h b2eda36e0f224365c8e23dc8f559311834f1c13f
|
||||
F ext/fts5/fts5_aux.c 67acf8d51723cf28ffc3828210ba662df4b8d267
|
||||
F ext/fts5/fts5_buffer.c 4c1502d4c956cd092c89ce4480867f9d8bf325cd
|
||||
F ext/fts5/fts5_config.c 5af9c360e99669d29f06492c370892394aba0857
|
||||
F ext/fts5/fts5_expr.c dc2cee9f56b1818b85df59304b8104a5dfb8ab60
|
||||
F ext/fts5/fts5_expr.c 33f0e8063ac7360bcd71c0ff0dcbacdc05fffe50
|
||||
F ext/fts5/fts5_hash.c 880998e596b60f078348d48732ca4ad9a90caad2
|
||||
F ext/fts5/fts5_index.c f67032a9a529ba52a545e6e3ab970764199c05d4
|
||||
F ext/fts5/fts5_main.c f85281445dcf8be32d18841c93a6f90fe27dbfe2
|
||||
@@ -184,11 +184,11 @@ F ext/fts5/test/fts5rowid.test 16908a99d6efc9ba21081b4f2b86b3fc699839a6
|
||||
F ext/fts5/test/fts5simple.test 5da9b15ed534eb0be9f279d8a2bb2e24d30e4e38
|
||||
F ext/fts5/test/fts5simple2.test 00839031878f52391562594fdab0503e424ee071
|
||||
F ext/fts5/test/fts5simple3.test 25faa8cb8ad470c6f01f670bcc1317c19a89f091
|
||||
F ext/fts5/test/fts5synonym.test 6475d189c2e20d60795808f83e36bf9318708d48
|
||||
F ext/fts5/test/fts5synonym.test f964f9a98580dec06c995d4638ecb36eee60ae4a
|
||||
F ext/fts5/test/fts5synonym2.test aa4c43bd3b691ff80f658cb064f5ab40690e834e
|
||||
F ext/fts5/test/fts5tok1.test beb894c6f3468f10a574302f69ebe4436b0287c7
|
||||
F ext/fts5/test/fts5tok2.test dcacb32d4a2a3f0dd3215d4a3987f78ae4be21a2
|
||||
F ext/fts5/test/fts5tokenizer.test ea4df698b35cc427ebf2ba22829d0e28386d8c89
|
||||
F ext/fts5/test/fts5tokenizer.test f64ab5fe48fa1317ff6315264bfe26e73cbdefde
|
||||
F ext/fts5/test/fts5unicode.test fbef8d8a3b4b88470536cc57604a82ca52e51841
|
||||
F ext/fts5/test/fts5unicode2.test 529ac7e8648c943bc87bfed1e427128a2f3f9e33
|
||||
F ext/fts5/test/fts5unicode3.test 35c3d02aa7acf7d43d8de3bfe32c15ba96e8928e
|
||||
@@ -202,7 +202,7 @@ F ext/fts5/tool/loadfts5.tcl 95b03429ee6b138645703c6ca192c3ac96eaf093
|
||||
F ext/fts5/tool/mkfts5c.tcl d1c2a9ab8e0ec690a52316f33dd9b1d379942f45
|
||||
F ext/fts5/tool/showfts5.tcl d54da0e067306663e2d5d523965ca487698e722c
|
||||
F ext/icu/README.txt d9fbbad0c2f647c3fdf715fc9fd64af53aedfc43
|
||||
F ext/icu/icu.c 03ff6f90f3004a7e5a86205b581b2b7035ebf6e1
|
||||
F ext/icu/icu.c 84900472a088a3a172c6c079f58a1d3a1952c332
|
||||
F ext/icu/sqliteicu.h 728867a802baa5a96de7495e9689a8e01715ef37
|
||||
F ext/misc/amatch.c 211108e201105e4bb0c076527b8cfd34330fc234
|
||||
F ext/misc/carray.c 40c27641010a4dc67e3690bdb7c9d36ca58b3c2d
|
||||
@@ -213,7 +213,7 @@ F ext/misc/eval.c f971962e92ebb8b0a4e6b62949463ee454d88fa2
|
||||
F ext/misc/fileio.c d4171c815d6543a9edef8308aab2951413cd8d0f
|
||||
F ext/misc/fuzzer.c 7c64b8197bb77b7d64eff7cac7848870235d4c25
|
||||
F ext/misc/ieee754.c f190d0cc5182529acb15babd177781be1ac1718c
|
||||
F ext/misc/json1.c e0797ef1bfa637429ceab9140ecba620809ee02a
|
||||
F ext/misc/json1.c 552a7d730863419e05dc947265b28bd411680e2e
|
||||
F ext/misc/memvfs.c e5225bc22e79dde6b28380f3a068ddf600683a33
|
||||
F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342
|
||||
F ext/misc/percentile.c 92699c8cd7d517ff610e6037e56506f8904dae2e
|
||||
@@ -222,12 +222,13 @@ F ext/misc/remember.c 8440f8d0b452c5cdefb62b57135ccd1267aa729d
|
||||
F ext/misc/rot13.c 1ac6f95f99b575907b9b09c81a349114cf9be45a
|
||||
F ext/misc/scrub.c 1c5bfb8b0cd18b602fcb55755e84abf0023ac2fb
|
||||
F ext/misc/series.c e11e534ada797d5b816d7e7a93c022306563ca35
|
||||
F ext/misc/sha1.c b2e4eb8e26f09701ec15548395baf698f00e5895
|
||||
F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
|
||||
F ext/misc/spellfix.c a4723b6aff748a417b5091b68a46443265c40f0d
|
||||
F ext/misc/totype.c 4a167594e791abeed95e0a8db028822b5e8fe512
|
||||
F ext/misc/vfslog.c fe40fab5c077a40477f7e5eba994309ecac6cc95
|
||||
F ext/misc/vfsstat.c bf10ef0bc51e1ad6756629e1edb142f7a8db1178
|
||||
F ext/misc/vtshim.c babb0dc2bf116029e3e7c9a618b8a1377045303e
|
||||
F ext/misc/vtshim.c 1976e6dd68dd0d64508c91a6dfab8e75f8aaf6cd
|
||||
F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212
|
||||
F ext/rbu/rbu.c b2c0b5e6ae1a89affc0edfc127ebfa5f637a0ce4
|
||||
F ext/rbu/rbu1.test 43836fac8c7179a358eaf38a8a1ef3d6e6285842
|
||||
@@ -252,16 +253,18 @@ F ext/rbu/rbudor.test 99b05cc0df613e962c2c8085cfb05686a09cf315
|
||||
F ext/rbu/rbufault.test cc0be8d5d392d98b0c2d6a51be377ea989250a89
|
||||
F ext/rbu/rbufault2.test 9a7f19edd6ea35c4c9f807d8a3db0a03a5670c06
|
||||
F ext/rbu/rbufault3.test 54a399888ac4af44c68f9f58afbed23149428bca
|
||||
F ext/rbu/rbufault4.test 34e70701cbec51571ffbd9fbf9d4e0f2ec495ca7
|
||||
F ext/rbu/rbufts.test 828cd689da825f0a7b7c53ffc1f6f7fdb6fa5bda
|
||||
F ext/rbu/rbuprogress.test e3e25fb7622641b8f2df7c6b7a7eb6fddfc46a4b
|
||||
F ext/rbu/rburesume.test 8acb77f4a422ff55acfcfc9cc15a5cb210b1de83
|
||||
F ext/rbu/rbusave.test 0f43b6686084f426ddd040b878426452fd2c2f48
|
||||
F ext/rbu/rbuvacuum.test 4a977447c15c2581ab668781d9ef4294382530e0
|
||||
F ext/rbu/rbuvacuum2.test 2569205b74ff40fbf3bda2fce33a58eb40eebdcc
|
||||
F ext/rbu/sqlite3rbu.c e074c38798b90591f7f0cf0032d62f152ce5a95e
|
||||
F ext/rbu/rbuvacuum2.test 2074ab14fe66e1c7e7210c62562650dcd215bbaa
|
||||
F ext/rbu/sqlite3rbu.c bb0de6cdbdb14a7d55a097238a434b7e99caf318
|
||||
F ext/rbu/sqlite3rbu.h 6fb6294c34a9ca93b5894a33bca530c6f08decba
|
||||
F ext/rbu/test_rbu.c 5aa22616afac6f71ebd3d9bc9bf1006cfabcca88
|
||||
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
|
||||
F ext/rtree/rtree.c 078fc27417b48aaebe988621bf96ef0e1645b201
|
||||
F ext/rtree/rtree.c 70f2488eef5c04dccf15a52cbd4961492124f825
|
||||
F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
|
||||
F ext/rtree/rtree1.test 42dadfc7b44a436cd74a1bebc0b9b689e4eaf7ec
|
||||
F ext/rtree/rtree2.test acbb3a4ce0f4fbc2c304d2b4b784cfa161856bba
|
||||
@@ -285,7 +288,7 @@ F ext/rtree/sqlite3rtree.h 9c5777af3d2921c7b4ae4954e8e5697502289d28
|
||||
F ext/rtree/tkt3363.test 142ab96eded44a3615ec79fba98c7bde7d0f96de
|
||||
F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
|
||||
F ext/session/changeset.c 4ccbaa4531944c24584bf6a61ba3a39c62b6267a
|
||||
F ext/session/session1.test e5125b216d1e8c91e0984b361b0b68529e7c5dfb
|
||||
F ext/session/session1.test c8a50e0e8581dc1a00e832aa59bb61f180404d44
|
||||
F ext/session/session2.test 284de45abae4cc1082bc52012ee81521d5ac58e0
|
||||
F ext/session/session3.test a7a9ce59b8d1e49e2cc23d81421ac485be0eea01
|
||||
F ext/session/session4.test 457b02bdc349eb01151e54de014df77abd3c08c8
|
||||
@@ -301,18 +304,20 @@ F ext/session/sessionE.test e60a238c47f0feb3bb707e7f35e22be09c7e8f26
|
||||
F ext/session/sessionF.test c2f178d4dfd723a5fd94a730ea2ccb44c669e3ce
|
||||
F ext/session/sessionG.test 01ef705096a9d3984eebdcca79807a211dee1b60
|
||||
F ext/session/session_common.tcl 9b696a341cf1d3744823715ed92bb19749b6c3d4
|
||||
F ext/session/session_speed_test.c edc1f96fd5e0e4b16eb03e2a73041013d59e8723
|
||||
F ext/session/sessionfault.test da273f2712b6411e85e71465a1733b8501dbf6f7
|
||||
F ext/session/sessionfault2.test 04aa0bc9aa70ea43d8de82c4f648db4de1e990b0
|
||||
F ext/session/sessionwor.test 2f3744236dc8b170a695b7d8ddc8c743c7e79fdc
|
||||
F ext/session/sqlite3session.c c61a43396368ec00dc127f7bc647e9bd6a4ee5fb
|
||||
F ext/session/sqlite3session.h 9345166bd8f80562145586cf817f707de5ecada2
|
||||
F ext/session/test_session.c eb0bd6c1ea791c1d66ee4ef94c16500dad936386
|
||||
F ext/userauth/sqlite3userauth.h 19cb6f0e31316d0ee4afdfb7a85ef9da3333a220
|
||||
F ext/userauth/user-auth.txt e6641021a9210364665fe625d067617d03f27b04
|
||||
F ext/userauth/userauth.c 5fa3bdb492f481bbc1709fc83c91ebd13460c69e
|
||||
F ext/userauth/userauth.c 3410be31283abba70255d71fd24734e017a4497f
|
||||
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
|
||||
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
|
||||
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
|
||||
F main.mk 2cf5f0362c5687fd8e912c3a327b49a2e8ba0f9b
|
||||
F main.mk afc52937b4e5fe08678e8d5a4fe4487d44e3bc61
|
||||
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
|
||||
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
|
||||
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
|
||||
@@ -325,14 +330,14 @@ F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
|
||||
F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786
|
||||
F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
|
||||
F src/alter.c 3b23977620ce9662ac54443f65b87ba996e36121
|
||||
F src/analyze.c 3c4a63ff7a55faefecf6eb1589932fdbc06b2415
|
||||
F src/analyze.c ac7a5d7e3cee07d074697904e00e4a8ab7b2b4f5
|
||||
F src/attach.c 8c476f8bd5d2afe11d925f890d30e527e5b0ce43
|
||||
F src/auth.c 930b376a9c56998557367e6f7f8aaeac82a2a792
|
||||
F src/backup.c faf17e60b43233c214aae6a8179d24503a61e83b
|
||||
F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33
|
||||
F src/btmutex.c 0e9ce2d56159b89b9bc8e197e023ee11e39ff8ca
|
||||
F src/btree.c d2c100618784bd89c089fcef03ff6e789768ecae
|
||||
F src/btree.h 2349a588abcd7e0c04f984e15c5c777b61637583
|
||||
F src/btree.c 9fe65ab418d99e80289f024016b5e5e74c3059dd
|
||||
F src/btree.h e6d352808956ec163a17f832193a3e198b3fb0ac
|
||||
F src/btreeInt.h 10c4b77c2fb399580babbcc7cf652ac10dba796e
|
||||
F src/build.c 9e799f1edd910dfa8a0bc29bd390d35d310596af
|
||||
F src/callback.c 2e76147783386374bf01b227f752c81ec872d730
|
||||
@@ -340,8 +345,8 @@ F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
|
||||
F src/ctime.c 9f2296a4e5d26ebf0e0d95a0af4628f1ea694e7a
|
||||
F src/date.c dc3f1391d9297f8c748132813aaffcb117090d6e
|
||||
F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d
|
||||
F src/delete.c a84f6229ccb9448460c287248024ceb70e10baab
|
||||
F src/expr.c f06f41e5e5daca10fb090e70a2502dcc0dbc992b
|
||||
F src/delete.c 0d9d5549d42e79ce4d82ff1db1e6c81e36d2f67c
|
||||
F src/expr.c d7ef6fdb70bc18259d7d36b22aa0bc4845c5c5b9
|
||||
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
|
||||
F src/fkey.c 2e9aabe1aee76273aff8a84ee92c464e095400ae
|
||||
F src/func.c c67273e1ec08abbdcc14c189892a3ff6eeece86b
|
||||
@@ -350,11 +355,11 @@ F src/hash.c 63d0ee752a3b92d4695b2b1f5259c4621b2cfebd
|
||||
F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4
|
||||
F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da
|
||||
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
|
||||
F src/insert.c 7761fd63136771d411f096f4d7a1af9c5057ddd4
|
||||
F src/insert.c 444354c23d4d140a57d6eb46f34e376a7f8f62e8
|
||||
F src/legacy.c 75d3023be8f0d2b99d60f905090341a03358c58e
|
||||
F src/loadext.c 5d6642d141c07d366e43d359e94ec9de47add41d
|
||||
F src/loadext.c a68d8d1d14cf7488bb29dc5311cb1ce9a4404258
|
||||
F src/main.c e207b81542d13b9f13d61e78ca441f9781f055b0
|
||||
F src/malloc.c f3fad34cd570022abca558c573f1761fb09a8212
|
||||
F src/malloc.c fc1b9f445290f2145da48fc08730c26e6082b640
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
F src/mem1.c 6919bcf12f221868ea066eec27e579fed95ce98b
|
||||
F src/mem2.c f1940d9e91948dd6a908fbb9ce3835c36b5d83c3
|
||||
@@ -375,29 +380,29 @@ F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586
|
||||
F src/os_unix.c 30e2c43e4955db990e5b5a81e901f8aa74cc8820
|
||||
F src/os_win.c cf90abd4e50d9f56d2c20ce8e005aff55d7bd8e9
|
||||
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
|
||||
F src/pager.c 9dc72d23eebbdf992bd69f2ab954d0d3a27c7340
|
||||
F src/pager.h d1e944291030351f362a0a7da9b5c3e34e603e39
|
||||
F src/parse.y 29153738a7322054359320eb00b5a4cd44389f20
|
||||
F src/pager.c ff1232b3088a39806035ecfac4fffeb22717d80b
|
||||
F src/pager.h f2a99646c5533ffe11afa43e9e0bea74054e4efa
|
||||
F src/parse.y 591704fce84f814d9a3642774c1f011d38f4149c
|
||||
F src/pcache.c 51070ec9b8251bbf9c6ea3d35fd96a458752929e
|
||||
F src/pcache.h 2cedcd8407eb23017d92790b112186886e179490
|
||||
F src/pcache1.c e3967219b2a92b9edcb9324a4ba75009090d3953
|
||||
F src/pragma.c 0e7a7c6f1c6fd8ff50c0fff65b8bb80174bc49c5
|
||||
F src/pragma.c 7831956012f5d764761fbd023e59b0ffc08f5e8d
|
||||
F src/pragma.h 61aa5389118594bebb28120a6720401aee34ce1a
|
||||
F src/prepare.c b1140c3d0cf59bc85ace00ce363153041b424b7a
|
||||
F src/printf.c ff10a9b9902cd2afe5f655f3013c6307d969b1fd
|
||||
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
|
||||
F src/resolve.c bb070cf5f23611c44ab7e4788803684e385fc3fb
|
||||
F src/resolve.c f9bc0de45a30a450da47b3766de00be89bf9be79
|
||||
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
|
||||
F src/select.c 533e55a4067278fef76eff951462383d4147880f
|
||||
F src/shell.c 6095531aa900decdaa765e0f3993fba7153c92c1
|
||||
F src/sqlite.h.in e71655293c9bde26939496f3aac9d1821d2c07a2
|
||||
F src/select.c 3856db523b942062bca8722ba03b61c324ff94d6
|
||||
F src/shell.c a84e453c213f3e0d6935a582024da4e242f85a19
|
||||
F src/sqlite.h.in 751ff125eb159c8f92c182b8df980a5e4f50e966
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h 8648034aa702469afb553231677306cc6492a1ae
|
||||
F src/sqliteInt.h 9fdfb8789b27a621f3401468bc1705c32308f877
|
||||
F src/sqliteInt.h 3724c48e82605b471bbf7e41109b5850c6e89f31
|
||||
F src/sqliteLimit.h c0373387c287c8d0932510b5547ecde31b5da247
|
||||
F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1
|
||||
F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9
|
||||
F src/tclsqlite.c 205c66b9b81d97978a155caa3ef5be9c4de2b174
|
||||
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
|
||||
F src/tclsqlite.c 418f5e5e0840425a7e5b33f3600dccd378a57549
|
||||
F src/test1.c 8a98191a1da8e100f77cdb5cc716df67d405028d
|
||||
F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5
|
||||
F src/test3.c d03f5b5da9a2410b7a91c64b0d3306ed28ab6fee
|
||||
@@ -414,7 +419,7 @@ F src/test_bestindex.c d23f80d334c59662af69191854c76b8d3d0c8c96
|
||||
F src/test_blob.c 6a4c7920d1d9c6cc0f7aa50c89c4f80016aeda83
|
||||
F src/test_btree.c 8b2dc8b8848cf3a4db93f11578f075e82252a274
|
||||
F src/test_config.c 83179ea845479b5be9a651d014649e3f2722a1fe
|
||||
F src/test_delete.c 8499d4d323f2ec8e28301deb3d6ddd8eef8b8139
|
||||
F src/test_delete.c af7eab5702f853fb1c62a5f7665e2234cf1ae17b
|
||||
F src/test_demovfs.c a0c3bdd45ed044115c2c9f7779e56eafff18741e
|
||||
F src/test_devsym.c 4e58dec2602d8e139ca08659f62a62450587cb58
|
||||
F src/test_fs.c e16cbe68d3b107e00a907c20a9a02629870eb69b
|
||||
@@ -444,23 +449,23 @@ F src/test_tclvar.c df9fe1213c2634687a9ca0b0bec0d2119d359ae3
|
||||
F src/test_thread.c 911d15fb14e19c0c542bdc8aabf981c2f10a4858
|
||||
F src/test_vfs.c f0186261a24de2671d080bcd8050732f0cb64f6e
|
||||
F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698
|
||||
F src/test_windirent.c 600398db0198ca1c77ca183831bf456746b6f5c4
|
||||
F src/test_windirent.h 7edc57e2faa727026dbd5d010dd0e2e665d5aa01
|
||||
F src/test_windirent.c 17f91f5f2aa1bb7328abb49414c363b5d2a9d3ff
|
||||
F src/test_windirent.h 5d67483a55442e31e1bde0f4a230e6e932ad5906
|
||||
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
|
||||
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
|
||||
F src/tokenize.c 5c2f516876fc27fbd7753913f032f49eb89e83b5
|
||||
F src/tokenize.c 25ccc63ae2c4163933221f3181c9982b47cd08d2
|
||||
F src/treeview.c 4e44ade3bfe59d82005039f72e09333ce2b4162c
|
||||
F src/trigger.c c9f0810043b265724fdb1bdd466894f984dfc182
|
||||
F src/update.c 1da7c462110bffed442a42884cb0d528c1db46d8
|
||||
F src/update.c 456d4a4656f8a03c2abc88a51b19172197400e58
|
||||
F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c
|
||||
F src/util.c a88b0466fddf445ce752226d4698ca3faada620a
|
||||
F src/vacuum.c 33c174b28886b2faf26e503b5a49a1c01a9b1c16
|
||||
F src/vdbe.c b7f39cec8b572df61cdbd26426e285f8fd759db3
|
||||
F src/vdbe.h b0866e4191f096f1c987a84b042c3599bdf5423b
|
||||
F src/vdbeInt.h 281cb70332dc8b593b8c7afe776f3a2ba7d4255e
|
||||
F src/vdbeapi.c d6ebaa465f070eb1af8ba4e7b34583ece87bdd24
|
||||
F src/vdbeaux.c 35c9a9908174e5a26c96d15e1f98214814a39147
|
||||
F src/vdbeblob.c f4f98ea672b242f807c08c92c7faaa79e5091b65
|
||||
F src/vdbe.c e7b1e860140f1d1803c6f4176b1e8f3801f3a290
|
||||
F src/vdbe.h 59998ffd71d7caa8886bc78dafaf8caeccd4c13c
|
||||
F src/vdbeInt.h 4e4b15b2e1330e1636e4e01974eab2b0b985092f
|
||||
F src/vdbeapi.c 3e4a8893feeb78620f4aac4ac5b85d92255b97e1
|
||||
F src/vdbeaux.c b9a36e530e6525ca9d9a685bc7b1d01fa77b5cf8
|
||||
F src/vdbeblob.c 359891617358deefc85bef7bcf787fa6b77facb9
|
||||
F src/vdbemem.c 3b5a9a5b375458d3e12a50ae1aaa41eeec2175fd
|
||||
F src/vdbesort.c eda25cb2d1727efca6f7862fea32b8aa33c0face
|
||||
F src/vdbetrace.c 41963d5376f0349842b5fc4aaaaacd7d9cdc0834
|
||||
@@ -469,12 +474,13 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
|
||||
F src/wal.c 40c543f0a2195d1b0dc88ef12142bea690009344
|
||||
F src/wal.h 06b2a0b599cc0f53ea97f497cf8c6b758c999f71
|
||||
F src/walker.c 91a6df7435827e41cff6bb7df50ea00934ee78b0
|
||||
F src/where.c 6bbf9284f4f15a6fa48663d033870cc0d7f5ee66
|
||||
F src/where.c bc71775e23d23334e8f449aa31012d692dc09cb2
|
||||
F src/whereInt.h 2bcc3d176e6091cb8f50a30b65c006e88a73614d
|
||||
F src/wherecode.c e04ac8f24c3ac8621df6c3be3ac8c7d4fa893745
|
||||
F src/whereexpr.c 87ecdf24beba4498e4380b31c4131febb0a6ceaa
|
||||
F src/wherecode.c 99a8ced164c75edf41b3a865a75381c9adb38b28
|
||||
F src/whereexpr.c 35ad025389a632a3987a35617c878be3b3d70dc6
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
|
||||
F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d
|
||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||
F test/aggnested.test b35b4cd69fc913f90d39a575e171e1116c3a4bb7
|
||||
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
|
||||
@@ -626,7 +632,7 @@ F test/cse.test 277350a26264495e86b1785f34d2d0c8600e021c
|
||||
F test/csv01.test e0ba3caaa57e4c667a0b45977689fb8082f14348
|
||||
F test/ctime.test ff6c38e822459d6ca743c34901caf57740b08b54
|
||||
F test/cursorhint.test 7bc346788390475e77a345da2b92270d04d35856
|
||||
F test/cursorhint2.test fa41f0d997e67db921d08c31e73111b32811201a
|
||||
F test/cursorhint2.test 8457e93d97f665f23f97cdbc8477d16e3480331b
|
||||
F test/date.test a6a5a48b90907bca9fbcc79a30be5a715c1ab2fc
|
||||
F test/dbfuzz.c 8cc2bdb818b4483a052f9f80f96be74cbd9a6e1d
|
||||
F test/dbstatus.test 73149851b3aff14fc6db478e58f9083a66422cf5
|
||||
@@ -676,7 +682,7 @@ F test/eqp.test 3fe051af50921284189d1970eb653f9fcf5117d2
|
||||
F test/errmsg.test f31592a594b44ee121371d25ddd5d63497bb3401
|
||||
F test/eval.test a64c9105d6ff163df7cf09d6ac29cdad5922078c
|
||||
F test/exclusive.test 9a57bd66e39144b888ca75c309914fcdefb4e3f9
|
||||
F test/exclusive2.test 32798111aae78a5deec980eee383213f189df308
|
||||
F test/exclusive2.test 984090e8e9d1b331d2e8111daf6e5d61dda0bef7
|
||||
F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7
|
||||
F test/exists.test 79a75323c78f02bbe9c251ea502a092f9ef63dac
|
||||
F test/expr.test 66a2c9ac34f74f036faa4092f5402c7d3162fc93
|
||||
@@ -829,7 +835,8 @@ F test/gcfault.test dd28c228a38976d6336a3fc42d7e5f1ad060cb8c
|
||||
F test/genesis.tcl 1e2e2e8e5cc4058549a154ff1892fe5c9de19f98
|
||||
F test/hexlit.test 4a6a5f46e3c65c4bf1fa06f5dd5a9507a5627751
|
||||
F test/hidden.test 23c1393a79e846d68fd902d72c85d5e5dcf98711
|
||||
F test/hook.test 3a01b876691f9151d3e44562354f7d663ff90fce
|
||||
F test/hook.test dbc0b87756e1e20e7497b56889c9e9cd2f8cc2b5
|
||||
F test/hook2.test b9ff3b8c6519fb67f33192f1afe86e7782ee4ac8
|
||||
F test/icu.test 73956798bace8982909c00476b216714a6d0559a
|
||||
F test/ieee754.test 806fc0ce7f305f57e3331eaceeddcfec9339e607
|
||||
F test/imposter1.test c3f1db2d3db2c24611a6596a3fc0ffc14f1466c8
|
||||
@@ -880,7 +887,7 @@ F test/ioerr4.test f130fe9e71008577b342b8874d52984bd04ede2c
|
||||
F test/ioerr5.test 2edfa4fb0f896f733071303b42224df8bedd9da4
|
||||
F test/ioerr6.test a395a6ab144b26a9e3e21059a1ab6a7149cca65b
|
||||
F test/join.test f9d4a28dec81c6e9dc21b73518e024d73b5ebf57
|
||||
F test/join2.test f2171c265e57ee298a27e57e7051d22962f9f324
|
||||
F test/join2.test a48f723c5692e2cbb23a9297ac2720cb77d51a70
|
||||
F test/join3.test 6f0c774ff1ba0489e6c88a3e77b9d3528fb4fda0
|
||||
F test/join4.test 1a352e4e267114444c29266ce79e941af5885916
|
||||
F test/join5.test bc98ea4b4e5003f5b1453701ebb8cd7d1c01a550
|
||||
@@ -895,7 +902,7 @@ F test/json101.test c0897616f32d95431f37fd291cb78742181980ac
|
||||
F test/json102.test bf3fe7a706d30936a76a0f7a0375e1e8e73aff5a
|
||||
F test/json103.test c5f6b85e69de05f6b3195f9f9d5ce9cd179099a0
|
||||
F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff
|
||||
F test/kvtest.c 2c66ddefcd03c2caa337f6dd79e6c82368af83df
|
||||
F test/kvtest.c 7a3c38ee56b9cc45dc5a5edc50fd9bc9425659a9
|
||||
F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63
|
||||
F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200
|
||||
F test/like.test 0603f4fa0dad50987f70032c05800cbfa8985302
|
||||
@@ -1128,7 +1135,7 @@ F test/speed3.test 694affeb9100526007436334cf7d08f3d74b85ef
|
||||
F test/speed4.test abc0ad3399dcf9703abed2fff8705e4f8e416715
|
||||
F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa
|
||||
F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b
|
||||
F test/speedtest1.c 4e8ea6165046f02e1cfe0f4700256e91c981ec10
|
||||
F test/speedtest1.c 02fe15bb784c5276a083ffe9969cc51e0bce7644
|
||||
F test/spellfix.test f9c1f431e2c096c8775fec032952320c0e4700db
|
||||
F test/spellfix2.test dfc8f519a3fc204cb2dfa8b4f29821ae90f6f8c3
|
||||
F test/spellfix3.test 0f9efaaa502a0e0a09848028518a6fb096c8ad33
|
||||
@@ -1160,7 +1167,7 @@ F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30
|
||||
F test/temptable2.test cd396beb41117a5302fff61767c35fa4270a0d5e
|
||||
F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637
|
||||
F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc
|
||||
F test/tester.tcl 4ce5afd5e192db4cae178e1a983b060e0f08c5d6
|
||||
F test/tester.tcl 2a49c1aff731f380ea640106377e19611a1443ae
|
||||
F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5
|
||||
F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
|
||||
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
|
||||
@@ -1351,6 +1358,7 @@ F test/unique2.test 3674e9f2a3f1fbbfd4772ac74b7a97090d0f77d2
|
||||
F test/unixexcl.test d936ba2b06794018e136418addd59a2354eeae97
|
||||
F test/unordered.test ca7adce0419e4ca0c50f039885e76ed2c531eda8
|
||||
F test/update.test 6c68446b8a0a33d522a7c72b320934596a2d7d32
|
||||
F test/update2.test 81772e5c9b93d1006d66d65e9a1327281bdec4e8
|
||||
F test/uri.test 3481026f00ade6dfe8adb7acb6e1e47b04369568
|
||||
F test/uri2.test 9d3ba7a53ee167572d53a298ee4a5d38ec4a8fb7
|
||||
F test/userauth01.test e740a2697a7b40d7c5003a7d7edaee16acd349a9
|
||||
@@ -1379,7 +1387,7 @@ F test/vtabC.test 4528f459a13136f982e75614d120aef165f17292
|
||||
F test/vtabD.test 05b3f1d77117271671089e48719524b676842e96
|
||||
F test/vtabE.test d5024aa42754962f6bb0afd261681686488e7afe
|
||||
F test/vtabF.test 1918844c7c902f6a16c8dacf1ec8f84886d6e78b
|
||||
F test/vtabH.test 97f61b0253260831af6232163f7852e2653baed6
|
||||
F test/vtabH.test a2912cd3ea3386aecc3cb74ebfdfcc4e6d4b7dd3
|
||||
F test/vtabI.test 751b07636700dbdea328e4265b6077ccd6811a3f
|
||||
F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5
|
||||
F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8
|
||||
@@ -1470,8 +1478,9 @@ F tool/fuzzershell.c dbf6c26eef936ec78cb0707570de3a4308b2507e
|
||||
F tool/genfkey.README cf68fddd4643bbe3ff8e31b8b6d8b0a1b85e20f4
|
||||
F tool/genfkey.test 4196a8928b78f51d54ef58e99e99401ab2f0a7e5
|
||||
F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce
|
||||
F tool/kvtest-speed.sh 4761a9c4b3530907562314d7757995787f7aef8f
|
||||
F tool/lemon.c 5ccba178a8e8a4b21e1c9232944d23973da38ad7
|
||||
F tool/lempar.c 320d630b44da693407684c64d9fa91a163419dac
|
||||
F tool/lempar.c db1bdb4821f2d8fbd76e577cf3ab18642c8d08d1
|
||||
F tool/libvers.c caafc3b689638a1d88d44bc5f526c2278760d9b9
|
||||
F tool/loadfts.c c3c64e4d5e90e8ba41159232c2189dba4be7b862
|
||||
F tool/logest.c 11346aa019e2e77a00902aa7d0cabd27bd2e8cca
|
||||
@@ -1503,7 +1512,7 @@ F tool/showstat4.c b14159aa062f661b394ba37b6b7b94bfb8012ab9
|
||||
F tool/showwal.c ec79959834f7b21f1e0a2aa52bb7c056d2203977
|
||||
F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe
|
||||
F tool/spaceanal.tcl ab7d9bf68062907282a64b3e12ccbfad47193c5a
|
||||
F tool/speed-check.sh e6ca0695b047af64201ebe0ef452e423f55d78b1
|
||||
F tool/speed-check.sh 9630ba0468b609c52f48309243d4eb6e9c34deda
|
||||
F tool/speedtest.tcl 06c76698485ccf597b9e7dbb1ac70706eb873355
|
||||
F tool/speedtest16.c ecb6542862151c3e6509bbc00509b234562ae81e
|
||||
F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
||||
@@ -1543,7 +1552,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P a5fe03bc419d9c7e6068ed38810e3f183de179b5
|
||||
R 1f3e0de052f41632eb2fb92e1db69d89
|
||||
P 0c66cf0f0a9ada2ddcb8d61001ef791b86226416
|
||||
R 329580c21139f900f64001f6acfc9557
|
||||
T *branch * savepoint-rollback
|
||||
T *sym-savepoint-rollback *
|
||||
T -sym-trunk *
|
||||
U drh
|
||||
Z 8ba5841c95779521fe8c2f52b8a5492f
|
||||
Z 5087946e64391fb6ac59f69d722efdd6
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
6e106acd74da3baa5c308a76443d2f0a7c904e5e
|
||||
01d97e5b6502b1811b52a681f445e1aaae6c0ee6
|
||||
+7
-1
@@ -797,6 +797,12 @@ static const FuncDef statPushFuncdef = {
|
||||
** The content to returned is determined by the parameter J
|
||||
** which is one of the STAT_GET_xxxx values defined above.
|
||||
**
|
||||
** The stat_get(P,J) function is not available to generic SQL. It is
|
||||
** inserted as part of a manually constructed bytecode program. (See
|
||||
** the callStatGet() routine below.) It is guaranteed that the P
|
||||
** parameter will always be a poiner to a Stat4Accum object, never a
|
||||
** NULL.
|
||||
**
|
||||
** If neither STAT3 nor STAT4 are enabled, then J is always
|
||||
** STAT_GET_STAT1 and is hence omitted and this routine becomes
|
||||
** a one-parameter function, stat_get(P), that always returns the
|
||||
@@ -1615,7 +1621,7 @@ static void initAvgEq(Index *pIdx){
|
||||
}
|
||||
}
|
||||
|
||||
if( nDist100>nSum100 ){
|
||||
if( nDist100>nSum100 && sumEq<nRow ){
|
||||
avgEq = ((i64)100 * (nRow - sumEq))/(nDist100 - nSum100);
|
||||
}
|
||||
if( avgEq==0 ) avgEq = 1;
|
||||
|
||||
+126
-83
@@ -3356,12 +3356,14 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){
|
||||
if( eType==PTRMAP_OVERFLOW1 ){
|
||||
CellInfo info;
|
||||
pPage->xParseCell(pPage, pCell, &info);
|
||||
if( info.nLocal<info.nPayload
|
||||
&& pCell+info.nSize-1<=pPage->aData+pPage->maskPage
|
||||
&& iFrom==get4byte(pCell+info.nSize-4)
|
||||
){
|
||||
put4byte(pCell+info.nSize-4, iTo);
|
||||
break;
|
||||
if( info.nLocal<info.nPayload ){
|
||||
if( pCell+info.nSize > pPage->aData+pPage->pBt->usableSize ){
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
if( iFrom==get4byte(pCell+info.nSize-4) ){
|
||||
put4byte(pCell+info.nSize-4, iTo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if( get4byte(pCell)==iFrom ){
|
||||
@@ -4036,7 +4038,12 @@ int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){
|
||||
assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK );
|
||||
assert( iSavepoint>=0 || (iSavepoint==-1 && op==SAVEPOINT_ROLLBACK) );
|
||||
sqlite3BtreeEnter(p);
|
||||
rc = sqlite3PagerSavepoint(pBt->pPager, op, iSavepoint);
|
||||
if( op==SAVEPOINT_ROLLBACK ){
|
||||
rc = saveAllCursors(pBt, 0, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3PagerSavepoint(pBt->pPager, op, iSavepoint);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
if( iSavepoint<0 && (pBt->btsFlags & BTS_INITIALLY_EMPTY)!=0 ){
|
||||
pBt->nPage = 0;
|
||||
@@ -4422,7 +4429,6 @@ static int copyPayload(
|
||||
**
|
||||
** 0: The operation is a read. Populate the overflow cache.
|
||||
** 1: The operation is a write. Populate the overflow cache.
|
||||
** 2: The operation is a read. Do not populate the overflow cache.
|
||||
**
|
||||
** A total of "amt" bytes are read or written beginning at "offset".
|
||||
** Data is read to or from the buffer pBuf.
|
||||
@@ -4430,13 +4436,13 @@ static int copyPayload(
|
||||
** The content being read or written might appear on the main page
|
||||
** or be scattered out on multiple overflow pages.
|
||||
**
|
||||
** If the current cursor entry uses one or more overflow pages and the
|
||||
** eOp argument is not 2, this function may allocate space for and lazily
|
||||
** populates the overflow page-list cache array (BtCursor.aOverflow).
|
||||
** If the current cursor entry uses one or more overflow pages
|
||||
** this function may allocate space for and lazily populate
|
||||
** the overflow page-list cache array (BtCursor.aOverflow).
|
||||
** Subsequent calls use this cache to make seeking to the supplied offset
|
||||
** more efficient.
|
||||
**
|
||||
** Once an overflow page-list cache has been allocated, it may be
|
||||
** Once an overflow page-list cache has been allocated, it must be
|
||||
** invalidated if some other cursor writes to the same table, or if
|
||||
** the cursor is moved to a different row. Additionally, in auto-vacuum
|
||||
** mode, the following events may invalidate an overflow page-list cache.
|
||||
@@ -4458,21 +4464,17 @@ static int accessPayload(
|
||||
MemPage *pPage = pCur->apPage[pCur->iPage]; /* Btree page of current entry */
|
||||
BtShared *pBt = pCur->pBt; /* Btree this cursor belongs to */
|
||||
#ifdef SQLITE_DIRECT_OVERFLOW_READ
|
||||
unsigned char * const pBufStart = pBuf;
|
||||
int bEnd; /* True if reading to end of data */
|
||||
unsigned char * const pBufStart = pBuf; /* Start of original out buffer */
|
||||
#endif
|
||||
|
||||
assert( pPage );
|
||||
assert( eOp==0 || eOp==1 );
|
||||
assert( pCur->eState==CURSOR_VALID );
|
||||
assert( pCur->aiIdx[pCur->iPage]<pPage->nCell );
|
||||
assert( cursorHoldsMutex(pCur) );
|
||||
assert( eOp!=2 || offset==0 ); /* Always start from beginning for eOp==2 */
|
||||
|
||||
getCellInfo(pCur);
|
||||
aPayload = pCur->info.pPayload;
|
||||
#ifdef SQLITE_DIRECT_OVERFLOW_READ
|
||||
bEnd = offset+amt==pCur->info.nPayload;
|
||||
#endif
|
||||
assert( offset+amt <= pCur->info.nPayload );
|
||||
|
||||
assert( aPayload > pPage->aData );
|
||||
@@ -4491,7 +4493,7 @@ static int accessPayload(
|
||||
if( a+offset>pCur->info.nLocal ){
|
||||
a = pCur->info.nLocal - offset;
|
||||
}
|
||||
rc = copyPayload(&aPayload[offset], pBuf, a, (eOp & 0x01), pPage->pDbPage);
|
||||
rc = copyPayload(&aPayload[offset], pBuf, a, eOp, pPage->pDbPage);
|
||||
offset = 0;
|
||||
pBuf += a;
|
||||
amt -= a;
|
||||
@@ -4507,53 +4509,46 @@ static int accessPayload(
|
||||
nextPage = get4byte(&aPayload[pCur->info.nLocal]);
|
||||
|
||||
/* If the BtCursor.aOverflow[] has not been allocated, allocate it now.
|
||||
** Except, do not allocate aOverflow[] for eOp==2.
|
||||
**
|
||||
** The aOverflow[] array is sized at one entry for each overflow page
|
||||
** in the overflow chain. The page number of the first overflow page is
|
||||
** stored in aOverflow[0], etc. A value of 0 in the aOverflow[] array
|
||||
** means "not yet known" (the cache is lazily populated).
|
||||
*/
|
||||
if( eOp!=2 && (pCur->curFlags & BTCF_ValidOvfl)==0 ){
|
||||
if( (pCur->curFlags & BTCF_ValidOvfl)==0 ){
|
||||
int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize;
|
||||
if( nOvfl>pCur->nOvflAlloc ){
|
||||
Pgno *aNew = (Pgno*)sqlite3Realloc(
|
||||
pCur->aOverflow, nOvfl*2*sizeof(Pgno)
|
||||
);
|
||||
if( aNew==0 ){
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}else{
|
||||
pCur->nOvflAlloc = nOvfl*2;
|
||||
pCur->aOverflow = aNew;
|
||||
}
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
memset(pCur->aOverflow, 0, nOvfl*sizeof(Pgno));
|
||||
pCur->curFlags |= BTCF_ValidOvfl;
|
||||
memset(pCur->aOverflow, 0, nOvfl*sizeof(Pgno));
|
||||
pCur->curFlags |= BTCF_ValidOvfl;
|
||||
}else{
|
||||
/* If the overflow page-list cache has been allocated and the
|
||||
** entry for the first required overflow page is valid, skip
|
||||
** directly to it.
|
||||
*/
|
||||
if( pCur->aOverflow[offset/ovflSize] ){
|
||||
iIdx = (offset/ovflSize);
|
||||
nextPage = pCur->aOverflow[iIdx];
|
||||
offset = (offset%ovflSize);
|
||||
}
|
||||
}
|
||||
|
||||
/* If the overflow page-list cache has been allocated and the
|
||||
** entry for the first required overflow page is valid, skip
|
||||
** directly to it.
|
||||
*/
|
||||
if( (pCur->curFlags & BTCF_ValidOvfl)!=0
|
||||
&& pCur->aOverflow[offset/ovflSize]
|
||||
){
|
||||
iIdx = (offset/ovflSize);
|
||||
nextPage = pCur->aOverflow[iIdx];
|
||||
offset = (offset%ovflSize);
|
||||
}
|
||||
|
||||
for( ; rc==SQLITE_OK && amt>0 && nextPage; iIdx++){
|
||||
|
||||
assert( rc==SQLITE_OK && amt>0 );
|
||||
while( nextPage ){
|
||||
/* If required, populate the overflow page-list cache. */
|
||||
if( (pCur->curFlags & BTCF_ValidOvfl)!=0 ){
|
||||
assert( pCur->aOverflow[iIdx]==0
|
||||
|| pCur->aOverflow[iIdx]==nextPage
|
||||
|| CORRUPT_DB );
|
||||
pCur->aOverflow[iIdx] = nextPage;
|
||||
}
|
||||
assert( pCur->aOverflow[iIdx]==0
|
||||
|| pCur->aOverflow[iIdx]==nextPage
|
||||
|| CORRUPT_DB );
|
||||
pCur->aOverflow[iIdx] = nextPage;
|
||||
|
||||
if( offset>=ovflSize ){
|
||||
/* The only reason to read this page is to obtain the page
|
||||
@@ -4561,11 +4556,7 @@ static int accessPayload(
|
||||
** data is not required. So first try to lookup the overflow
|
||||
** page-list cache, if any, then fall back to the getOverflowPage()
|
||||
** function.
|
||||
**
|
||||
** Note that the aOverflow[] array must be allocated because eOp!=2
|
||||
** here. If eOp==2, then offset==0 and this branch is never taken.
|
||||
*/
|
||||
assert( eOp!=2 );
|
||||
assert( pCur->curFlags & BTCF_ValidOvfl );
|
||||
assert( pCur->pBtree->db==pBt->db );
|
||||
if( pCur->aOverflow[iIdx+1] ){
|
||||
@@ -4579,7 +4570,7 @@ static int accessPayload(
|
||||
** range of data that is being read (eOp==0) or written (eOp!=0).
|
||||
*/
|
||||
#ifdef SQLITE_DIRECT_OVERFLOW_READ
|
||||
sqlite3_file *fd;
|
||||
sqlite3_file *fd; /* File from which to do direct overflow read */
|
||||
#endif
|
||||
int a = amt;
|
||||
if( a + offset > ovflSize ){
|
||||
@@ -4591,27 +4582,25 @@ static int accessPayload(
|
||||
**
|
||||
** 1) this is a read operation, and
|
||||
** 2) data is required from the start of this overflow page, and
|
||||
** 3) the database is file-backed, and
|
||||
** 4) there is no open write-transaction, and
|
||||
** 5) the database is not a WAL database,
|
||||
** 6) all data from the page is being read.
|
||||
** 7) at least 4 bytes have already been read into the output buffer
|
||||
** 3) there is no open write-transaction, and
|
||||
** 4) the database is file-backed, and
|
||||
** 5) the page is not in the WAL file
|
||||
** 6) at least 4 bytes have already been read into the output buffer
|
||||
**
|
||||
** then data can be read directly from the database file into the
|
||||
** output buffer, bypassing the page-cache altogether. This speeds
|
||||
** up loading large records that span many overflow pages.
|
||||
*/
|
||||
if( (eOp&0x01)==0 /* (1) */
|
||||
if( eOp==0 /* (1) */
|
||||
&& offset==0 /* (2) */
|
||||
&& (bEnd || a==ovflSize) /* (6) */
|
||||
&& pBt->inTransaction==TRANS_READ /* (4) */
|
||||
&& (fd = sqlite3PagerFile(pBt->pPager))->pMethods /* (3) */
|
||||
&& 0==sqlite3PagerUseWal(pBt->pPager) /* (5) */
|
||||
&& &pBuf[-4]>=pBufStart /* (7) */
|
||||
&& pBt->inTransaction==TRANS_READ /* (3) */
|
||||
&& (fd = sqlite3PagerFile(pBt->pPager))->pMethods /* (4) */
|
||||
&& 0==sqlite3PagerUseWal(pBt->pPager, nextPage) /* (5) */
|
||||
&& &pBuf[-4]>=pBufStart /* (6) */
|
||||
){
|
||||
u8 aSave[4];
|
||||
u8 *aWrite = &pBuf[-4];
|
||||
assert( aWrite>=pBufStart ); /* hence (7) */
|
||||
assert( aWrite>=pBufStart ); /* due to (6) */
|
||||
memcpy(aSave, aWrite, 4);
|
||||
rc = sqlite3OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1));
|
||||
nextPage = get4byte(aWrite);
|
||||
@@ -4622,24 +4611,27 @@ static int accessPayload(
|
||||
{
|
||||
DbPage *pDbPage;
|
||||
rc = sqlite3PagerGet(pBt->pPager, nextPage, &pDbPage,
|
||||
((eOp&0x01)==0 ? PAGER_GET_READONLY : 0)
|
||||
(eOp==0 ? PAGER_GET_READONLY : 0)
|
||||
);
|
||||
if( rc==SQLITE_OK ){
|
||||
aPayload = sqlite3PagerGetData(pDbPage);
|
||||
nextPage = get4byte(aPayload);
|
||||
rc = copyPayload(&aPayload[offset+4], pBuf, a, (eOp&0x01), pDbPage);
|
||||
rc = copyPayload(&aPayload[offset+4], pBuf, a, eOp, pDbPage);
|
||||
sqlite3PagerUnref(pDbPage);
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
amt -= a;
|
||||
if( amt==0 ) return rc;
|
||||
pBuf += a;
|
||||
}
|
||||
if( rc ) break;
|
||||
iIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && amt>0 ){
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
return SQLITE_CORRUPT_BKPT; /* Overflow chain ends prematurely */
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -4668,21 +4660,34 @@ int sqlite3BtreePayload(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
|
||||
assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell );
|
||||
return accessPayload(pCur, offset, amt, (unsigned char*)pBuf, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
** This variant of sqlite3BtreePayload() works even if the cursor has not
|
||||
** in the CURSOR_VALID state. It is only used by the sqlite3_blob_read()
|
||||
** interface.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_INCRBLOB
|
||||
int sqlite3BtreePayloadChecked(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
|
||||
static SQLITE_NOINLINE int accessPayloadChecked(
|
||||
BtCursor *pCur,
|
||||
u32 offset,
|
||||
u32 amt,
|
||||
void *pBuf
|
||||
){
|
||||
int rc;
|
||||
if ( pCur->eState==CURSOR_INVALID ){
|
||||
return SQLITE_ABORT;
|
||||
}
|
||||
assert( cursorOwnsBtShared(pCur) );
|
||||
rc = restoreCursorPosition(pCur);
|
||||
if( rc==SQLITE_OK ){
|
||||
assert( pCur->eState==CURSOR_VALID );
|
||||
assert( pCur->iPage>=0 && pCur->apPage[pCur->iPage] );
|
||||
assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell );
|
||||
rc = accessPayload(pCur, offset, amt, pBuf, 0);
|
||||
rc = btreeRestoreCursorPosition(pCur);
|
||||
return rc ? rc : accessPayload(pCur, offset, amt, pBuf, 0);
|
||||
}
|
||||
int sqlite3BtreePayloadChecked(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
|
||||
if( pCur->eState==CURSOR_VALID ){
|
||||
assert( cursorOwnsBtShared(pCur) );
|
||||
return accessPayload(pCur, offset, amt, pBuf, 0);
|
||||
}else{
|
||||
return accessPayloadChecked(pCur, offset, amt, pBuf);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
#endif /* SQLITE_OMIT_INCRBLOB */
|
||||
|
||||
@@ -5088,9 +5093,26 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
*pRes = 0;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
if( (pCur->curFlags & BTCF_AtLast)!=0 && pCur->info.nKey<intKey ){
|
||||
*pRes = -1;
|
||||
return SQLITE_OK;
|
||||
if( pCur->info.nKey<intKey ){
|
||||
if( (pCur->curFlags & BTCF_AtLast)!=0 ){
|
||||
*pRes = -1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
/* If the requested key is one more than the previous key, then
|
||||
** try to get there using sqlite3BtreeNext() rather than a full
|
||||
** binary search. This is an optimization only. The correct answer
|
||||
** is still obtained without this ase, only a little more slowely */
|
||||
if( pCur->info.nKey+1==intKey && !pCur->skipNext ){
|
||||
*pRes = 0;
|
||||
rc = sqlite3BtreeNext(pCur, pRes);
|
||||
if( rc ) return rc;
|
||||
if( *pRes==0 ){
|
||||
getCellInfo(pCur);
|
||||
if( pCur->info.nKey==intKey ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5226,7 +5248,8 @@ int sqlite3BtreeMovetoUnpacked(
|
||||
goto moveto_finish;
|
||||
}
|
||||
pCur->aiIdx[pCur->iPage] = (u16)idx;
|
||||
rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 2);
|
||||
rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0);
|
||||
pCur->curFlags &= ~BTCF_ValidOvfl;
|
||||
if( rc ){
|
||||
sqlite3_free(pCellKey);
|
||||
goto moveto_finish;
|
||||
@@ -7269,7 +7292,6 @@ static int balance_nonroot(
|
||||
for(i=0; i<nOld; i++){
|
||||
MemPage *p = apOld[i];
|
||||
szNew[i] = usableSpace - p->nFree;
|
||||
if( szNew[i]<0 ){ rc = SQLITE_CORRUPT_BKPT; goto balance_cleanup; }
|
||||
for(j=0; j<p->nOverflow; j++){
|
||||
szNew[i] += 2 + p->xCellSize(p, p->apOvfl[j]);
|
||||
}
|
||||
@@ -7948,7 +7970,7 @@ static int balance(BtCursor *pCur){
|
||||
int sqlite3BtreeInsert(
|
||||
BtCursor *pCur, /* Insert data into the table of this cursor */
|
||||
const BtreePayload *pX, /* Content of the row to be inserted */
|
||||
int appendBias, /* True if this is likely an append */
|
||||
int flags, /* True if this is likely an append */
|
||||
int seekResult /* Result of prior MovetoUnpacked() call */
|
||||
){
|
||||
int rc;
|
||||
@@ -7961,6 +7983,8 @@ int sqlite3BtreeInsert(
|
||||
unsigned char *oldCell;
|
||||
unsigned char *newCell = 0;
|
||||
|
||||
assert( (flags & (BTREE_SAVEPOSITION|BTREE_APPEND))==flags );
|
||||
|
||||
if( pCur->eState==CURSOR_FAULT ){
|
||||
assert( pCur->skipNext!=SQLITE_OK );
|
||||
return pCur->skipNext;
|
||||
@@ -8001,6 +8025,11 @@ int sqlite3BtreeInsert(
|
||||
** cursors open on the row being replaced */
|
||||
invalidateIncrblobCursors(p, pX->nKey, 0);
|
||||
|
||||
/* If BTREE_SAVEPOSITION is set, the cursor must already be pointing
|
||||
** to a row with the same key as the new entry being inserted. */
|
||||
assert( (flags & BTREE_SAVEPOSITION)==0 ||
|
||||
((pCur->curFlags&BTCF_ValidNKey)!=0 && pX->nKey==pCur->info.nKey) );
|
||||
|
||||
/* If the cursor is currently on the last row and we are appending a
|
||||
** new row onto the end, set the "loc" to avoid an unnecessary
|
||||
** btreeMoveto() call */
|
||||
@@ -8010,10 +8039,10 @@ int sqlite3BtreeInsert(
|
||||
&& pCur->info.nKey==pX->nKey-1 ){
|
||||
loc = -1;
|
||||
}else if( loc==0 ){
|
||||
rc = sqlite3BtreeMovetoUnpacked(pCur, 0, pX->nKey, appendBias, &loc);
|
||||
rc = sqlite3BtreeMovetoUnpacked(pCur, 0, pX->nKey, flags!=0, &loc);
|
||||
if( rc ) return rc;
|
||||
}
|
||||
}else if( loc==0 ){
|
||||
}else if( loc==0 && (flags & BTREE_SAVEPOSITION)==0 ){
|
||||
if( pX->nMem ){
|
||||
UnpackedRecord r;
|
||||
r.pKeyInfo = pCur->pKeyInfo;
|
||||
@@ -8024,9 +8053,9 @@ int sqlite3BtreeInsert(
|
||||
r.r1 = 0;
|
||||
r.r2 = 0;
|
||||
r.eqSeen = 0;
|
||||
rc = sqlite3BtreeMovetoUnpacked(pCur, &r, 0, appendBias, &loc);
|
||||
rc = sqlite3BtreeMovetoUnpacked(pCur, &r, 0, flags!=0, &loc);
|
||||
}else{
|
||||
rc = btreeMoveto(pCur, pX->pKey, pX->nKey, appendBias, &loc);
|
||||
rc = btreeMoveto(pCur, pX->pKey, pX->nKey, flags!=0, &loc);
|
||||
}
|
||||
if( rc ) return rc;
|
||||
}
|
||||
@@ -8114,6 +8143,20 @@ int sqlite3BtreeInsert(
|
||||
** from trying to save the current position of the cursor. */
|
||||
pCur->apPage[pCur->iPage]->nOverflow = 0;
|
||||
pCur->eState = CURSOR_INVALID;
|
||||
if( (flags & BTREE_SAVEPOSITION) && rc==SQLITE_OK ){
|
||||
rc = moveToRoot(pCur);
|
||||
if( pCur->pKeyInfo ){
|
||||
assert( pCur->pKey==0 );
|
||||
pCur->pKey = sqlite3Malloc( pX->nKey );
|
||||
if( pCur->pKey==0 ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}else{
|
||||
memcpy(pCur->pKey, pX->pKey, pX->nKey);
|
||||
}
|
||||
}
|
||||
pCur->eState = CURSOR_REQUIRESEEK;
|
||||
pCur->nKey = pX->nKey;
|
||||
}
|
||||
}
|
||||
assert( pCur->apPage[pCur->iPage]->nOverflow==0 );
|
||||
|
||||
|
||||
+3
-2
@@ -249,9 +249,10 @@ int sqlite3BtreeCursorHasMoved(BtCursor*);
|
||||
int sqlite3BtreeCursorRestore(BtCursor*, int*);
|
||||
int sqlite3BtreeDelete(BtCursor*, u8 flags);
|
||||
|
||||
/* Allowed flags for the 2nd argument to sqlite3BtreeDelete() */
|
||||
/* Allowed flags for sqlite3BtreeDelete() and sqlite3BtreeInsert() */
|
||||
#define BTREE_SAVEPOSITION 0x02 /* Leave cursor pointing at NEXT or PREV */
|
||||
#define BTREE_AUXDELETE 0x04 /* not the primary delete operation */
|
||||
#define BTREE_APPEND 0x08 /* Insert is likely an append */
|
||||
|
||||
/* An instance of the BtreePayload object describes the content of a single
|
||||
** entry in either an index or table btree.
|
||||
@@ -282,7 +283,7 @@ struct BtreePayload {
|
||||
};
|
||||
|
||||
int sqlite3BtreeInsert(BtCursor*, const BtreePayload *pPayload,
|
||||
int bias, int seekResult);
|
||||
int flags, int seekResult);
|
||||
int sqlite3BtreeFirst(BtCursor*, int *pRes);
|
||||
int sqlite3BtreeLast(BtCursor*, int *pRes);
|
||||
int sqlite3BtreeNext(BtCursor*, int *pRes);
|
||||
|
||||
+7
-1
@@ -715,7 +715,9 @@ void sqlite3GenerateRowDelete(
|
||||
u8 p5 = 0;
|
||||
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,iIdxNoSeek);
|
||||
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, (count?OPFLAG_NCHANGE:0));
|
||||
sqlite3VdbeAppendP4(v, (char*)pTab, P4_TABLE);
|
||||
if( pParse->nested==0 ){
|
||||
sqlite3VdbeAppendP4(v, (char*)pTab, P4_TABLE);
|
||||
}
|
||||
if( eMode!=ONEPASS_OFF ){
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_AUXDELETE);
|
||||
}
|
||||
@@ -873,6 +875,10 @@ int sqlite3GenerateIndexKey(
|
||||
}
|
||||
if( regOut ){
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regOut);
|
||||
if( pIdx->pTable->pSelect ){
|
||||
const char *zAff = sqlite3IndexAffinityStr(pParse->db, pIdx);
|
||||
sqlite3VdbeChangeP4(v, -1, zAff, P4_TRANSIENT);
|
||||
}
|
||||
}
|
||||
sqlite3ReleaseTempRange(pParse, regBase, nCol);
|
||||
return regBase;
|
||||
|
||||
+8
-2
@@ -967,8 +967,13 @@ void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr, u32 n){
|
||||
/* Wildcard of the form "?nnn". Convert "nnn" to an integer and
|
||||
** use it as the variable number */
|
||||
i64 i;
|
||||
int bOk = 0==sqlite3Atoi64(&z[1], &i, n-1, SQLITE_UTF8);
|
||||
x = (ynVar)i;
|
||||
int bOk;
|
||||
if( n==2 ){ /*OPTIMIZATION-IF-TRUE*/
|
||||
i = z[1]-'0'; /* The common case of ?N for a single digit N */
|
||||
bOk = 1;
|
||||
}else{
|
||||
bOk = 0==sqlite3Atoi64(&z[1], &i, n-1, SQLITE_UTF8);
|
||||
}
|
||||
testcase( i==0 );
|
||||
testcase( i==1 );
|
||||
testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]-1 );
|
||||
@@ -978,6 +983,7 @@ void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr, u32 n){
|
||||
db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]);
|
||||
return;
|
||||
}
|
||||
x = (ynVar)i;
|
||||
if( x>pParse->nVar ){
|
||||
pParse->nVar = (int)x;
|
||||
doAdd = 1;
|
||||
|
||||
+40
-7
@@ -1668,6 +1668,25 @@ void sqlite3GenerateConstraintChecks(
|
||||
VdbeModuleComment((v, "END: GenCnstCks(%d)", seenReplace));
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_NULL_TRIM
|
||||
/*
|
||||
** Change the P5 operand on the last opcode (which should be an OP_MakeRecord)
|
||||
** to be the number of columns in table pTab that must not be NULL-trimmed.
|
||||
**
|
||||
** Or if no columns of pTab may be NULL-trimmed, leave P5 at zero.
|
||||
*/
|
||||
void sqlite3SetMakeRecordP5(Vdbe *v, Table *pTab){
|
||||
u16 i;
|
||||
|
||||
/* Records with omitted columns are only allowed for schema format
|
||||
** version 2 and later (SQLite version 3.1.4, 2005-02-20). */
|
||||
if( pTab->pSchema->file_format<2 ) return;
|
||||
|
||||
for(i=pTab->nCol; i>1 && pTab->aCol[i-1].pDflt==0; i--){}
|
||||
sqlite3VdbeChangeP5(v, i);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** This routine generates code to finish the INSERT or UPDATE operation
|
||||
** that was started by a prior call to sqlite3GenerateConstraintChecks.
|
||||
@@ -1684,7 +1703,7 @@ void sqlite3CompleteInsertion(
|
||||
int iIdxCur, /* First index cursor */
|
||||
int regNewData, /* Range of content */
|
||||
int *aRegIdx, /* Register used by each index. 0 for unused indices */
|
||||
int isUpdate, /* True for UPDATE, False for INSERT */
|
||||
int update_flags, /* True for UPDATE, False for INSERT */
|
||||
int appendBias, /* True if this is likely to be an append */
|
||||
int useSeekResult /* True to set the USESEEKRESULT flag on OP_[Idx]Insert */
|
||||
){
|
||||
@@ -1696,6 +1715,11 @@ void sqlite3CompleteInsertion(
|
||||
int i; /* Loop counter */
|
||||
u8 bAffinityDone = 0; /* True if OP_Affinity has been run already */
|
||||
|
||||
assert( update_flags==0
|
||||
|| update_flags==OPFLAG_ISUPDATE
|
||||
|| update_flags==(OPFLAG_ISUPDATE|OPFLAG_SAVEPOSITION)
|
||||
);
|
||||
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
assert( v!=0 );
|
||||
assert( pTab->pSelect==0 ); /* This table is not a VIEW */
|
||||
@@ -1706,21 +1730,30 @@ void sqlite3CompleteInsertion(
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, aRegIdx[i], sqlite3VdbeCurrentAddr(v)+2);
|
||||
VdbeCoverage(v);
|
||||
}
|
||||
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iIdxCur+i, aRegIdx[i],
|
||||
aRegIdx[i]+1,
|
||||
pIdx->uniqNotNull ? pIdx->nKeyCol: pIdx->nColumn);
|
||||
pik_flags = 0;
|
||||
if( useSeekResult ) pik_flags = OPFLAG_USESEEKRESULT;
|
||||
pik_flags = (useSeekResult ? OPFLAG_USESEEKRESULT : 0);
|
||||
if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){
|
||||
assert( pParse->nested==0 );
|
||||
pik_flags |= OPFLAG_NCHANGE;
|
||||
pik_flags |= (update_flags & OPFLAG_SAVEPOSITION);
|
||||
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
||||
if( update_flags==0 ){
|
||||
sqlite3VdbeAddOp4(v, OP_InsertInt,
|
||||
iIdxCur+i, aRegIdx[i], 0, (char*)pTab, P4_TABLE
|
||||
);
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_ISNOOP);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iIdxCur+i, aRegIdx[i],
|
||||
aRegIdx[i]+1,
|
||||
pIdx->uniqNotNull ? pIdx->nKeyCol: pIdx->nColumn);
|
||||
sqlite3VdbeChangeP5(v, pik_flags);
|
||||
}
|
||||
if( !HasRowid(pTab) ) return;
|
||||
regData = regNewData + 1;
|
||||
regRec = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regData, pTab->nCol, regRec);
|
||||
sqlite3SetMakeRecordP5(v, pTab);
|
||||
if( !bAffinityDone ){
|
||||
sqlite3TableAffinity(v, pTab, 0);
|
||||
sqlite3ExprCacheAffinityChange(pParse, regData, pTab->nCol);
|
||||
@@ -1729,7 +1762,7 @@ void sqlite3CompleteInsertion(
|
||||
pik_flags = 0;
|
||||
}else{
|
||||
pik_flags = OPFLAG_NCHANGE;
|
||||
pik_flags |= (isUpdate?OPFLAG_ISUPDATE:OPFLAG_LASTROWID);
|
||||
pik_flags |= (update_flags?update_flags:OPFLAG_LASTROWID);
|
||||
}
|
||||
if( appendBias ){
|
||||
pik_flags |= OPFLAG_APPEND;
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#endif
|
||||
#include "sqlite3ext.h"
|
||||
#include "sqliteInt.h"
|
||||
#include <string.h>
|
||||
|
||||
#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
||||
/*
|
||||
|
||||
+5
-6
@@ -217,14 +217,14 @@ static void sqlite3MallocAlarm(int nByte){
|
||||
** Do a memory allocation with statistics and alarms. Assume the
|
||||
** lock is already held.
|
||||
*/
|
||||
static int mallocWithAlarm(int n, void **pp){
|
||||
int nFull;
|
||||
static void mallocWithAlarm(int n, void **pp){
|
||||
void *p;
|
||||
int nFull = 0;
|
||||
assert( sqlite3_mutex_held(mem0.mutex) );
|
||||
nFull = sqlite3GlobalConfig.m.xRoundup(n);
|
||||
sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, n);
|
||||
if( mem0.alarmThreshold>0 ){
|
||||
sqlite3_int64 nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
|
||||
nFull = sqlite3GlobalConfig.m.xRoundup(n);
|
||||
if( nUsed >= mem0.alarmThreshold - nFull ){
|
||||
mem0.nearlyFull = 1;
|
||||
sqlite3MallocAlarm(nFull);
|
||||
@@ -232,11 +232,11 @@ static int mallocWithAlarm(int n, void **pp){
|
||||
mem0.nearlyFull = 0;
|
||||
}
|
||||
}
|
||||
p = sqlite3GlobalConfig.m.xMalloc(nFull);
|
||||
p = sqlite3GlobalConfig.m.xMalloc(n);
|
||||
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
||||
if( p==0 && mem0.alarmThreshold>0 ){
|
||||
sqlite3MallocAlarm(nFull);
|
||||
p = sqlite3GlobalConfig.m.xMalloc(nFull);
|
||||
p = sqlite3GlobalConfig.m.xMalloc(n);
|
||||
}
|
||||
#endif
|
||||
if( p ){
|
||||
@@ -245,7 +245,6 @@ static int mallocWithAlarm(int n, void **pp){
|
||||
sqlite3StatusUp(SQLITE_STATUS_MALLOC_COUNT, 1);
|
||||
}
|
||||
*pp = p;
|
||||
return nFull;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+12
-6
@@ -814,14 +814,20 @@ static const unsigned char aJournalMagic[] = {
|
||||
#define isOpen(pFd) ((pFd)->pMethods!=0)
|
||||
|
||||
/*
|
||||
** Return true if this pager uses a write-ahead log instead of the usual
|
||||
** rollback journal. Otherwise false.
|
||||
** Return true if this pager uses a write-ahead log to read page pgno.
|
||||
** Return false if the pager reads pgno directly from the database.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
int sqlite3PagerUseWal(Pager *pPager){
|
||||
return (pPager->pWal!=0);
|
||||
#if !defined(SQLITE_OMIT_WAL) && defined(SQLITE_DIRECT_OVERFLOW_READ)
|
||||
int sqlite3PagerUseWal(Pager *pPager, Pgno pgno){
|
||||
u32 iRead = 0;
|
||||
int rc;
|
||||
if( pPager->pWal==0 ) return 0;
|
||||
rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iRead);
|
||||
return rc || iRead;
|
||||
}
|
||||
# define pagerUseWal(x) sqlite3PagerUseWal(x)
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
# define pagerUseWal(x) ((x)->pWal!=0)
|
||||
#else
|
||||
# define pagerUseWal(x) 0
|
||||
# define pagerRollbackWal(x) 0
|
||||
|
||||
+4
-2
@@ -178,14 +178,16 @@ int sqlite3PagerSharedLock(Pager *pPager);
|
||||
int sqlite3PagerWalCallback(Pager *pPager);
|
||||
int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen);
|
||||
int sqlite3PagerCloseWal(Pager *pPager, sqlite3*);
|
||||
int sqlite3PagerUseWal(Pager *pPager);
|
||||
# ifdef SQLITE_DIRECT_OVERFLOW_READ
|
||||
int sqlite3PagerUseWal(Pager *pPager, Pgno);
|
||||
# endif
|
||||
# ifdef SQLITE_ENABLE_SNAPSHOT
|
||||
int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot);
|
||||
int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot);
|
||||
int sqlite3PagerSnapshotRecover(Pager *pPager);
|
||||
# endif
|
||||
#else
|
||||
# define sqlite3PagerUseWal(x) 0
|
||||
# define sqlite3PagerUseWal(x,y) 0
|
||||
#endif
|
||||
|
||||
#ifdef SQLITE_ENABLE_ZIPVFS
|
||||
|
||||
+13
@@ -65,6 +65,19 @@
|
||||
*/
|
||||
#define YYPARSEFREENEVERNULL 1
|
||||
|
||||
/*
|
||||
** In the amalgamation, the parse.c file generated by lemon and the
|
||||
** tokenize.c file are concatenated. In that case, sqlite3RunParser()
|
||||
** has access to the the size of the yyParser object and so the parser
|
||||
** engine can be allocated from stack. In that case, only the
|
||||
** sqlite3ParserInit() and sqlite3ParserFinalize() routines are invoked
|
||||
** and the sqlite3ParserAlloc() and sqlite3ParserFree() routines can be
|
||||
** omitted.
|
||||
*/
|
||||
#ifdef SQLITE_AMALGAMATION
|
||||
# define sqlite3Parser_ENGINEALWAYSONSTACK 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Alternative datatype for the argument to the malloc() routine passed
|
||||
** into sqlite3ParserAlloc(). The default is size_t.
|
||||
|
||||
+1
-1
@@ -279,7 +279,7 @@ const char *sqlite3JournalModename(int eMode){
|
||||
** Locate a pragma in the aPragmaName[] array.
|
||||
*/
|
||||
static const PragmaName *pragmaLocate(const char *zName){
|
||||
int upr, lwr, mid, rc;
|
||||
int upr, lwr, mid = 0, rc;
|
||||
lwr = 0;
|
||||
upr = ArraySize(aPragmaName)-1;
|
||||
while( lwr<=upr ){
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
** table and column.
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
** Walk the expression tree pExpr and increase the aggregate function
|
||||
|
||||
+1
-1
@@ -5652,7 +5652,7 @@ int sqlite3Select(
|
||||
** of output.
|
||||
*/
|
||||
resetAccumulator(pParse, &sAggInfo);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pMinMax,0,flag,0);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pMinMax, 0,flag,0);
|
||||
if( pWInfo==0 ){
|
||||
sqlite3ExprListDelete(db, pDel);
|
||||
goto select_end;
|
||||
|
||||
+6
-1
@@ -4288,15 +4288,20 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
int c2 = zMode[0];
|
||||
if( c2=='l' && n2>2 && strncmp(azArg[1],"lines",n2)==0 ){
|
||||
p->mode = MODE_Line;
|
||||
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
|
||||
}else if( c2=='c' && strncmp(azArg[1],"columns",n2)==0 ){
|
||||
p->mode = MODE_Column;
|
||||
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
|
||||
}else if( c2=='l' && n2>2 && strncmp(azArg[1],"list",n2)==0 ){
|
||||
p->mode = MODE_List;
|
||||
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Column);
|
||||
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
|
||||
}else if( c2=='h' && strncmp(azArg[1],"html",n2)==0 ){
|
||||
p->mode = MODE_Html;
|
||||
}else if( c2=='t' && strncmp(azArg[1],"tcl",n2)==0 ){
|
||||
p->mode = MODE_Tcl;
|
||||
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Space);
|
||||
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
|
||||
}else if( c2=='c' && strncmp(azArg[1],"csv",n2)==0 ){
|
||||
p->mode = MODE_Csv;
|
||||
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma);
|
||||
@@ -5297,7 +5302,7 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
|
||||
if( c=='v' && strncmp(azArg[0], "vfsinfo", n)==0 ){
|
||||
const char *zDbName = nArg==2 ? azArg[1] : "main";
|
||||
sqlite3_vfs *pVfs;
|
||||
sqlite3_vfs *pVfs = 0;
|
||||
if( p->db ){
|
||||
sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFS_POINTER, &pVfs);
|
||||
if( pVfs ){
|
||||
|
||||
+31
-14
@@ -576,7 +576,7 @@ int sqlite3_exec(
|
||||
** file that were written at the application level might have changed
|
||||
** and that adjacent bytes, even bytes within the same sector are
|
||||
** guaranteed to be unchanged. The SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN
|
||||
** flag indicate that a file cannot be deleted when open. The
|
||||
** flag indicates that a file cannot be deleted when open. The
|
||||
** SQLITE_IOCAP_IMMUTABLE flag indicates that the file is on
|
||||
** read-only media and cannot be changed even by processes with
|
||||
** elevated privileges.
|
||||
@@ -726,6 +726,9 @@ struct sqlite3_file {
|
||||
** <li> [SQLITE_IOCAP_ATOMIC64K]
|
||||
** <li> [SQLITE_IOCAP_SAFE_APPEND]
|
||||
** <li> [SQLITE_IOCAP_SEQUENTIAL]
|
||||
** <li> [SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN]
|
||||
** <li> [SQLITE_IOCAP_POWERSAFE_OVERWRITE]
|
||||
** <li> [SQLITE_IOCAP_IMMUTABLE]
|
||||
** </ul>
|
||||
**
|
||||
** The SQLITE_IOCAP_ATOMIC property means that all writes of
|
||||
@@ -5414,7 +5417,7 @@ void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
|
||||
** ^The update hook is not invoked when [WITHOUT ROWID] tables are modified.
|
||||
**
|
||||
** ^In the current implementation, the update hook
|
||||
** is not invoked when duplication rows are deleted because of an
|
||||
** is not invoked when conflicting rows are deleted because of an
|
||||
** [ON CONFLICT | ON CONFLICT REPLACE] clause. ^Nor is the update hook
|
||||
** invoked when rows are deleted using the [truncate optimization].
|
||||
** The exceptions defined in this paragraph might change in a future
|
||||
@@ -6196,6 +6199,12 @@ typedef struct sqlite3_blob sqlite3_blob;
|
||||
** [database connection] error code and message accessible via
|
||||
** [sqlite3_errcode()] and [sqlite3_errmsg()] and related functions.
|
||||
**
|
||||
** A BLOB referenced by sqlite3_blob_open() may be read using the
|
||||
** [sqlite3_blob_read()] interface and modified by using
|
||||
** [sqlite3_blob_write()]. The [BLOB handle] can be moved to a
|
||||
** different row of the same table using the [sqlite3_blob_reopen()]
|
||||
** interface. However, the column, table, or database of a [BLOB handle]
|
||||
** cannot be changed after the [BLOB handle] is opened.
|
||||
**
|
||||
** ^(If the row that a BLOB handle points to is modified by an
|
||||
** [UPDATE], [DELETE], or by [ON CONFLICT] side-effects
|
||||
@@ -6219,6 +6228,10 @@ typedef struct sqlite3_blob sqlite3_blob;
|
||||
**
|
||||
** To avoid a resource leak, every open [BLOB handle] should eventually
|
||||
** be released by a call to [sqlite3_blob_close()].
|
||||
**
|
||||
** See also: [sqlite3_blob_close()],
|
||||
** [sqlite3_blob_reopen()], [sqlite3_blob_read()],
|
||||
** [sqlite3_blob_bytes()], [sqlite3_blob_write()].
|
||||
*/
|
||||
int sqlite3_blob_open(
|
||||
sqlite3*,
|
||||
@@ -6234,11 +6247,11 @@ int sqlite3_blob_open(
|
||||
** CAPI3REF: Move a BLOB Handle to a New Row
|
||||
** METHOD: sqlite3_blob
|
||||
**
|
||||
** ^This function is used to move an existing blob handle so that it points
|
||||
** ^This function is used to move an existing [BLOB handle] so that it points
|
||||
** to a different row of the same database table. ^The new row is identified
|
||||
** by the rowid value passed as the second argument. Only the row can be
|
||||
** changed. ^The database, table and column on which the blob handle is open
|
||||
** remain the same. Moving an existing blob handle to a new row can be
|
||||
** remain the same. Moving an existing [BLOB handle] to a new row is
|
||||
** faster than closing the existing handle and opening a new one.
|
||||
**
|
||||
** ^(The new row must meet the same criteria as for [sqlite3_blob_open()] -
|
||||
@@ -8167,7 +8180,7 @@ int sqlite3_db_cacheflush(sqlite3*);
|
||||
**
|
||||
** ^The [sqlite3_preupdate_hook()] interface registers a callback function
|
||||
** that is invoked prior to each [INSERT], [UPDATE], and [DELETE] operation
|
||||
** on a [rowid table].
|
||||
** on a database table.
|
||||
** ^At most one preupdate hook may be registered at a time on a single
|
||||
** [database connection]; each call to [sqlite3_preupdate_hook()] overrides
|
||||
** the previous setting.
|
||||
@@ -8176,9 +8189,9 @@ int sqlite3_db_cacheflush(sqlite3*);
|
||||
** ^The third parameter to [sqlite3_preupdate_hook()] is passed through as
|
||||
** the first parameter to callbacks.
|
||||
**
|
||||
** ^The preupdate hook only fires for changes to [rowid tables]; the preupdate
|
||||
** hook is not invoked for changes to [virtual tables] or [WITHOUT ROWID]
|
||||
** tables.
|
||||
** ^The preupdate hook only fires for changes to real database tables; the
|
||||
** preupdate hook is not invoked for changes to [virtual tables] or to
|
||||
** system tables like sqlite_master or sqlite_stat1.
|
||||
**
|
||||
** ^The second parameter to the preupdate callback is a pointer to
|
||||
** the [database connection] that registered the preupdate hook.
|
||||
@@ -8192,12 +8205,16 @@ int sqlite3_db_cacheflush(sqlite3*);
|
||||
** databases.)^
|
||||
** ^The fifth parameter to the preupdate callback is the name of the
|
||||
** table that is being modified.
|
||||
** ^The sixth parameter to the preupdate callback is the initial [rowid] of the
|
||||
** row being changes for SQLITE_UPDATE and SQLITE_DELETE changes and is
|
||||
** undefined for SQLITE_INSERT changes.
|
||||
** ^The seventh parameter to the preupdate callback is the final [rowid] of
|
||||
** the row being changed for SQLITE_UPDATE and SQLITE_INSERT changes and is
|
||||
** undefined for SQLITE_DELETE changes.
|
||||
**
|
||||
** For an UPDATE or DELETE operation on a [rowid table], the sixth
|
||||
** parameter passed to the preupdate callback is the initial [rowid] of the
|
||||
** row being modified or deleted. For an INSERT operation on a rowid table,
|
||||
** or any operation on a WITHOUT ROWID table, the value of the sixth
|
||||
** parameter is undefined. For an INSERT or UPDATE on a rowid table the
|
||||
** seventh parameter is the final rowid value of the row being inserted
|
||||
** or updated. The value of the seventh parameter passed to the callback
|
||||
** function is not defined for operations on WITHOUT ROWID tables, or for
|
||||
** INSERT operations on rowid tables.
|
||||
**
|
||||
** The [sqlite3_preupdate_old()], [sqlite3_preupdate_new()],
|
||||
** [sqlite3_preupdate_count()], and [sqlite3_preupdate_depth()] interfaces
|
||||
|
||||
+26
-8
@@ -111,7 +111,7 @@
|
||||
#endif
|
||||
|
||||
/* What version of CLANG is being used. 0 means CLANG is not being used */
|
||||
#ifdef __clang__
|
||||
#if defined(__clang__) && !defined(_WIN32)
|
||||
# define CLANG_VERSION \
|
||||
(__clang_major__*1000000+__clang_minor__*1000+__clang_patchlevel__)
|
||||
#else
|
||||
@@ -511,6 +511,18 @@
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/*
|
||||
** Use a macro to replace memcpy() if compiled with SQLITE_INLINE_MEMCPY.
|
||||
** This allows better measurements of where memcpy() is used when running
|
||||
** cachegrind. But this macro version of memcpy() is very slow so it
|
||||
** should not be used in production. This is a performance measurement
|
||||
** hack only.
|
||||
*/
|
||||
#ifdef SQLITE_INLINE_MEMCPY
|
||||
# define memcpy(D,S,N) {char*xxd=(char*)(D);const char*xxs=(const char*)(S);\
|
||||
int xxn=(N);while(xxn-->0)*(xxd++)=*(xxs++);}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** If compiling for a processor that lacks floating point support,
|
||||
** substitute integer for floating-point
|
||||
@@ -2580,7 +2592,7 @@ struct SrcList {
|
||||
#define WHERE_SORTBYGROUP 0x0200 /* Support sqlite3WhereIsSorted() */
|
||||
#define WHERE_SEEK_TABLE 0x0400 /* Do not defer seeks on main table */
|
||||
#define WHERE_ORDERBY_LIMIT 0x0800 /* ORDERBY+LIMIT on the inner loop */
|
||||
/* 0x1000 not currently used */
|
||||
#define WHERE_SEEK_UNIQ_TABLE 0x1000 /* Do not defer seeks if unique */
|
||||
/* 0x2000 not currently used */
|
||||
#define WHERE_USE_LIMIT 0x4000 /* Use the LIMIT in cost estimates */
|
||||
/* 0x8000 not currently used */
|
||||
@@ -3041,13 +3053,11 @@ struct AuthContext {
|
||||
#define OPFLAG_NCHANGE 0x01 /* OP_Insert: Set to update db->nChange */
|
||||
/* Also used in P2 (not P5) of OP_Delete */
|
||||
#define OPFLAG_EPHEM 0x01 /* OP_Column: Ephemeral output is ok */
|
||||
#define OPFLAG_LASTROWID 0x02 /* Set to update db->lastRowid */
|
||||
#define OPFLAG_LASTROWID 0x20 /* Set to update db->lastRowid */
|
||||
#define OPFLAG_ISUPDATE 0x04 /* This OP_Insert is an sql UPDATE */
|
||||
#define OPFLAG_APPEND 0x08 /* This is likely to be an append */
|
||||
#define OPFLAG_USESEEKRESULT 0x10 /* Try to avoid a seek in BtreeInsert() */
|
||||
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
||||
#define OPFLAG_ISNOOP 0x40 /* OP_Delete does pre-update-hook only */
|
||||
#endif
|
||||
#define OPFLAG_LENGTHARG 0x40 /* OP_Column only used for length() */
|
||||
#define OPFLAG_TYPEOFARG 0x80 /* OP_Column only used for typeof() */
|
||||
#define OPFLAG_BULKCSR 0x01 /* OP_Open** used to open bulk cursor */
|
||||
@@ -3055,7 +3065,7 @@ struct AuthContext {
|
||||
#define OPFLAG_FORDELETE 0x08 /* OP_Open should use BTREE_FORDELETE */
|
||||
#define OPFLAG_P2ISREG 0x10 /* P2 to OP_Open** is a register number */
|
||||
#define OPFLAG_PERMUTE 0x01 /* OP_Compare: use the permutation */
|
||||
#define OPFLAG_SAVEPOSITION 0x02 /* OP_Delete: keep cursor position */
|
||||
#define OPFLAG_SAVEPOSITION 0x02 /* OP_Delete/Insert: save cursor pos */
|
||||
#define OPFLAG_AUXDELETE 0x04 /* OP_Delete: index in a DELETE op */
|
||||
|
||||
/*
|
||||
@@ -3778,6 +3788,11 @@ int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int, int*,Index*,int);
|
||||
void sqlite3ResolvePartIdxLabel(Parse*,int);
|
||||
void sqlite3GenerateConstraintChecks(Parse*,Table*,int*,int,int,int,int,
|
||||
u8,u8,int,int*,int*);
|
||||
#ifdef SQLITE_ENABLE_NULL_TRIM
|
||||
void sqlite3SetMakeRecordP5(Vdbe*,Table*);
|
||||
#else
|
||||
# define sqlite3SetMakeRecordP5(A,B)
|
||||
#endif
|
||||
void sqlite3CompleteInsertion(Parse*,Table*,int,int,int,int*,int,int,int);
|
||||
int sqlite3OpenTableAndIndices(Parse*, Table*, int, u8, int, u8*, int*, int*);
|
||||
void sqlite3BeginWriteOperation(Parse*, int, int);
|
||||
@@ -4056,8 +4071,10 @@ char sqlite3IndexColumnAffinity(sqlite3*, Index*, int);
|
||||
/*
|
||||
** The interface to the LEMON-generated parser
|
||||
*/
|
||||
void *sqlite3ParserAlloc(void*(*)(u64));
|
||||
void sqlite3ParserFree(void*, void(*)(void*));
|
||||
#ifndef SQLITE_AMALGAMATION
|
||||
void *sqlite3ParserAlloc(void*(*)(u64));
|
||||
void sqlite3ParserFree(void*, void(*)(void*));
|
||||
#endif
|
||||
void sqlite3Parser(void*, int, Token, Parse*);
|
||||
#ifdef YYTRACKMAXSTACKDEPTH
|
||||
int sqlite3ParserStackPeak(void*);
|
||||
@@ -4167,6 +4184,7 @@ const char *sqlite3JournalModename(int);
|
||||
#define sqlite3FkDropTable(a,b,c)
|
||||
#define sqlite3FkOldmask(a,b) 0
|
||||
#define sqlite3FkRequired(a,b,c,d) 0
|
||||
#define sqlite3FkReferences(a) 0
|
||||
#endif
|
||||
#ifndef SQLITE_OMIT_FOREIGN_KEY
|
||||
void sqlite3FkDelete(sqlite3 *, Table*);
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
** if they are not used.
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef SQLITE_OMIT_GET_TABLE
|
||||
|
||||
|
||||
+4
-4
@@ -2307,7 +2307,7 @@ static int SQLITE_TCLAPI DbObjCmd(
|
||||
}
|
||||
in = fopen(zFile, "rb");
|
||||
if( in==0 ){
|
||||
Tcl_AppendResult(interp, "Error: cannot open file: ", zFile, NULL);
|
||||
Tcl_AppendResult(interp, "Error: cannot open file: ", zFile, (char*)0);
|
||||
sqlite3_finalize(pStmt);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
@@ -2536,7 +2536,7 @@ static int SQLITE_TCLAPI DbObjCmd(
|
||||
int n = strlen30(z);
|
||||
if( n>2 && strncmp(z, "-argcount",n)==0 ){
|
||||
if( i==(objc-2) ){
|
||||
Tcl_AppendResult(interp, "option requires an argument: ", z, 0);
|
||||
Tcl_AppendResult(interp, "option requires an argument: ", z,(char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetIntFromObj(interp, objv[i+1], &nArg) ) return TCL_ERROR;
|
||||
@@ -2551,7 +2551,7 @@ static int SQLITE_TCLAPI DbObjCmd(
|
||||
flags |= SQLITE_DETERMINISTIC;
|
||||
}else{
|
||||
Tcl_AppendResult(interp, "bad option \"", z,
|
||||
"\": must be -argcount or -deterministic", 0
|
||||
"\": must be -argcount or -deterministic", (char*)0
|
||||
);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
@@ -3208,7 +3208,7 @@ static int SQLITE_TCLAPI DbObjCmd(
|
||||
pObj = Tcl_NewStringObj((char*)sqlite3_value_text(pValue), -1);
|
||||
Tcl_SetObjResult(interp, pObj);
|
||||
}else{
|
||||
Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), 0);
|
||||
Tcl_AppendResult(interp, sqlite3_errmsg(pDb->db), (char*)0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -80,7 +80,7 @@ static int sqlite3DeleteUnlinkIfExists(const char *zFile, int *pbExists){
|
||||
** Delete the database file identified by the string argument passed to this
|
||||
** function. The string must contain a filename, not an SQLite URI.
|
||||
*/
|
||||
int sqlite3_delete_database(
|
||||
SQLITE_API int sqlite3_delete_database(
|
||||
const char *zFile /* File to delete */
|
||||
){
|
||||
char *zBuf; /* Buffer to sprintf() filenames to */
|
||||
|
||||
+15
-8
@@ -63,6 +63,7 @@ LPDIR opendir(
|
||||
dirname = windirent_getenv("SystemDrive");
|
||||
}
|
||||
|
||||
memset(&data, 0, sizeof(struct _finddata_t));
|
||||
_snprintf(data.name, namesize, "%s\\*", dirname);
|
||||
dirp->d_handle = _findfirst(data.name, &data);
|
||||
|
||||
@@ -71,12 +72,18 @@ LPDIR opendir(
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* TODO: Remove this block to allow hidden and system files. */
|
||||
if( data.attrib&_A_HIDDEN || data.attrib&_A_SYSTEM ){
|
||||
/* TODO: Remove this block to allow hidden and/or system files. */
|
||||
if( is_filtered(data) ){
|
||||
next:
|
||||
|
||||
memset(&data, 0, sizeof(struct _finddata_t));
|
||||
if( _findnext(dirp->d_handle, &data)==-1 ){
|
||||
closedir(dirp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* TODO: Remove this block to allow hidden and/or system files. */
|
||||
if( is_filtered(data) ) goto next;
|
||||
}
|
||||
|
||||
dirp->d_first.d_attributes = data.attrib;
|
||||
@@ -105,11 +112,11 @@ LPDIRENT readdir(
|
||||
|
||||
next:
|
||||
|
||||
memset(&data, 0, sizeof(struct _finddata_t));
|
||||
if( _findnext(dirp->d_handle, &data)==-1 ) return NULL;
|
||||
|
||||
/* TODO: Remove this block to allow hidden and system files. */
|
||||
if( data.attrib&_A_HIDDEN ) goto next;
|
||||
if( data.attrib&_A_SYSTEM ) goto next;
|
||||
/* TODO: Remove this block to allow hidden and/or system files. */
|
||||
if( is_filtered(data) ) goto next;
|
||||
|
||||
dirp->d_next.d_ino++;
|
||||
dirp->d_next.d_attributes = data.attrib;
|
||||
@@ -146,14 +153,14 @@ INT readdir_r(
|
||||
|
||||
next:
|
||||
|
||||
memset(&data, 0, sizeof(struct _finddata_t));
|
||||
if( _findnext(dirp->d_handle, &data)==-1 ){
|
||||
*result = NULL;
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
/* TODO: Remove this block to allow hidden and system files. */
|
||||
if( data.attrib&_A_HIDDEN ) goto next;
|
||||
if( data.attrib&_A_SYSTEM ) goto next;
|
||||
/* TODO: Remove this block to allow hidden and/or system files. */
|
||||
if( is_filtered(data) ) goto next;
|
||||
|
||||
entry->d_ino = (ino_t)-1; /* not available */
|
||||
entry->d_attributes = data.attrib;
|
||||
|
||||
@@ -92,6 +92,17 @@ struct DIR {
|
||||
DIRENT d_next; /* DIRENT constructed based on "_findnext". */
|
||||
};
|
||||
|
||||
/*
|
||||
** Provide a macro, for use by the implementation, to determine if a
|
||||
** particular directory entry should be skipped over when searching for
|
||||
** the next directory entry that should be returned by the readdir() or
|
||||
** readdir_r() functions.
|
||||
*/
|
||||
|
||||
#ifndef is_filtered
|
||||
# define is_filtered(a) ((((a).attrib)&_A_HIDDEN) || (((a).attrib)&_A_SYSTEM))
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Provide the function prototype for the POSIX compatiable getenv()
|
||||
** function. This function is not thread-safe.
|
||||
|
||||
@@ -481,6 +481,9 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
|
||||
int lastTokenParsed = -1; /* type of the previous token */
|
||||
sqlite3 *db = pParse->db; /* The database connection */
|
||||
int mxSqlLen; /* Max length of an SQL string */
|
||||
#ifdef sqlite3Parser_ENGINEALWAYSONSTACK
|
||||
unsigned char zSpace[sizeof(yyParser)]; /* Space for parser engine object */
|
||||
#endif
|
||||
|
||||
assert( zSql!=0 );
|
||||
mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH];
|
||||
@@ -492,11 +495,16 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
|
||||
i = 0;
|
||||
assert( pzErrMsg!=0 );
|
||||
/* sqlite3ParserTrace(stdout, "parser: "); */
|
||||
#ifdef sqlite3Parser_ENGINEALWAYSONSTACK
|
||||
pEngine = zSpace;
|
||||
sqlite3ParserInit(pEngine);
|
||||
#else
|
||||
pEngine = sqlite3ParserAlloc(sqlite3Malloc);
|
||||
if( pEngine==0 ){
|
||||
sqlite3OomFault(db);
|
||||
return SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
#endif
|
||||
assert( pParse->pNewTable==0 );
|
||||
assert( pParse->pNewTrigger==0 );
|
||||
assert( pParse->nVar==0 );
|
||||
@@ -548,7 +556,11 @@ int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
|
||||
);
|
||||
sqlite3_mutex_leave(sqlite3MallocMutex());
|
||||
#endif /* YYDEBUG */
|
||||
#ifdef sqlite3Parser_ENGINEALWAYSONSTACK
|
||||
sqlite3ParserFinalize(pEngine);
|
||||
#else
|
||||
sqlite3ParserFree(pEngine, sqlite3_free);
|
||||
#endif
|
||||
if( db->mallocFailed ){
|
||||
pParse->rc = SQLITE_NOMEM_BKPT;
|
||||
}
|
||||
|
||||
+113
-69
@@ -71,12 +71,12 @@ void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){
|
||||
if( pValue ){
|
||||
sqlite3VdbeAppendP4(v, pValue, P4_MEM);
|
||||
}
|
||||
#ifndef SQLITE_OMIT_FLOATING_POINT
|
||||
if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){
|
||||
sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#ifndef SQLITE_OMIT_FLOATING_POINT
|
||||
if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){
|
||||
sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -105,7 +105,7 @@ void sqlite3Update(
|
||||
int iDataCur; /* Cursor for the canonical data btree */
|
||||
int iIdxCur; /* Cursor for the first index */
|
||||
sqlite3 *db; /* The database structure */
|
||||
int *aRegIdx = 0; /* One register assigned to each index to be updated */
|
||||
int *aRegIdx = 0; /* First register in array assigned to each index */
|
||||
int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the
|
||||
** an expression for the i-th column of the table.
|
||||
** aXRef[i]==-1 if the i-th column is not changed. */
|
||||
@@ -117,10 +117,11 @@ void sqlite3Update(
|
||||
AuthContext sContext; /* The authorization context */
|
||||
NameContext sNC; /* The name-context to resolve expressions in */
|
||||
int iDb; /* Database containing the table being updated */
|
||||
int okOnePass; /* True for one-pass algorithm without the FIFO */
|
||||
int eOnePass; /* ONEPASS_XXX value from where.c */
|
||||
int hasFK; /* True if foreign key processing is required */
|
||||
int labelBreak; /* Jump here to break out of UPDATE loop */
|
||||
int labelContinue; /* Jump here to continue next step of UPDATE loop */
|
||||
int flags; /* Flags for sqlite3WhereBegin() */
|
||||
|
||||
#ifndef SQLITE_OMIT_TRIGGER
|
||||
int isView; /* True when updating a view (INSTEAD OF trigger) */
|
||||
@@ -131,6 +132,10 @@ void sqlite3Update(
|
||||
int iEph = 0; /* Ephemeral table holding all primary key values */
|
||||
int nKey = 0; /* Number of elements in regKey for WITHOUT ROWID */
|
||||
int aiCurOnePass[2]; /* The write cursors opened by WHERE_ONEPASS */
|
||||
int addrOpen = 0; /* Address of OP_OpenEphemeral */
|
||||
int iPk = 0; /* First of nPk cells holding PRIMARY KEY value */
|
||||
i16 nPk = 0; /* Number of components of the PRIMARY KEY */
|
||||
int bReplace = 0; /* True if REPLACE conflict resolution might happen */
|
||||
|
||||
/* Register Allocations */
|
||||
int regRowCount = 0; /* A count of rows changed */
|
||||
@@ -290,6 +295,11 @@ void sqlite3Update(
|
||||
if( iIdxCol<0 || aXRef[iIdxCol]>=0 ){
|
||||
reg = ++pParse->nMem;
|
||||
pParse->nMem += pIdx->nColumn;
|
||||
if( (onError==OE_Replace)
|
||||
|| (onError==OE_Default && pIdx->onError==OE_Replace)
|
||||
){
|
||||
bReplace = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -297,6 +307,11 @@ void sqlite3Update(
|
||||
if( reg==0 ) aToOpen[j+1] = 0;
|
||||
aRegIdx[j] = reg;
|
||||
}
|
||||
if( bReplace ){
|
||||
/* If REPLACE conflict resolution might be invoked, open cursors on all
|
||||
** indexes in case they are needed to delete records. */
|
||||
memset(aToOpen, 1, nIdx+1);
|
||||
}
|
||||
|
||||
/* Begin generating code. */
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
@@ -349,51 +364,79 @@ void sqlite3Update(
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Begin the database scan
|
||||
*/
|
||||
/* Initialize the count of updated rows */
|
||||
if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab ){
|
||||
regRowCount = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
|
||||
}
|
||||
|
||||
if( HasRowid(pTab) ){
|
||||
sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid);
|
||||
pWInfo = sqlite3WhereBegin(
|
||||
pParse, pTabList, pWhere, 0, 0,
|
||||
WHERE_ONEPASS_DESIRED | WHERE_SEEK_TABLE, iIdxCur
|
||||
);
|
||||
if( pWInfo==0 ) goto update_cleanup;
|
||||
okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
|
||||
|
||||
/* Remember the rowid of every item to be updated.
|
||||
*/
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid);
|
||||
if( !okOnePass ){
|
||||
sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid);
|
||||
}
|
||||
|
||||
/* End the database scan loop.
|
||||
*/
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
}else{
|
||||
int iPk; /* First of nPk memory cells holding PRIMARY KEY value */
|
||||
i16 nPk; /* Number of components of the PRIMARY KEY */
|
||||
int addrOpen; /* Address of the OpenEphemeral instruction */
|
||||
|
||||
assert( pPk!=0 );
|
||||
nPk = pPk->nKeyCol;
|
||||
iPk = pParse->nMem+1;
|
||||
pParse->nMem += nPk;
|
||||
regKey = ++pParse->nMem;
|
||||
iEph = pParse->nTab++;
|
||||
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, iPk);
|
||||
addrOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nPk);
|
||||
sqlite3VdbeSetP4KeyInfo(pParse, pPk);
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0,
|
||||
WHERE_ONEPASS_DESIRED, iIdxCur);
|
||||
if( pWInfo==0 ) goto update_cleanup;
|
||||
okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
|
||||
}
|
||||
|
||||
/* Begin the database scan.
|
||||
**
|
||||
** Do not consider a single-pass strategy for a multi-row update if
|
||||
** there are any triggers or foreign keys to process, or rows may
|
||||
** be deleted as a result of REPLACE conflict handling. Any of these
|
||||
** things might disturb a cursor being used to scan through the table
|
||||
** or index, causing a single-pass approach to malfunction. */
|
||||
flags = WHERE_ONEPASS_DESIRED|WHERE_SEEK_UNIQ_TABLE;
|
||||
if( !pParse->nested && !pTrigger && !hasFK && !chngKey && !bReplace ){
|
||||
flags |= WHERE_ONEPASS_MULTIROW;
|
||||
}
|
||||
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, flags, iIdxCur);
|
||||
if( pWInfo==0 ) goto update_cleanup;
|
||||
|
||||
/* A one-pass strategy that might update more than one row may not
|
||||
** be used if any column of the index used for the scan is being
|
||||
** updated. Otherwise, if there is an index on "b", statements like
|
||||
** the following could create an infinite loop:
|
||||
**
|
||||
** UPDATE t1 SET b=b+1 WHERE b>?
|
||||
**
|
||||
** Fall back to ONEPASS_OFF if where.c has selected a ONEPASS_MULTI
|
||||
** strategy that uses an index for which one or more columns are being
|
||||
** updated. */
|
||||
eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
|
||||
if( eOnePass==ONEPASS_MULTI ){
|
||||
int iCur = aiCurOnePass[1];
|
||||
if( iCur>=0 && iCur!=iDataCur && aToOpen[iCur-iBaseCur] ){
|
||||
eOnePass = ONEPASS_OFF;
|
||||
}
|
||||
assert( iCur!=iDataCur || !HasRowid(pTab) );
|
||||
}
|
||||
|
||||
if( HasRowid(pTab) ){
|
||||
/* Read the rowid of the current row of the WHERE scan. In ONEPASS_OFF
|
||||
** mode, write the rowid into the FIFO. In either of the one-pass modes,
|
||||
** leave it in register regOldRowid. */
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid);
|
||||
if( eOnePass==ONEPASS_OFF ){
|
||||
sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid);
|
||||
}
|
||||
}else{
|
||||
/* Read the PK of the current row into an array of registers. In
|
||||
** ONEPASS_OFF mode, serialize the array into a record and store it in
|
||||
** the ephemeral table. Or, in ONEPASS_SINGLE or MULTI mode, change
|
||||
** the OP_OpenEphemeral instruction to a Noop (the ephemeral table
|
||||
** is not required) and leave the PK fields in the array of registers. */
|
||||
for(i=0; i<nPk; i++){
|
||||
assert( pPk->aiColumn[i]>=0 );
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, pPk->aiColumn[i],
|
||||
iPk+i);
|
||||
sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur,pPk->aiColumn[i],iPk+i);
|
||||
}
|
||||
if( okOnePass ){
|
||||
if( eOnePass ){
|
||||
sqlite3VdbeChangeToNoop(v, addrOpen);
|
||||
nKey = nPk;
|
||||
regKey = iPk;
|
||||
@@ -402,50 +445,42 @@ void sqlite3Update(
|
||||
sqlite3IndexAffinityStr(db, pPk), nPk);
|
||||
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iEph, regKey, iPk, nPk);
|
||||
}
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
}
|
||||
|
||||
/* Initialize the count of updated rows
|
||||
*/
|
||||
if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab ){
|
||||
regRowCount = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
|
||||
if( eOnePass!=ONEPASS_MULTI ){
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
}
|
||||
|
||||
labelBreak = sqlite3VdbeMakeLabel(v);
|
||||
if( !isView ){
|
||||
/*
|
||||
** Open every index that needs updating. Note that if any
|
||||
** index could potentially invoke a REPLACE conflict resolution
|
||||
** action, then we need to open all indices because we might need
|
||||
** to be deleting some records.
|
||||
*/
|
||||
if( onError==OE_Replace ){
|
||||
memset(aToOpen, 1, nIdx+1);
|
||||
}else{
|
||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||
if( pIdx->onError==OE_Replace ){
|
||||
memset(aToOpen, 1, nIdx+1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( okOnePass ){
|
||||
int addrOnce = 0;
|
||||
|
||||
/* Open every index that needs updating. */
|
||||
if( eOnePass!=ONEPASS_OFF ){
|
||||
if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iBaseCur] = 0;
|
||||
if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iBaseCur] = 0;
|
||||
}
|
||||
|
||||
if( eOnePass==ONEPASS_MULTI && (nIdx-(aiCurOnePass[1]>=0))>0 ){
|
||||
addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
|
||||
}
|
||||
sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, 0, iBaseCur, aToOpen,
|
||||
0, 0);
|
||||
if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
|
||||
}
|
||||
|
||||
/* Top of the update loop */
|
||||
if( okOnePass ){
|
||||
if( aToOpen[iDataCur-iBaseCur] && !isView ){
|
||||
if( eOnePass!=ONEPASS_OFF ){
|
||||
if( !isView && aiCurOnePass[0]!=iDataCur && aiCurOnePass[1]!=iDataCur ){
|
||||
assert( pPk );
|
||||
sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelBreak, regKey, nKey);
|
||||
VdbeCoverageNeverTaken(v);
|
||||
}
|
||||
labelContinue = labelBreak;
|
||||
if( eOnePass==ONEPASS_SINGLE ){
|
||||
labelContinue = labelBreak;
|
||||
}else{
|
||||
labelContinue = sqlite3VdbeMakeLabel(v);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_IsNull, pPk ? regKey : regOldRowid, labelBreak);
|
||||
VdbeCoverageIf(v, pPk==0);
|
||||
VdbeCoverageIf(v, pPk!=0);
|
||||
@@ -570,7 +605,6 @@ void sqlite3Update(
|
||||
|
||||
if( !isView ){
|
||||
int addr1 = 0; /* Address of jump instruction */
|
||||
int bReplace = 0; /* True if REPLACE conflict resolution might happen */
|
||||
|
||||
/* Do constraint checks. */
|
||||
assert( regOldRowid>0 );
|
||||
@@ -606,14 +640,18 @@ void sqlite3Update(
|
||||
assert( regNew==regNewRowid+1 );
|
||||
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
||||
sqlite3VdbeAddOp3(v, OP_Delete, iDataCur,
|
||||
OPFLAG_ISUPDATE | ((hasFK || chngKey || pPk!=0) ? 0 : OPFLAG_ISNOOP),
|
||||
OPFLAG_ISUPDATE | ((hasFK || chngKey) ? 0 : OPFLAG_ISNOOP),
|
||||
regNewRowid
|
||||
);
|
||||
if( eOnePass==ONEPASS_MULTI ){
|
||||
assert( hasFK==0 && chngKey==0 );
|
||||
sqlite3VdbeChangeP5(v, OPFLAG_SAVEPOSITION);
|
||||
}
|
||||
if( !pParse->nested ){
|
||||
sqlite3VdbeAppendP4(v, pTab, P4_TABLE);
|
||||
}
|
||||
#else
|
||||
if( hasFK || chngKey || pPk!=0 ){
|
||||
if( hasFK || chngKey ){
|
||||
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, 0);
|
||||
}
|
||||
#endif
|
||||
@@ -626,8 +664,11 @@ void sqlite3Update(
|
||||
}
|
||||
|
||||
/* Insert the new index entries and the new record. */
|
||||
sqlite3CompleteInsertion(pParse, pTab, iDataCur, iIdxCur,
|
||||
regNewRowid, aRegIdx, 1, 0, 0);
|
||||
sqlite3CompleteInsertion(
|
||||
pParse, pTab, iDataCur, iIdxCur, regNewRowid, aRegIdx,
|
||||
OPFLAG_ISUPDATE | (eOnePass==ONEPASS_MULTI ? OPFLAG_SAVEPOSITION : 0),
|
||||
0, 0
|
||||
);
|
||||
|
||||
/* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to
|
||||
** handle rows (possibly in other tables) that refer via a foreign key
|
||||
@@ -649,8 +690,11 @@ void sqlite3Update(
|
||||
/* Repeat the above with the next record to be updated, until
|
||||
** all record selected by the WHERE clause have been updated.
|
||||
*/
|
||||
if( okOnePass ){
|
||||
if( eOnePass==ONEPASS_SINGLE ){
|
||||
/* Nothing to do at end-of-loop for a single-pass */
|
||||
}else if( eOnePass==ONEPASS_MULTI ){
|
||||
sqlite3VdbeResolveLabel(v, labelContinue);
|
||||
sqlite3WhereEnd(pWInfo);
|
||||
}else if( pPk ){
|
||||
sqlite3VdbeResolveLabel(v, labelContinue);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop); VdbeCoverage(v);
|
||||
|
||||
+44
-26
@@ -583,8 +583,6 @@ int sqlite3VdbeExec(
|
||||
Mem *pIn2 = 0; /* 2nd input operand */
|
||||
Mem *pIn3 = 0; /* 3rd input operand */
|
||||
Mem *pOut = 0; /* Output operand */
|
||||
int *aPermute = 0; /* Permutation of columns for OP_Compare */
|
||||
i64 lastRowid = db->lastRowid; /* Saved value of the last insert ROWID */
|
||||
#ifdef VDBE_PROFILE
|
||||
u64 start; /* CPU clock count at start of opcode */
|
||||
#endif
|
||||
@@ -599,7 +597,6 @@ int sqlite3VdbeExec(
|
||||
}
|
||||
assert( p->rc==SQLITE_OK || (p->rc&0xff)==SQLITE_BUSY );
|
||||
assert( p->bIsReader || p->readOnly!=0 );
|
||||
p->rc = SQLITE_OK;
|
||||
p->iCurrentTime = 0;
|
||||
assert( p->explain==0 );
|
||||
p->pResultSet = 0;
|
||||
@@ -960,7 +957,6 @@ case OP_Halt: {
|
||||
p->nFrame--;
|
||||
sqlite3VdbeSetChanges(db, p->nChange);
|
||||
pcx = sqlite3VdbeFrameRestore(pFrame);
|
||||
lastRowid = db->lastRowid;
|
||||
if( pOp->p2==OE_Ignore ){
|
||||
/* Instruction pcx is the OP_Program that invoked the sub-program
|
||||
** currently being halted. If the p2 instruction of this OP_Halt
|
||||
@@ -1195,7 +1191,7 @@ case OP_Variable: { /* out2 */
|
||||
if( sqlite3VdbeMemTooBig(pVar) ){
|
||||
goto too_big;
|
||||
}
|
||||
pOut = out2Prerelease(p, pOp);
|
||||
pOut = &aMem[pOp->p2];
|
||||
sqlite3VdbeMemShallowCopy(pOut, pVar, MEM_Static);
|
||||
UPDATE_MAX_BLOBSIZE(pOut);
|
||||
break;
|
||||
@@ -1682,9 +1678,7 @@ case OP_Function: {
|
||||
#endif
|
||||
MemSetTypeFlag(pCtx->pOut, MEM_Null);
|
||||
pCtx->fErrorOrAux = 0;
|
||||
db->lastRowid = lastRowid;
|
||||
(*pCtx->pFunc->xSFunc)(pCtx, pCtx->argc, pCtx->argv);/* IMP: R-24505-23230 */
|
||||
lastRowid = db->lastRowid; /* Remember rowid changes made by xSFunc */
|
||||
|
||||
/* If the function returned an error, throw an exception */
|
||||
if( pCtx->fErrorOrAux ){
|
||||
@@ -2140,8 +2134,8 @@ case OP_ElseNotEq: { /* same as TK_ESCAPE, jump */
|
||||
|
||||
/* Opcode: Permutation * * * P4 *
|
||||
**
|
||||
** Set the permutation used by the OP_Compare operator to be the array
|
||||
** of integers in P4.
|
||||
** Set the permutation used by the OP_Compare operator in the next
|
||||
** instruction. The permutation is stored in the P4 operand.
|
||||
**
|
||||
** The permutation is only valid until the next OP_Compare that has
|
||||
** the OPFLAG_PERMUTE bit set in P5. Typically the OP_Permutation should
|
||||
@@ -2153,7 +2147,8 @@ case OP_ElseNotEq: { /* same as TK_ESCAPE, jump */
|
||||
case OP_Permutation: {
|
||||
assert( pOp->p4type==P4_INTARRAY );
|
||||
assert( pOp->p4.ai );
|
||||
aPermute = pOp->p4.ai + 1;
|
||||
assert( pOp[1].opcode==OP_Compare );
|
||||
assert( pOp[1].p5 & OPFLAG_PERMUTE );
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2186,8 +2181,17 @@ case OP_Compare: {
|
||||
int idx;
|
||||
CollSeq *pColl; /* Collating sequence to use on this term */
|
||||
int bRev; /* True for DESCENDING sort order */
|
||||
int *aPermute; /* The permutation */
|
||||
|
||||
if( (pOp->p5 & OPFLAG_PERMUTE)==0 ) aPermute = 0;
|
||||
if( (pOp->p5 & OPFLAG_PERMUTE)==0 ){
|
||||
aPermute = 0;
|
||||
}else{
|
||||
assert( pOp>aOp );
|
||||
assert( pOp[-1].opcode==OP_Permutation );
|
||||
assert( pOp[-1].p4type==P4_INTARRAY );
|
||||
aPermute = pOp[-1].p4.ai + 1;
|
||||
assert( aPermute!=0 );
|
||||
}
|
||||
n = pOp->p3;
|
||||
pKeyInfo = pOp->p4.pKeyInfo;
|
||||
assert( n>0 );
|
||||
@@ -2220,7 +2224,6 @@ case OP_Compare: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
aPermute = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2777,6 +2780,20 @@ case OP_MakeRecord: {
|
||||
}while( zAffinity[0] );
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_NULL_TRIM
|
||||
/* NULLs can be safely trimmed from the end of the record, as long as
|
||||
** as the schema format is 2 or more and none of the omitted columns
|
||||
** have a non-NULL default value. Also, the record must be left with
|
||||
** at least one field. If P5>0 then it will be one more than the
|
||||
** index of the right-most column with a non-NULL default value */
|
||||
if( pOp->p5 ){
|
||||
while( (pLast->flags & MEM_Null)!=0 && nField>pOp->p5 ){
|
||||
pLast--;
|
||||
nField--;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Loop through the elements that will make up the record to figure
|
||||
** out how much space is required for the new record.
|
||||
*/
|
||||
@@ -4366,7 +4383,7 @@ case OP_InsertInt: {
|
||||
assert( pC!=0 );
|
||||
assert( pC->eCurType==CURTYPE_BTREE );
|
||||
assert( pC->uc.pCursor!=0 );
|
||||
assert( pC->isTable );
|
||||
assert( (pOp->p5 & OPFLAG_ISNOOP) || pC->isTable );
|
||||
assert( pOp->p4type==P4_TABLE || pOp->p4type>=P4_STATIC );
|
||||
REGISTER_TRACE(pOp->p2, pData);
|
||||
|
||||
@@ -4382,14 +4399,13 @@ case OP_InsertInt: {
|
||||
}
|
||||
|
||||
if( pOp->p4type==P4_TABLE && HAS_UPDATE_HOOK(db) ){
|
||||
assert( pC->isTable );
|
||||
assert( pC->iDb>=0 );
|
||||
zDb = db->aDb[pC->iDb].zDbSName;
|
||||
pTab = pOp->p4.pTab;
|
||||
assert( HasRowid(pTab) );
|
||||
assert( (pOp->p5 & OPFLAG_ISNOOP) || HasRowid(pTab) );
|
||||
op = ((pOp->p5 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT);
|
||||
}else{
|
||||
pTab = 0; /* Not needed. Silence a comiler warning. */
|
||||
pTab = 0; /* Not needed. Silence a compiler warning. */
|
||||
zDb = 0; /* Not needed. Silence a compiler warning. */
|
||||
}
|
||||
|
||||
@@ -4401,10 +4417,11 @@ case OP_InsertInt: {
|
||||
){
|
||||
sqlite3VdbePreUpdateHook(p, pC, SQLITE_INSERT, zDb, pTab, x.nKey, pOp->p2);
|
||||
}
|
||||
if( pOp->p5 & OPFLAG_ISNOOP ) break;
|
||||
#endif
|
||||
|
||||
if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
|
||||
if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = lastRowid = x.nKey;
|
||||
if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = x.nKey;
|
||||
if( pData->flags & MEM_Null ){
|
||||
x.pData = 0;
|
||||
x.nData = 0;
|
||||
@@ -4421,7 +4438,7 @@ case OP_InsertInt: {
|
||||
}
|
||||
x.pKey = 0;
|
||||
rc = sqlite3BtreeInsert(pC->uc.pCursor, &x,
|
||||
(pOp->p5 & OPFLAG_APPEND)!=0, seekResult
|
||||
(pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION)), seekResult
|
||||
);
|
||||
pC->deferredMoveto = 0;
|
||||
pC->cacheStatus = CACHE_STALE;
|
||||
@@ -4513,8 +4530,11 @@ case OP_Delete: {
|
||||
|
||||
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
|
||||
/* Invoke the pre-update-hook if required. */
|
||||
if( db->xPreUpdateCallback && pOp->p4.pTab && HasRowid(pTab) ){
|
||||
assert( !(opflags & OPFLAG_ISUPDATE) || (aMem[pOp->p3].flags & MEM_Int) );
|
||||
if( db->xPreUpdateCallback && pOp->p4.pTab ){
|
||||
assert( !(opflags & OPFLAG_ISUPDATE)
|
||||
|| HasRowid(pTab)==0
|
||||
|| (aMem[pOp->p3].flags & MEM_Int)
|
||||
);
|
||||
sqlite3VdbePreUpdateHook(p, pC,
|
||||
(opflags & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_DELETE,
|
||||
zDb, pTab, pC->movetoTarget,
|
||||
@@ -4657,7 +4677,7 @@ case OP_SorterData: {
|
||||
**
|
||||
** If P3!=0 then the content of the P2 register is unsuitable for use
|
||||
** in OP_Result and any OP_Result will invalidate the P2 register content.
|
||||
** The P2 register content is invalided by opcodes like OP_Function or
|
||||
** The P2 register content is invalidated by opcodes like OP_Function or
|
||||
** by any use of another cursor pointing to the same table.
|
||||
*/
|
||||
case OP_RowData: {
|
||||
@@ -5086,7 +5106,7 @@ case OP_IdxInsert: { /* in2 */
|
||||
x.aMem = aMem + pOp->p3;
|
||||
x.nMem = (u16)pOp->p4.i;
|
||||
rc = sqlite3BtreeInsert(pC->uc.pCursor, &x,
|
||||
(pOp->p5 & OPFLAG_APPEND)!=0,
|
||||
(pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION)),
|
||||
((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0)
|
||||
);
|
||||
assert( pC->deferredMoveto==0 );
|
||||
@@ -5208,7 +5228,6 @@ case OP_IdxRowid: { /* out2 */
|
||||
}else{
|
||||
pOut = out2Prerelease(p, pOp);
|
||||
pOut->u.i = rowid;
|
||||
pOut->flags = MEM_Int;
|
||||
}
|
||||
}else{
|
||||
assert( pOp->opcode==OP_IdxRowid );
|
||||
@@ -5850,7 +5869,7 @@ case OP_Program: { /* jump */
|
||||
|
||||
p->nFrame++;
|
||||
pFrame->pParent = p->pFrame;
|
||||
pFrame->lastRowid = lastRowid;
|
||||
pFrame->lastRowid = db->lastRowid;
|
||||
pFrame->nChange = p->nChange;
|
||||
pFrame->nDbChange = p->db->nChange;
|
||||
assert( pFrame->pAuxData==0 );
|
||||
@@ -6791,7 +6810,7 @@ case OP_VUpdate: {
|
||||
sqlite3VtabImportErrmsg(p, pVtab);
|
||||
if( rc==SQLITE_OK && pOp->p1 ){
|
||||
assert( nArg>1 && apArg[0] && (apArg[0]->flags&MEM_Null) );
|
||||
db->lastRowid = lastRowid = rowid;
|
||||
db->lastRowid = rowid;
|
||||
}
|
||||
if( (rc&0xff)==SQLITE_CONSTRAINT && pOp->p4.pVtab->bConstraint ){
|
||||
if( pOp->p5==OE_Ignore ){
|
||||
@@ -7027,7 +7046,6 @@ abort_due_to_error:
|
||||
** release the mutexes on btrees that were acquired at the
|
||||
** top. */
|
||||
vdbe_return:
|
||||
db->lastRowid = lastRowid;
|
||||
testcase( nVmStep>0 );
|
||||
p->aCounter[SQLITE_STMTSTATUS_VM_STEP] += (int)nVmStep;
|
||||
sqlite3VdbeLeave(p);
|
||||
|
||||
+2
-3
@@ -41,8 +41,7 @@ typedef struct SubProgram SubProgram;
|
||||
struct VdbeOp {
|
||||
u8 opcode; /* What operation to perform */
|
||||
signed char p4type; /* One of the P4_xxx constants for p4 */
|
||||
u8 notUsed1;
|
||||
u8 p5; /* Fifth parameter is an unsigned character */
|
||||
u16 p5; /* Fifth parameter is an unsigned 16-bit integer */
|
||||
int p1; /* First operand */
|
||||
int p2; /* Second parameter (often the jump destination) */
|
||||
int p3; /* The third parameter */
|
||||
@@ -194,7 +193,7 @@ void sqlite3VdbeChangeOpcode(Vdbe*, u32 addr, u8);
|
||||
void sqlite3VdbeChangeP1(Vdbe*, u32 addr, int P1);
|
||||
void sqlite3VdbeChangeP2(Vdbe*, u32 addr, int P2);
|
||||
void sqlite3VdbeChangeP3(Vdbe*, u32 addr, int P3);
|
||||
void sqlite3VdbeChangeP5(Vdbe*, u8 P5);
|
||||
void sqlite3VdbeChangeP5(Vdbe*, u16 P5);
|
||||
void sqlite3VdbeJumpHere(Vdbe*, int addr);
|
||||
int sqlite3VdbeChangeToNoop(Vdbe*, int addr);
|
||||
int sqlite3VdbeDeletePriorOpcode(Vdbe*, u8 op);
|
||||
|
||||
@@ -434,6 +434,7 @@ struct PreUpdate {
|
||||
i64 iKey2; /* Second key value passed to hook */
|
||||
Mem *aNew; /* Array of new.* values */
|
||||
Table *pTab; /* Schema object being upated */
|
||||
Index *pPk; /* PK index if pTab is WITHOUT ROWID */
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
+18
-16
@@ -1660,6 +1660,7 @@ static UnpackedRecord *vdbeUnpackRecord(
|
||||
*/
|
||||
int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
|
||||
PreUpdate *p = db->pPreUpdate;
|
||||
Mem *pMem;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
/* Test that this call is being made from within an SQLITE_DELETE or
|
||||
@@ -1668,6 +1669,9 @@ int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
|
||||
rc = SQLITE_MISUSE_BKPT;
|
||||
goto preupdate_old_out;
|
||||
}
|
||||
if( p->pPk ){
|
||||
iIdx = sqlite3ColumnOfIndex(p->pPk, iIdx);
|
||||
}
|
||||
if( iIdx>=p->pCsr->nField || iIdx<0 ){
|
||||
rc = SQLITE_RANGE;
|
||||
goto preupdate_old_out;
|
||||
@@ -1693,17 +1697,14 @@ int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
|
||||
p->aRecord = aRec;
|
||||
}
|
||||
|
||||
if( iIdx>=p->pUnpacked->nField ){
|
||||
pMem = *ppValue = &p->pUnpacked->aMem[iIdx];
|
||||
if( iIdx==p->pTab->iPKey ){
|
||||
sqlite3VdbeMemSetInt64(pMem, p->iKey1);
|
||||
}else if( iIdx>=p->pUnpacked->nField ){
|
||||
*ppValue = (sqlite3_value *)columnNullValue();
|
||||
}else{
|
||||
Mem *pMem = *ppValue = &p->pUnpacked->aMem[iIdx];
|
||||
*ppValue = &p->pUnpacked->aMem[iIdx];
|
||||
if( iIdx==p->pTab->iPKey ){
|
||||
sqlite3VdbeMemSetInt64(pMem, p->iKey1);
|
||||
}else if( p->pTab->aCol[iIdx].affinity==SQLITE_AFF_REAL ){
|
||||
if( pMem->flags & MEM_Int ){
|
||||
sqlite3VdbeMemRealify(pMem);
|
||||
}
|
||||
}else if( p->pTab->aCol[iIdx].affinity==SQLITE_AFF_REAL ){
|
||||
if( pMem->flags & MEM_Int ){
|
||||
sqlite3VdbeMemRealify(pMem);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1756,6 +1757,9 @@ int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
|
||||
rc = SQLITE_MISUSE_BKPT;
|
||||
goto preupdate_new_out;
|
||||
}
|
||||
if( p->pPk && p->op!=SQLITE_UPDATE ){
|
||||
iIdx = sqlite3ColumnOfIndex(p->pPk, iIdx);
|
||||
}
|
||||
if( iIdx>=p->pCsr->nField || iIdx<0 ){
|
||||
rc = SQLITE_RANGE;
|
||||
goto preupdate_new_out;
|
||||
@@ -1776,13 +1780,11 @@ int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
|
||||
}
|
||||
p->pNewUnpacked = pUnpack;
|
||||
}
|
||||
if( iIdx>=pUnpack->nField ){
|
||||
pMem = &pUnpack->aMem[iIdx];
|
||||
if( iIdx==p->pTab->iPKey ){
|
||||
sqlite3VdbeMemSetInt64(pMem, p->iKey2);
|
||||
}else if( iIdx>=pUnpack->nField ){
|
||||
pMem = (sqlite3_value *)columnNullValue();
|
||||
}else{
|
||||
pMem = &pUnpack->aMem[iIdx];
|
||||
if( iIdx==p->pTab->iPKey ){
|
||||
sqlite3VdbeMemSetInt64(pMem, p->iKey2);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
/* For an UPDATE, memory cell (p->iNewReg+1+iIdx) contains the required
|
||||
|
||||
+51
-47
@@ -783,7 +783,7 @@ void sqlite3VdbeChangeP2(Vdbe *p, u32 addr, int val){
|
||||
void sqlite3VdbeChangeP3(Vdbe *p, u32 addr, int val){
|
||||
sqlite3VdbeGetOp(p,addr)->p3 = val;
|
||||
}
|
||||
void sqlite3VdbeChangeP5(Vdbe *p, u8 p5){
|
||||
void sqlite3VdbeChangeP5(Vdbe *p, u16 p5){
|
||||
assert( p->nOp>0 || p->db->mallocFailed );
|
||||
if( p->nOp>0 ) p->aOp[p->nOp-1].p5 = p5;
|
||||
}
|
||||
@@ -2497,60 +2497,59 @@ static void checkActiveVdbeCnt(sqlite3 *db){
|
||||
** If an IO error occurs, an SQLITE_IOERR_XXX error code is returned.
|
||||
** Otherwise SQLITE_OK.
|
||||
*/
|
||||
int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){
|
||||
static SQLITE_NOINLINE int vdbeCloseStatement(Vdbe *p, int eOp){
|
||||
sqlite3 *const db = p->db;
|
||||
int rc = SQLITE_OK;
|
||||
int i;
|
||||
const int iSavepoint = p->iStatement-1;
|
||||
|
||||
/* If p->iStatement is greater than zero, then this Vdbe opened a
|
||||
** statement transaction that should be closed here. The only exception
|
||||
** is that an IO error may have occurred, causing an emergency rollback.
|
||||
** In this case (db->nStatement==0), and there is nothing to do.
|
||||
*/
|
||||
if( db->nStatement && p->iStatement ){
|
||||
int i;
|
||||
const int iSavepoint = p->iStatement-1;
|
||||
assert( eOp==SAVEPOINT_ROLLBACK || eOp==SAVEPOINT_RELEASE);
|
||||
assert( db->nStatement>0 );
|
||||
assert( p->iStatement==(db->nStatement+db->nSavepoint) );
|
||||
|
||||
assert( eOp==SAVEPOINT_ROLLBACK || eOp==SAVEPOINT_RELEASE);
|
||||
assert( db->nStatement>0 );
|
||||
assert( p->iStatement==(db->nStatement+db->nSavepoint) );
|
||||
|
||||
for(i=0; i<db->nDb; i++){
|
||||
int rc2 = SQLITE_OK;
|
||||
Btree *pBt = db->aDb[i].pBt;
|
||||
if( pBt ){
|
||||
if( eOp==SAVEPOINT_ROLLBACK ){
|
||||
rc2 = sqlite3BtreeSavepoint(pBt, SAVEPOINT_ROLLBACK, iSavepoint);
|
||||
}
|
||||
if( rc2==SQLITE_OK ){
|
||||
rc2 = sqlite3BtreeSavepoint(pBt, SAVEPOINT_RELEASE, iSavepoint);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = rc2;
|
||||
}
|
||||
}
|
||||
}
|
||||
db->nStatement--;
|
||||
p->iStatement = 0;
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
for(i=0; i<db->nDb; i++){
|
||||
int rc2 = SQLITE_OK;
|
||||
Btree *pBt = db->aDb[i].pBt;
|
||||
if( pBt ){
|
||||
if( eOp==SAVEPOINT_ROLLBACK ){
|
||||
rc = sqlite3VtabSavepoint(db, SAVEPOINT_ROLLBACK, iSavepoint);
|
||||
rc2 = sqlite3BtreeSavepoint(pBt, SAVEPOINT_ROLLBACK, iSavepoint);
|
||||
}
|
||||
if( rc2==SQLITE_OK ){
|
||||
rc2 = sqlite3BtreeSavepoint(pBt, SAVEPOINT_RELEASE, iSavepoint);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3VtabSavepoint(db, SAVEPOINT_RELEASE, iSavepoint);
|
||||
rc = rc2;
|
||||
}
|
||||
}
|
||||
}
|
||||
db->nStatement--;
|
||||
p->iStatement = 0;
|
||||
|
||||
/* If the statement transaction is being rolled back, also restore the
|
||||
** database handles deferred constraint counter to the value it had when
|
||||
** the statement transaction was opened. */
|
||||
if( rc==SQLITE_OK ){
|
||||
if( eOp==SAVEPOINT_ROLLBACK ){
|
||||
db->nDeferredCons = p->nStmtDefCons;
|
||||
db->nDeferredImmCons = p->nStmtDefImmCons;
|
||||
rc = sqlite3VtabSavepoint(db, SAVEPOINT_ROLLBACK, iSavepoint);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3VtabSavepoint(db, SAVEPOINT_RELEASE, iSavepoint);
|
||||
}
|
||||
}
|
||||
|
||||
/* If the statement transaction is being rolled back, also restore the
|
||||
** database handles deferred constraint counter to the value it had when
|
||||
** the statement transaction was opened. */
|
||||
if( eOp==SAVEPOINT_ROLLBACK ){
|
||||
db->nDeferredCons = p->nStmtDefCons;
|
||||
db->nDeferredImmCons = p->nStmtDefImmCons;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){
|
||||
if( p->db->nStatement && p->iStatement ){
|
||||
return vdbeCloseStatement(p, eOp);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This function is called when a transaction opened by the database
|
||||
@@ -4585,10 +4584,10 @@ void sqlite3VtabImportErrmsg(Vdbe *p, sqlite3_vtab *pVtab){
|
||||
** This function is used to free UnpackedRecord structures allocated by
|
||||
** the vdbeUnpackRecord() function found in vdbeapi.c.
|
||||
*/
|
||||
static void vdbeFreeUnpacked(sqlite3 *db, UnpackedRecord *p){
|
||||
static void vdbeFreeUnpacked(sqlite3 *db, int nField, UnpackedRecord *p){
|
||||
if( p ){
|
||||
int i;
|
||||
for(i=0; i<p->nField; i++){
|
||||
for(i=0; i<nField; i++){
|
||||
Mem *pMem = &p->aMem[i];
|
||||
if( pMem->zMalloc ) sqlite3VdbeMemRelease(pMem);
|
||||
}
|
||||
@@ -4621,10 +4620,15 @@ void sqlite3VdbePreUpdateHook(
|
||||
|
||||
assert( db->pPreUpdate==0 );
|
||||
memset(&preupdate, 0, sizeof(PreUpdate));
|
||||
if( op==SQLITE_UPDATE ){
|
||||
iKey2 = v->aMem[iReg].u.i;
|
||||
if( HasRowid(pTab)==0 ){
|
||||
iKey1 = iKey2 = 0;
|
||||
preupdate.pPk = sqlite3PrimaryKeyIndex(pTab);
|
||||
}else{
|
||||
iKey2 = iKey1;
|
||||
if( op==SQLITE_UPDATE ){
|
||||
iKey2 = v->aMem[iReg].u.i;
|
||||
}else{
|
||||
iKey2 = iKey1;
|
||||
}
|
||||
}
|
||||
|
||||
assert( pCsr->nField==pTab->nCol
|
||||
@@ -4647,8 +4651,8 @@ void sqlite3VdbePreUpdateHook(
|
||||
db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2);
|
||||
db->pPreUpdate = 0;
|
||||
sqlite3DbFree(db, preupdate.aRecord);
|
||||
vdbeFreeUnpacked(db, preupdate.pUnpacked);
|
||||
vdbeFreeUnpacked(db, preupdate.pNewUnpacked);
|
||||
vdbeFreeUnpacked(db, preupdate.keyinfo.nField+1, preupdate.pUnpacked);
|
||||
vdbeFreeUnpacked(db, preupdate.keyinfo.nField+1, preupdate.pNewUnpacked);
|
||||
if( preupdate.aNew ){
|
||||
int i;
|
||||
for(i=0; i<pCsr->nField; i++){
|
||||
|
||||
+32
-27
@@ -23,10 +23,9 @@
|
||||
*/
|
||||
typedef struct Incrblob Incrblob;
|
||||
struct Incrblob {
|
||||
int flags; /* Copy of "flags" passed to sqlite3_blob_open() */
|
||||
int nByte; /* Size of open blob, in bytes */
|
||||
int iOffset; /* Byte offset of blob in cursor data */
|
||||
int iCol; /* Table column this handle is open on */
|
||||
u16 iCol; /* Table column this handle is open on */
|
||||
BtCursor *pCsr; /* Cursor pointing at blob row */
|
||||
sqlite3_stmt *pStmt; /* Statement holding cursor open */
|
||||
sqlite3 *db; /* The associated database */
|
||||
@@ -57,17 +56,27 @@ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){
|
||||
char *zErr = 0; /* Error message */
|
||||
Vdbe *v = (Vdbe *)p->pStmt;
|
||||
|
||||
/* Set the value of the SQL statements only variable to integer iRow.
|
||||
** This is done directly instead of using sqlite3_bind_int64() to avoid
|
||||
** triggering asserts related to mutexes.
|
||||
/* Set the value of register r[1] in the SQL statement to integer iRow.
|
||||
** This is done directly as a performance optimization
|
||||
*/
|
||||
assert( v->aVar[0].flags&MEM_Int );
|
||||
v->aVar[0].u.i = iRow;
|
||||
v->aMem[1].flags = MEM_Int;
|
||||
v->aMem[1].u.i = iRow;
|
||||
|
||||
rc = sqlite3_step(p->pStmt);
|
||||
/* If the statement has been run before (and is paused at the OP_ResultRow)
|
||||
** then back it up to the point where it does the OP_SeekRowid. This could
|
||||
** have been down with an extra OP_Goto, but simply setting the program
|
||||
** counter is faster. */
|
||||
if( v->pc>3 ){
|
||||
v->pc = 3;
|
||||
rc = sqlite3VdbeExec(v);
|
||||
}else{
|
||||
rc = sqlite3_step(p->pStmt);
|
||||
}
|
||||
if( rc==SQLITE_ROW ){
|
||||
VdbeCursor *pC = v->apCsr[0];
|
||||
u32 type = pC->aType[p->iCol];
|
||||
u32 type = pC->nHdrParsed>p->iCol ? pC->aType[p->iCol] : 0;
|
||||
testcase( pC->nHdrParsed==p->iCol );
|
||||
testcase( pC->nHdrParsed==p->iCol+1 );
|
||||
if( type<12 ){
|
||||
zErr = sqlite3MPrintf(p->db, "cannot open value of type %s",
|
||||
type==0?"null": type==7?"real": "integer"
|
||||
@@ -112,7 +121,7 @@ int sqlite3_blob_open(
|
||||
const char *zTable, /* The table containing the blob */
|
||||
const char *zColumn, /* The column containing the blob */
|
||||
sqlite_int64 iRow, /* The row containing the glob */
|
||||
int flags, /* True -> read/write access, false -> read-only */
|
||||
int wrFlag, /* True -> read/write access, false -> read-only */
|
||||
sqlite3_blob **ppBlob /* Handle for accessing the blob returned here */
|
||||
){
|
||||
int nAttempt = 0;
|
||||
@@ -134,7 +143,7 @@ int sqlite3_blob_open(
|
||||
return SQLITE_MISUSE_BKPT;
|
||||
}
|
||||
#endif
|
||||
flags = !!flags; /* flags = (flags ? 1 : 0); */
|
||||
wrFlag = !!wrFlag; /* wrFlag = (wrFlag ? 1 : 0); */
|
||||
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
|
||||
@@ -194,9 +203,8 @@ int sqlite3_blob_open(
|
||||
|
||||
/* If the value is being opened for writing, check that the
|
||||
** column is not indexed, and that it is not part of a foreign key.
|
||||
** It is against the rules to open a column to which either of these
|
||||
** descriptions applies for writing. */
|
||||
if( flags ){
|
||||
*/
|
||||
if( wrFlag ){
|
||||
const char *zFault = 0;
|
||||
Index *pIdx;
|
||||
#ifndef SQLITE_OMIT_FOREIGN_KEY
|
||||
@@ -257,18 +265,17 @@ int sqlite3_blob_open(
|
||||
static const VdbeOpList openBlob[] = {
|
||||
{OP_TableLock, 0, 0, 0}, /* 0: Acquire a read or write lock */
|
||||
{OP_OpenRead, 0, 0, 0}, /* 1: Open a cursor */
|
||||
{OP_Variable, 1, 1, 0}, /* 2: Move ?1 into reg[1] */
|
||||
{OP_NotExists, 0, 7, 1}, /* 3: Seek the cursor */
|
||||
{OP_Column, 0, 0, 1}, /* 4 */
|
||||
{OP_ResultRow, 1, 0, 0}, /* 5 */
|
||||
{OP_Goto, 0, 2, 0}, /* 6 */
|
||||
{OP_Halt, 0, 0, 0}, /* 7 */
|
||||
/* blobSeekToRow() will initialize r[1] to the desired rowid */
|
||||
{OP_NotExists, 0, 5, 1}, /* 2: Seek the cursor to rowid=r[1] */
|
||||
{OP_Column, 0, 0, 1}, /* 3 */
|
||||
{OP_ResultRow, 1, 0, 0}, /* 4 */
|
||||
{OP_Halt, 0, 0, 0}, /* 5 */
|
||||
};
|
||||
Vdbe *v = (Vdbe *)pBlob->pStmt;
|
||||
int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
|
||||
VdbeOp *aOp;
|
||||
|
||||
sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, flags,
|
||||
sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, wrFlag,
|
||||
pTab->pSchema->schema_cookie,
|
||||
pTab->pSchema->iGeneration);
|
||||
sqlite3VdbeChangeP5(v, 1);
|
||||
@@ -285,7 +292,7 @@ int sqlite3_blob_open(
|
||||
#else
|
||||
aOp[0].p1 = iDb;
|
||||
aOp[0].p2 = pTab->tnum;
|
||||
aOp[0].p3 = flags;
|
||||
aOp[0].p3 = wrFlag;
|
||||
sqlite3VdbeChangeP4(v, 1, pTab->zName, P4_TRANSIENT);
|
||||
}
|
||||
if( db->mallocFailed==0 ){
|
||||
@@ -293,7 +300,7 @@ int sqlite3_blob_open(
|
||||
|
||||
/* Remove either the OP_OpenWrite or OpenRead. Set the P2
|
||||
** parameter of the other to pTab->tnum. */
|
||||
if( flags ) aOp[1].opcode = OP_OpenWrite;
|
||||
if( wrFlag ) aOp[1].opcode = OP_OpenWrite;
|
||||
aOp[1].p2 = pTab->tnum;
|
||||
aOp[1].p3 = iDb;
|
||||
|
||||
@@ -306,23 +313,21 @@ int sqlite3_blob_open(
|
||||
*/
|
||||
aOp[1].p4type = P4_INT32;
|
||||
aOp[1].p4.i = pTab->nCol+1;
|
||||
aOp[4].p2 = pTab->nCol;
|
||||
aOp[3].p2 = pTab->nCol;
|
||||
|
||||
pParse->nVar = 1;
|
||||
pParse->nVar = 0;
|
||||
pParse->nMem = 1;
|
||||
pParse->nTab = 1;
|
||||
sqlite3VdbeMakeReady(v, pParse);
|
||||
}
|
||||
}
|
||||
|
||||
pBlob->flags = flags;
|
||||
pBlob->iCol = iCol;
|
||||
pBlob->db = db;
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
if( db->mallocFailed ){
|
||||
goto blob_open_out;
|
||||
}
|
||||
sqlite3_bind_int64(pBlob->pStmt, 1, iRow);
|
||||
rc = blobSeekToRow(pBlob, iRow, &zErr);
|
||||
} while( (++nAttempt)<SQLITE_MAX_SCHEMA_RETRY && rc==SQLITE_SCHEMA );
|
||||
|
||||
|
||||
+2
-1
@@ -4949,7 +4949,8 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
|
||||
pOp->p2 = x;
|
||||
pOp->p1 = pLevel->iIdxCur;
|
||||
}
|
||||
assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 || x>=0 );
|
||||
assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 || x>=0
|
||||
|| pWInfo->eOnePass );
|
||||
}else if( pOp->opcode==OP_Rowid ){
|
||||
pOp->p1 = pLevel->iIdxCur;
|
||||
pOp->opcode = OP_IdxRowid;
|
||||
|
||||
+4
-1
@@ -1591,7 +1591,10 @@ Bitmask sqlite3WhereCodeOneLoopStart(
|
||||
if( omitTable ){
|
||||
/* pIdx is a covering index. No need to access the main table. */
|
||||
}else if( HasRowid(pIdx->pTable) ){
|
||||
if( (pWInfo->wctrlFlags & WHERE_SEEK_TABLE)!=0 ){
|
||||
if( (pWInfo->wctrlFlags & WHERE_SEEK_TABLE) || (
|
||||
(pWInfo->wctrlFlags & WHERE_SEEK_UNIQ_TABLE)
|
||||
&& (pWInfo->eOnePass==ONEPASS_SINGLE)
|
||||
)){
|
||||
iRowidReg = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, iRowidReg);
|
||||
sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
|
||||
|
||||
+9
-5
@@ -913,6 +913,7 @@ static void exprAnalyze(
|
||||
Parse *pParse = pWInfo->pParse; /* Parsing context */
|
||||
sqlite3 *db = pParse->db; /* Database connection */
|
||||
unsigned char eOp2; /* op2 value for LIKE/REGEXP/GLOB */
|
||||
int nLeft; /* Number of elements on left side vector */
|
||||
|
||||
if( db->mallocFailed ){
|
||||
return;
|
||||
@@ -942,6 +943,10 @@ static void exprAnalyze(
|
||||
prereqAll |= x;
|
||||
extraRight = x-1; /* ON clause terms may not be used with an index
|
||||
** on left table of a LEFT JOIN. Ticket #3015 */
|
||||
if( (prereqAll>>1)>=x ){
|
||||
sqlite3ErrorMsg(pParse, "ON clause references tables to its right");
|
||||
return;
|
||||
}
|
||||
}
|
||||
pTerm->prereqAll = prereqAll;
|
||||
pTerm->leftCursor = -1;
|
||||
@@ -1184,13 +1189,12 @@ static void exprAnalyze(
|
||||
** is not a sub-select. */
|
||||
if( pWC->op==TK_AND
|
||||
&& (pExpr->op==TK_EQ || pExpr->op==TK_IS)
|
||||
&& sqlite3ExprIsVector(pExpr->pLeft)
|
||||
&& (nLeft = sqlite3ExprVectorSize(pExpr->pLeft))>1
|
||||
&& sqlite3ExprVectorSize(pExpr->pRight)==nLeft
|
||||
&& ( (pExpr->pLeft->flags & EP_xIsSelect)==0
|
||||
|| (pExpr->pRight->flags & EP_xIsSelect)==0
|
||||
)){
|
||||
int nLeft = sqlite3ExprVectorSize(pExpr->pLeft);
|
||||
|| (pExpr->pRight->flags & EP_xIsSelect)==0)
|
||||
){
|
||||
int i;
|
||||
assert( nLeft==sqlite3ExprVectorSize(pExpr->pRight) );
|
||||
for(i=0; i<nLeft; i++){
|
||||
int idxNew;
|
||||
Expr *pNew;
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
# 2017-01-16
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
# Test cases for bugs:
|
||||
#
|
||||
# https://www.sqlite.org/src/info/91e2e8ba6ff2e2
|
||||
# https://www.sqlite.org/src/info/7ffd1ca1d2ad4ecf
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
|
||||
# Ticket https://www.sqlite.org/src/info/91e2e8ba6ff2e2 (2011-09-19)
|
||||
# Automatic index causes undesired type conversions
|
||||
#
|
||||
do_execsql_test affinity3-100 {
|
||||
CREATE TABLE customer (id INT PRIMARY KEY);
|
||||
CREATE TABLE apr (id INT PRIMARY KEY, apr REAL);
|
||||
|
||||
CREATE VIEW v1 AS
|
||||
SELECT c.id, i.apr
|
||||
FROM customer c
|
||||
LEFT JOIN apr i ON i.id=c.id;
|
||||
|
||||
CREATE VIEW v2 AS
|
||||
SELECT c.id, v1.apr
|
||||
FROM customer c
|
||||
LEFT JOIN v1 ON v1.id=c.id;
|
||||
|
||||
INSERT INTO customer (id) VALUES (1);
|
||||
INSERT INTO apr (id, apr) VALUES (1, 12);
|
||||
INSERT INTO customer (id) VALUES (2);
|
||||
INSERT INTO apr (id, apr) VALUES (2, 12.01);
|
||||
}
|
||||
do_execsql_test affinity3-110 {
|
||||
PRAGMA automatic_index=ON;
|
||||
SELECT id, (apr / 100), typeof(apr) apr_type FROM v1;
|
||||
} {1 0.12 real 2 0.1201 real}
|
||||
do_execsql_test affinity3-120 {
|
||||
SELECT id, (apr / 100), typeof(apr) apr_type FROM v2;
|
||||
} {1 0.12 real 2 0.1201 real}
|
||||
do_execsql_test affinity3-130 {
|
||||
PRAGMA automatic_index=OFF;
|
||||
SELECT id, (apr / 100), typeof(apr) apr_type FROM v1;
|
||||
} {1 0.12 real 2 0.1201 real}
|
||||
do_execsql_test affinity3-140 {
|
||||
SELECT id, (apr / 100), typeof(apr) apr_type FROM v2;
|
||||
} {1 0.12 real 2 0.1201 real}
|
||||
|
||||
# Ticket https://www.sqlite.org/src/info/7ffd1ca1d2ad4ecf (2017-01-16)
|
||||
# Incorrect affinity when using automatic indexes
|
||||
#
|
||||
do_execsql_test affinity3-200 {
|
||||
CREATE TABLE map_integer (id INT, name);
|
||||
INSERT INTO map_integer VALUES(1,'a');
|
||||
CREATE TABLE map_text (id TEXT, name);
|
||||
INSERT INTO map_text VALUES('4','e');
|
||||
CREATE TABLE data (id TEXT, name);
|
||||
INSERT INTO data VALUES(1,'abc');
|
||||
INSERT INTO data VALUES('4','xyz');
|
||||
CREATE VIEW idmap as
|
||||
SELECT * FROM map_integer
|
||||
UNION SELECT * FROM map_text;
|
||||
CREATE TABLE mzed AS SELECT * FROM idmap;
|
||||
}
|
||||
|
||||
do_execsql_test affinity3-210 {
|
||||
PRAGMA automatic_index=ON;
|
||||
SELECT * FROM data JOIN idmap USING(id);
|
||||
} {1 abc a 4 xyz e}
|
||||
do_execsql_test affinity3-220 {
|
||||
SELECT * FROM data JOIN mzed USING(id);
|
||||
} {1 abc a 4 xyz e}
|
||||
|
||||
do_execsql_test affinity3-250 {
|
||||
PRAGMA automatic_index=OFF;
|
||||
SELECT * FROM data JOIN idmap USING(id);
|
||||
} {1 abc a 4 xyz e}
|
||||
do_execsql_test affinity3-260 {
|
||||
SELECT * FROM data JOIN mzed USING(id);
|
||||
} {1 abc a 4 xyz e}
|
||||
|
||||
finish_test
|
||||
@@ -91,10 +91,12 @@ do_extract_hints_test 1.6 {
|
||||
t2 {EQ(r[2],c0)} t3 {EQ(r[6],c1)}
|
||||
}
|
||||
|
||||
do_extract_hints_test 1.7 {
|
||||
SELECT * FROM t1 LEFT JOIN t2 ON (a=c AND d=e) LEFT JOIN t3 ON (d=f);
|
||||
} {
|
||||
t2 {EQ(r[2],c0)} t3 {AND(EQ(r[6],c0),EQ(r[7],c1))}
|
||||
if 0 {
|
||||
do_extract_hints_test 1.7 {
|
||||
SELECT * FROM t1 LEFT JOIN t2 ON (a=c AND d=e) LEFT JOIN t3 ON (d=f);
|
||||
} {
|
||||
t2 {EQ(r[2],c0)} t3 {AND(EQ(r[6],c0),EQ(r[7],c1))}
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@@ -121,13 +121,13 @@ do_test exclusive2-1.1 {
|
||||
execsql {
|
||||
BEGIN;
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1(a) VALUES(randstr(10, 400));
|
||||
INSERT INTO t1(a) VALUES(randstr(10, 400));
|
||||
INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
|
||||
INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
|
||||
INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
|
||||
INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
|
||||
INSERT INTO t1(a) SELECT randstr(10, 400) FROM t1;
|
||||
INSERT INTO t1(a, b) VALUES(randstr(10, 400), 0);
|
||||
INSERT INTO t1(a, b) VALUES(randstr(10, 400), 0);
|
||||
INSERT INTO t1(a, b) SELECT randstr(10, 400), 0 FROM t1;
|
||||
INSERT INTO t1(a, b) SELECT randstr(10, 400), 0 FROM t1;
|
||||
INSERT INTO t1(a, b) SELECT randstr(10, 400), 0 FROM t1;
|
||||
INSERT INTO t1(a, b) SELECT randstr(10, 400), 0 FROM t1;
|
||||
INSERT INTO t1(a, b) SELECT randstr(10, 400), 0 FROM t1;
|
||||
COMMIT;
|
||||
SELECT count(*) FROM t1;
|
||||
}
|
||||
@@ -154,7 +154,7 @@ do_test exclusive2-1.4 {
|
||||
} $::sig
|
||||
do_test exclusive2-1.5 {
|
||||
execsql {
|
||||
UPDATE t1 SET b=a, a=NULL;
|
||||
UPDATE t1 SET b=a, a=0;
|
||||
} db2
|
||||
expr {[t1sig db2] eq $::sig}
|
||||
} 0
|
||||
|
||||
+56
-3
@@ -849,9 +849,62 @@ do_preupdate_test 7.6.4 {
|
||||
}
|
||||
|
||||
# No preupdate callbacks for modifying sqlite_master.
|
||||
do_preupdate_test 8.1 {
|
||||
CREATE TABLE x1(x, y);
|
||||
} {
|
||||
do_preupdate_test 8.1 { CREATE TABLE x1(x, y); } { }
|
||||
do_preupdate_test 8.2 { ALTER TABLE x1 ADD COLUMN z } { }
|
||||
do_preupdate_test 8.3 { ALTER TABLE x1 RENAME TO y1 } { }
|
||||
do_preupdate_test 8.4 { CREATE INDEX y1x ON y1(x) } { }
|
||||
do_preupdate_test 8.5 { CREATE VIEW v1 AS SELECT * FROM y1 } { }
|
||||
do_preupdate_test 8.6 { DROP TABLE y1 } { }
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
reset_db
|
||||
db preupdate hook preupdate_hook
|
||||
do_execsql_test 9.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
|
||||
CREATE TABLE t2(a, b INTEGER PRIMARY KEY);
|
||||
}
|
||||
do_preupdate_test 9.1 {
|
||||
INSERT INTO t1 VALUES(456, NULL, NULL);
|
||||
} {
|
||||
INSERT main t1 456 456 0 456 {} {}
|
||||
}
|
||||
do_execsql_test 9.2 {
|
||||
ALTER TABLE t1 ADD COLUMN d;
|
||||
}
|
||||
do_preupdate_test 9.3 {
|
||||
INSERT INTO t1(a, b, c) VALUES(457, NULL, NULL);
|
||||
} {
|
||||
INSERT main t1 457 457 0 457 {} {} {}
|
||||
}
|
||||
do_preupdate_test 9.4 {
|
||||
DELETE FROM t1 WHERE a=456
|
||||
} {
|
||||
DELETE main t1 456 456 0 456 {} {} {}
|
||||
}
|
||||
do_preupdate_test 9.5 {
|
||||
INSERT INTO t2 DEFAULT VALUES;
|
||||
} {
|
||||
INSERT main t2 1 1 0 {} 1
|
||||
}
|
||||
do_preupdate_test 9.6 {
|
||||
INSERT INTO t1 DEFAULT VALUES;
|
||||
} {
|
||||
INSERT main t1 458 458 0 458 {} {} {}
|
||||
}
|
||||
|
||||
|
||||
do_execsql_test 10.0 {
|
||||
CREATE TABLE t3(a, b INTEGER PRIMARY KEY);
|
||||
}
|
||||
do_preupdate_test 10.1 {
|
||||
INSERT INTO t3 DEFAULT VALUES
|
||||
} {
|
||||
INSERT main t3 1 1 0 {} 1
|
||||
}
|
||||
do_execsql_test 10.2 { SELECT * FROM t3 } {{} 1}
|
||||
do_preupdate_test 10.3 {
|
||||
DELETE FROM t3 WHERE b=1
|
||||
} {DELETE main t3 1 1 0 {} 1}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
# 2017 Jan 30
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
# The tests in this file focus on the pre-update hook.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set ::testprefix hook2
|
||||
|
||||
ifcapable !preupdate {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
proc do_preupdate_test {tn sql x} {
|
||||
set X [list]
|
||||
foreach elem $x {lappend X $elem}
|
||||
uplevel do_test $tn [list "
|
||||
set ::preupdate \[list\]
|
||||
execsql { $sql }
|
||||
set ::preupdate
|
||||
"] [list $X]
|
||||
}
|
||||
|
||||
proc preupdate_hook {args} {
|
||||
set type [lindex $args 0]
|
||||
eval lappend ::preupdate $args
|
||||
if {$type != "INSERT"} {
|
||||
for {set i 0} {$i < [db preupdate count]} {incr i} {
|
||||
lappend ::preupdate [db preupdate old $i]
|
||||
}
|
||||
}
|
||||
if {$type != "DELETE"} {
|
||||
for {set i 0} {$i < [db preupdate count]} {incr i} {
|
||||
set rc [catch { db preupdate new $i } v]
|
||||
lappend ::preupdate $v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Simple tests - INSERT, UPDATE and DELETE on a WITHOUT ROWID table.
|
||||
#
|
||||
db preupdate hook preupdate_hook
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a PRIMARY KEY, b) WITHOUT ROWID;
|
||||
}
|
||||
do_preupdate_test 1.1 {
|
||||
INSERT INTO t1 VALUES('one', 1);
|
||||
} {
|
||||
INSERT main t1 0 0 one 1
|
||||
}
|
||||
do_preupdate_test 1.2 {
|
||||
UPDATE t1 SET b=2 WHERE a='one';
|
||||
} {
|
||||
UPDATE main t1 0 0 one 1 one 2
|
||||
}
|
||||
do_preupdate_test 1.3 {
|
||||
DELETE FROM t1 WHERE a='one';
|
||||
} {
|
||||
DELETE main t1 0 0 one 2
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Some more complex tests for the pre-update callback on WITHOUT ROWID
|
||||
# tables.
|
||||
#
|
||||
# 2.1.1 - INSERT statement.
|
||||
# 2.1.2 - INSERT INTO ... SELECT statement.
|
||||
# 2.1.3 - REPLACE INTO ... (PK conflict)
|
||||
# 2.1.4 - REPLACE INTO ... (other index conflicts)
|
||||
# 2.1.5 - REPLACE INTO ... (both PK and other index conflicts)
|
||||
#
|
||||
# 2.2.1 - DELETE statement.
|
||||
# 2.2.2 - DELETE statement that uses the truncate optimization.
|
||||
#
|
||||
# 2.3.1 - UPDATE statement.
|
||||
# 2.3.2 - UPDATE statement that modifies the PK.
|
||||
# 2.3.3 - UPDATE OR REPLACE ... (PK conflict).
|
||||
# 2.3.4 - UPDATE OR REPLACE ... (other index conflicts)
|
||||
# 2.3.4 - UPDATE OR REPLACE ... (both PK and other index conflicts)
|
||||
#
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE t2(a DEFAULT 4, b, c, PRIMARY KEY(b, c)) WITHOUT ROWID;
|
||||
CREATE UNIQUE INDEX t2a ON t2(a);
|
||||
}
|
||||
|
||||
do_preupdate_test 2.1.1 {
|
||||
INSERT INTO t2(b, c) VALUES(1, 1);
|
||||
} {
|
||||
INSERT main t2 0 0 4 1 1
|
||||
}
|
||||
|
||||
do_execsql_test 2.1.2.0 {
|
||||
CREATE TABLE d1(a DEFAULT 4, b, c, PRIMARY KEY(b, c)) WITHOUT ROWID;
|
||||
CREATE UNIQUE INDEX d1a ON d1(a);
|
||||
INSERT INTO d1 VALUES(1, 2, 3);
|
||||
INSERT INTO d1 VALUES(11, 12, 13);
|
||||
}
|
||||
do_preupdate_test 2.1.2.1 {
|
||||
INSERT INTO t2 SELECT * FROM d1;
|
||||
} {
|
||||
INSERT main t2 0 0 1 2 3
|
||||
INSERT main t2 0 0 11 12 13
|
||||
}
|
||||
do_preupdate_test 2.1.2.2 {
|
||||
INSERT INTO t2 SELECT a+20, b+20, c+20 FROM d1;
|
||||
} {
|
||||
INSERT main t2 0 0 21 22 23
|
||||
INSERT main t2 0 0 31 32 33
|
||||
}
|
||||
do_execsql_test 2.1.2.3 {
|
||||
SELECT * FROM t2 ORDER BY b, c;
|
||||
} {
|
||||
4 1 1
|
||||
1 2 3
|
||||
11 12 13
|
||||
21 22 23
|
||||
31 32 33
|
||||
}
|
||||
do_preupdate_test 2.1.3 {
|
||||
REPLACE INTO t2 VALUES(45, 22, 23);
|
||||
} {
|
||||
DELETE main t2 0 0 21 22 23
|
||||
INSERT main t2 0 0 45 22 23
|
||||
}
|
||||
do_preupdate_test 2.1.4 {
|
||||
REPLACE INTO t2 VALUES(11, 100, 100);
|
||||
} {
|
||||
DELETE main t2 0 0 11 12 13
|
||||
INSERT main t2 0 0 11 100 100
|
||||
}
|
||||
do_preupdate_test 2.1.5 {
|
||||
REPLACE INTO t2(c, b) VALUES(33, 32)
|
||||
} {
|
||||
DELETE main t2 0 0 4 1 1
|
||||
DELETE main t2 0 0 31 32 33
|
||||
INSERT main t2 0 0 4 32 33
|
||||
}
|
||||
|
||||
do_execsql_test 2.2.0 {
|
||||
SELECT * FROM t2 ORDER BY b,c;
|
||||
} {
|
||||
1 2 3
|
||||
45 22 23
|
||||
4 32 33
|
||||
11 100 100
|
||||
}
|
||||
do_preupdate_test 2.2.1 {
|
||||
DELETE FROM t2 WHERE b=22;
|
||||
} {
|
||||
DELETE main t2 0 0 45 22 23
|
||||
}
|
||||
do_preupdate_test 2.2.2 {
|
||||
DELETE FROM t2;
|
||||
} {
|
||||
DELETE main t2 0 0 1 2 3
|
||||
DELETE main t2 0 0 4 32 33
|
||||
DELETE main t2 0 0 11 100 100
|
||||
}
|
||||
|
||||
do_execsql_test 2.3.0 {
|
||||
CREATE TABLE t3(x, y PRIMARY KEY, z UNIQUE) WITHOUT ROWID;
|
||||
INSERT INTO t3 VALUES('a', 'b', 'c');
|
||||
INSERT INTO t3 VALUES('d', 'e', 'f');
|
||||
|
||||
INSERT INTO t3 VALUES(1, 1, 1);
|
||||
INSERT INTO t3 VALUES(2, 2, 2);
|
||||
INSERT INTO t3 VALUES(3, 3, 3);
|
||||
}
|
||||
|
||||
do_preupdate_test 2.3.1 {
|
||||
UPDATE t3 SET x=4 WHERE y IN ('b', 'e', 'x');
|
||||
} {
|
||||
UPDATE main t3 0 0 a b c 4 b c
|
||||
UPDATE main t3 0 0 d e f 4 e f
|
||||
}
|
||||
|
||||
do_preupdate_test 2.3.2 {
|
||||
UPDATE t3 SET y=y||y WHERE z IN('c', 'f');
|
||||
} {
|
||||
UPDATE main t3 0 0 4 b c 4 bb c
|
||||
UPDATE main t3 0 0 4 e f 4 ee f
|
||||
}
|
||||
|
||||
do_preupdate_test 2.3.3 {
|
||||
UPDATE OR REPLACE t3 SET y='bb' WHERE z='f'
|
||||
} {
|
||||
DELETE main t3 0 0 4 bb c
|
||||
UPDATE main t3 0 0 4 ee f 4 bb f
|
||||
}
|
||||
|
||||
do_preupdate_test 2.3.4 {
|
||||
UPDATE OR REPLACE t3 SET z=2 WHERE y=1;
|
||||
} {
|
||||
DELETE main t3 0 0 2 2 2
|
||||
UPDATE main t3 0 0 1 1 1 1 1 2
|
||||
}
|
||||
|
||||
do_preupdate_test 2.3.5 {
|
||||
UPDATE OR REPLACE t3 SET z=2, y='bb' WHERE y=3;
|
||||
} {
|
||||
DELETE main t3 0 0 1 1 2
|
||||
DELETE main t3 0 0 4 bb f
|
||||
UPDATE main t3 0 0 3 3 3 3 bb 2
|
||||
}
|
||||
|
||||
|
||||
finish_test
|
||||
+21
-1
@@ -12,10 +12,10 @@
|
||||
#
|
||||
# This file implements tests for joins, including outer joins.
|
||||
#
|
||||
# $Id: join2.test,v 1.2 2005/01/21 03:12:16 danielk1977 Exp $
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix join2
|
||||
|
||||
do_test join2-1.1 {
|
||||
execsql {
|
||||
@@ -72,4 +72,24 @@ ifcapable subquery {
|
||||
} {1 11 111 1111 2 22 {} {} 3 33 {} {}}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that ticket [25e335f802ddc] has been resolved. It should be an
|
||||
# error for the ON clause of a LEFT JOIN to refer to a table to its right.
|
||||
#
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE aa(a);
|
||||
CREATE TABLE bb(b);
|
||||
CREATE TABLE cc(c);
|
||||
INSERT INTO aa VALUES('one');
|
||||
INSERT INTO bb VALUES('one');
|
||||
INSERT INTO cc VALUES('one');
|
||||
}
|
||||
|
||||
do_catchsql_test 2.1 {
|
||||
SELECT * FROM aa LEFT JOIN cc ON (a=b) JOIN bb ON (b=c);
|
||||
} {1 {ON clause references tables to its right}}
|
||||
do_catchsql_test 2.2 {
|
||||
SELECT * FROM aa JOIN cc ON (a=b) JOIN bb ON (b=c);
|
||||
} {0 {one one one}}
|
||||
|
||||
finish_test
|
||||
|
||||
+327
-30
@@ -61,32 +61,41 @@
|
||||
** ./kvtest run x1 --count 10000 --max-id 1000000
|
||||
*/
|
||||
static const char zHelp[] =
|
||||
"Usage: kvhelp COMMAND ARGS...\n"
|
||||
"Usage: kvtest COMMAND ARGS...\n"
|
||||
"\n"
|
||||
" kvhelp init DBFILE --count N --size M --pagesize X\n"
|
||||
" kvtest init DBFILE --count N --size M --pagesize X\n"
|
||||
"\n"
|
||||
" Generate a new test database file named DBFILE containing N\n"
|
||||
" BLOBs each of size M bytes. The page size of the new database\n"
|
||||
" file will be X\n"
|
||||
" file will be X. Additional options:\n"
|
||||
"\n"
|
||||
" kvhelp export DBFILE DIRECTORY\n"
|
||||
" --variance V Randomly vary M by plus or minus V\n"
|
||||
"\n"
|
||||
" kvtest export DBFILE DIRECTORY\n"
|
||||
"\n"
|
||||
" Export all the blobs in the kv table of DBFILE into separate\n"
|
||||
" files in DIRECTORY.\n"
|
||||
"\n"
|
||||
" kvhelp run DBFILE [options]\n"
|
||||
" kvtest stat DBFILE\n"
|
||||
"\n"
|
||||
" Display summary information about DBFILE\n"
|
||||
"\n"
|
||||
" kvtest run DBFILE [options]\n"
|
||||
"\n"
|
||||
" Run a performance test. DBFILE can be either the name of a\n"
|
||||
" database or a directory containing sample files. Options:\n"
|
||||
"\n"
|
||||
" --asc Read blobs in ascending order\n"
|
||||
" --blob-api Use the BLOB API\n"
|
||||
" --cache-size N Database cache size\n"
|
||||
" --count N Read N blobs\n"
|
||||
" --desc Read blobs in descending order\n"
|
||||
" --max-id N Maximum blob key to use\n"
|
||||
" --random Read blobs in a random order\n"
|
||||
" --start N Start reading with this blob key\n"
|
||||
" --asc Read blobs in ascending order\n"
|
||||
" --blob-api Use the BLOB API\n"
|
||||
" --cache-size N Database cache size\n"
|
||||
" --count N Read N blobs\n"
|
||||
" --desc Read blobs in descending order\n"
|
||||
" --max-id N Maximum blob key to use\n"
|
||||
" --mmap N Mmap as much as N bytes of DBFILE\n"
|
||||
" --jmode MODE Set MODE journal mode prior to starting\n"
|
||||
" --random Read blobs in a random order\n"
|
||||
" --start N Start reading with this blob key\n"
|
||||
" --stats Output operating stats before exiting\n"
|
||||
;
|
||||
|
||||
/* Reference resources used */
|
||||
@@ -131,6 +140,64 @@ static void fatalError(const char *zFormat, ...){
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the value of a hexadecimal digit. Return -1 if the input
|
||||
** is not a hex digit.
|
||||
*/
|
||||
static int hexDigitValue(char c){
|
||||
if( c>='0' && c<='9' ) return c - '0';
|
||||
if( c>='a' && c<='f' ) return c - 'a' + 10;
|
||||
if( c>='A' && c<='F' ) return c - 'A' + 10;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
** Interpret zArg as an integer value, possibly with suffixes.
|
||||
*/
|
||||
static int integerValue(const char *zArg){
|
||||
int v = 0;
|
||||
static const struct { char *zSuffix; int iMult; } aMult[] = {
|
||||
{ "KiB", 1024 },
|
||||
{ "MiB", 1024*1024 },
|
||||
{ "GiB", 1024*1024*1024 },
|
||||
{ "KB", 1000 },
|
||||
{ "MB", 1000000 },
|
||||
{ "GB", 1000000000 },
|
||||
{ "K", 1000 },
|
||||
{ "M", 1000000 },
|
||||
{ "G", 1000000000 },
|
||||
};
|
||||
int i;
|
||||
int isNeg = 0;
|
||||
if( zArg[0]=='-' ){
|
||||
isNeg = 1;
|
||||
zArg++;
|
||||
}else if( zArg[0]=='+' ){
|
||||
zArg++;
|
||||
}
|
||||
if( zArg[0]=='0' && zArg[1]=='x' ){
|
||||
int x;
|
||||
zArg += 2;
|
||||
while( (x = hexDigitValue(zArg[0]))>=0 ){
|
||||
v = (v<<4) + x;
|
||||
zArg++;
|
||||
}
|
||||
}else{
|
||||
while( zArg[0]>='0' && zArg[0]<='9' ){
|
||||
v = v*10 + zArg[0] - '0';
|
||||
zArg++;
|
||||
}
|
||||
}
|
||||
for(i=0; i<sizeof(aMult)/sizeof(aMult[0]); i++){
|
||||
if( sqlite3_stricmp(aMult[i].zSuffix, zArg)==0 ){
|
||||
v *= aMult[i].iMult;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return isNeg? -v : v;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Check the filesystem object zPath. Determine what it is:
|
||||
**
|
||||
@@ -190,6 +257,7 @@ static int initMain(int argc, char **argv){
|
||||
int i, rc;
|
||||
int nCount = 1000;
|
||||
int sz = 10000;
|
||||
int iVariance = 0;
|
||||
int pgsz = 4096;
|
||||
sqlite3 *db;
|
||||
char *zSql;
|
||||
@@ -204,19 +272,24 @@ static int initMain(int argc, char **argv){
|
||||
if( z[1]=='-' ) z++;
|
||||
if( strcmp(z, "-count")==0 ){
|
||||
if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
|
||||
nCount = atoi(argv[++i]);
|
||||
nCount = integerValue(argv[++i]);
|
||||
if( nCount<1 ) fatalError("the --count must be positive");
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-size")==0 ){
|
||||
if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
|
||||
sz = atoi(argv[++i]);
|
||||
sz = integerValue(argv[++i]);
|
||||
if( sz<1 ) fatalError("the --size must be positive");
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-variance")==0 ){
|
||||
if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
|
||||
iVariance = integerValue(argv[++i]);
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-pagesize")==0 ){
|
||||
if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
|
||||
pgsz = atoi(argv[++i]);
|
||||
pgsz = integerValue(argv[++i]);
|
||||
if( pgsz<512 || pgsz>65536 || ((pgsz-1)&pgsz)!=0 ){
|
||||
fatalError("the --pagesize must be power of 2 between 512 and 65536");
|
||||
}
|
||||
@@ -235,9 +308,9 @@ static int initMain(int argc, char **argv){
|
||||
"BEGIN;\n"
|
||||
"CREATE TABLE kv(k INTEGER PRIMARY KEY, v BLOB);\n"
|
||||
"WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<%d)"
|
||||
" INSERT INTO kv(k,v) SELECT x, randomblob(%d) FROM c;\n"
|
||||
" INSERT INTO kv(k,v) SELECT x, randomblob(%d+(random()%%(%d))) FROM c;\n"
|
||||
"COMMIT;\n",
|
||||
pgsz, nCount, sz
|
||||
pgsz, nCount, sz, iVariance
|
||||
);
|
||||
rc = sqlite3_exec(db, zSql, 0, 0, &zErrMsg);
|
||||
if( rc ) fatalError("database create failed: %s", zErrMsg);
|
||||
@@ -246,6 +319,65 @@ static int initMain(int argc, char **argv){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Analyze an existing database file. Report its content.
|
||||
*/
|
||||
static int statMain(int argc, char **argv){
|
||||
char *zDb;
|
||||
int i, rc;
|
||||
sqlite3 *db;
|
||||
char *zSql;
|
||||
sqlite3_stmt *pStmt;
|
||||
|
||||
assert( strcmp(argv[1],"stat")==0 );
|
||||
assert( argc>=3 );
|
||||
zDb = argv[2];
|
||||
for(i=3; i<argc; i++){
|
||||
char *z = argv[i];
|
||||
if( z[0]!='-' ) fatalError("unknown argument: \"%s\"", z);
|
||||
if( z[1]=='-' ) z++;
|
||||
fatalError("unknown option: \"%s\"", argv[i]);
|
||||
}
|
||||
rc = sqlite3_open(zDb, &db);
|
||||
if( rc ){
|
||||
fatalError("cannot open database \"%s\": %s", zDb, sqlite3_errmsg(db));
|
||||
}
|
||||
zSql = sqlite3_mprintf(
|
||||
"SELECT count(*), min(length(v)), max(length(v)), avg(length(v))"
|
||||
" FROM kv"
|
||||
);
|
||||
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
|
||||
if( rc ) fatalError("cannot prepare SQL [%s]: %s", zSql, sqlite3_errmsg(db));
|
||||
sqlite3_free(zSql);
|
||||
if( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
printf("Number of entries: %8d\n", sqlite3_column_int(pStmt, 0));
|
||||
printf("Average value size: %8d\n", sqlite3_column_int(pStmt, 3));
|
||||
printf("Minimum value size: %8d\n", sqlite3_column_int(pStmt, 1));
|
||||
printf("Maximum value size: %8d\n", sqlite3_column_int(pStmt, 2));
|
||||
}else{
|
||||
printf("No rows\n");
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
zSql = sqlite3_mprintf("PRAGMA page_size");
|
||||
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
|
||||
if( rc ) fatalError("cannot prepare SQL [%s]: %s", zSql, sqlite3_errmsg(db));
|
||||
sqlite3_free(zSql);
|
||||
if( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
printf("Page-size: %8d\n", sqlite3_column_int(pStmt, 0));
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
zSql = sqlite3_mprintf("PRAGMA page_count");
|
||||
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
|
||||
if( rc ) fatalError("cannot prepare SQL [%s]: %s", zSql, sqlite3_errmsg(db));
|
||||
sqlite3_free(zSql);
|
||||
if( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
printf("Page-count: %8d\n", sqlite3_column_int(pStmt, 0));
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
sqlite3_close(db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the "writefile(X,Y)" SQL function. The argument Y
|
||||
** is written into file X. The number of bytes written is returned. Or
|
||||
@@ -369,11 +501,125 @@ static sqlite3_int64 timeOfDay(void){
|
||||
return t;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
/*
|
||||
** Attempt to display I/O stats on Linux using /proc/PID/io
|
||||
*/
|
||||
static void displayLinuxIoStats(FILE *out){
|
||||
FILE *in;
|
||||
char z[200];
|
||||
sqlite3_snprintf(sizeof(z), z, "/proc/%d/io", getpid());
|
||||
in = fopen(z, "rb");
|
||||
if( in==0 ) return;
|
||||
while( fgets(z, sizeof(z), in)!=0 ){
|
||||
static const struct {
|
||||
const char *zPattern;
|
||||
const char *zDesc;
|
||||
} aTrans[] = {
|
||||
{ "rchar: ", "Bytes received by read():" },
|
||||
{ "wchar: ", "Bytes sent to write():" },
|
||||
{ "syscr: ", "Read() system calls:" },
|
||||
{ "syscw: ", "Write() system calls:" },
|
||||
{ "read_bytes: ", "Bytes read from storage:" },
|
||||
{ "write_bytes: ", "Bytes written to storage:" },
|
||||
{ "cancelled_write_bytes: ", "Cancelled write bytes:" },
|
||||
};
|
||||
int i;
|
||||
for(i=0; i<sizeof(aTrans)/sizeof(aTrans[0]); i++){
|
||||
int n = (int)strlen(aTrans[i].zPattern);
|
||||
if( strncmp(aTrans[i].zPattern, z, n)==0 ){
|
||||
fprintf(out, "%-36s %s", aTrans[i].zDesc, &z[n]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(in);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Display memory stats.
|
||||
*/
|
||||
static int display_stats(
|
||||
sqlite3 *db, /* Database to query */
|
||||
int bReset /* True to reset SQLite stats */
|
||||
){
|
||||
int iCur;
|
||||
int iHiwtr;
|
||||
FILE *out = stdout;
|
||||
|
||||
fprintf(out, "\n");
|
||||
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_status(SQLITE_STATUS_MEMORY_USED, &iCur, &iHiwtr, bReset);
|
||||
fprintf(out,
|
||||
"Memory Used: %d (max %d) bytes\n",
|
||||
iCur, iHiwtr);
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_status(SQLITE_STATUS_MALLOC_COUNT, &iCur, &iHiwtr, bReset);
|
||||
fprintf(out, "Number of Outstanding Allocations: %d (max %d)\n",
|
||||
iCur, iHiwtr);
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_status(SQLITE_STATUS_PAGECACHE_USED, &iCur, &iHiwtr, bReset);
|
||||
fprintf(out,
|
||||
"Number of Pcache Pages Used: %d (max %d) pages\n",
|
||||
iCur, iHiwtr);
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &iCur, &iHiwtr, bReset);
|
||||
fprintf(out,
|
||||
"Number of Pcache Overflow Bytes: %d (max %d) bytes\n",
|
||||
iCur, iHiwtr);
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_status(SQLITE_STATUS_SCRATCH_USED, &iCur, &iHiwtr, bReset);
|
||||
fprintf(out,
|
||||
"Number of Scratch Allocations Used: %d (max %d)\n",
|
||||
iCur, iHiwtr);
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_status(SQLITE_STATUS_SCRATCH_OVERFLOW, &iCur, &iHiwtr, bReset);
|
||||
fprintf(out,
|
||||
"Number of Scratch Overflow Bytes: %d (max %d) bytes\n",
|
||||
iCur, iHiwtr);
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &iCur, &iHiwtr, bReset);
|
||||
fprintf(out, "Largest Allocation: %d bytes\n",
|
||||
iHiwtr);
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_status(SQLITE_STATUS_PAGECACHE_SIZE, &iCur, &iHiwtr, bReset);
|
||||
fprintf(out, "Largest Pcache Allocation: %d bytes\n",
|
||||
iHiwtr);
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_status(SQLITE_STATUS_SCRATCH_SIZE, &iCur, &iHiwtr, bReset);
|
||||
fprintf(out, "Largest Scratch Allocation: %d bytes\n",
|
||||
iHiwtr);
|
||||
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset);
|
||||
fprintf(out, "Pager Heap Usage: %d bytes\n",
|
||||
iCur);
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1);
|
||||
fprintf(out, "Page cache hits: %d\n", iCur);
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1);
|
||||
fprintf(out, "Page cache misses: %d\n", iCur);
|
||||
iHiwtr = iCur = -1;
|
||||
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1);
|
||||
fprintf(out, "Page cache writes: %d\n", iCur);
|
||||
iHiwtr = iCur = -1;
|
||||
|
||||
#ifdef __linux__
|
||||
displayLinuxIoStats(out);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Blob access order */
|
||||
#define ORDER_ASC 1
|
||||
#define ORDER_DESC 2
|
||||
#define ORDER_RANDOM 3
|
||||
|
||||
|
||||
/*
|
||||
** Run a performance test
|
||||
*/
|
||||
@@ -385,19 +631,23 @@ static int runMain(int argc, char **argv){
|
||||
int nCount = 1000; /* Number of blob fetch operations */
|
||||
int nExtra = 0; /* Extra cycles */
|
||||
int iKey = 1; /* Next blob key */
|
||||
int iMax = 1000; /* Largest allowed key */
|
||||
int iMax = 0; /* Largest allowed key */
|
||||
int iPagesize = 0; /* Database page size */
|
||||
int iCache = 1000; /* Database cache size in kibibytes */
|
||||
int bBlobApi = 0; /* Use the incremental blob I/O API */
|
||||
int bStats = 0; /* Print stats before exiting */
|
||||
int eOrder = ORDER_ASC; /* Access order */
|
||||
sqlite3 *db = 0; /* Database connection */
|
||||
sqlite3_stmt *pStmt = 0; /* Prepared statement for SQL access */
|
||||
sqlite3_blob *pBlob = 0; /* Handle for incremental Blob I/O */
|
||||
sqlite3_int64 tmStart; /* Start time */
|
||||
sqlite3_int64 tmElapsed; /* Elapsed time */
|
||||
int mmapSize = 0; /* --mmap N argument */
|
||||
int nData = 0; /* Bytes of data */
|
||||
sqlite3_int64 nTotal = 0; /* Total data read */
|
||||
unsigned char *pData; /* Content of the blob */
|
||||
unsigned char *pData = 0; /* Content of the blob */
|
||||
int nAlloc = 0; /* Space allocated for pData[] */
|
||||
const char *zJMode = 0; /* Journal mode */
|
||||
|
||||
|
||||
assert( strcmp(argv[1],"run")==0 );
|
||||
@@ -412,25 +662,35 @@ static int runMain(int argc, char **argv){
|
||||
if( z[1]=='-' ) z++;
|
||||
if( strcmp(z, "-count")==0 ){
|
||||
if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
|
||||
nCount = atoi(argv[++i]);
|
||||
nCount = integerValue(argv[++i]);
|
||||
if( nCount<1 ) fatalError("the --count must be positive");
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-mmap")==0 ){
|
||||
if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
|
||||
mmapSize = integerValue(argv[++i]);
|
||||
if( nCount<0 ) fatalError("the --mmap must be non-negative");
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-max-id")==0 ){
|
||||
if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
|
||||
iMax = atoi(argv[++i]);
|
||||
if( iMax<1 ) fatalError("the --max-id must be positive");
|
||||
iMax = integerValue(argv[++i]);
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-start")==0 ){
|
||||
if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
|
||||
iKey = atoi(argv[++i]);
|
||||
iKey = integerValue(argv[++i]);
|
||||
if( iKey<1 ) fatalError("the --start must be positive");
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-cache-size")==0 ){
|
||||
if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
|
||||
iCache = atoi(argv[++i]);
|
||||
iCache = integerValue(argv[++i]);
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-jmode")==0 ){
|
||||
if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
|
||||
zJMode = argv[++i];
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-random")==0 ){
|
||||
@@ -449,6 +709,10 @@ static int runMain(int argc, char **argv){
|
||||
bBlobApi = 1;
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-stats")==0 ){
|
||||
bStats = 1;
|
||||
continue;
|
||||
}
|
||||
fatalError("unknown option: \"%s\"", argv[i]);
|
||||
}
|
||||
tmStart = timeOfDay();
|
||||
@@ -458,6 +722,8 @@ static int runMain(int argc, char **argv){
|
||||
if( rc ){
|
||||
fatalError("cannot open database \"%s\": %s", zDb, sqlite3_errmsg(db));
|
||||
}
|
||||
zSql = sqlite3_mprintf("PRAGMA mmap_size=%d", mmapSize);
|
||||
sqlite3_exec(db, zSql, 0, 0, 0);
|
||||
zSql = sqlite3_mprintf("PRAGMA cache_size=%d", iCache);
|
||||
sqlite3_exec(db, zSql, 0, 0, 0);
|
||||
sqlite3_free(zSql);
|
||||
@@ -475,8 +741,29 @@ static int runMain(int argc, char **argv){
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
pStmt = 0;
|
||||
if( zJMode ){
|
||||
zSql = sqlite3_mprintf("PRAGMA journal_mode=%Q", zJMode);
|
||||
sqlite3_exec(db, zSql, 0, 0, 0);
|
||||
sqlite3_free(zSql);
|
||||
}
|
||||
sqlite3_prepare_v2(db, "PRAGMA journal_mode", -1, &pStmt, 0);
|
||||
if( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
zJMode = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0));
|
||||
}else{
|
||||
zJMode = "???";
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
if( iMax<=0 ){
|
||||
sqlite3_prepare_v2(db, "SELECT max(k) FROM kv", -1, &pStmt, 0);
|
||||
if( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
iMax = sqlite3_column_int(pStmt, 0);
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
}
|
||||
pStmt = 0;
|
||||
sqlite3_exec(db, "BEGIN", 0, 0, 0);
|
||||
}
|
||||
if( iMax<=0 ) iMax = 1000;
|
||||
for(i=0; i<nCount; i++){
|
||||
if( eType==PATH_DIR ){
|
||||
/* CASE 1: Reading blobs out of separate files */
|
||||
@@ -499,14 +786,16 @@ static int runMain(int argc, char **argv){
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
nData = sqlite3_blob_bytes(pBlob);
|
||||
pData = sqlite3_malloc( nData+1 );
|
||||
if( nAlloc<nData+1 ){
|
||||
nAlloc = nData+100;
|
||||
pData = sqlite3_realloc(pData, nAlloc);
|
||||
}
|
||||
if( pData==0 ) fatalError("cannot allocate %d bytes", nData+1);
|
||||
rc = sqlite3_blob_read(pBlob, pData, nData, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
fatalError("could not read the blob at %d: %s", iKey,
|
||||
sqlite3_errmsg(db));
|
||||
}
|
||||
sqlite3_free(pData);
|
||||
}
|
||||
}else{
|
||||
/* CASE 3: Reading from database using SQL */
|
||||
@@ -540,8 +829,12 @@ static int runMain(int argc, char **argv){
|
||||
nTotal += nData;
|
||||
if( nData==0 ){ nCount++; nExtra++; }
|
||||
}
|
||||
if( nAlloc ) sqlite3_free(pData);
|
||||
if( pStmt ) sqlite3_finalize(pStmt);
|
||||
if( pBlob ) sqlite3_blob_close(pBlob);
|
||||
if( bStats ){
|
||||
display_stats(db, 0);
|
||||
}
|
||||
if( db ) sqlite3_close(db);
|
||||
tmElapsed = timeOfDay() - tmStart;
|
||||
if( nExtra ){
|
||||
@@ -551,14 +844,15 @@ static int runMain(int argc, char **argv){
|
||||
printf("SQLite version: %s\n", sqlite3_libversion());
|
||||
}
|
||||
printf("--count %d --max-id %d", nCount-nExtra, iMax);
|
||||
if( eType==PATH_DB ){
|
||||
printf(" --cache-size %d", iCache);
|
||||
}
|
||||
switch( eOrder ){
|
||||
case ORDER_RANDOM: printf(" --random\n"); break;
|
||||
case ORDER_DESC: printf(" --desc\n"); break;
|
||||
default: printf(" --asc\n"); break;
|
||||
}
|
||||
if( eType==PATH_DB ){
|
||||
printf("--cache-size %d --jmode %s\n", iCache, zJMode);
|
||||
printf("--mmap %d%s\n", mmapSize, bBlobApi ? " --blob-api" : "");
|
||||
}
|
||||
if( iPagesize ) printf("Database page size: %d\n", iPagesize);
|
||||
printf("Total elapsed time: %.3f\n", tmElapsed/1000.0);
|
||||
printf("Microseconds per BLOB read: %.3f\n", tmElapsed*1000.0/nCount);
|
||||
@@ -578,6 +872,9 @@ int main(int argc, char **argv){
|
||||
if( strcmp(argv[1],"run")==0 ){
|
||||
return runMain(argc, argv);
|
||||
}
|
||||
if( strcmp(argv[1],"stat")==0 ){
|
||||
return statMain(argc, argv);
|
||||
}
|
||||
showHelp();
|
||||
return 0;
|
||||
}
|
||||
|
||||
+10
-18
@@ -61,9 +61,6 @@ static const char zHelp[] =
|
||||
#if SQLITE_VERSION_NUMBER<3005000
|
||||
# define sqlite3_int64 sqlite_int64
|
||||
#endif
|
||||
#ifdef SQLITE_ENABLE_RBU
|
||||
# include "sqlite3rbu.h"
|
||||
#endif
|
||||
|
||||
/* All global state is held in this structure */
|
||||
static struct Global {
|
||||
@@ -1181,10 +1178,10 @@ void testset_rtree(int p1, int p2){
|
||||
unsigned mxCoord;
|
||||
unsigned x0, x1, y0, y1, z0, z1;
|
||||
unsigned iStep;
|
||||
int *aCheck = sqlite3_malloc( sizeof(int)*g.szTest*100 );
|
||||
int *aCheck = sqlite3_malloc( sizeof(int)*g.szTest*500 );
|
||||
|
||||
mxCoord = 15000;
|
||||
n = g.szTest*100;
|
||||
n = g.szTest*500;
|
||||
speedtest1_begin_test(100, "%d INSERTs into an r-tree", n);
|
||||
speedtest1_exec("BEGIN");
|
||||
speedtest1_exec("CREATE VIRTUAL TABLE rt1 USING rtree(id,x0,x1,y0,y1,z0,z1)");
|
||||
@@ -1207,11 +1204,11 @@ void testset_rtree(int p1, int p2){
|
||||
speedtest1_end_test();
|
||||
|
||||
speedtest1_begin_test(101, "Copy from rtree to a regular table");
|
||||
speedtest1_exec(" TABLE t1(id INTEGER PRIMARY KEY,x0,x1,y0,y1,z0,z1)");
|
||||
speedtest1_exec("CREATE TABLE t1(id INTEGER PRIMARY KEY,x0,x1,y0,y1,z0,z1)");
|
||||
speedtest1_exec("INSERT INTO t1 SELECT * FROM rt1");
|
||||
speedtest1_end_test();
|
||||
|
||||
n = g.szTest*20;
|
||||
n = g.szTest*100;
|
||||
speedtest1_begin_test(110, "%d one-dimensional intersect slice queries", n);
|
||||
speedtest1_prepare("SELECT count(*) FROM rt1 WHERE x0>=?1 AND x1<=?2");
|
||||
iStep = mxCoord/n;
|
||||
@@ -1224,7 +1221,7 @@ void testset_rtree(int p1, int p2){
|
||||
speedtest1_end_test();
|
||||
|
||||
if( g.bVerify ){
|
||||
n = g.szTest*20;
|
||||
n = g.szTest*100;
|
||||
speedtest1_begin_test(111, "Verify result from 1-D intersect slice queries");
|
||||
speedtest1_prepare("SELECT count(*) FROM t1 WHERE x0>=?1 AND x1<=?2");
|
||||
iStep = mxCoord/n;
|
||||
@@ -1240,7 +1237,7 @@ void testset_rtree(int p1, int p2){
|
||||
speedtest1_end_test();
|
||||
}
|
||||
|
||||
n = g.szTest*20;
|
||||
n = g.szTest*100;
|
||||
speedtest1_begin_test(120, "%d one-dimensional overlap slice queries", n);
|
||||
speedtest1_prepare("SELECT count(*) FROM rt1 WHERE y1>=?1 AND y0<=?2");
|
||||
iStep = mxCoord/n;
|
||||
@@ -1253,7 +1250,7 @@ void testset_rtree(int p1, int p2){
|
||||
speedtest1_end_test();
|
||||
|
||||
if( g.bVerify ){
|
||||
n = g.szTest*20;
|
||||
n = g.szTest*100;
|
||||
speedtest1_begin_test(121, "Verify result from 1-D overlap slice queries");
|
||||
speedtest1_prepare("SELECT count(*) FROM t1 WHERE y1>=?1 AND y0<=?2");
|
||||
iStep = mxCoord/n;
|
||||
@@ -1270,7 +1267,7 @@ void testset_rtree(int p1, int p2){
|
||||
}
|
||||
|
||||
|
||||
n = g.szTest*20;
|
||||
n = g.szTest*100;
|
||||
speedtest1_begin_test(125, "%d custom geometry callback queries", n);
|
||||
sqlite3_rtree_geometry_callback(g.db, "xslice", xsliceGeometryCallback, 0);
|
||||
speedtest1_prepare("SELECT count(*) FROM rt1 WHERE id MATCH xslice(?1,?2)");
|
||||
@@ -1286,7 +1283,7 @@ void testset_rtree(int p1, int p2){
|
||||
}
|
||||
speedtest1_end_test();
|
||||
|
||||
n = g.szTest*80;
|
||||
n = g.szTest*400;
|
||||
speedtest1_begin_test(130, "%d three-dimensional intersect box queries", n);
|
||||
speedtest1_prepare("SELECT count(*) FROM rt1 WHERE x1>=?1 AND x0<=?2"
|
||||
" AND y1>=?1 AND y0<=?2 AND z1>=?1 AND z0<=?2");
|
||||
@@ -1299,7 +1296,7 @@ void testset_rtree(int p1, int p2){
|
||||
}
|
||||
speedtest1_end_test();
|
||||
|
||||
n = g.szTest*100;
|
||||
n = g.szTest*500;
|
||||
speedtest1_begin_test(140, "%d rowid queries", n);
|
||||
speedtest1_prepare("SELECT * FROM rt1 WHERE id=?1");
|
||||
for(i=1; i<=n; i++){
|
||||
@@ -1458,11 +1455,6 @@ int main(int argc, char **argv){
|
||||
noSync = 1;
|
||||
}else if( strcmp(z,"notnull")==0 ){
|
||||
g.zNN = "NOT NULL";
|
||||
#ifdef SQLITE_ENABLE_RBU
|
||||
}else if( strcmp(z,"rbu")==0 ){
|
||||
sqlite3ota_create_vfs("rbu", 0);
|
||||
sqlite3_vfs_register(sqlite3_vfs_find("rbu"), 1);
|
||||
#endif
|
||||
}else if( strcmp(z,"pagesize")==0 ){
|
||||
if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]);
|
||||
pageSize = integerValue(argv[++i]);
|
||||
|
||||
+1
-1
@@ -461,7 +461,7 @@ if {[info exists cmdlinearg]==0} {
|
||||
}
|
||||
{^-+backtrace=.+$} {
|
||||
foreach {dummy cmdlinearg(backtrace)} [split $a =] break
|
||||
sqlite3_memdebug_backtrace $value
|
||||
sqlite3_memdebug_backtrace $cmdlinearg(backtrace)
|
||||
}
|
||||
{^-+binarylog=.+$} {
|
||||
foreach {dummy cmdlinearg(binarylog)} [split $a =] break
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
# 2017 January 9
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#***********************************************************************
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
set testprefix update2
|
||||
|
||||
db func repeat [list string repeat]
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# 1.1.* A one-pass UPDATE that does balance() operations on the IPK index
|
||||
# that it is scanning.
|
||||
#
|
||||
# 1.2.* Same again, but with a WITHOUT ROWID table.
|
||||
#
|
||||
set nrow [expr 10]
|
||||
do_execsql_test 1.1.0 {
|
||||
CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
|
||||
CREATE TABLE t2(a INTEGER PRIMARY KEY, b);
|
||||
WITH s(i) AS ( SELECT 0 UNION ALL SELECT i+1 FROM s WHERE i<$nrow )
|
||||
INSERT INTO t1(b) SELECT char((i % 26) + 65) FROM s;
|
||||
INSERT INTO t2 SELECT * FROM t1;
|
||||
}
|
||||
|
||||
do_execsql_test 1.1.1 {
|
||||
UPDATE t1 SET b = repeat(b, 100)
|
||||
}
|
||||
|
||||
do_execsql_test 1.1.2 {
|
||||
SELECT * FROM t1;
|
||||
} [db eval { SELECT a, repeat(b, 100) FROM t2 }]
|
||||
|
||||
do_execsql_test 1.2.0 {
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(a INT PRIMARY KEY, b) WITHOUT ROWID;
|
||||
WITH s(i) AS ( SELECT 0 UNION ALL SELECT i+1 FROM s WHERE i<$nrow )
|
||||
INSERT INTO t1(a, b) SELECT i+1, char((i % 26) + 65) FROM s;
|
||||
}
|
||||
|
||||
#explain_i { UPDATE t1 SET b = repeat(b, 100) }
|
||||
do_execsql_test 1.2.1 {
|
||||
UPDATE t1 SET b = repeat(b, 100)
|
||||
}
|
||||
|
||||
do_execsql_test 1.2.2 {
|
||||
SELECT * FROM t1;
|
||||
} [db eval { SELECT a, repeat(b, 100) FROM t2 }]
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# A one-pass UPDATE that does balance() operations on the IPK index
|
||||
# that it is scanning.
|
||||
#
|
||||
do_execsql_test 2.1 {
|
||||
CREATE TABLE t3(a PRIMARY KEY, b, c);
|
||||
CREATE INDEX t3i ON t3(b);
|
||||
} {}
|
||||
do_execsql_test 2.2 { UPDATE t3 SET c=1 WHERE b=? } {}
|
||||
do_execsql_test 2.3 { UPDATE t3 SET c=1 WHERE rowid=? } {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 3.0 {
|
||||
CREATE TABLE t4(a PRIMARY KEY, b, c) WITHOUT ROWID;
|
||||
CREATE INDEX t4c ON t4(c);
|
||||
INSERT INTO t4 VALUES(1, 2, 3);
|
||||
INSERT INTO t4 VALUES(2, 3, 4);
|
||||
}
|
||||
|
||||
do_execsql_test 3.1 {
|
||||
UPDATE t4 SET c=c+2 WHERE c>2;
|
||||
SELECT a, c FROM t4 ORDER BY a;
|
||||
} {1 5 2 6}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
foreach {tn sql} {
|
||||
1 {
|
||||
CREATE TABLE b1(a INTEGER PRIMARY KEY, b, c);
|
||||
CREATE TABLE c1(a INTEGER PRIMARY KEY, b, c, d)
|
||||
}
|
||||
2 {
|
||||
CREATE TABLE b1(a INT PRIMARY KEY, b, c) WITHOUT ROWID;
|
||||
CREATE TABLE c1(a INT PRIMARY KEY, b, c, d) WITHOUT ROWID;
|
||||
}
|
||||
} {
|
||||
execsql { DROP TABLE IF EXISTS b1; DROP TABLE IF EXISTS c1; }
|
||||
execsql $sql
|
||||
|
||||
do_execsql_test 4.$tn.0 {
|
||||
CREATE UNIQUE INDEX b1c ON b1(c);
|
||||
INSERT INTO b1 VALUES(1, 'a', 1);
|
||||
INSERT INTO b1 VALUES(2, 'b', 15);
|
||||
INSERT INTO b1 VALUES(3, 'c', 3);
|
||||
INSERT INTO b1 VALUES(4, 'd', 4);
|
||||
INSERT INTO b1 VALUES(5, 'e', 5);
|
||||
INSERT INTO b1 VALUES(6, 'f', 6);
|
||||
INSERT INTO b1 VALUES(7, 'g', 7);
|
||||
}
|
||||
|
||||
do_execsql_test 4.$tn.1 {
|
||||
UPDATE OR REPLACE b1 SET c=c+10 WHERE a BETWEEN 4 AND 7;
|
||||
SELECT * FROM b1 ORDER BY a;
|
||||
} {
|
||||
1 a 1
|
||||
3 c 3
|
||||
4 d 14
|
||||
5 e 15
|
||||
6 f 16
|
||||
7 g 17
|
||||
}
|
||||
|
||||
do_execsql_test 4.$tn.2 {
|
||||
CREATE INDEX c1d ON c1(d, b);
|
||||
CREATE UNIQUE INDEX c1c ON c1(c, b);
|
||||
|
||||
INSERT INTO c1 VALUES(1, 'a', 1, 1);
|
||||
INSERT INTO c1 VALUES(2, 'a', 15, 2);
|
||||
INSERT INTO c1 VALUES(3, 'a', 3, 3);
|
||||
INSERT INTO c1 VALUES(4, 'a', 4, 4);
|
||||
INSERT INTO c1 VALUES(5, 'a', 5, 5);
|
||||
INSERT INTO c1 VALUES(6, 'a', 6, 6);
|
||||
INSERT INTO c1 VALUES(7, 'a', 7, 7);
|
||||
}
|
||||
|
||||
do_execsql_test 4.$tn.3 {
|
||||
UPDATE OR REPLACE c1 SET c=c+10 WHERE d BETWEEN 4 AND 7;
|
||||
SELECT * FROM c1 ORDER BY a;
|
||||
} {
|
||||
1 a 1 1
|
||||
3 a 3 3
|
||||
4 a 14 4
|
||||
5 a 15 5
|
||||
6 a 16 6
|
||||
7 a 17 7
|
||||
}
|
||||
|
||||
do_execsql_test 4.$tn.4 { PRAGMA integrity_check } ok
|
||||
|
||||
do_execsql_test 4.$tn.5 {
|
||||
DROP INDEX c1d;
|
||||
DROP INDEX c1c;
|
||||
DELETE FROM c1;
|
||||
|
||||
INSERT INTO c1 VALUES(1, 'a', 1, 1);
|
||||
INSERT INTO c1 VALUES(2, 'a', 15, 2);
|
||||
INSERT INTO c1 VALUES(3, 'a', 3, 3);
|
||||
INSERT INTO c1 VALUES(4, 'a', 4, 4);
|
||||
INSERT INTO c1 VALUES(5, 'a', 5, 5);
|
||||
INSERT INTO c1 VALUES(6, 'a', 6, 6);
|
||||
INSERT INTO c1 VALUES(7, 'a', 7, 7);
|
||||
|
||||
CREATE INDEX c1d ON c1(d);
|
||||
CREATE UNIQUE INDEX c1c ON c1(c);
|
||||
}
|
||||
|
||||
do_execsql_test 4.$tn.6 {
|
||||
UPDATE OR REPLACE c1 SET c=c+10 WHERE d BETWEEN 4 AND 7;
|
||||
SELECT * FROM c1 ORDER BY a;
|
||||
} {
|
||||
1 a 1 1
|
||||
3 a 3 3
|
||||
4 a 14 4
|
||||
5 a 15 5
|
||||
6 a 16 6
|
||||
7 a 17 7
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
do_execsql_test 5.0 {
|
||||
CREATE TABLE x1(a INTEGER PRIMARY KEY, b, c);
|
||||
CREATE INDEX x1c ON x1(b, c);
|
||||
INSERT INTO x1 VALUES(1, 'a', 1);
|
||||
INSERT INTO x1 VALUES(2, 'a', 2);
|
||||
INSERT INTO x1 VALUES(3, 'a', 3);
|
||||
}
|
||||
|
||||
do_execsql_test 5.1.1 {
|
||||
UPDATE x1 SET c=c+1 WHERE b='a';
|
||||
}
|
||||
|
||||
do_execsql_test 5.1.2 {
|
||||
SELECT * FROM x1;
|
||||
} {1 a 2 2 a 3 3 a 4}
|
||||
|
||||
do_test 5.2 {
|
||||
catch { array unset A }
|
||||
db eval { EXPLAIN UPDATE x1 SET c=c+1 WHERE b='a' } { incr A($opcode) }
|
||||
set A(NotExists)
|
||||
} {1}
|
||||
|
||||
|
||||
finish_test
|
||||
|
||||
+31
-16
@@ -121,29 +121,43 @@ if {$tcl_platform(platform)!="windows" || \
|
||||
SELECT name FROM fsdir WHERE dir = '.' AND name = '.'
|
||||
} {test.db .}
|
||||
|
||||
proc sort_files { names {nocase false} } {
|
||||
if {$nocase && $::tcl_platform(platform) eq "windows"} {
|
||||
return [lsort -nocase $names]
|
||||
} else {
|
||||
return [lsort $names]
|
||||
}
|
||||
}
|
||||
|
||||
proc list_root_files {} {
|
||||
if {$::tcl_platform(platform) eq "windows"} {
|
||||
set res [list]
|
||||
foreach name [glob -directory $::env(fstreeDrive)/ -- *] {
|
||||
set res [list]; set dir $::env(fstreeDrive)/; set names [list]
|
||||
eval lappend names [glob -nocomplain -directory $dir -- *]
|
||||
foreach name $names {
|
||||
if {[string index [file tail $name] 0] eq "."} continue
|
||||
if {[file attributes $name -hidden]} continue
|
||||
if {[file attributes $name -system]} continue
|
||||
lappend res $name
|
||||
}
|
||||
return $res
|
||||
return [sort_files $res true]
|
||||
} else {
|
||||
return [string map {/ {}} [glob /*]]
|
||||
return [sort_files [string map {/ {}} [glob -nocomplain -- /*]]]
|
||||
}
|
||||
}
|
||||
|
||||
proc list_files { pattern } {
|
||||
if {$::tcl_platform(platform) eq "windows"} {
|
||||
set res [list]
|
||||
foreach name [glob -nocomplain $pattern] {
|
||||
set res [list]; set names [list]
|
||||
eval lappend names [glob -nocomplain -- $pattern]
|
||||
foreach name $names {
|
||||
if {[string index [file tail $name] 0] eq "."} continue
|
||||
if {[file attributes $name -hidden]} continue
|
||||
if {[file attributes $name -system]} continue
|
||||
lappend res $name
|
||||
}
|
||||
return $res
|
||||
return [sort_files $res]
|
||||
} else {
|
||||
return [glob -nocomplain $pattern]
|
||||
return [sort_files [glob -nocomplain -- $pattern]]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,18 +167,19 @@ if {$tcl_platform(platform)!="windows" || \
|
||||
#
|
||||
set res [list]
|
||||
set root_files [list_root_files]
|
||||
set num_root_files [llength $root_files]
|
||||
set lim_root_files [expr {$num_root_files > 5 ? 5 : $num_root_files}]
|
||||
foreach p [lrange $root_files 0 [expr {$lim_root_files - 1}]] {
|
||||
foreach p $root_files {
|
||||
if {$::tcl_platform(platform) eq "windows"} {
|
||||
if {[regexp {\$} $p]} {incr lim_root_files -1} else {lappend res $p}
|
||||
if {![regexp {\$} $p]} {lappend res $p}
|
||||
} else {
|
||||
lappend res "/$p"
|
||||
}
|
||||
}
|
||||
do_execsql_test 3.1 [subst {
|
||||
SELECT path FROM fstree WHERE path NOT GLOB '*\$*' LIMIT $lim_root_files;
|
||||
}] $res
|
||||
set num_root_files [llength $root_files]
|
||||
do_test 3.1 {
|
||||
sort_files [execsql {
|
||||
SELECT path FROM fstree WHERE path NOT GLOB '*\$*' LIMIT $num_root_files;
|
||||
}] true
|
||||
} [sort_files $res true]
|
||||
|
||||
# Read all entries in the current directory.
|
||||
#
|
||||
@@ -182,7 +197,7 @@ if {$tcl_platform(platform)!="windows" || \
|
||||
set res [contents $pwd]
|
||||
do_execsql_test 3.2 {
|
||||
SELECT path FROM fstree WHERE path GLOB $pwd ORDER BY 1
|
||||
} [lsort $res]
|
||||
} [sort_files $res]
|
||||
|
||||
# Add some sub-directories and files to the current directory.
|
||||
#
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# A script for running speed tests using kvtest.
|
||||
#
|
||||
# The test database must be set up first. Recommended
|
||||
# command-line:
|
||||
#
|
||||
# ./kvtest init kvtest.db --count 100K --size 12K --variance 5K
|
||||
|
||||
if test "$1" = ""
|
||||
then
|
||||
echo "Usage: $0 OUTPUTFILE [OPTIONS]"
|
||||
exit
|
||||
fi
|
||||
NAME=$1
|
||||
shift
|
||||
OPTS="-DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_DIRECT_OVERFLOW_READ -DUSE_PREAD"
|
||||
KVARGS="--count 100K --stats"
|
||||
gcc -g -Os -I. $OPTS $* kvtest.c sqlite3.c -o kvtest
|
||||
|
||||
# First run using SQL
|
||||
rm cachegrind.out.[1-9][0-9]*
|
||||
valgrind --tool=cachegrind ./kvtest run kvtest.db $KVARGS 2>&1 | tee summary-kvtest-$NAME.txt
|
||||
mv cachegrind.out.[1-9][0-9]* cachegrind.out.sql-$NAME
|
||||
cg_anno.tcl cachegrind.out.sql-$NAME >cout-kvtest-sql-$NAME.txt
|
||||
|
||||
# Second run using the sqlite3_blob object
|
||||
valgrind --tool=cachegrind ./kvtest run kvtest.db $KVARGS --blob-api 2>&1 | tee -a summary-kvtest-$NAME.txt
|
||||
mv cachegrind.out.[1-9][0-9]* cachegrind.out.$NAME
|
||||
cg_anno.tcl cachegrind.out.$NAME >cout-kvtest-$NAME.txt
|
||||
|
||||
# Diff the sqlite3_blob API analysis for non-trunk runs.
|
||||
if test "$NAME" != "trunk"; then
|
||||
fossil test-diff --tk cout-kvtest-trunk.txt cout-kvtest-$NAME.txt &
|
||||
fi
|
||||
+44
-27
@@ -316,6 +316,31 @@ static int yyGrowStack(yyParser *p){
|
||||
# define YYMALLOCARGTYPE size_t
|
||||
#endif
|
||||
|
||||
/* Initialize a new parser that has already been allocated.
|
||||
*/
|
||||
void ParseInit(void *yypParser){
|
||||
yyParser *pParser = (yyParser*)yypParser;
|
||||
#ifdef YYTRACKMAXSTACKDEPTH
|
||||
pParser->yyhwm = 0;
|
||||
#endif
|
||||
#if YYSTACKDEPTH<=0
|
||||
pParser->yytos = NULL;
|
||||
pParser->yystack = NULL;
|
||||
pParser->yystksz = 0;
|
||||
if( yyGrowStack(pParser) ){
|
||||
pParser->yystack = &pParser->yystk0;
|
||||
pParser->yystksz = 1;
|
||||
}
|
||||
#endif
|
||||
#ifndef YYNOERRORRECOVERY
|
||||
pParser->yyerrcnt = -1;
|
||||
#endif
|
||||
pParser->yytos = pParser->yystack;
|
||||
pParser->yystack[0].stateno = 0;
|
||||
pParser->yystack[0].major = 0;
|
||||
}
|
||||
|
||||
#ifndef Parse_ENGINEALWAYSONSTACK
|
||||
/*
|
||||
** This function allocates a new parser.
|
||||
** The only argument is a pointer to a function which works like
|
||||
@@ -331,28 +356,11 @@ static int yyGrowStack(yyParser *p){
|
||||
void *ParseAlloc(void *(*mallocProc)(YYMALLOCARGTYPE)){
|
||||
yyParser *pParser;
|
||||
pParser = (yyParser*)(*mallocProc)( (YYMALLOCARGTYPE)sizeof(yyParser) );
|
||||
if( pParser ){
|
||||
#ifdef YYTRACKMAXSTACKDEPTH
|
||||
pParser->yyhwm = 0;
|
||||
#endif
|
||||
#if YYSTACKDEPTH<=0
|
||||
pParser->yytos = NULL;
|
||||
pParser->yystack = NULL;
|
||||
pParser->yystksz = 0;
|
||||
if( yyGrowStack(pParser) ){
|
||||
pParser->yystack = &pParser->yystk0;
|
||||
pParser->yystksz = 1;
|
||||
}
|
||||
#endif
|
||||
#ifndef YYNOERRORRECOVERY
|
||||
pParser->yyerrcnt = -1;
|
||||
#endif
|
||||
pParser->yytos = pParser->yystack;
|
||||
pParser->yystack[0].stateno = 0;
|
||||
pParser->yystack[0].major = 0;
|
||||
}
|
||||
if( pParser ) ParseInit(pParser);
|
||||
return pParser;
|
||||
}
|
||||
#endif /* Parse_ENGINEALWAYSONSTACK */
|
||||
|
||||
|
||||
/* The following function deletes the "minor type" or semantic value
|
||||
** associated with a symbol. The symbol can be either a terminal
|
||||
@@ -406,6 +414,18 @@ static void yy_pop_parser_stack(yyParser *pParser){
|
||||
yy_destructor(pParser, yytos->major, &yytos->minor);
|
||||
}
|
||||
|
||||
/*
|
||||
** Clear all secondary memory allocations from the parser
|
||||
*/
|
||||
void ParseFinalize(void *p){
|
||||
yyParser *pParser = (yyParser*)p;
|
||||
while( pParser->yytos>pParser->yystack ) yy_pop_parser_stack(pParser);
|
||||
#if YYSTACKDEPTH<=0
|
||||
if( pParser->yystack!=&pParser->yystk0 ) free(pParser->yystack);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef Parse_ENGINEALWAYSONSTACK
|
||||
/*
|
||||
** Deallocate and destroy a parser. Destructors are called for
|
||||
** all stack elements before shutting the parser down.
|
||||
@@ -418,16 +438,13 @@ void ParseFree(
|
||||
void *p, /* The parser to be deleted */
|
||||
void (*freeProc)(void*) /* Function used to reclaim memory */
|
||||
){
|
||||
yyParser *pParser = (yyParser*)p;
|
||||
#ifndef YYPARSEFREENEVERNULL
|
||||
if( pParser==0 ) return;
|
||||
if( p==0 ) return;
|
||||
#endif
|
||||
while( pParser->yytos>pParser->yystack ) yy_pop_parser_stack(pParser);
|
||||
#if YYSTACKDEPTH<=0
|
||||
if( pParser->yystack!=&pParser->yystk0 ) free(pParser->yystack);
|
||||
#endif
|
||||
(*freeProc)((void*)pParser);
|
||||
ParseFinalize(p);
|
||||
(*freeProc)(p);
|
||||
}
|
||||
#endif /* Parse_ENGINEALWAYSONSTACK */
|
||||
|
||||
/*
|
||||
** Return the peak depth of the stack for a parser.
|
||||
|
||||
+19
-3
@@ -23,6 +23,7 @@ NAME=$1
|
||||
shift
|
||||
#CC_OPTS="-DSQLITE_ENABLE_RTREE -DSQLITE_ENABLE_MEMSYS5"
|
||||
CC_OPTS="-DSQLITE_ENABLE_MEMSYS5"
|
||||
CC=gcc
|
||||
SPEEDTEST_OPTS="--shrink-memory --reprepare --stats --heap 10000000 64"
|
||||
SIZE=5
|
||||
LEAN_OPTS="-DSQLITE_THREADSAFE=0"
|
||||
@@ -80,11 +81,19 @@ while test "$1" != ""; do
|
||||
--lean)
|
||||
CC_OPTS="$CC_OPTS $LEAN_OPTS"
|
||||
;;
|
||||
--clang)
|
||||
CC=clang
|
||||
;;
|
||||
--heap)
|
||||
CC_OPTS="$CC_OPTS -DSQLITE_ENABLE_MEMSYS5"
|
||||
shift;
|
||||
SPEEDTEST_OPTS="$SPEEDTEST_OPTS --heap $1 64"
|
||||
;;
|
||||
--lookaside)
|
||||
shift;
|
||||
SPEEDTEST_OPTS="$SPEEDTEST_OPTS --lookaside $1 $2"
|
||||
shift;
|
||||
;;
|
||||
--repeat)
|
||||
CC_OPTS="$CC_OPTS -DSQLITE_ENABLE_RCACHE"
|
||||
shift;
|
||||
@@ -94,6 +103,10 @@ while test "$1" != ""; do
|
||||
shift;
|
||||
SPEEDTEST_OPTS="$SPEEDTEST_OPTS --mmap $1"
|
||||
;;
|
||||
--rtree)
|
||||
SPEEDTEST_OPTS="$SPEEDTEST_OPTS --testset rtree"
|
||||
CC_OPTS="$CC_OPTS -DSQLITE_ENABLE_RTREE"
|
||||
;;
|
||||
*)
|
||||
CC_OPTS="$CC_OPTS $1"
|
||||
;;
|
||||
@@ -105,15 +118,15 @@ echo "NAME = $NAME" | tee summary-$NAME.txt
|
||||
echo "SPEEDTEST_OPTS = $SPEEDTEST_OPTS" | tee -a summary-$NAME.txt
|
||||
echo "CC_OPTS = $CC_OPTS" | tee -a summary-$NAME.txt
|
||||
rm -f cachegrind.out.* speedtest1 speedtest1.db sqlite3.o
|
||||
gcc -g -Os -Wall -I. $CC_OPTS -c sqlite3.c
|
||||
$CC -g -Os -Wall -I. $CC_OPTS -c sqlite3.c
|
||||
size sqlite3.o | tee -a summary-$NAME.txt
|
||||
if test $doExplain -eq 1; then
|
||||
gcc -g -Os -Wall -I. $CC_OPTS \
|
||||
$CC -g -Os -Wall -I. $CC_OPTS \
|
||||
-DSQLITE_ENABLE_EXPLAIN_COMMENTS \
|
||||
./shell.c ./sqlite3.c -o sqlite3 -ldl -lpthread
|
||||
fi
|
||||
SRC=./speedtest1.c
|
||||
gcc -g -Os -Wall -I. $CC_OPTS $SRC ./sqlite3.o -o speedtest1 -ldl -lpthread
|
||||
$CC -g -Os -Wall -I. $CC_OPTS $SRC ./sqlite3.o -o speedtest1 -ldl -lpthread
|
||||
ls -l speedtest1 | tee -a summary-$NAME.txt
|
||||
if test $doCachegrind -eq 1; then
|
||||
valgrind --tool=cachegrind ./speedtest1 speedtest1.db \
|
||||
@@ -129,3 +142,6 @@ fi
|
||||
if test $doExplain -eq 1; then
|
||||
./speedtest1 --explain $SPEEDTEST_OPTS | ./sqlite3 >explain-$NAME.txt
|
||||
fi
|
||||
if test "$NAME" != "trunk"; then
|
||||
fossil test-diff --tk cout-trunk.txt cout-$NAME.txt
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user